@mherod/get-cookie 1.3.0 → 2.0.0-beta.10

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.
Files changed (43) hide show
  1. package/.idea/codeStyles/Project.xml +59 -0
  2. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  3. package/.idea/get-cookie.iml +1 -0
  4. package/.prettierrc.json +1 -0
  5. package/dist/cli.js +1 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.js +563 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/module.js +533 -0
  10. package/dist/module.js.map +1 -0
  11. package/dist/types.d.ts +35 -0
  12. package/dist/types.d.ts.map +1 -0
  13. package/package.json +38 -13
  14. package/src/CookieRequest.ts +4 -0
  15. package/src/CookieRow.ts +6 -0
  16. package/src/ExportedCookie.ts +6 -0
  17. package/src/IsCookieRow.ts +5 -0
  18. package/src/IsExportedCookie.ts +5 -0
  19. package/src/argv.ts +1 -0
  20. package/src/browsers/ChromeCookieQueryStrategy.ts +259 -0
  21. package/src/browsers/CompositeCookieQueryStrategy.ts +30 -0
  22. package/src/browsers/CookieQueryStrategy.ts +5 -0
  23. package/src/browsers/FirefoxCookieQueryStrategy.ts +92 -0
  24. package/src/browsers/SafariCookieQueryStrategy.ts +8 -0
  25. package/src/cli.ts +82 -0
  26. package/src/doSqliteQuery1.ts +41 -0
  27. package/src/fetchResponse.ts +11 -0
  28. package/src/fetchWithCookies.ts +62 -0
  29. package/src/findAllFiles.ts +68 -0
  30. package/src/getGroupedRenderedCookies.ts +32 -0
  31. package/src/global.ts +7 -0
  32. package/src/index.ts +59 -0
  33. package/src/isValidJwt.ts +14 -0
  34. package/src/queryCookies.ts +27 -0
  35. package/src/resultsRendered.ts +8 -0
  36. package/src/utils.ts +56 -0
  37. package/tsconfig.json +23 -0
  38. package/dist/main.js +0 -430
  39. package/dist/main.js.map +0 -1
  40. package/src/cli.js +0 -58
  41. package/src/global.js +0 -3
  42. package/src/index.js +0 -462
  43. package/src/utils.js +0 -185
@@ -0,0 +1 @@
1
+ {"mappings":";ACAA;IACE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AILD;IACE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;ASLD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AGID,0CAEE,EACE,IAAI,EACJ,MAAM,EAEP,EAAE,aAAa,GAEf,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBnB;AC/BD;IACE,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,QAAQ,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IACzC,WAAW,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/B;ACDD,iCACE,GAAG,EAAE,WAAW,GAAG,GAAG,EACtB,OAAO,GAAE,WAAW,GAAG,SAAc,GACpC,OAAO,CAAC,aAAa,CAAC,CAiDxB;ACjDD,0BAAgC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAW1F;AAED,iCAAuC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAWjG;AAED,gCAAsC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAWhG","sources":["src/src/global.ts","src/src/ExportedCookie.ts","src/src/browsers/CookieQueryStrategy.ts","src/src/utils.ts","src/src/findAllFiles.ts","src/src/CookieRow.ts","src/src/doSqliteQuery1.ts","src/src/IsCookieRow.ts","src/src/IsExportedCookie.ts","src/src/browsers/ChromeCookieQueryStrategy.ts","src/src/browsers/FirefoxCookieQueryStrategy.ts","src/src/browsers/SafariCookieQueryStrategy.ts","src/src/browsers/CompositeCookieQueryStrategy.ts","src/src/isValidJwt.ts","src/src/CookieRequest.ts","src/src/queryCookies.ts","src/src/resultsRendered.ts","src/src/getGroupedRenderedCookies.ts","src/src/fetchResponse.ts","src/src/fetchWithCookies.ts","src/src/index.ts","src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"#!/usr/bin/env node\n// noinspection JSUnusedGlobalSymbols\n\nimport { queryCookies } from \"./queryCookies\";\nimport FirefoxCookieQueryStrategy from \"./browsers/FirefoxCookieQueryStrategy\";\nimport ChromeCookieQueryStrategy from \"./browsers/ChromeCookieQueryStrategy\";\nimport CompositeCookieQueryStrategy from \"./browsers/CompositeCookieQueryStrategy\";\nimport { CookieRequest } from \"./CookieRequest\";\nimport { ExportedCookie } from \"./ExportedCookie\";\nimport { getGroupedRenderedCookies } from \"./getGroupedRenderedCookies\";\nimport { fetchWithCookies } from \"./fetchWithCookies\";\n\nexport async function getCookie(params: CookieRequest): Promise<ExportedCookie | undefined> {\n const cookies = await queryCookies(\n params,\n new CompositeCookieQueryStrategy()\n //\n );\n if (Array.isArray(cookies) && cookies.length > 0) {\n return cookies.find((cookie) => cookie != null);\n } else {\n throw new Error(\"Cookie not found\");\n }\n}\n\nexport async function getFirefoxCookie(params: CookieRequest): Promise<ExportedCookie | undefined> {\n const cookies = await queryCookies(\n params,\n new FirefoxCookieQueryStrategy()\n //\n );\n if (Array.isArray(cookies) && cookies.length > 0) {\n return cookies.find((cookie) => cookie != null);\n } else {\n throw new Error(\"Cookie not found\");\n }\n}\n\nexport async function getChromeCookie(params: CookieRequest): Promise<ExportedCookie | undefined> {\n const cookies = await queryCookies(\n params,\n new ChromeCookieQueryStrategy()\n //\n );\n if (Array.isArray(cookies) && cookies.length > 0) {\n return cookies.find((cookie) => cookie != null);\n } else {\n throw new Error(\"Cookie not found\");\n }\n}\n\nexport {\n getGroupedRenderedCookies,\n fetchWithCookies,\n};\n\nexport * from \"./CookieRequest\";\nexport * from \"./CookieRow\";\nexport * from \"./ExportedCookie\";\n"],"names":[],"version":3,"file":"types.d.ts.map","sourceRoot":"../"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@mherod/get-cookie",
3
- "version": "1.3.0",
3
+ "version": "2.0.0-beta.10",
4
4
  "description": "Node.js module for querying a local user's Chrome cookie",
5
- "source": "src/index.js",
5
+ "source": "src/index.ts",
6
6
  "bin": "dist/cli.js",
7
- "main": "dist/main.js",
7
+ "main": "dist/index.js",
8
8
  "module": "dist/module.js",
9
+ "types": "dist/types.d.ts",
9
10
  "publishConfig": {
10
11
  "access": "public"
11
12
  },
@@ -13,30 +14,41 @@
13
14
  "clean": "git clean -fXd",
14
15
  "build": "parcel build",
15
16
  "test": "jest",
16
- "postbuild": "npm run install:global",
17
+ "postbuild": "npm run check && npm run install:global",
18
+ "prepublish": "npm run build",
17
19
  "install:global": "npm install --force --global .",
18
20
  "postinstall:global": "find $NVM_BIN -exec chmod +x {} \\; || true",
19
- "prettier": "prettier --write 'src/{**/*,*}.{js,json}'"
21
+ "prettier": "prettier --write 'src/{**/*,*}.{js,json,ts}'",
22
+ "check": "tsc --noEmit"
20
23
  },
21
24
  "targets": {
22
- "main": {
23
- "source": "src/index.js",
25
+ "main": {},
26
+ "types": {},
27
+ "library": {
28
+ "source": [
29
+ "src/index.ts"
30
+ ],
24
31
  "distDir": "dist",
32
+ "isLibrary": true,
25
33
  "engines": {
26
34
  "node": "16"
27
35
  }
28
36
  },
29
37
  "cli": {
30
- "source": "src/cli.js",
38
+ "source": "src/cli.ts",
31
39
  "distDir": "dist",
40
+ "isLibrary": false,
32
41
  "engines": {
33
42
  "node": "16"
34
- }
43
+ },
44
+ "includeNodeModules": [
45
+ "colorette"
46
+ ]
35
47
  }
36
48
  },
37
49
  "@parcel/transformer-js": {
38
50
  "inlineFS": false,
39
- "inlineEnvironment": false
51
+ "inlineEnvironment": true
40
52
  },
41
53
  "engines": {
42
54
  "node": "16"
@@ -45,12 +57,25 @@
45
57
  "author": "Matthew Herod",
46
58
  "license": "ISC",
47
59
  "dependencies": {
60
+ "colorette": "^2.0.19",
61
+ "cross-fetch": "^3.1.5",
62
+ "destr": "^1.1.1",
48
63
  "jsonwebtoken": "^8.5.1",
49
64
  "lodash": "^4.17.21",
50
- "sqlite3": "^5.0.8"
65
+ "sqlite3": "^5.1.1",
66
+ "user-agents": "^1.0.1163"
51
67
  },
52
68
  "devDependencies": {
53
- "jest": "^28.1.0",
54
- "prettier": "^2.7.1"
69
+ "@parcel/packager-ts": "^2.7.0",
70
+ "@parcel/transformer-typescript-types": "^2.7.0",
71
+ "@types/jsonwebtoken": "^8.5.9",
72
+ "@types/lodash": "^4.14.186",
73
+ "@types/node": "^18.8.2",
74
+ "@types/user-agents": "^1.0.2",
75
+ "jest": "^29.0.3",
76
+ "parcel": "^2.7.0",
77
+ "prettier": "^2.7.1",
78
+ "ts-node": "^10.9.1",
79
+ "typescript": "^4.8.4"
55
80
  }
56
81
  }
@@ -0,0 +1,4 @@
1
+ export interface CookieRequest {
2
+ name: string;
3
+ domain: string;
4
+ }
@@ -0,0 +1,6 @@
1
+ export interface CookieRow {
2
+ domain: string;
3
+ name: string;
4
+ value: Buffer;
5
+ meta?: any;
6
+ }
@@ -0,0 +1,6 @@
1
+ export interface ExportedCookie {
2
+ domain: string;
3
+ name: string;
4
+ value: string;
5
+ meta?: any;
6
+ }
@@ -0,0 +1,5 @@
1
+ import { CookieRow } from "./CookieRow";
2
+
3
+ export function isCookieRow(obj: any): obj is CookieRow {
4
+ return obj.domain !== undefined && obj.name !== undefined && obj.value !== undefined;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { ExportedCookie } from "./ExportedCookie";
2
+
3
+ export function isExportedCookie(obj: any): obj is ExportedCookie {
4
+ return obj.domain !== undefined && obj.name !== undefined && obj.value !== undefined;
5
+ }
package/src/argv.ts ADDED
@@ -0,0 +1 @@
1
+ export const argv: string[] = process.argv ?? [];
@@ -0,0 +1,259 @@
1
+ import CookieQueryStrategy from "./CookieQueryStrategy";
2
+ import { execSimple } from "../utils";
3
+ import { env, HOME } from "../global";
4
+ import { existsSync } from "fs";
5
+ import { findAllFiles } from "../findAllFiles";
6
+ import * as crypto from "crypto";
7
+ import * as path from "path";
8
+ import { doSqliteQuery1 } from "../doSqliteQuery1";
9
+ import { merge } from "lodash";
10
+ import { isCookieRow } from "../IsCookieRow";
11
+ import { isExportedCookie } from "../IsExportedCookie";
12
+ import { CookieRow } from "../CookieRow";
13
+ import { ExportedCookie } from "../ExportedCookie";
14
+
15
+ export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
16
+ async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
17
+ if (process.platform !== "darwin") {
18
+ throw new Error("This only works on macOS");
19
+ }
20
+ if (env.FIREFOX_ONLY) {
21
+ return [];
22
+ }
23
+ return getChromeCookies({
24
+ requireJwt: false,
25
+ name,
26
+ domain
27
+ });
28
+ }
29
+ }
30
+
31
+ async function getPromise1(name: string, domain: string, file: string): Promise<CookieRow[]> {
32
+ try {
33
+ return await getEncryptedChromeCookie({
34
+ name: name,
35
+ domain: domain,
36
+ file: file
37
+ });
38
+ } catch (e) {
39
+ if (env.VERBOSE) {
40
+ console.log("Error getting encrypted cookie", e);
41
+ }
42
+ return [];
43
+ }
44
+ }
45
+
46
+ async function getPromise(name: string, domain: string): Promise<CookieRow[]> {
47
+ try {
48
+ const files: string[] = await findAllFiles({
49
+ path: chromeLocal,
50
+ name: "Cookies"
51
+ });
52
+ const promises: Promise<CookieRow[]>[] = files.map((file) => getPromise1(name, domain, file));
53
+ const results1: Awaited<CookieRow[]>[] = await Promise.all(promises);
54
+ return results1.flat().filter(isCookieRow);
55
+ } catch (error) {
56
+ if (env.VERBOSE) {
57
+ console.log("error", error);
58
+ }
59
+ return [];
60
+ }
61
+ }
62
+
63
+ async function decryptValue(password: string, encryptedValue: Buffer) {
64
+ let d: string | null;
65
+ try {
66
+ d = await decrypt(password, encryptedValue);
67
+ } catch (e) {
68
+ if (env.VERBOSE) {
69
+ console.log("Error decrypting cookie", e);
70
+ }
71
+ d = null;
72
+ }
73
+ return d ?? encryptedValue.toString("utf-8");
74
+ }
75
+
76
+ async function getChromeCookies(
77
+ {
78
+ name,
79
+ domain = "%",
80
+ requireJwt = false
81
+ }: {
82
+ name: string;
83
+ domain: string;
84
+ requireJwt: boolean | undefined;
85
+ //
86
+ }
87
+ //
88
+ ): Promise<ExportedCookie[]> {
89
+ const encryptedDataItems: CookieRow[] = await getPromise(name, domain);
90
+ const password: string = await getChromePassword();
91
+ const decrypted: Promise<ExportedCookie | null>[] = encryptedDataItems
92
+ .filter(({ value }) => value != null && value.length > 0)
93
+ .map(async (cookieRow: CookieRow) => {
94
+ const encryptedValue: Buffer = cookieRow.value;
95
+ const decryptedValue = await decryptValue(password, encryptedValue);
96
+ const meta = {};
97
+ merge(meta, cookieRow.meta ?? {});
98
+ return {
99
+ domain: cookieRow.domain,
100
+ name: cookieRow.name,
101
+ value: decryptedValue,
102
+ meta: meta
103
+ };
104
+ });
105
+ const results: ExportedCookie[] = (await Promise.all(decrypted)).filter(isExportedCookie);
106
+ if (env.VERBOSE) {
107
+ console.log("results", results);
108
+ }
109
+ return results;
110
+ }
111
+
112
+ const chromeLocal = path.join(
113
+ HOME,
114
+ "Library",
115
+ "Application Support",
116
+ "Google",
117
+ "Chrome"
118
+ );
119
+
120
+ async function getEncryptedChromeCookie(
121
+ {
122
+ name,
123
+ domain,
124
+ file = path.join(chromeLocal, "Default", "Cookies")
125
+ //
126
+ }: {
127
+ name: string;
128
+ domain: string;
129
+ file: string;
130
+ }): Promise<CookieRow[]> {
131
+ if (!existsSync(file)) {
132
+ throw new Error(`File ${file} does not exist`);
133
+ }
134
+ if (env.VERBOSE) {
135
+ const s = file.split("/").slice(-3).join("/");
136
+ console.log(`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`);
137
+ }
138
+ let sql;
139
+ sql = `SELECT encrypted_value, name, host_key FROM cookies`;
140
+ const wildcardRegexp = /^([*%])$/i;
141
+ const specifiedName = name.match(wildcardRegexp) == null;
142
+ const specifiedDomain = domain.match(wildcardRegexp) == null;
143
+ if (specifiedName || specifiedDomain) {
144
+ sql += ` WHERE `;
145
+ if (specifiedName) {
146
+ sql += `name = '${name}'`;
147
+ if (specifiedDomain) {
148
+ sql += ` AND `;
149
+ }
150
+ }
151
+ if (specifiedDomain) {
152
+ sql += `host_key LIKE '${domain}';`;
153
+ }
154
+ }
155
+ return doSqliteQuery1(file, sql);
156
+ }
157
+
158
+ async function getChromePassword(): Promise<string> {
159
+ return execSimple(
160
+ "security find-generic-password -w -s \"Chrome Safe Storage\""
161
+ );
162
+ }
163
+
164
+ async function decrypt(password: crypto.BinaryLike, encryptedData: Buffer): Promise<string> {
165
+ if (typeof password !== "string") {
166
+ throw new Error("password must be a string: " + password);
167
+ }
168
+ let encryptedData1: any;
169
+ encryptedData1 = encryptedData;
170
+ if (encryptedData1 == null || typeof encryptedData1 !== "object") {
171
+ throw new Error("encryptedData must be a object: " + encryptedData1);
172
+ }
173
+ if (!(encryptedData1 instanceof Buffer)) {
174
+ if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
175
+ [encryptedData1] = encryptedData1;
176
+ if (env.VERBOSE) {
177
+ console.log(
178
+ `encryptedData is an array of buffers, selected first: ${encryptedData1}`
179
+ );
180
+ }
181
+ } else {
182
+ throw new Error("encryptedData must be a Buffer: " + encryptedData1);
183
+ }
184
+ encryptedData1 = Buffer.from(encryptedData1);
185
+ }
186
+ if (env.VERBOSE) {
187
+ console.log(`Trying to decrypt with password ${password}`);
188
+ }
189
+ return new Promise((resolve, reject) => {
190
+ crypto.pbkdf2(password,
191
+ "saltysalt",
192
+ 1003,
193
+ 16,
194
+ "sha1",
195
+ (error, buffer) => {
196
+ try {
197
+ if (error) {
198
+ if (env.VERBOSE) {
199
+ console.log("Error doing pbkdf2", error);
200
+ }
201
+ reject(error);
202
+ return;
203
+ }
204
+
205
+ if (buffer.length !== 16) {
206
+ if (env.VERBOSE) {
207
+ console.log(
208
+ "Error doing pbkdf2, buffer length is not 16",
209
+ buffer.length
210
+ );
211
+ }
212
+ reject(new Error("Buffer length is not 16"));
213
+ return;
214
+ }
215
+
216
+ const str = new Array(17).join(" ");
217
+ const iv = Buffer.from(str, "binary");
218
+ const decipher = crypto.createDecipheriv("aes-128-cbc", buffer, iv);
219
+ decipher.setAutoPadding(false);
220
+
221
+ if (encryptedData1 && encryptedData1.slice) {
222
+ encryptedData1 = encryptedData1.slice(3);
223
+ }
224
+
225
+ if (encryptedData1.length % 16 !== 0) {
226
+ if (env.VERBOSE) {
227
+ console.log(
228
+ "Error doing pbkdf2, encryptedData length is not a multiple of 16",
229
+ encryptedData1.length
230
+ );
231
+ }
232
+ reject(new Error("encryptedData length is not a multiple of 16"));
233
+ return;
234
+ }
235
+
236
+ let decoded = decipher.update(encryptedData1);
237
+ try {
238
+ decipher.final("utf-8");
239
+ } catch (e) {
240
+ if (env.VERBOSE) {
241
+ console.log("Error doing decipher.final()", e);
242
+ }
243
+ reject(e);
244
+ return;
245
+ }
246
+
247
+ const padding = decoded[decoded.length - 1];
248
+ if (padding) {
249
+ decoded = decoded.slice(0, 0 - padding);
250
+ }
251
+ // noinspection JSCheckFunctionSignatures
252
+ const decodedString = decoded.toString("utf8");
253
+ resolve(decodedString);
254
+ } catch (e) {
255
+ reject(e);
256
+ }
257
+ });
258
+ });
259
+ }
@@ -0,0 +1,30 @@
1
+ import ChromeCookieQueryStrategy from "./ChromeCookieQueryStrategy";
2
+ import FirefoxCookieQueryStrategy from "./FirefoxCookieQueryStrategy";
3
+ import SafariCookieQueryStrategy from "./SafariCookieQueryStrategy";
4
+ import CookieQueryStrategy from "./CookieQueryStrategy";
5
+ import { ExportedCookie } from "../ExportedCookie";
6
+
7
+ export default class CompositeCookieQueryStrategy implements CookieQueryStrategy {
8
+ #strategies;
9
+
10
+ constructor() {
11
+ this.#strategies = [
12
+ ChromeCookieQueryStrategy,
13
+ FirefoxCookieQueryStrategy,
14
+ SafariCookieQueryStrategy,
15
+ ].map((strategy) => {
16
+ return new strategy();
17
+ });
18
+ }
19
+
20
+ async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
21
+ const results = await Promise.all(
22
+ this.#strategies.map(async (strategy) => {
23
+ // @ts-ignore
24
+ const cookies: Promise<ExportedCookie[]> = strategy.queryCookies(name, domain);
25
+ return cookies.catch(() => []);
26
+ })
27
+ );
28
+ return results.flat();
29
+ }
30
+ }
@@ -0,0 +1,5 @@
1
+ import { ExportedCookie } from "../ExportedCookie";
2
+
3
+ export default interface CookieQueryStrategy {
4
+ queryCookies(name: string, domain: string): Promise<ExportedCookie[]>
5
+ }
@@ -0,0 +1,92 @@
1
+ import CookieQueryStrategy from "./CookieQueryStrategy";
2
+ import { env, HOME } from "../global";
3
+ import { existsSync } from "fs";
4
+ import { toStringOrNull } from "../utils";
5
+ import { findAllFiles } from "../findAllFiles";
6
+ import * as path from "path";
7
+ import { doSqliteQuery1 } from "../doSqliteQuery1";
8
+ import { ExportedCookie } from "../ExportedCookie";
9
+
10
+ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
11
+ async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
12
+ if (process.platform !== "darwin") {
13
+ throw new Error("This only works on macOS");
14
+ }
15
+ if (env.CHROME_ONLY) {
16
+ return [];
17
+ }
18
+ const cookies = await this.#getFirefoxCookie({ name, domain });
19
+ return Array.isArray(cookies) ? cookies.map(toStringOrNull) : [];
20
+ }
21
+
22
+ /**
23
+ *
24
+ * @param name
25
+ * @param domain
26
+ * @returns {Promise<Buffer>}
27
+ */
28
+ async #getFirefoxCookie(
29
+ //
30
+ {
31
+ name,
32
+ domain
33
+ }: {
34
+ name: string,
35
+ domain: string
36
+ //
37
+ }
38
+ //
39
+ ) {
40
+ const files: string[] = await findAllFiles({
41
+ path: path.join(
42
+ HOME,
43
+ "Library",
44
+ "Application Support",
45
+ "Firefox",
46
+ "Profiles"
47
+ ),
48
+ name: "cookies.sqlite"
49
+ });
50
+ const all = await Promise.all(
51
+ files.map((file) => {
52
+ return this.#queryCookiesDb(file, name, domain);
53
+ })
54
+ );
55
+ return all.flat();
56
+ }
57
+
58
+ #queryCookiesDb(file: string, name: string, domain: string) {
59
+ if (file && !existsSync(file)) {
60
+ throw new Error(`File ${file} does not exist`);
61
+ }
62
+ if (env.VERBOSE) {
63
+ console.log(`Trying Firefox cookie ${name} for domain ${domain}`);
64
+ }
65
+ let sql;
66
+ //language=SQL
67
+ sql = "SELECT value FROM moz_cookies";
68
+ if (typeof name === "string" || typeof domain === "string") {
69
+ sql += ` WHERE `;
70
+ if (typeof name === "string") {
71
+ sql += `name = '${name}'`;
72
+ if (typeof domain === "string") {
73
+ sql += ` AND `;
74
+ }
75
+ }
76
+ if (typeof domain === "string") {
77
+ sql += `host LIKE '${domain}';`;
78
+ }
79
+ }
80
+ return doSqliteQuery1(file, sql)
81
+ .then((rows) => {
82
+ return rows.map((row) => {
83
+ return {
84
+ domain: row.domain,
85
+ name: row.name,
86
+ value: row.value.toString("utf8")
87
+ };
88
+ });
89
+ })
90
+ .catch(() => []);
91
+ }
92
+ }
@@ -0,0 +1,8 @@
1
+ import CookieQueryStrategy from "./CookieQueryStrategy";
2
+ import { ExportedCookie } from "../ExportedCookie";
3
+
4
+ export default class SafariCookieQueryStrategy implements CookieQueryStrategy {
5
+ async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
6
+ return [];
7
+ }
8
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { env } from "./global";
4
+ import { queryCookies } from "./queryCookies";
5
+ import { argv } from "./argv";
6
+ import { groupBy } from "lodash";
7
+ import { blue, green } from "colorette";
8
+ import { resultsRendered } from "./resultsRendered";
9
+ import { ExportedCookie } from "./ExportedCookie";
10
+
11
+ function combinedString(results: ExportedCookie[]) {
12
+ return blue(resultsRendered(results));
13
+ }
14
+
15
+ async function cliQueryCookies(name: string, domain: string) {
16
+ try {
17
+ const results = await queryCookies({ name, domain });
18
+ if (results.length > 0) {
19
+ if (argv.includes("--combined-string")) {
20
+ console.log(combinedString(results));
21
+ } else if (argv.includes("--dump")) {
22
+ console.log(results);
23
+ } else if (argv.includes("--dump-grouped")) {
24
+ const groupedByFile = groupBy(results, (r) => r.meta?.file);
25
+ console.log(green(JSON.stringify(groupedByFile, null, 2)));
26
+ } else if (argv.includes("--combined-string-grouped")) {
27
+ const groupedByFile = groupBy(results, (r) => r.meta?.file);
28
+ for (const file of Object.keys(groupedByFile)) {
29
+ let results = groupedByFile[file];
30
+ console.log(green(file) + ": ", combinedString(results));
31
+ }
32
+ } else {
33
+ for (const result of results) {
34
+ console.log(result.value);
35
+ }
36
+ }
37
+ } else {
38
+ console.error("No results");
39
+ }
40
+ } catch (e) {
41
+ console.error(e);
42
+ }
43
+ }
44
+
45
+ if (argv && argv.length > 2) {
46
+ const name = argv[2];
47
+
48
+ let domain;
49
+ if (argv[3] != null && argv[3].indexOf(".") > -1) {
50
+ domain = argv[3];
51
+ } else {
52
+ domain = "%";
53
+ }
54
+
55
+ const tru = `${true}`;
56
+
57
+ if (argv.includes("--require-jwt")) {
58
+ env.REQUIRE_JWT = tru;
59
+ }
60
+ if (argv.includes("--verbose")) {
61
+ env.VERBOSE = tru;
62
+ }
63
+ if (argv.includes("--chrome-only")) {
64
+ env.CHROME_ONLY = tru;
65
+ }
66
+ if (argv.includes("--firefox-only")) {
67
+ env.FIREFOX_ONLY = tru;
68
+ }
69
+ if (argv.includes("--ignore-expired")) {
70
+ env.IGNORE_EXPIRED = tru;
71
+ }
72
+
73
+ if (argv.includes("--single")) {
74
+ env.SINGLE = tru;
75
+ }
76
+
77
+ if (env.VERBOSE) {
78
+ console.log("Verbose mode", argv);
79
+ }
80
+
81
+ cliQueryCookies(name, domain).catch(console.error);
82
+ }
@@ -0,0 +1,41 @@
1
+ import * as fs from "fs";
2
+ import * as sqlite3 from "sqlite3";
3
+ import { CookieRow } from "./CookieRow";
4
+
5
+ export async function doSqliteQuery1(file: string, sql: string): Promise<CookieRow[]> {
6
+ if (!fs.existsSync(file)) {
7
+ throw new Error(`doSqliteQuery1: file ${file} does not exist`);
8
+ }
9
+ const db = new sqlite3.Database(file);
10
+ return new Promise((resolve, reject) => {
11
+ db.all(sql, (err: Error, rows: any[]) => {
12
+ if (err) {
13
+ reject(err);
14
+ return;
15
+ }
16
+ const rows1: any[] = rows;
17
+ if (rows1 == null || rows1.length === 0) {
18
+ resolve([]);
19
+ return;
20
+ }
21
+ if (Array.isArray(rows1)) {
22
+ const cookieRows: CookieRow[] = rows1.map((row) => {
23
+ return {
24
+ domain: row["host_key"],
25
+ name: row["name"],
26
+ value: row["encrypted_value"],
27
+ meta: {
28
+ file: file
29
+ }
30
+ };
31
+ });
32
+ resolve(cookieRows);
33
+ return;
34
+ }
35
+ if (process.env.VERBOSE) {
36
+ console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
37
+ }
38
+ resolve([rows1]);
39
+ });
40
+ });
41
+ }
@@ -0,0 +1,11 @@
1
+ export default interface FetchResponse {
2
+ status: number;
3
+ statusText: string;
4
+ headers: Headers;
5
+ url: string;
6
+ text: () => Promise<string>;
7
+ json: () => Promise<any>;
8
+ formData: () => Promise<URLSearchParams>;
9
+ arrayBuffer: () => Promise<ArrayBuffer>;
10
+ buffer: () => Promise<Buffer>;
11
+ }