-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathencrypt-url.js
More file actions
executable file
·162 lines (133 loc) · 4.63 KB
/
encrypt-url.js
File metadata and controls
executable file
·162 lines (133 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env node
import crypto from 'crypto';
import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
function encryptUrl(url) {
try {
const secret = process.env.APP_SECRET;
if (!secret) {
throw new Error('APP_SECRET not configured in .env file');
}
// Generate random IV (16 bytes for AES-128-CTR)
const iv = crypto.randomBytes(16);
// Create cipher with CTR mode
const cipher = crypto.createCipheriv('aes-128-ctr', Buffer.from(secret.slice(0, 16), 'utf8'), iv);
// Encrypt the URL
let encrypted = cipher.update(url, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
// Combine IV and encrypted data
const combined = Buffer.concat([iv, encrypted]);
// Base64 encode with URL-safe characters and remove padding
return combined.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
} catch (error) {
console.error('Encryption failed:', error.message);
process.exit(1);
}
}
function decryptUrl(encryptedHash) {
try {
const secret = process.env.APP_SECRET;
if (!secret) {
throw new Error('APP_SECRET not configured in .env file');
}
// Restore URL-safe base64 to standard base64 and add padding if needed
let base64 = encryptedHash
.replace(/-/g, '+')
.replace(/_/g, '/');
// Add padding if needed
while (base64.length % 4) {
base64 += '=';
}
// Base64 decode the hash
const encryptedBuffer = Buffer.from(base64, 'base64');
// Extract IV (16 bytes) and encrypted data
const iv = encryptedBuffer.subarray(0, 16);
const encrypted = encryptedBuffer.subarray(16);
// Create decipher with CTR mode
const decipher = crypto.createDecipheriv('aes-128-ctr', Buffer.from(secret.slice(0, 16), 'utf8'), iv);
// Decrypt
let decrypted = decipher.update(encrypted, null, 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (error) {
console.error('Decryption failed:', error.message);
process.exit(1);
}
}
function showUsage() {
console.log(`
Usage: node encrypt-url.js [command] [url]
Commands:
encrypt <url> Encrypt a URL and return the hash
decrypt <hash> Decrypt a hash and return the original URL
test <url> Encrypt then decrypt to test round-trip
Examples:
node encrypt-url.js encrypt "https://example.com/report.json"
node encrypt-url.js decrypt "base64-encoded-hash"
node encrypt-url.js test "https://example.com/report.json"
`);
}
// Parse command line arguments
const args = process.argv.slice(2);
if (args.length === 0) {
showUsage();
process.exit(1);
}
const command = args[0];
const input = args[1];
switch (command) {
case 'encrypt':
if (!input) {
console.error('Error: URL required for encrypt command');
showUsage();
process.exit(1);
}
const hash = encryptUrl(input);
console.log('\n✅ Encryption successful!');
console.log(`Original URL: ${input}`);
console.log(`Encrypted hash: ${hash}`);
console.log(`Hash length: ${hash.length} characters`);
console.log(`\nTest URL: http://localhost:3000/report/${hash}`);
break;
case 'decrypt':
if (!input) {
console.error('Error: Hash required for decrypt command');
showUsage();
process.exit(1);
}
const url = decryptUrl(input);
console.log('\n✅ Decryption successful!');
console.log(`Encrypted hash: ${input}`);
console.log(`Original URL: ${url}`);
break;
case 'test':
if (!input) {
console.error('Error: URL required for test command');
showUsage();
process.exit(1);
}
console.log('\n🔄 Testing round-trip encryption...');
const testHash = encryptUrl(input);
const testUrl = decryptUrl(testHash);
console.log(`Original URL: ${input} (${input.length} chars)`);
console.log(`Encrypted hash: ${testHash} (${testHash.length} chars)`);
console.log(`Decrypted URL: ${testUrl}`);
const reduction = Math.round((1 - testHash.length / input.length) * 100);
console.log(`\n📊 Hash is ${reduction > 0 ? `${Math.abs(reduction)}% shorter` : `${Math.abs(reduction)}% longer`} than original URL`);
if (input === testUrl) {
console.log('\n✅ Round-trip test PASSED! Encryption/decryption working correctly.');
console.log(`\nTest URL: http://localhost:3000/report/${testHash}`);
} else {
console.log('\n❌ Round-trip test FAILED! URLs do not match.');
process.exit(1);
}
break;
default:
console.error(`Error: Unknown command '${command}'`);
showUsage();
process.exit(1);
}