@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.
@@ -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,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="JavaScriptLibraryMappings">
4
+ <includedPredefinedLibrary name="Node.js Core" />
5
+ </component>
6
+ </project>
@@ -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
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
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
+ }