@mherod/get-cookie 2.0.0-beta.9 → 2.0.0-rc.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 (45) hide show
  1. package/.github/dependabot.yml +6 -0
  2. package/dist/index.js +459 -174
  3. package/package-lock.json +12295 -0
  4. package/package.json +16 -9
  5. package/src/CookieRow.ts +2 -1
  6. package/src/CookieSpec.ts +4 -0
  7. package/src/CookieStore.ts +10 -0
  8. package/src/ExportedCookie.ts +2 -1
  9. package/src/{fetchResponse.ts → FetchResponse.ts} +5 -1
  10. package/src/IsCookieRow.ts +6 -2
  11. package/src/IsExportedCookie.ts +6 -2
  12. package/src/SpecialCases.ts +14 -0
  13. package/src/StringToRegex.ts +6 -0
  14. package/src/argv.ts +4 -0
  15. package/src/browsers/ChromeCookieQueryStrategy.ts +190 -106
  16. package/src/browsers/CompositeCookieQueryStrategy.ts +49 -7
  17. package/src/browsers/CookieQueryStrategy.ts +3 -2
  18. package/src/browsers/CookieStoreQueryStrategy.ts +94 -0
  19. package/src/browsers/FirefoxCookieQueryStrategy.ts +54 -46
  20. package/src/browsers/SafariCookieQueryStrategy.ts +3 -1
  21. package/src/cli.ts +90 -61
  22. package/src/doSqliteQuery1.ts +17 -11
  23. package/src/doSqliteQuery1Params.ts +7 -0
  24. package/src/fetchWithCookies.ts +84 -31
  25. package/src/findAllFiles.ts +16 -22
  26. package/src/getChromeCookie.ts +19 -0
  27. package/src/getCookie.ts +19 -0
  28. package/src/getFirefoxCookie.ts +19 -0
  29. package/src/getGroupedRenderedCookies.ts +19 -22
  30. package/src/getMergedRenderedCookies.ts +25 -0
  31. package/src/global.ts +2 -1
  32. package/src/index.ts +10 -46
  33. package/src/isValidJwt.ts +13 -3
  34. package/src/queryCookies.ts +17 -16
  35. package/src/resultsRendered.ts +7 -4
  36. package/src/unpackHeaders.ts +16 -0
  37. package/src/utils.ts +1 -32
  38. package/dist/cli.js +0 -3
  39. package/dist/cli.js.map +0 -1
  40. package/dist/index.js.map +0 -1
  41. package/dist/module.js +0 -533
  42. package/dist/module.js.map +0 -1
  43. package/dist/types.d.ts +0 -35
  44. package/dist/types.d.ts.map +0 -1
  45. package/src/CookieRequest.ts +0 -4
package/package.json CHANGED
@@ -1,21 +1,23 @@
1
1
  {
2
2
  "name": "@mherod/get-cookie",
3
- "version": "2.0.0-beta.9",
3
+ "version": "2.0.0-rc.10",
4
4
  "description": "Node.js module for querying a local user's Chrome cookie",
5
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
9
  "types": "dist/types.d.ts",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
13
  "scripts": {
14
- "clean": "git clean -fXd",
14
+ "clean": "git clean -fdX && rm -rf node_modules dist && yarn install",
15
15
  "build": "parcel build",
16
+ "prepare": "npm run build && npm run prettier",
16
17
  "test": "jest",
17
- "postbuild": "npm run check && npm run install:global",
18
+ "postbuild": "npm run check",
18
19
  "prepublish": "npm run build",
20
+ "preinstall:global": "npm run build",
19
21
  "install:global": "npm install --force --global .",
20
22
  "postinstall:global": "find $NVM_BIN -exec chmod +x {} \\; || true",
21
23
  "prettier": "prettier --write 'src/{**/*,*}.{js,json,ts}'",
@@ -25,14 +27,13 @@
25
27
  "main": {},
26
28
  "types": {},
27
29
  "library": {
28
- "source": [
29
- "src/index.ts"
30
- ],
30
+ "source": "src/index.ts",
31
31
  "distDir": "dist",
32
32
  "isLibrary": true,
33
33
  "engines": {
34
34
  "node": "16"
35
- }
35
+ },
36
+ "includeNodeModules": false
36
37
  },
37
38
  "cli": {
38
39
  "source": "src/cli.ts",
@@ -62,15 +63,21 @@
62
63
  "destr": "^1.1.1",
63
64
  "jsonwebtoken": "^8.5.1",
64
65
  "lodash": "^4.17.21",
66
+ "lru-cache": "^7.14.0",
67
+ "minimist": "^1.2.6",
65
68
  "sqlite3": "^5.1.1",
66
- "user-agents": "^1.0.1163"
69
+ "tough-cookie": "^4.1.2",
70
+ "tough-cookie-file-store": "^2.0.3",
71
+ "user-agents": "^1.0.1165"
67
72
  },
68
73
  "devDependencies": {
69
74
  "@parcel/packager-ts": "^2.7.0",
70
75
  "@parcel/transformer-typescript-types": "^2.7.0",
71
76
  "@types/jsonwebtoken": "^8.5.9",
72
77
  "@types/lodash": "^4.14.186",
78
+ "@types/minimist": "^1.2.2",
73
79
  "@types/node": "^18.8.2",
80
+ "@types/tough-cookie": "^4.0.2",
74
81
  "@types/user-agents": "^1.0.2",
75
82
  "jest": "^29.0.3",
76
83
  "parcel": "^2.7.0",
package/src/CookieRow.ts CHANGED
@@ -1,4 +1,5 @@
1
- export interface CookieRow {
1
+ export default interface CookieRow {
2
+ expiry?: number;
2
3
  domain: string;
3
4
  name: string;
4
5
  value: Buffer;
@@ -0,0 +1,4 @@
1
+ export default interface CookieSpec {
2
+ domain: string;
3
+ name: string;
4
+ }
@@ -0,0 +1,10 @@
1
+ import { CookieJar } from "tough-cookie";
2
+ import { env } from "./global";
3
+ const { FileCookieStore } = require("tough-cookie-file-store");
4
+
5
+ export const cookieStore = new FileCookieStore(
6
+ `${env["HOME"]}/cookie-star.json`
7
+ );
8
+ // export const cookieStore = new MemoryCookieStore();
9
+
10
+ export const cookieJar = new CookieJar(cookieStore);
@@ -1,6 +1,7 @@
1
- export interface ExportedCookie {
1
+ export default interface ExportedCookie {
2
2
  domain: string;
3
3
  name: string;
4
4
  value: string;
5
+ expiry?: Date | "Infinity";
5
6
  meta?: any;
6
7
  }
@@ -1,4 +1,8 @@
1
- export default interface FetchResponse {
1
+ // noinspection JSUnusedGlobalSymbols
2
+
3
+ import { Response } from "cross-fetch";
4
+
5
+ export default interface FetchResponse extends Response {
2
6
  status: number;
3
7
  statusText: string;
4
8
  headers: Headers;
@@ -1,5 +1,9 @@
1
- import { CookieRow } from "./CookieRow";
1
+ import CookieRow from "./CookieRow";
2
2
 
3
3
  export function isCookieRow(obj: any): obj is CookieRow {
4
- return obj.domain !== undefined && obj.name !== undefined && obj.value !== undefined;
4
+ return (
5
+ obj.domain !== undefined &&
6
+ obj.name !== undefined &&
7
+ obj.value !== undefined
8
+ );
5
9
  }
@@ -1,5 +1,9 @@
1
- import { ExportedCookie } from "./ExportedCookie";
1
+ import ExportedCookie from "./ExportedCookie";
2
2
 
3
3
  export function isExportedCookie(obj: any): obj is ExportedCookie {
4
- return obj.domain !== undefined && obj.name !== undefined && obj.value !== undefined;
4
+ return (
5
+ obj.domain !== undefined &&
6
+ obj.name !== undefined &&
7
+ obj.value !== undefined
8
+ );
5
9
  }
@@ -0,0 +1,14 @@
1
+ import CookieSpec from "./CookieSpec";
2
+
3
+ export function specialCases({ name, domain }: CookieSpec): {
4
+ specifiedName: boolean;
5
+ specifiedDomain: boolean;
6
+ //
7
+ } {
8
+ const wildcardRegexp = /^([*%])$/i;
9
+ return {
10
+ specifiedName: name.match(wildcardRegexp) == null,
11
+ specifiedDomain: domain.match(wildcardRegexp) == null,
12
+ //
13
+ };
14
+ }
@@ -0,0 +1,6 @@
1
+ export function stringToRegex(s: string): RegExp {
2
+ const s1 = s.replaceAll(/\./gi, ".");
3
+ const s2 = s1.replace(/%/g, ".*");
4
+ const s3 = s2.replace(/\*/g, ".*");
5
+ return new RegExp(s3);
6
+ }
package/src/argv.ts CHANGED
@@ -1 +1,5 @@
1
+ import minimist from "minimist";
2
+
1
3
  export const argv: string[] = process.argv ?? [];
4
+
5
+ export const parsedArgs: minimist.ParsedArgs = minimist(argv.slice(2));
@@ -9,10 +9,17 @@ import { doSqliteQuery1 } from "../doSqliteQuery1";
9
9
  import { merge } from "lodash";
10
10
  import { isCookieRow } from "../IsCookieRow";
11
11
  import { isExportedCookie } from "../IsExportedCookie";
12
- import { CookieRow } from "../CookieRow";
13
- import { ExportedCookie } from "../ExportedCookie";
12
+ import CookieRow from "../CookieRow";
13
+ import ExportedCookie from "../ExportedCookie";
14
+ import { stringToRegex } from "../StringToRegex";
15
+ import { parsedArgs } from "../argv";
16
+ import fs from "fs";
17
+ import * as sqlite3 from "sqlite3";
18
+ import { DoSqliteQuery1Params } from "../doSqliteQuery1Params";
14
19
 
15
20
  export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
21
+ browserName = "Chrome";
22
+
16
23
  async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
17
24
  if (process.platform !== "darwin") {
18
25
  throw new Error("This only works on macOS");
@@ -23,20 +30,24 @@ export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
23
30
  return getChromeCookies({
24
31
  requireJwt: false,
25
32
  name,
26
- domain
33
+ domain,
27
34
  });
28
35
  }
29
36
  }
30
37
 
31
- async function getPromise1(name: string, domain: string, file: string): Promise<CookieRow[]> {
38
+ async function getPromise1(
39
+ name: string,
40
+ domain: string,
41
+ file: string
42
+ ): Promise<CookieRow[]> {
32
43
  try {
33
44
  return await getEncryptedChromeCookie({
34
45
  name: name,
35
46
  domain: domain,
36
- file: file
47
+ file: file,
37
48
  });
38
49
  } catch (e) {
39
- if (env.VERBOSE) {
50
+ if (parsedArgs.verbose) {
40
51
  console.log("Error getting encrypted cookie", e);
41
52
  }
42
53
  return [];
@@ -47,13 +58,15 @@ async function getPromise(name: string, domain: string): Promise<CookieRow[]> {
47
58
  try {
48
59
  const files: string[] = await findAllFiles({
49
60
  path: chromeLocal,
50
- name: "Cookies"
61
+ name: "Cookies",
51
62
  });
52
- const promises: Promise<CookieRow[]>[] = files.map((file) => getPromise1(name, domain, file));
53
- const results1: Awaited<CookieRow[]>[] = await Promise.all(promises);
63
+ const promises: Promise<CookieRow[]>[] = files.map((file) =>
64
+ getPromise1(name, domain, file)
65
+ );
66
+ const results1: CookieRow[][] = await Promise.all(promises);
54
67
  return results1.flat().filter(isCookieRow);
55
68
  } catch (error) {
56
- if (env.VERBOSE) {
69
+ if (parsedArgs.verbose) {
57
70
  console.log("error", error);
58
71
  }
59
72
  return [];
@@ -65,7 +78,7 @@ async function decryptValue(password: string, encryptedValue: Buffer) {
65
78
  try {
66
79
  d = await decrypt(password, encryptedValue);
67
80
  } catch (e) {
68
- if (env.VERBOSE) {
81
+ if (parsedArgs.verbose) {
69
82
  console.log("Error decrypting cookie", e);
70
83
  }
71
84
  d = null;
@@ -73,19 +86,17 @@ async function decryptValue(password: string, encryptedValue: Buffer) {
73
86
  return d ?? encryptedValue.toString("utf-8");
74
87
  }
75
88
 
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
- }
89
+ async function getChromeCookies({
90
+ name,
91
+ domain = "%",
92
+ requireJwt = false,
93
+ }: {
94
+ name: string;
95
+ domain: string;
96
+ requireJwt: boolean | undefined;
87
97
  //
88
- ): Promise<ExportedCookie[]> {
98
+ }): //
99
+ Promise<ExportedCookie[]> {
89
100
  const encryptedDataItems: CookieRow[] = await getPromise(name, domain);
90
101
  const password: string = await getChromePassword();
91
102
  const decrypted: Promise<ExportedCookie | null>[] = encryptedDataItems
@@ -95,15 +106,24 @@ async function getChromeCookies(
95
106
  const decryptedValue = await decryptValue(password, encryptedValue);
96
107
  const meta = {};
97
108
  merge(meta, cookieRow.meta ?? {});
98
- return {
109
+ const exportedCookie: ExportedCookie = {
99
110
  domain: cookieRow.domain,
100
111
  name: cookieRow.name,
101
112
  value: decryptedValue,
102
- meta: meta
113
+ meta: meta,
103
114
  };
115
+ const expiry = cookieRow.expiry;
116
+ if (expiry) {
117
+ merge(exportedCookie, {
118
+ expiry: new Date(expiry),
119
+ });
120
+ }
121
+ return exportedCookie;
104
122
  });
105
- const results: ExportedCookie[] = (await Promise.all(decrypted)).filter(isExportedCookie);
106
- if (env.VERBOSE) {
123
+ const results: ExportedCookie[] = (await Promise.all(decrypted)).filter(
124
+ isExportedCookie
125
+ );
126
+ if (parsedArgs.verbose) {
107
127
  console.log("results", results);
108
128
  }
109
129
  return results;
@@ -117,51 +137,79 @@ const chromeLocal = path.join(
117
137
  "Chrome"
118
138
  );
119
139
 
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[]> {
140
+ async function getEncryptedChromeCookie({
141
+ name,
142
+ domain,
143
+ file = path.join(chromeLocal, "Default", "Cookies"),
144
+ }: //
145
+ {
146
+ name: string;
147
+ domain: string;
148
+ file: string;
149
+ }): Promise<CookieRow[]> {
131
150
  if (!existsSync(file)) {
132
151
  throw new Error(`File ${file} does not exist`);
133
152
  }
134
- if (env.VERBOSE) {
153
+ if (parsedArgs.verbose) {
135
154
  const s = file.split("/").slice(-3).join("/");
136
155
  console.log(`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`);
137
156
  }
138
157
  let sql;
139
- sql = `SELECT encrypted_value, name, host_key FROM cookies`;
158
+ //language=SQL
159
+ sql = "SELECT * FROM cookies";
160
+ // sql = "SELECT encrypted_value, name, host_key FROM cookies";
161
+
140
162
  const wildcardRegexp = /^([*%])$/i;
141
163
  const specifiedName = name.match(wildcardRegexp) == null;
142
164
  const specifiedDomain = domain.match(wildcardRegexp) == null;
143
- if (specifiedName || specifiedDomain) {
165
+ const wildcardDomain = domain.match(/[%*]/) != null;
166
+ const queryDomain = specifiedDomain && !wildcardDomain;
167
+ // if we have a wildcard domain, we need to use a regexp
168
+ if (specifiedName || queryDomain) {
144
169
  sql += ` WHERE `;
145
170
  if (specifiedName) {
146
171
  sql += `name = '${name}'`;
147
- if (specifiedDomain) {
172
+ if (queryDomain) {
148
173
  sql += ` AND `;
149
174
  }
150
175
  }
151
- if (specifiedDomain) {
152
- sql += `host_key LIKE '${domain}';`;
176
+ if (queryDomain) {
177
+ // leading dot replaced with % to match subdomains
178
+ const sqlEmbedDomain = domain.replace(/^[.%]?/, "%");
179
+ sql += `host_key LIKE '${sqlEmbedDomain}';`;
153
180
  }
154
181
  }
155
- return doSqliteQuery1(file, sql);
182
+ const sqliteQuery1: CookieRow[] = await doChromeSqliteQuery1({
183
+ file: file,
184
+ sql: sql,
185
+ rowTransform: (row) => {
186
+ const cookieRow = {
187
+ expiry: (row["expires_utc"] / 1000000 - 11644473600) * 1000,
188
+ domain: row["host_key"],
189
+ name: row["name"],
190
+ value: row["encrypted_value"],
191
+ };
192
+ if (parsedArgs.verbose) {
193
+ console.log("CookieRow", cookieRow);
194
+ }
195
+ return cookieRow;
196
+ },
197
+ });
198
+ return sqliteQuery1.filter((row) => {
199
+ return row.domain.match(stringToRegex(domain)) != null;
200
+ });
156
201
  }
157
202
 
158
203
  async function getChromePassword(): Promise<string> {
159
204
  return execSimple(
160
- "security find-generic-password -w -s \"Chrome Safe Storage\""
205
+ 'security find-generic-password -w -s "Chrome Safe Storage"'
161
206
  );
162
207
  }
163
208
 
164
- async function decrypt(password: crypto.BinaryLike, encryptedData: Buffer): Promise<string> {
209
+ async function decrypt(
210
+ password: crypto.BinaryLike,
211
+ encryptedData: Buffer
212
+ ): Promise<string> {
165
213
  if (typeof password !== "string") {
166
214
  throw new Error("password must be a string: " + password);
167
215
  }
@@ -173,7 +221,7 @@ async function decrypt(password: crypto.BinaryLike, encryptedData: Buffer): Prom
173
221
  if (!(encryptedData1 instanceof Buffer)) {
174
222
  if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
175
223
  [encryptedData1] = encryptedData1;
176
- if (env.VERBOSE) {
224
+ if (parsedArgs.verbose) {
177
225
  console.log(
178
226
  `encryptedData is an array of buffers, selected first: ${encryptedData1}`
179
227
  );
@@ -183,77 +231,113 @@ async function decrypt(password: crypto.BinaryLike, encryptedData: Buffer): Prom
183
231
  }
184
232
  encryptedData1 = Buffer.from(encryptedData1);
185
233
  }
186
- if (env.VERBOSE) {
234
+ if (parsedArgs.verbose) {
187
235
  console.log(`Trying to decrypt with password ${password}`);
188
236
  }
189
237
  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;
238
+ crypto.pbkdf2(password, "saltysalt", 1003, 16, "sha1", (error, buffer) => {
239
+ try {
240
+ if (error) {
241
+ if (parsedArgs.verbose) {
242
+ console.log("Error doing pbkdf2", error);
203
243
  }
244
+ reject(error);
245
+ return;
246
+ }
204
247
 
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;
248
+ if (buffer.length !== 16) {
249
+ if (parsedArgs.verbose) {
250
+ console.log(
251
+ "Error doing pbkdf2, buffer length is not 16",
252
+ buffer.length
253
+ );
214
254
  }
255
+ reject(new Error("Buffer length is not 16"));
256
+ return;
257
+ }
215
258
 
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
- }
259
+ const str = new Array(17).join(" ");
260
+ const iv = Buffer.from(str, "binary");
261
+ const decipher = crypto.createDecipheriv("aes-128-cbc", buffer, iv);
262
+ decipher.setAutoPadding(false);
224
263
 
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
- }
264
+ if (encryptedData1 && encryptedData1.slice) {
265
+ encryptedData1 = encryptedData1.slice(3);
266
+ }
235
267
 
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;
268
+ if (encryptedData1.length % 16 !== 0) {
269
+ if (parsedArgs.verbose) {
270
+ console.log(
271
+ "Error doing pbkdf2, encryptedData length is not a multiple of 16",
272
+ encryptedData1.length
273
+ );
245
274
  }
275
+ reject(new Error("encryptedData length is not a multiple of 16"));
276
+ return;
277
+ }
246
278
 
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);
279
+ let decoded = decipher.update(encryptedData1);
280
+ try {
281
+ decipher.final("utf-8");
254
282
  } catch (e) {
283
+ if (parsedArgs.verbose) {
284
+ console.log("Error doing decipher.final()", e);
285
+ }
255
286
  reject(e);
287
+ return;
288
+ }
289
+
290
+ const padding = decoded[decoded.length - 1];
291
+ if (padding) {
292
+ decoded = decoded.slice(0, 0 - padding);
256
293
  }
257
- });
294
+ // noinspection JSCheckFunctionSignatures
295
+ const decodedString = decoded.toString("utf8");
296
+ resolve(decodedString);
297
+ } catch (e) {
298
+ reject(e);
299
+ }
300
+ });
301
+ });
302
+ }
303
+
304
+ export async function doChromeSqliteQuery1({
305
+ file,
306
+ sql,
307
+ rowTransform,
308
+ }: DoSqliteQuery1Params): Promise<CookieRow[]> {
309
+ if (!file || (file && !fs.existsSync(file))) {
310
+ throw new Error(`doSqliteQuery1: file ${file} does not exist`);
311
+ }
312
+ const db = new sqlite3.Database(file);
313
+ return new Promise((resolve, reject) => {
314
+ db.all(sql, (err: Error, rows: any[]) => {
315
+ if (err) {
316
+ reject(err);
317
+ return;
318
+ }
319
+ const rows1: any[] = rows;
320
+ if (rows1 == null || rows1.length === 0) {
321
+ resolve([]);
322
+ return;
323
+ }
324
+ if (Array.isArray(rows1)) {
325
+ const cookieRows: CookieRow[] = rows1.map((row: any) => {
326
+ const newVar = {
327
+ meta: {
328
+ file: file,
329
+ },
330
+ };
331
+ const cookieRow: CookieRow = rowTransform(row);
332
+ return merge(newVar, cookieRow);
333
+ });
334
+ resolve(cookieRows);
335
+ return;
336
+ }
337
+ if (parsedArgs.verbose) {
338
+ console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
339
+ }
340
+ resolve([rows1]);
341
+ });
258
342
  });
259
343
  }
@@ -2,13 +2,28 @@ import ChromeCookieQueryStrategy from "./ChromeCookieQueryStrategy";
2
2
  import FirefoxCookieQueryStrategy from "./FirefoxCookieQueryStrategy";
3
3
  import SafariCookieQueryStrategy from "./SafariCookieQueryStrategy";
4
4
  import CookieQueryStrategy from "./CookieQueryStrategy";
5
- import { ExportedCookie } from "../ExportedCookie";
5
+ import ExportedCookie from "../ExportedCookie";
6
+ import LRUCache from "lru-cache";
7
+ import CookieStoreQueryStrategy from "./CookieStoreQueryStrategy";
8
+ import { red } from "colorette";
9
+ import { merge } from "lodash";
10
+ import { parsedArgs } from "../argv";
11
+
12
+ const cache = new LRUCache<string, ExportedCookie[]>({
13
+ ttl: 1000 * 2,
14
+ max: 10,
15
+ });
16
+
17
+ export default class CompositeCookieQueryStrategy
18
+ implements CookieQueryStrategy
19
+ {
20
+ browserName = "all";
6
21
 
7
- export default class CompositeCookieQueryStrategy implements CookieQueryStrategy {
8
22
  #strategies;
9
23
 
10
24
  constructor() {
11
25
  this.#strategies = [
26
+ CookieStoreQueryStrategy,
12
27
  ChromeCookieQueryStrategy,
13
28
  FirefoxCookieQueryStrategy,
14
29
  SafariCookieQueryStrategy,
@@ -18,13 +33,40 @@ export default class CompositeCookieQueryStrategy implements CookieQueryStrategy
18
33
  }
19
34
 
20
35
  async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
21
- const results = await Promise.all(
22
- this.#strategies.map(async (strategy) => {
36
+ // domain = domain.match(/(\w+.+\w+)/gi)?.pop() ?? domain;
37
+ const key = `${name}:${domain}`;
38
+ const cached = cache.get(key);
39
+ if (cached) {
40
+ return cached;
41
+ }
42
+ if (parsedArgs.verbose) {
43
+ console.log("Querying cookies:", name, domain);
44
+ }
45
+ const results: ExportedCookie[][] = await Promise.all(
46
+ this.#strategies.map(async (strategy: CookieQueryStrategy) => {
23
47
  // @ts-ignore
24
- const cookies: Promise<ExportedCookie[]> = strategy.queryCookies(name, domain);
25
- return cookies.catch(() => []);
48
+ return strategy
49
+ .queryCookies(name, domain)
50
+ .then((cookies: ExportedCookie[]) => {
51
+ return cookies.map((cookie: ExportedCookie) => {
52
+ return merge(cookie, {
53
+ meta: {
54
+ browser: strategy.browserName,
55
+ },
56
+ });
57
+ });
58
+ })
59
+ .catch((e) => {
60
+ console.log(
61
+ red(`Error querying ${strategy.browserName} cookies`),
62
+ e
63
+ );
64
+ return [];
65
+ });
26
66
  })
27
67
  );
28
- return results.flat();
68
+ const flat: ExportedCookie[] = results.flat();
69
+ cache.set(`${name}:${domain}`, flat);
70
+ return flat;
29
71
  }
30
72
  }
@@ -1,5 +1,6 @@
1
- import { ExportedCookie } from "../ExportedCookie";
1
+ import ExportedCookie from "../ExportedCookie";
2
2
 
3
3
  export default interface CookieQueryStrategy {
4
- queryCookies(name: string, domain: string): Promise<ExportedCookie[]>
4
+ browserName: string;
5
+ queryCookies(name: string, domain: string): Promise<ExportedCookie[]>;
5
6
  }