@mherod/get-cookie 1.0.6 → 1.1.2
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/index.js +122 -101
- package/package.json +3 -4
package/index.js
CHANGED
|
@@ -53,44 +53,21 @@ async function getFirefoxCookie({name, domain}) {
|
|
|
53
53
|
if (process.env.VERBOSE) {
|
|
54
54
|
console.log(`Trying Firefox cookie ${name} for domain ${domain}`);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
sql += `name = '${name}'`;
|
|
63
|
-
if (typeof domain === 'string') {
|
|
64
|
-
sql += ` AND `;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
56
|
+
let sql;
|
|
57
|
+
sql = "SELECT value FROM moz_cookies";
|
|
58
|
+
if (typeof name === 'string' || typeof domain === 'string') {
|
|
59
|
+
sql += ` WHERE `;
|
|
60
|
+
if (typeof name === 'string') {
|
|
61
|
+
sql += `name = '${name}'`;
|
|
67
62
|
if (typeof domain === 'string') {
|
|
68
|
-
sql += `
|
|
63
|
+
sql += ` AND `;
|
|
69
64
|
}
|
|
70
65
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log(command);
|
|
66
|
+
if (typeof domain === 'string') {
|
|
67
|
+
sql += `host LIKE '${domain}';`;
|
|
74
68
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
reject(error);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
if (stderr) {
|
|
81
|
-
reject(error);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
let stdoutAsBuffer = stdout;
|
|
85
|
-
if (typeof stdoutAsBuffer === 'string' && stdoutAsBuffer.length > 0) {
|
|
86
|
-
// noinspection JSCheckFunctionSignatures
|
|
87
|
-
stdoutAsBuffer = Buffer.from(stdoutAsBuffer, 'binary').slice(0, -1);
|
|
88
|
-
}
|
|
89
|
-
if (stdoutAsBuffer && stdoutAsBuffer.length > 0) {
|
|
90
|
-
resolve(stdoutAsBuffer);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
});
|
|
69
|
+
}
|
|
70
|
+
return await doSqliteQuery1(file, sql);
|
|
94
71
|
}
|
|
95
72
|
|
|
96
73
|
async function getEncryptedChromeCookie(name, domain) {
|
|
@@ -100,33 +77,64 @@ async function getEncryptedChromeCookie(name, domain) {
|
|
|
100
77
|
if (domain && typeof domain !== 'string') {
|
|
101
78
|
throw new Error('domain must be a string');
|
|
102
79
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
80
|
+
const file = `${process.env.HOME}/Library/Application Support/Google/Chrome/Default/Cookies`;
|
|
81
|
+
if (!fs.existsSync(file)) {
|
|
82
|
+
throw new Error(`File ${file} does not exist`);
|
|
83
|
+
}
|
|
84
|
+
if (process.env.VERBOSE) {
|
|
85
|
+
console.log(`Trying Chrome cookie ${name} for domain ${domain}`);
|
|
86
|
+
}
|
|
87
|
+
let sql;
|
|
88
|
+
sql = `SELECT encrypted_value FROM cookies`;
|
|
89
|
+
if (typeof name === 'string' || typeof domain === 'string') {
|
|
90
|
+
sql += ` WHERE `;
|
|
91
|
+
if (typeof name === 'string') {
|
|
92
|
+
sql += `name = '${name}'`;
|
|
93
|
+
if (typeof domain === 'string') {
|
|
94
|
+
sql += ` AND `;
|
|
95
|
+
}
|
|
108
96
|
}
|
|
109
|
-
if (
|
|
110
|
-
|
|
97
|
+
if (typeof domain === 'string') {
|
|
98
|
+
sql += `host_key LIKE '${domain}';`;
|
|
111
99
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
100
|
+
}
|
|
101
|
+
return await doSqliteQuery1(file, sql);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function doSqliteQuery1(file, sql) {
|
|
105
|
+
const sqlite3 = require('sqlite3');
|
|
106
|
+
const db = new sqlite3.Database(file);
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
db.all(sql, (err, rows) => {
|
|
109
|
+
if (err) {
|
|
110
|
+
reject(err);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const rows1 = rows;
|
|
114
|
+
if (rows1.length === 0) {
|
|
115
|
+
if (process.env.VERBOSE) {
|
|
116
|
+
console.log(`No rows found`);
|
|
120
117
|
}
|
|
118
|
+
resolve(null);
|
|
119
|
+
return;
|
|
121
120
|
}
|
|
122
|
-
if (
|
|
123
|
-
|
|
121
|
+
if (Array.isArray(rows1)) {
|
|
122
|
+
// noinspection JSCheckFunctionSignatures
|
|
123
|
+
const [value] = rows1.flatMap(row => Object.values(row));
|
|
124
|
+
resolve(Buffer.from(value));
|
|
125
|
+
return;
|
|
124
126
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
resolve(rows1);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function doSqliteQuery(file, sql) {
|
|
133
|
+
const command = `sqlite3 "${file}" "${sql}"`;
|
|
134
|
+
if (process.env.VERBOSE) {
|
|
135
|
+
console.log(command);
|
|
136
|
+
}
|
|
137
|
+
return await new Promise((resolve, reject) => {
|
|
130
138
|
exec(command, {encoding: 'binary', maxBuffer: 5 * 1024}, (error, stdout, stderr) => {
|
|
131
139
|
if (error) {
|
|
132
140
|
reject(error);
|
|
@@ -156,63 +164,72 @@ async function getEncryptedChromeCookie(name, domain) {
|
|
|
156
164
|
*/
|
|
157
165
|
async function decrypt(password, encryptedData) {
|
|
158
166
|
if (typeof password !== 'string') {
|
|
159
|
-
throw new Error('password must be a string');
|
|
167
|
+
throw new Error('password must be a string: ' + password);
|
|
160
168
|
}
|
|
161
|
-
|
|
162
|
-
|
|
169
|
+
const encryptedData1 = encryptedData;
|
|
170
|
+
if (encryptedData1 == null || typeof encryptedData1 !== 'object') {
|
|
171
|
+
throw new Error('encryptedData must be a object: ' + encryptedData1);
|
|
163
172
|
}
|
|
164
173
|
if (process.env.VERBOSE) {
|
|
165
174
|
console.log(`Trying to decrypt with password ${password}`);
|
|
166
175
|
}
|
|
167
176
|
return await new Promise((resolve, reject) => {
|
|
168
177
|
crypto.pbkdf2(password, 'saltysalt', 1003, 16, 'sha1', (error, buffer) => {
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
178
|
+
try {
|
|
179
|
+
if (error) {
|
|
180
|
+
if (process.env.VERBOSE) {
|
|
181
|
+
console.log("Error doing pbkdf2", error);
|
|
182
|
+
}
|
|
183
|
+
reject(error);
|
|
184
|
+
return;
|
|
172
185
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
186
|
+
|
|
187
|
+
if (buffer.length !== 16) {
|
|
188
|
+
if (process.env.VERBOSE) {
|
|
189
|
+
console.log("Error doing pbkdf2, buffer length is not 16", buffer.length);
|
|
190
|
+
}
|
|
191
|
+
reject(new Error('Buffer length is not 16'));
|
|
192
|
+
return;
|
|
179
193
|
}
|
|
180
|
-
reject(new Error('Buffer length is not 16'));
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
194
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
encryptedData = encryptedData.slice(3);
|
|
195
|
+
const iv = new Buffer.from(new Array(17).join(' '), 'binary');
|
|
196
|
+
const decipher = crypto.createDecipheriv('aes-128-cbc', buffer, iv);
|
|
197
|
+
decipher.setAutoPadding(false);
|
|
188
198
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
console.log("Error doing pbkdf2, encryptedData length is not a multiple of 16", encryptedData.length);
|
|
199
|
+
if (encryptedData1 && encryptedData1.slice) {
|
|
200
|
+
encryptedData = encryptedData1.slice(3);
|
|
192
201
|
}
|
|
193
|
-
reject(new Error('encryptedData length is not a multiple of 16'));
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
console.log("Error doing decipher.final()", e);
|
|
203
|
+
if (encryptedData1.length % 16 !== 0) {
|
|
204
|
+
if (process.env.VERBOSE) {
|
|
205
|
+
console.log("Error doing pbkdf2, encryptedData length is not a multiple of 16", encryptedData1.length);
|
|
206
|
+
}
|
|
207
|
+
reject(new Error('encryptedData length is not a multiple of 16'));
|
|
208
|
+
return;
|
|
204
209
|
}
|
|
205
|
-
reject(e);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
210
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
let decoded = decipher.update(encryptedData1, 'binary', 'utf8');
|
|
212
|
+
// let decoded = decipher.update(encryptedData);
|
|
213
|
+
try {
|
|
214
|
+
decipher.final('utf-8');
|
|
215
|
+
} catch (e) {
|
|
216
|
+
if (process.env.VERBOSE) {
|
|
217
|
+
console.log("Error doing decipher.final()", e);
|
|
218
|
+
}
|
|
219
|
+
reject(e);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let padding = decoded[decoded.length - 1];
|
|
224
|
+
if (padding) {
|
|
225
|
+
decoded = decoded.slice(0, 0 - padding);
|
|
226
|
+
}
|
|
227
|
+
// noinspection JSCheckFunctionSignatures
|
|
228
|
+
decoded = decoded.toString('utf8');
|
|
229
|
+
resolve(decoded);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
reject(e);
|
|
212
232
|
}
|
|
213
|
-
// noinspection JSCheckFunctionSignatures
|
|
214
|
-
decoded = decoded.toString('utf8');
|
|
215
|
-
resolve(decoded)
|
|
216
233
|
});
|
|
217
234
|
});
|
|
218
235
|
}
|
|
@@ -230,8 +247,10 @@ async function getChromeCookie({name, domain}) {
|
|
|
230
247
|
if (domain && typeof domain !== 'string') {
|
|
231
248
|
throw new Error('domain must be a string');
|
|
232
249
|
}
|
|
233
|
-
const
|
|
234
|
-
const
|
|
250
|
+
const chromePasswordPromise = getChromePassword();
|
|
251
|
+
const encryptedChromeCookiePromise = getEncryptedChromeCookie(name, domain);
|
|
252
|
+
const password = await chromePasswordPromise;
|
|
253
|
+
const encryptedData = await encryptedChromeCookiePromise;
|
|
235
254
|
if (process.env.VERBOSE) {
|
|
236
255
|
console.log("Received encrypted", encryptedData);
|
|
237
256
|
}
|
|
@@ -239,7 +258,9 @@ async function getChromeCookie({name, domain}) {
|
|
|
239
258
|
try {
|
|
240
259
|
s = await decrypt(password, encryptedData);
|
|
241
260
|
} catch (e) {
|
|
242
|
-
|
|
261
|
+
if (process.env.VERBOSE) {
|
|
262
|
+
console.error("Error decrypting", e);
|
|
263
|
+
}
|
|
243
264
|
throw new Error('Failed to decrypt');
|
|
244
265
|
}
|
|
245
266
|
if (process.env.VERBOSE) {
|
|
@@ -293,7 +314,7 @@ module.exports = {
|
|
|
293
314
|
if (process.argv) {
|
|
294
315
|
if (process.argv.length > 2) {
|
|
295
316
|
const name = process.argv[2];
|
|
296
|
-
const domain = process.argv[3];
|
|
317
|
+
const domain = process.argv[3] ?? '%';
|
|
297
318
|
|
|
298
319
|
if (process.argv.includes('--verbose')) {
|
|
299
320
|
process.env.VERBOSE = "true";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mherod/get-cookie",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Node.js module for querying a local user's Chrome cookie",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": "index.js",
|
|
@@ -9,13 +9,12 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
-
"
|
|
13
|
-
"installForMac": "pkg . --target node17-macos-x64 --out-path /usr/local/bin/ --debug",
|
|
14
|
-
"installForHost": "pkg . --target host --out-path /usr/local/bin/ --debug"
|
|
12
|
+
"installGlobal": "rm -rf /usr/local/bin/get-cookie ; npm install --force --global ."
|
|
15
13
|
},
|
|
16
14
|
"keywords": [],
|
|
17
15
|
"author": "Matthew Herod",
|
|
18
16
|
"license": "ISC",
|
|
19
17
|
"dependencies": {
|
|
18
|
+
"sqlite3": "^5.0.8"
|
|
20
19
|
}
|
|
21
20
|
}
|