@mherod/get-cookie 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/get-cookie.iml +12 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/index.js +342 -0
- package/package.json +22 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$">
|
|
5
|
+
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
6
|
+
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
7
|
+
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
+
</content>
|
|
9
|
+
<orderEntry type="inheritedJdk" />
|
|
10
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
+
</component>
|
|
12
|
+
</module>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/get-cookie.iml" filepath="$PROJECT_DIR$/.idea/get-cookie.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
package/.idea/vcs.xml
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const {exec} = require("child_process");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
|
|
5
|
+
if (process.platform !== 'darwin') {
|
|
6
|
+
throw new Error('This script only works on macOS');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @returns {Promise<string>}
|
|
12
|
+
*/
|
|
13
|
+
async function getChromePassword() {
|
|
14
|
+
return await new Promise((resolve, reject) => {
|
|
15
|
+
exec("security find-generic-password -w -s \"Chrome Safe Storage\"", (error, stdout, stderr) => {
|
|
16
|
+
if (error) {
|
|
17
|
+
reject(error);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (stderr) {
|
|
21
|
+
reject(error);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let s = stdout.toString().trim();
|
|
25
|
+
resolve(s);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param name
|
|
33
|
+
* @param domain
|
|
34
|
+
* @returns {Promise<Buffer>}
|
|
35
|
+
*/
|
|
36
|
+
async function getFirefoxCookie({name, domain}) {
|
|
37
|
+
if (name && typeof name !== 'string') {
|
|
38
|
+
throw new Error('name must be a string');
|
|
39
|
+
}
|
|
40
|
+
if (domain && typeof domain !== 'string') {
|
|
41
|
+
throw new Error('domain must be a string');
|
|
42
|
+
}
|
|
43
|
+
const file = await findFile(`${process.env.HOME}/Library/Application Support/Firefox/Profiles/`, "cookies.sqlite")
|
|
44
|
+
if (!fs.existsSync(file)) {
|
|
45
|
+
throw new Error(`File ${file} does not exist`);
|
|
46
|
+
}
|
|
47
|
+
if (process.env.VERBOSE) {
|
|
48
|
+
console.log(`Trying Firefox cookie ${name} for domain ${domain}`);
|
|
49
|
+
}
|
|
50
|
+
return await new Promise((resolve, reject) => {
|
|
51
|
+
let sql;
|
|
52
|
+
sql = `SELECT value FROM moz_cookies`;
|
|
53
|
+
if (typeof name === 'string' || typeof domain === 'string') {
|
|
54
|
+
sql += ` WHERE `;
|
|
55
|
+
if (typeof name === 'string') {
|
|
56
|
+
sql += `name = '${name}'`;
|
|
57
|
+
if (typeof domain === 'string') {
|
|
58
|
+
sql += ` AND `;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (typeof domain === 'string') {
|
|
62
|
+
sql += `host LIKE '${domain}';`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const command = `sqlite3 "${file}" "${sql}"`;
|
|
66
|
+
if (process.env.VERBOSE) {
|
|
67
|
+
console.log(command);
|
|
68
|
+
}
|
|
69
|
+
exec(command, {encoding: 'binary', maxBuffer: 1024}, (error, stdout, stderr) => {
|
|
70
|
+
if (error) {
|
|
71
|
+
reject(error);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (stderr) {
|
|
75
|
+
reject(error);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
let stdoutAsBuffer = stdout;
|
|
79
|
+
if (typeof stdoutAsBuffer === 'string' && stdoutAsBuffer.length > 0) {
|
|
80
|
+
// noinspection JSCheckFunctionSignatures
|
|
81
|
+
stdoutAsBuffer = Buffer.from(stdoutAsBuffer, 'binary').slice(0, -1);
|
|
82
|
+
}
|
|
83
|
+
if (stdoutAsBuffer && stdoutAsBuffer.length > 0) {
|
|
84
|
+
resolve(stdoutAsBuffer);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function getEncryptedChromeCookie(name, domain) {
|
|
91
|
+
if (name && typeof name !== 'string') {
|
|
92
|
+
throw new Error('name must be a string');
|
|
93
|
+
}
|
|
94
|
+
if (domain && typeof domain !== 'string') {
|
|
95
|
+
throw new Error('domain must be a string');
|
|
96
|
+
}
|
|
97
|
+
return await new Promise((resolve, reject) => {
|
|
98
|
+
const file = `${process.env.HOME}/Library/Application Support/Google/Chrome/Default/Cookies`;
|
|
99
|
+
if (!fs.existsSync(file)) {
|
|
100
|
+
reject(new Error(`File ${file} does not exist`));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (process.env.VERBOSE) {
|
|
104
|
+
console.log(`Trying Chrome cookie ${name} for domain ${domain}`);
|
|
105
|
+
}
|
|
106
|
+
let sql;
|
|
107
|
+
sql = `SELECT encrypted_value FROM cookies`;
|
|
108
|
+
if (typeof name === 'string' || typeof domain === 'string') {
|
|
109
|
+
sql += ` WHERE `;
|
|
110
|
+
if (typeof name === 'string') {
|
|
111
|
+
sql += `name = '${name}'`;
|
|
112
|
+
if (typeof domain === 'string') {
|
|
113
|
+
sql += ` AND `;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (typeof domain === 'string') {
|
|
117
|
+
sql += `host_key LIKE '${domain}';`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const command = `sqlite3 "${file}" "${sql}"`;
|
|
121
|
+
if (process.env.VERBOSE) {
|
|
122
|
+
console.log(command);
|
|
123
|
+
}
|
|
124
|
+
exec(command, {encoding: 'binary', maxBuffer: 1024}, (error, stdout, stderr) => {
|
|
125
|
+
if (error) {
|
|
126
|
+
reject(error);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (stderr) {
|
|
130
|
+
reject(error);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
let stdoutAsBuffer = stdout;
|
|
134
|
+
if (typeof stdoutAsBuffer === 'string' && stdoutAsBuffer.length > 0) {
|
|
135
|
+
// noinspection JSCheckFunctionSignatures
|
|
136
|
+
stdoutAsBuffer = Buffer.from(stdoutAsBuffer, 'binary').slice(0, -1);
|
|
137
|
+
}
|
|
138
|
+
if (stdoutAsBuffer && stdoutAsBuffer.length > 0) {
|
|
139
|
+
resolve(stdoutAsBuffer);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
*
|
|
147
|
+
* @param {string} password
|
|
148
|
+
* @param encryptedData
|
|
149
|
+
* @returns {Promise<string>}
|
|
150
|
+
*/
|
|
151
|
+
async function decrypt(password, encryptedData) {
|
|
152
|
+
if (typeof password !== 'string') {
|
|
153
|
+
throw new Error('password must be a string');
|
|
154
|
+
}
|
|
155
|
+
if (typeof encryptedData !== 'object') {
|
|
156
|
+
throw new Error('encryptedData must be a object');
|
|
157
|
+
}
|
|
158
|
+
if (process.env.VERBOSE) {
|
|
159
|
+
console.log(`Trying to decrypt with password ${password}`);
|
|
160
|
+
}
|
|
161
|
+
return await new Promise((resolve, reject) => {
|
|
162
|
+
crypto.pbkdf2(password, 'saltysalt', 1003, 16, 'sha1', (error, buffer) => {
|
|
163
|
+
if (error) {
|
|
164
|
+
if (process.env.VERBOSE) {
|
|
165
|
+
console.log("Error doing pbkdf2", error);
|
|
166
|
+
}
|
|
167
|
+
reject(error);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (buffer.length !== 16) {
|
|
171
|
+
if (process.env.VERBOSE) {
|
|
172
|
+
console.log("Error doing pbkdf2, buffer length is not 16", buffer.length);
|
|
173
|
+
}
|
|
174
|
+
reject(new Error('Buffer length is not 16'));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const iv = new Buffer.from(new Array(17).join(' '), 'binary');
|
|
179
|
+
const decipher = crypto.createDecipheriv('aes-128-cbc', buffer, iv);
|
|
180
|
+
decipher.setAutoPadding(false);
|
|
181
|
+
encryptedData = encryptedData.slice(3);
|
|
182
|
+
|
|
183
|
+
let decoded = decipher.update(encryptedData, 'binary', 'utf8');
|
|
184
|
+
// let decoded = decipher.update(encryptedData);
|
|
185
|
+
try {
|
|
186
|
+
decipher.final('utf-8');
|
|
187
|
+
} catch (e) {
|
|
188
|
+
if (process.env.VERBOSE) {
|
|
189
|
+
console.log("Error doing decipher.final()", e);
|
|
190
|
+
}
|
|
191
|
+
reject(e);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let padding = decoded[decoded.length - 1];
|
|
196
|
+
if (padding) {
|
|
197
|
+
decoded = decoded.slice(0, decoded.length - padding);
|
|
198
|
+
}
|
|
199
|
+
// noinspection JSCheckFunctionSignatures
|
|
200
|
+
decoded = decoded.toString('utf8');
|
|
201
|
+
resolve(decoded)
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
* @param {string|undefined} name
|
|
209
|
+
* @param {string|undefined} domain
|
|
210
|
+
* @returns {Promise<string>}
|
|
211
|
+
*/
|
|
212
|
+
async function getChromeCookie({name, domain}) {
|
|
213
|
+
if (name && typeof name !== 'string') {
|
|
214
|
+
throw new Error('name must be a string');
|
|
215
|
+
}
|
|
216
|
+
if (domain && typeof domain !== 'string') {
|
|
217
|
+
throw new Error('domain must be a string');
|
|
218
|
+
}
|
|
219
|
+
const password = await getChromePassword();
|
|
220
|
+
const encryptedData = await getEncryptedChromeCookie(name, domain);
|
|
221
|
+
if (process.env.VERBOSE) {
|
|
222
|
+
console.log("Received encrypted", encryptedData);
|
|
223
|
+
}
|
|
224
|
+
let s;
|
|
225
|
+
try {
|
|
226
|
+
s = await decrypt(password, encryptedData);
|
|
227
|
+
} catch (e) {
|
|
228
|
+
console.error(e);
|
|
229
|
+
throw new Error('Failed to decrypt');
|
|
230
|
+
}
|
|
231
|
+
if (process.env.VERBOSE) {
|
|
232
|
+
console.log("Decrypted", s);
|
|
233
|
+
}
|
|
234
|
+
return s;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
*
|
|
239
|
+
* @param path
|
|
240
|
+
* @param name
|
|
241
|
+
* @returns {Promise<string>}
|
|
242
|
+
*/
|
|
243
|
+
async function findFile(path, name) {
|
|
244
|
+
return await new Promise((resolve, reject) => {
|
|
245
|
+
for (const file of fs.readdirSync(path)) {
|
|
246
|
+
const filePath = path + '/' + file;
|
|
247
|
+
const stat = fs.statSync(filePath);
|
|
248
|
+
if (stat.isDirectory()) {
|
|
249
|
+
findFile(filePath, name).then(resolve).catch(reject);
|
|
250
|
+
} else if (file === name) {
|
|
251
|
+
resolve(filePath);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function printStringValue(r) {
|
|
258
|
+
if (process.env.VERBOSE) {
|
|
259
|
+
console.log("Printing value", r);
|
|
260
|
+
}
|
|
261
|
+
if (typeof r === 'string') {
|
|
262
|
+
console.log(r);
|
|
263
|
+
} else {
|
|
264
|
+
// noinspection JSCheckFunctionSignatures
|
|
265
|
+
console.log(r.toString('utf8'));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// noinspection JSUnusedGlobalSymbols
|
|
270
|
+
module.exports = {
|
|
271
|
+
getDecryptedCookie: getChromeCookie,
|
|
272
|
+
getChromeCookie
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
if (process.argv) {
|
|
276
|
+
if (process.argv.length > 2) {
|
|
277
|
+
const name = process.argv[2];
|
|
278
|
+
const domain = process.argv[3];
|
|
279
|
+
|
|
280
|
+
if (process.argv.includes('--verbose')) {
|
|
281
|
+
process.env.VERBOSE = "true";
|
|
282
|
+
}
|
|
283
|
+
if (process.argv.includes("--chrome-only")) {
|
|
284
|
+
process.env.CHROME_ONLY = "true";
|
|
285
|
+
}
|
|
286
|
+
if (process.argv.includes("--firefox-only")) {
|
|
287
|
+
process.env.FIREFOX_ONLY = "true";
|
|
288
|
+
}
|
|
289
|
+
if (process.argv.includes("--ignore-expired")) {
|
|
290
|
+
process.env.IGNORE_EXPIRED = "true";
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (process.env.CHROME_ONLY) {
|
|
294
|
+
if (process.env.VERBOSE) {
|
|
295
|
+
console.log('chrome only');
|
|
296
|
+
}
|
|
297
|
+
getChromeCookie({name, domain})
|
|
298
|
+
.then(printStringValue)
|
|
299
|
+
.catch(err => {
|
|
300
|
+
if (process.env.VERBOSE) {
|
|
301
|
+
console.error(err);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
} else if (process.env.FIREFOX_ONLY) {
|
|
305
|
+
if (process.env.VERBOSE) {
|
|
306
|
+
console.log('firefox only');
|
|
307
|
+
}
|
|
308
|
+
getFirefoxCookie({name, domain})
|
|
309
|
+
.then(printStringValue)
|
|
310
|
+
.catch(err => {
|
|
311
|
+
if (process.env.VERBOSE) {
|
|
312
|
+
console.error("Error getting Firefox cookie", err);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
} else {
|
|
316
|
+
getChromeCookie({name, domain})
|
|
317
|
+
.catch(err => {
|
|
318
|
+
if (process.env.VERBOSE) {
|
|
319
|
+
console.error("Error getting Chrome cookie", err);
|
|
320
|
+
}
|
|
321
|
+
})
|
|
322
|
+
.then(r => {
|
|
323
|
+
if (typeof r === 'string' && r.trim().length > 0) {
|
|
324
|
+
return r;
|
|
325
|
+
} else {
|
|
326
|
+
return getFirefoxCookie({name, domain})
|
|
327
|
+
.catch(err => {
|
|
328
|
+
if (process.env.VERBOSE) {
|
|
329
|
+
console.error("Error getting Firefox cookie", err);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
.catch((e) => {
|
|
335
|
+
if (process.env.VERBOSE) {
|
|
336
|
+
console.error("Error getting Chrome or Firefox cookie", e);
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
.then(printStringValue);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mherod/get-cookie",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": "index.js",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
+
"install": "pkg . --target node17-macos-x64 --out-path $HOMEBREW_FORMULA_PREFIX/bin/ --debug",
|
|
13
|
+
"install2": "pkg . --target host --out-path $HOMEBREW_FORMULA_PREFIX/bin/ --debug",
|
|
14
|
+
"install3": "pkg . --target node17-macos-x64 --out-path /usr/local/bin/ --debug",
|
|
15
|
+
"install4": "pkg . --target host --out-path /usr/local/bin/ --debug"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
}
|
|
22
|
+
}
|