@mherod/get-cookie 2.0.0-beta.9 → 2.0.0-rc.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.
@@ -23,17 +23,21 @@ export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
23
23
  return getChromeCookies({
24
24
  requireJwt: false,
25
25
  name,
26
- domain
26
+ domain,
27
27
  });
28
28
  }
29
29
  }
30
30
 
31
- async function getPromise1(name: string, domain: string, file: string): Promise<CookieRow[]> {
31
+ async function getPromise1(
32
+ name: string,
33
+ domain: string,
34
+ file: string
35
+ ): Promise<CookieRow[]> {
32
36
  try {
33
37
  return await getEncryptedChromeCookie({
34
38
  name: name,
35
39
  domain: domain,
36
- file: file
40
+ file: file,
37
41
  });
38
42
  } catch (e) {
39
43
  if (env.VERBOSE) {
@@ -47,9 +51,11 @@ async function getPromise(name: string, domain: string): Promise<CookieRow[]> {
47
51
  try {
48
52
  const files: string[] = await findAllFiles({
49
53
  path: chromeLocal,
50
- name: "Cookies"
54
+ name: "Cookies",
51
55
  });
52
- const promises: Promise<CookieRow[]>[] = files.map((file) => getPromise1(name, domain, file));
56
+ const promises: Promise<CookieRow[]>[] = files.map((file) =>
57
+ getPromise1(name, domain, file)
58
+ );
53
59
  const results1: Awaited<CookieRow[]>[] = await Promise.all(promises);
54
60
  return results1.flat().filter(isCookieRow);
55
61
  } catch (error) {
@@ -73,19 +79,17 @@ async function decryptValue(password: string, encryptedValue: Buffer) {
73
79
  return d ?? encryptedValue.toString("utf-8");
74
80
  }
75
81
 
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
- }
82
+ async function getChromeCookies({
83
+ name,
84
+ domain = "%",
85
+ requireJwt = false,
86
+ }: {
87
+ name: string;
88
+ domain: string;
89
+ requireJwt: boolean | undefined;
87
90
  //
88
- ): Promise<ExportedCookie[]> {
91
+ }): //
92
+ Promise<ExportedCookie[]> {
89
93
  const encryptedDataItems: CookieRow[] = await getPromise(name, domain);
90
94
  const password: string = await getChromePassword();
91
95
  const decrypted: Promise<ExportedCookie | null>[] = encryptedDataItems
@@ -99,10 +103,12 @@ async function getChromeCookies(
99
103
  domain: cookieRow.domain,
100
104
  name: cookieRow.name,
101
105
  value: decryptedValue,
102
- meta: meta
106
+ meta: meta,
103
107
  };
104
108
  });
105
- const results: ExportedCookie[] = (await Promise.all(decrypted)).filter(isExportedCookie);
109
+ const results: ExportedCookie[] = (await Promise.all(decrypted)).filter(
110
+ isExportedCookie
111
+ );
106
112
  if (env.VERBOSE) {
107
113
  console.log("results", results);
108
114
  }
@@ -117,17 +123,16 @@ const chromeLocal = path.join(
117
123
  "Chrome"
118
124
  );
119
125
 
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[]> {
126
+ async function getEncryptedChromeCookie({
127
+ name,
128
+ domain,
129
+ file = path.join(chromeLocal, "Default", "Cookies"),
130
+ }: //
131
+ {
132
+ name: string;
133
+ domain: string;
134
+ file: string;
135
+ }): Promise<CookieRow[]> {
131
136
  if (!existsSync(file)) {
132
137
  throw new Error(`File ${file} does not exist`);
133
138
  }
@@ -136,7 +141,8 @@ async function getEncryptedChromeCookie(
136
141
  console.log(`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`);
137
142
  }
138
143
  let sql;
139
- sql = `SELECT encrypted_value, name, host_key FROM cookies`;
144
+ //language=SQL
145
+ sql = "SELECT encrypted_value, name, host_key FROM cookies";
140
146
  const wildcardRegexp = /^([*%])$/i;
141
147
  const specifiedName = name.match(wildcardRegexp) == null;
142
148
  const specifiedDomain = domain.match(wildcardRegexp) == null;
@@ -152,16 +158,29 @@ async function getEncryptedChromeCookie(
152
158
  sql += `host_key LIKE '${domain}';`;
153
159
  }
154
160
  }
155
- return doSqliteQuery1(file, sql);
161
+ return doSqliteQuery1({
162
+ file: file,
163
+ sql: sql,
164
+ rowTransform: (row) => {
165
+ return {
166
+ domain: row["host_key"],
167
+ name: row["name"],
168
+ value: row["encrypted_value"],
169
+ };
170
+ },
171
+ });
156
172
  }
157
173
 
158
174
  async function getChromePassword(): Promise<string> {
159
175
  return execSimple(
160
- "security find-generic-password -w -s \"Chrome Safe Storage\""
176
+ 'security find-generic-password -w -s "Chrome Safe Storage"'
161
177
  );
162
178
  }
163
179
 
164
- async function decrypt(password: crypto.BinaryLike, encryptedData: Buffer): Promise<string> {
180
+ async function decrypt(
181
+ password: crypto.BinaryLike,
182
+ encryptedData: Buffer
183
+ ): Promise<string> {
165
184
  if (typeof password !== "string") {
166
185
  throw new Error("password must be a string: " + password);
167
186
  }
@@ -187,73 +206,68 @@ async function decrypt(password: crypto.BinaryLike, encryptedData: Buffer): Prom
187
206
  console.log(`Trying to decrypt with password ${password}`);
188
207
  }
189
208
  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;
209
+ crypto.pbkdf2(password, "saltysalt", 1003, 16, "sha1", (error, buffer) => {
210
+ try {
211
+ if (error) {
212
+ if (env.VERBOSE) {
213
+ console.log("Error doing pbkdf2", error);
203
214
  }
215
+ reject(error);
216
+ return;
217
+ }
204
218
 
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;
219
+ if (buffer.length !== 16) {
220
+ if (env.VERBOSE) {
221
+ console.log(
222
+ "Error doing pbkdf2, buffer length is not 16",
223
+ buffer.length
224
+ );
214
225
  }
226
+ reject(new Error("Buffer length is not 16"));
227
+ return;
228
+ }
215
229
 
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
- }
230
+ const str = new Array(17).join(" ");
231
+ const iv = Buffer.from(str, "binary");
232
+ const decipher = crypto.createDecipheriv("aes-128-cbc", buffer, iv);
233
+ decipher.setAutoPadding(false);
224
234
 
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
+ if (encryptedData1 && encryptedData1.slice) {
236
+ encryptedData1 = encryptedData1.slice(3);
237
+ }
235
238
 
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;
239
+ if (encryptedData1.length % 16 !== 0) {
240
+ if (env.VERBOSE) {
241
+ console.log(
242
+ "Error doing pbkdf2, encryptedData length is not a multiple of 16",
243
+ encryptedData1.length
244
+ );
245
245
  }
246
+ reject(new Error("encryptedData length is not a multiple of 16"));
247
+ return;
248
+ }
246
249
 
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);
250
+ let decoded = decipher.update(encryptedData1);
251
+ try {
252
+ decipher.final("utf-8");
254
253
  } catch (e) {
254
+ if (env.VERBOSE) {
255
+ console.log("Error doing decipher.final()", e);
256
+ }
255
257
  reject(e);
258
+ return;
259
+ }
260
+
261
+ const padding = decoded[decoded.length - 1];
262
+ if (padding) {
263
+ decoded = decoded.slice(0, 0 - padding);
256
264
  }
257
- });
265
+ // noinspection JSCheckFunctionSignatures
266
+ const decodedString = decoded.toString("utf8");
267
+ resolve(decodedString);
268
+ } catch (e) {
269
+ reject(e);
270
+ }
271
+ });
258
272
  });
259
273
  }
@@ -3,8 +3,16 @@ import FirefoxCookieQueryStrategy from "./FirefoxCookieQueryStrategy";
3
3
  import SafariCookieQueryStrategy from "./SafariCookieQueryStrategy";
4
4
  import CookieQueryStrategy from "./CookieQueryStrategy";
5
5
  import { ExportedCookie } from "../ExportedCookie";
6
+ import LRUCache from "lru-cache";
6
7
 
7
- export default class CompositeCookieQueryStrategy implements CookieQueryStrategy {
8
+ const cache = new LRUCache<string, ExportedCookie[]>({
9
+ ttl: 1000 * 2,
10
+ max: 10,
11
+ });
12
+
13
+ export default class CompositeCookieQueryStrategy
14
+ implements CookieQueryStrategy
15
+ {
8
16
  #strategies;
9
17
 
10
18
  constructor() {
@@ -18,13 +26,23 @@ export default class CompositeCookieQueryStrategy implements CookieQueryStrategy
18
26
  }
19
27
 
20
28
  async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
29
+ const key = `${name}:${domain}`;
30
+ const cached = cache.get(key);
31
+ if (cached) {
32
+ return cached;
33
+ }
21
34
  const results = await Promise.all(
22
35
  this.#strategies.map(async (strategy) => {
23
36
  // @ts-ignore
24
- const cookies: Promise<ExportedCookie[]> = strategy.queryCookies(name, domain);
37
+ const cookies: Promise<ExportedCookie[]> = strategy.queryCookies(
38
+ name,
39
+ domain
40
+ );
25
41
  return cookies.catch(() => []);
26
42
  })
27
43
  );
28
- return results.flat();
44
+ const flat: ExportedCookie[] = results.flat();
45
+ cache.set(`${name}:${domain}`, flat);
46
+ return flat;
29
47
  }
30
48
  }
@@ -1,5 +1,5 @@
1
1
  import { ExportedCookie } from "../ExportedCookie";
2
2
 
3
3
  export default interface CookieQueryStrategy {
4
- queryCookies(name: string, domain: string): Promise<ExportedCookie[]>
4
+ queryCookies(name: string, domain: string): Promise<ExportedCookie[]>;
5
5
  }
@@ -1,11 +1,13 @@
1
+ import * as path from "path";
1
2
  import CookieQueryStrategy from "./CookieQueryStrategy";
2
3
  import { env, HOME } from "../global";
3
4
  import { existsSync } from "fs";
4
- import { toStringOrNull } from "../utils";
5
5
  import { findAllFiles } from "../findAllFiles";
6
- import * as path from "path";
7
6
  import { doSqliteQuery1 } from "../doSqliteQuery1";
8
7
  import { ExportedCookie } from "../ExportedCookie";
8
+ import { CookieRow } from "../CookieRow";
9
+ import { CookieSpec } from "../CookieSpec";
10
+ import { specialCases } from "../SpecialCases";
9
11
 
10
12
  export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
11
13
  async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
@@ -16,26 +18,21 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
16
18
  return [];
17
19
  }
18
20
  const cookies = await this.#getFirefoxCookie({ name, domain });
19
- return Array.isArray(cookies) ? cookies.map(toStringOrNull) : [];
21
+ if (Array.isArray(cookies)) {
22
+ return cookies.map((cookie: CookieRow) => {
23
+ return {
24
+ domain: cookie.domain,
25
+ name: cookie.name,
26
+ value: cookie.value.toString("utf8"),
27
+ };
28
+ });
29
+ } else {
30
+ return [];
31
+ }
20
32
  }
21
33
 
22
- /**
23
- *
24
- * @param name
25
- * @param domain
26
- * @returns {Promise<Buffer>}
27
- */
28
34
  async #getFirefoxCookie(
29
- //
30
- {
31
- name,
32
- domain
33
- }: {
34
- name: string,
35
- domain: string
36
- //
37
- }
38
- //
35
+ { name, domain }: CookieSpec //
39
36
  ) {
40
37
  const files: string[] = await findAllFiles({
41
38
  path: path.join(
@@ -45,48 +42,57 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
45
42
  "Firefox",
46
43
  "Profiles"
47
44
  ),
48
- name: "cookies.sqlite"
45
+ name: "cookies.sqlite",
49
46
  });
50
- const all = await Promise.all(
51
- files.map((file) => {
52
- return this.#queryCookiesDb(file, name, domain);
53
- })
54
- );
47
+ const fn: (file: string) => Promise<CookieRow[]> = async (file: string) => {
48
+ return await this.#queryCookiesDb(file, name, domain);
49
+ };
50
+ const all: Awaited<CookieRow[]>[] = await Promise.all(files.map(fn));
55
51
  return all.flat();
56
52
  }
57
53
 
58
- #queryCookiesDb(file: string, name: string, domain: string) {
54
+ async #queryCookiesDb(
55
+ file: string,
56
+ name: string,
57
+ domain: string
58
+ ): Promise<CookieRow[]> {
59
59
  if (file && !existsSync(file)) {
60
60
  throw new Error(`File ${file} does not exist`);
61
61
  }
62
- if (env.VERBOSE) {
63
- console.log(`Trying Firefox cookie ${name} for domain ${domain}`);
64
- }
65
62
  let sql;
66
63
  //language=SQL
67
- sql = "SELECT value FROM moz_cookies";
68
- if (typeof name === "string" || typeof domain === "string") {
64
+ sql = "SELECT value, name, host FROM moz_cookies";
65
+ const { specifiedName, specifiedDomain } = specialCases({ name, domain });
66
+ if (specifiedName || specifiedDomain) {
69
67
  sql += ` WHERE `;
70
- if (typeof name === "string") {
68
+ if (specifiedName) {
71
69
  sql += `name = '${name}'`;
72
- if (typeof domain === "string") {
70
+ if (specifiedDomain) {
73
71
  sql += ` AND `;
74
72
  }
75
73
  }
76
- if (typeof domain === "string") {
74
+ if (specifiedDomain) {
77
75
  sql += `host LIKE '${domain}';`;
78
76
  }
79
77
  }
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(() => []);
78
+ const rowTransform = (row: any) => {
79
+ // row is object key by column name
80
+ const value = row.value as string;
81
+ return {
82
+ domain: row.domain as string,
83
+ name: row.name as string,
84
+ value: Buffer.from(value, "utf8"),
85
+ };
86
+ };
87
+ try {
88
+ return await doSqliteQuery1({
89
+ file,
90
+ sql,
91
+ rowTransform,
92
+ });
93
+ } catch (e) {
94
+ console.error(`Error querying ${file}`, e);
95
+ return [];
96
+ }
91
97
  }
92
98
  }
package/src/cli.ts CHANGED
@@ -1,24 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import minimist from "minimist";
4
+ import { argv } from "./argv";
3
5
  import { env } from "./global";
4
6
  import { queryCookies } from "./queryCookies";
5
- import { argv } from "./argv";
6
7
  import { groupBy } from "lodash";
7
- import { blue, green } from "colorette";
8
+ import { green, yellow } from "colorette";
8
9
  import { resultsRendered } from "./resultsRendered";
9
- import { ExportedCookie } from "./ExportedCookie";
10
+ import { fetchWithCookies } from "./fetchWithCookies";
11
+ import { unpackHeaders } from "./unpackHeaders";
10
12
 
11
- function combinedString(results: ExportedCookie[]) {
12
- return blue(resultsRendered(results));
13
- }
13
+ const parsedArgs: minimist.ParsedArgs = minimist(argv);
14
14
 
15
15
  async function cliQueryCookies(name: string, domain: string) {
16
16
  try {
17
17
  const results = await queryCookies({ name, domain });
18
18
  if (results.length > 0) {
19
19
  if (argv.includes("--combined-string")) {
20
- console.log(combinedString(results));
21
- } else if (argv.includes("--dump")) {
20
+ console.log(yellow(resultsRendered(results)));
21
+ } else if (argv.includes("--render") || argv.includes("-r")) {
22
+ console.log(yellow(resultsRendered(results)));
23
+ } else if (argv.includes("--dump") || argv.includes("-d")) {
22
24
  console.log(results);
23
25
  } else if (argv.includes("--dump-grouped")) {
24
26
  const groupedByFile = groupBy(results, (r) => r.meta?.file);
@@ -27,7 +29,7 @@ async function cliQueryCookies(name: string, domain: string) {
27
29
  const groupedByFile = groupBy(results, (r) => r.meta?.file);
28
30
  for (const file of Object.keys(groupedByFile)) {
29
31
  let results = groupedByFile[file];
30
- console.log(green(file) + ": ", combinedString(results));
32
+ console.log(green(file) + ": ", yellow(resultsRendered(results)));
31
33
  }
32
34
  } else {
33
35
  for (const result of results) {
@@ -42,41 +44,75 @@ async function cliQueryCookies(name: string, domain: string) {
42
44
  }
43
45
  }
44
46
 
45
- if (argv && argv.length > 2) {
46
- const name = argv[2];
47
+ function main() {
48
+ if (argv && argv.length > 2) {
49
+ const arg2 = argv[2];
50
+ const arg3 = argv[3];
51
+ if (arg2 == "--fetch" || arg2 == "-f") {
52
+ const f = parsedArgs["f"];
53
+ let url: URL;
54
+ try {
55
+ url = new URL(f);
56
+ } catch (e) {
57
+ console.error("Invalid URL", arg3);
58
+ return;
59
+ }
60
+ const headerArgs: string[] | string = parsedArgs["H"];
61
+ const headers = unpackHeaders(headerArgs);
62
+ const onfulfilled = (res: Response) => {
63
+ return res.text().then((r) => {
64
+ console.log(r);
65
+ });
66
+ };
67
+ fetchWithCookies(
68
+ url,
69
+ {
70
+ //
71
+ headers,
72
+ }
73
+ //
74
+ ).then(
75
+ onfulfilled,
76
+ console.error
77
+ //
78
+ );
79
+ return;
80
+ }
81
+ let domain;
82
+ if (arg3 != null && arg3.indexOf(".") > -1) {
83
+ domain = arg3;
84
+ } else {
85
+ domain = "%";
86
+ }
47
87
 
48
- let domain;
49
- if (argv[3] != null && argv[3].indexOf(".") > -1) {
50
- domain = argv[3];
51
- } else {
52
- domain = "%";
53
- }
88
+ const tru = `${true}`;
54
89
 
55
- const tru = `${true}`;
90
+ if (argv.includes("--require-jwt")) {
91
+ env.REQUIRE_JWT = tru;
92
+ }
93
+ if (argv.includes("--verbose")) {
94
+ env.VERBOSE = tru;
95
+ }
96
+ if (argv.includes("--chrome-only")) {
97
+ env.CHROME_ONLY = tru;
98
+ }
99
+ if (argv.includes("--firefox-only")) {
100
+ env.FIREFOX_ONLY = tru;
101
+ }
102
+ if (argv.includes("--ignore-expired")) {
103
+ env.IGNORE_EXPIRED = tru;
104
+ }
56
105
 
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
- }
106
+ if (argv.includes("--single")) {
107
+ env.SINGLE = tru;
108
+ }
72
109
 
73
- if (argv.includes("--single")) {
74
- env.SINGLE = tru;
75
- }
110
+ if (env.VERBOSE) {
111
+ console.log("Verbose mode", argv);
112
+ }
76
113
 
77
- if (env.VERBOSE) {
78
- console.log("Verbose mode", argv);
114
+ cliQueryCookies(arg2, domain).catch(console.error);
79
115
  }
80
-
81
- cliQueryCookies(name, domain).catch(console.error);
82
116
  }
117
+
118
+ main();
@@ -1,9 +1,20 @@
1
1
  import * as fs from "fs";
2
2
  import * as sqlite3 from "sqlite3";
3
3
  import { CookieRow } from "./CookieRow";
4
+ import { merge } from "lodash";
4
5
 
5
- export async function doSqliteQuery1(file: string, sql: string): Promise<CookieRow[]> {
6
- if (!fs.existsSync(file)) {
6
+ interface DoSqliteQuery1Params {
7
+ file: string;
8
+ sql: string;
9
+ rowTransform: (row: any) => CookieRow;
10
+ }
11
+
12
+ export async function doSqliteQuery1({
13
+ file,
14
+ sql,
15
+ rowTransform,
16
+ }: DoSqliteQuery1Params): Promise<CookieRow[]> {
17
+ if (!file || (file && !fs.existsSync(file))) {
7
18
  throw new Error(`doSqliteQuery1: file ${file} does not exist`);
8
19
  }
9
20
  const db = new sqlite3.Database(file);
@@ -19,15 +30,14 @@ export async function doSqliteQuery1(file: string, sql: string): Promise<CookieR
19
30
  return;
20
31
  }
21
32
  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"],
33
+ const cookieRows: CookieRow[] = rows1.map((row: any) => {
34
+ const newVar = {
27
35
  meta: {
28
- file: file
29
- }
36
+ file: file,
37
+ },
30
38
  };
39
+ const cookieRow: CookieRow = rowTransform(row);
40
+ return merge(newVar, cookieRow);
31
41
  });
32
42
  resolve(cookieRows);
33
43
  return;