@mherod/get-cookie 2.0.0-beta.1 → 2.0.0-beta.3

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/src/cli.js CHANGED
@@ -3,15 +3,39 @@
3
3
  import { version } from "../package.json";
4
4
  import { env } from "./global";
5
5
  import { queryCookies } from "./queryCookies";
6
+ import { argv } from "./argv";
7
+ import { groupBy, uniqBy } from "lodash";
8
+ import { blue, green } from "colorette";
6
9
 
7
- const argv = process.argv;
10
+ function combinedString(results) {
11
+ return blue(
12
+ uniqBy(results, (r) => r.name)
13
+ .map((r) => r.name + "=" + r.value)
14
+ .join("; ")
15
+ );
16
+ }
8
17
 
9
18
  async function cliQueryCookies(name, domain) {
10
19
  try {
11
20
  const results = await queryCookies({ name, domain });
12
21
  if (results.length > 0) {
13
- for (const result of results) {
14
- console.log(result);
22
+ if (argv.includes("--combined-string")) {
23
+ console.log(combinedString(results));
24
+ } else if (argv.includes("--dump")) {
25
+ console.log(results);
26
+ } else if (argv.includes("--dump-grouped")) {
27
+ const groupedByFile = groupBy(results, (r) => r.meta?.file);
28
+ console.log(green(JSON.stringify(groupedByFile, null, 2)));
29
+ } else if (argv.includes("--combined-string-grouped")) {
30
+ const groupedByFile = groupBy(results, (r) => r.meta?.file);
31
+ for (const file of Object.keys(groupedByFile)) {
32
+ let results = groupedByFile[file];
33
+ console.log(green(file) + ": ", combinedString(results));
34
+ }
35
+ } else {
36
+ for (const result of results) {
37
+ console.log(result.value);
38
+ }
15
39
  }
16
40
  } else {
17
41
  console.error("No results");
@@ -62,9 +86,5 @@ if (argv && argv.length > 2) {
62
86
  console.log("Verbose mode", argv);
63
87
  }
64
88
 
65
- cliQueryCookies(name, domain)
66
- .then(() => {
67
- //
68
- })
69
- .catch(console.error);
89
+ cliQueryCookies(name, domain).catch(console.error);
70
90
  }
@@ -0,0 +1,47 @@
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
+ if (process.env.VERBOSE) {
14
+ console.log(`doSqliteQuery1: error ${err}`);
15
+ }
16
+ reject(err);
17
+ return;
18
+ }
19
+ const rows1: any[] = rows;
20
+ if (rows1 == null || rows1.length === 0) {
21
+ if (process.env.VERBOSE) {
22
+ console.log(`doSqliteQuery1: no rows`);
23
+ }
24
+ resolve([]);
25
+ return;
26
+ }
27
+ if (Array.isArray(rows1)) {
28
+ const cookieRows: CookieRow[] = rows1.map((row) => {
29
+ return {
30
+ domain: row["host_key"],
31
+ name: row["name"],
32
+ value: row["encrypted_value"],
33
+ meta: {
34
+ file: file
35
+ }
36
+ }
37
+ })
38
+ resolve(cookieRows);
39
+ return;
40
+ }
41
+ if (process.env.VERBOSE) {
42
+ console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
43
+ }
44
+ resolve([rows1]);
45
+ });
46
+ });
47
+ }
@@ -1,28 +1,24 @@
1
1
  import { env } from "./global";
2
- import fs from "fs";
2
+ import * as fs from "fs";
3
3
 
4
- /**
5
- *
6
- * @param path
7
- * @param name
8
- * @param rootSegments
9
- * @param maxDepth
10
- * @returns {Promise<[string]>}
11
- */
12
- export async function findAllFiles({ path, name, maxDepth = 2 }) {
13
- if (typeof path !== "string") {
14
- throw new Error("path must be a string");
4
+ export async function findAllFiles(
5
+ {
6
+ path,
7
+ name,
8
+ maxDepth = 2
9
+ //
10
+ }: {
11
+ path: string;
12
+ name: string;
13
+ maxDepth?: number;
15
14
  }
16
- if (typeof name !== "string") {
17
- throw new Error("name must be a string");
18
- }
19
-
15
+ ): Promise<string[]> {
20
16
  const rootSegments = path.split("/").length;
21
17
 
22
18
  if (env.VERBOSE) {
23
19
  console.log(`Searching for ${name} in ${path}`);
24
20
  }
25
- const files = [];
21
+ const files: string[] = [];
26
22
  let readdirSync;
27
23
  try {
28
24
  readdirSync = fs.readdirSync(path);
@@ -30,7 +26,7 @@ export async function findAllFiles({ path, name, maxDepth = 2 }) {
30
26
  if (env.VERBOSE) {
31
27
  console.log(`Error reading ${path}`, e);
32
28
  }
33
- return files;
29
+ return [];
34
30
  }
35
31
  for (const file of readdirSync) {
36
32
  const filePath = path + "/" + file;
@@ -49,8 +45,7 @@ export async function findAllFiles({ path, name, maxDepth = 2 }) {
49
45
  const subFiles = await findAllFiles({
50
46
  path: filePath,
51
47
  name: name,
52
- rootSegments: rootSegments,
53
- maxDepth: 2,
48
+ maxDepth: 2
54
49
  });
55
50
  files.push(...subFiles);
56
51
  } catch (e) {
@@ -1,7 +1,7 @@
1
1
  const { merge } = require("lodash");
2
- export const env = {};
2
+ export const env: any = {};
3
3
  merge(env, process.env);
4
- export const HOME = env["HOME"];
4
+ export const HOME: string = env["HOME"];
5
5
  if (!HOME) {
6
6
  throw new Error("HOME environment variable is not set");
7
7
  }
@@ -1,10 +1,7 @@
1
1
  import jsonwebtoken from "jsonwebtoken";
2
2
  import { env } from "./global";
3
3
 
4
- export default function isValidJwt(token) {
5
- if (typeof token !== "string") {
6
- return false;
7
- }
4
+ export default function isValidJwt(token: any) {
8
5
  try {
9
6
  const result = jsonwebtoken.decode(token, { complete: true });
10
7
  if (env.VERBOSE) {
@@ -1,20 +1,21 @@
1
1
  import CompositeCookieQueryStrategy from "./browsers/CompositeCookieQueryStrategy";
2
- import { uniq } from "lodash";
2
+ import { uniqBy } from "lodash";
3
3
  import { env } from "./global";
4
4
  import isValidJwt from "./isValidJwt";
5
+ import { ExportedCookie } from "./CookieRow";
5
6
 
6
7
  export async function queryCookies(
7
- { name, domain },
8
+ {
9
+ name,
10
+ domain
11
+ }: {
12
+ name: string;
13
+ domain: string;
14
+ },
8
15
  strategy = new CompositeCookieQueryStrategy()
9
16
  ) {
10
- const results = await strategy.queryCookies(name, domain);
11
- const results1 = uniq(results).map((cookie) => {
12
- return {
13
- name: name,
14
- domain: domain,
15
- value: cookie,
16
- };
17
- });
17
+ const results: ExportedCookie[] = await strategy.queryCookies(name, domain);
18
+ const results1: ExportedCookie[] = uniqBy(results, JSON.stringify);
18
19
  const jwtCookies = [];
19
20
  for (const result of results1) {
20
21
  const value = result.value;
@@ -22,7 +23,6 @@ export async function queryCookies(
22
23
  jwtCookies.push(result);
23
24
  }
24
25
  }
25
- const results2 = env.REQUIRE_JWT ? jwtCookies : results1;
26
- const resultsUniq = uniq(results2).map((cookie) => cookie.value);
26
+ const resultsUniq = env.REQUIRE_JWT ? jwtCookies : results1;
27
27
  return env.SINGLE ? [resultsUniq[0]] : resultsUniq;
28
28
  }
package/src/utils.ts ADDED
@@ -0,0 +1,118 @@
1
+ // noinspection JSUnusedGlobalSymbols
2
+
3
+ import {exec, ExecException} from "child_process";
4
+
5
+ export async function execSimple(command: string): Promise<string> {
6
+ if (process.env.VERBOSE) {
7
+ console.log(command);
8
+ }
9
+ return new Promise((resolve, reject) => {
10
+ exec(
11
+ command,
12
+ { encoding: "binary", maxBuffer: 5 * 1024 },
13
+ (error: ExecException | null, stdout: string, stderr: string) => {
14
+ if (error) {
15
+ reject(error);
16
+ return;
17
+ }
18
+ if (stderr) {
19
+ reject(error);
20
+ return;
21
+ }
22
+ if (stdout) {
23
+ resolve(stdout.trim());
24
+ }
25
+ }
26
+ );
27
+ });
28
+ }
29
+
30
+ // export async function execAsBuffer(command: string) {
31
+ // if (process.env.VERBOSE) {
32
+ // console.log(command);
33
+ // }
34
+ // return await new Promise((resolve, reject) => {
35
+ // exec(
36
+ // command,
37
+ // { encoding: "binary", maxBuffer: 5 * 1024 },
38
+ // (error: ExecException | null, stdout: string, stderr: string) => {
39
+ // if (error) {
40
+ // reject(error);
41
+ // return;
42
+ // }
43
+ // if (stderr) {
44
+ // reject(error);
45
+ // return;
46
+ // }
47
+ // let stdoutAsBuffer: string = stdout;
48
+ // if (typeof stdoutAsBuffer === "string" && stdoutAsBuffer.length > 0) {
49
+ // // noinspection JSCheckFunctionSignatures
50
+ // stdoutAsBuffer = Buffer.from(stdoutAsBuffer, "binary").slice(0, -1);
51
+ // }
52
+ // if (stdoutAsBuffer && stdoutAsBuffer.length > 0) {
53
+ // resolve(stdoutAsBuffer);
54
+ // }
55
+ // }
56
+ // );
57
+ // });
58
+ // }
59
+
60
+ // @ts-ignore
61
+ export function toStringValue(r: any): string {
62
+ if (process.env.VERBOSE) {
63
+ console.log("Printing value", r);
64
+ }
65
+ if (r) {
66
+ if (typeof r === "string") {
67
+ return r;
68
+ } else if (r.toString) {
69
+ // noinspection JSCheckFunctionSignatures
70
+ return r.toString("utf8");
71
+ }
72
+ }
73
+ }
74
+
75
+ export function printStringValue(r: any) {
76
+ if (process.env.VERBOSE) {
77
+ console.log("Printing value", r);
78
+ }
79
+ if (r) {
80
+ if (typeof r === "string") {
81
+ console.log(r);
82
+ } else if (r.toString) {
83
+ // noinspection JSCheckFunctionSignatures
84
+ console.log(r.toString("utf8"));
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ *
91
+ * @param result
92
+ * @returns {string|null}
93
+ */
94
+ export function toStringOrNull(result: any) {
95
+ if (result == null) {
96
+ return null;
97
+ }
98
+ if (process.env.VERBOSE) {
99
+ console.log("result", result);
100
+ }
101
+ if (typeof result === "string" && result.length > 0) {
102
+ return result;
103
+ }
104
+ if (result.slice && result.toString) {
105
+ // noinspection JSCheckFunctionSignatures
106
+ return result.toString("utf8");
107
+ }
108
+ return null;
109
+ }
110
+
111
+ export function invalidString(input: any): boolean {
112
+ return typeof input !== "string" || input.length === 0;
113
+ }
114
+
115
+
116
+ export function validString(input: any): input is string {
117
+ return typeof input == "string" && input.length > 0;
118
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "include": [
3
+ "src/**.ts",
4
+ "src/**.tsx",
5
+ "src/**.js",
6
+ "src/**.jsx"
7
+ ],
8
+ "compilerOptions": {
9
+ "moduleResolution": "node",
10
+ "target": "es2021",
11
+ "types": [
12
+ "node"
13
+ ],
14
+ "lib": [
15
+ "es2015",
16
+ "es2017",
17
+ "es2021",
18
+ "dom"
19
+ ],
20
+ "strict": true,
21
+ "allowSyntheticDefaultImports": true,
22
+ }
23
+ }
@@ -1,263 +0,0 @@
1
- import AbstractCookieQueryStrategy from "./AbstractCookieQueryStrategy";
2
- import {
3
- doSqliteQuery1,
4
- execSimple,
5
- invalidString,
6
- toStringOrNull,
7
- toStringValue,
8
- } from "../utils";
9
- import { env, HOME } from "../global";
10
- import { existsSync } from "fs";
11
- import { findAllFiles } from "../findAllFiles";
12
- import crypto from "crypto";
13
- import * as path from "path";
14
-
15
- export default class ChromeCookieQueryStrategy extends AbstractCookieQueryStrategy {
16
- async queryCookies(name, domain) {
17
- if (process.platform !== "darwin") {
18
- throw new Error("This only works on macOS");
19
- }
20
- if (env.FIREFOX_ONLY) {
21
- return [];
22
- }
23
- const cookies = await getChromeCookies({ name, domain });
24
- return cookies.map(toStringValue);
25
- }
26
- }
27
-
28
- async function getPromise1(name, domain, file) {
29
- try {
30
- return await getEncryptedChromeCookie({
31
- name: name,
32
- domain: domain,
33
- file: file,
34
- });
35
- } catch (e) {
36
- if (env.VERBOSE) {
37
- console.log("Error getting encrypted cookie", e);
38
- }
39
- return [];
40
- }
41
- }
42
-
43
- async function getPromise(name, domain) {
44
- try {
45
- const files = await findAllFiles({
46
- path: chromeLocal,
47
- name: "Cookies",
48
- });
49
- const promises = files.map((file) => getPromise1(name, domain, file));
50
- const results1 = await Promise.all(promises);
51
- const results2 = results1.flat();
52
- if (env.VERBOSE) {
53
- console.log("getEncryptedChromeCookie results", results2);
54
- }
55
- return results2.filter((result) => result);
56
- } catch (error) {
57
- if (env.VERBOSE) {
58
- console.log("error", error);
59
- }
60
- return [];
61
- }
62
- }
63
-
64
- /**
65
- *
66
- * @param {string|undefined} name
67
- * @param {string} domain
68
- * @param {boolean} requireJwt
69
- * @returns {Promise<[string]>}
70
- */
71
- async function getChromeCookies({ name, domain = "%", requireJwt = false }) {
72
- if (invalidString(name)) {
73
- throw new Error("name must be a string");
74
- }
75
- if (invalidString(domain)) {
76
- throw new Error("domain must be a string");
77
- }
78
- const encryptedDataItems = await getPromise(name, domain);
79
- const password = await getChromePassword();
80
- const decrypted = encryptedDataItems
81
- .filter((encryptedData) => {
82
- return encryptedData != null && encryptedData.length > 0;
83
- })
84
- .map(async (encryptedData) => {
85
- if (env.VERBOSE) {
86
- console.log("Received encrypted", encryptedData);
87
- }
88
- let d;
89
- try {
90
- d = await decrypt(password, encryptedData);
91
- } catch (e) {
92
- if (env.VERBOSE) {
93
- console.log("Error decrypting cookie", e);
94
- }
95
- return null;
96
- }
97
- if (d) {
98
- if (env.VERBOSE) {
99
- console.log("Decrypted", d);
100
- }
101
- return d;
102
- }
103
- return null;
104
- });
105
- const results = await Promise.all(decrypted);
106
- if (env.VERBOSE) {
107
- console.log("results", results);
108
- }
109
- return results.map(toStringOrNull);
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
- name,
122
- domain,
123
- file = path.join(chromeLocal, "Default", "Cookies"),
124
- }) {
125
- if (name && typeof name !== "string") {
126
- throw new Error("name must be a string");
127
- }
128
- if (domain && typeof domain !== "string") {
129
- throw new Error("domain must be a string");
130
- }
131
- if (file && typeof file !== "string") {
132
- throw new Error("file must be a string");
133
- }
134
- if (!existsSync(file)) {
135
- throw new Error(`File ${file} does not exist`);
136
- }
137
- if (env.VERBOSE) {
138
- const s = file.split("/").slice(-3).join("/");
139
- console.log(`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`);
140
- }
141
- let sql;
142
- sql = `SELECT encrypted_value FROM cookies`;
143
- if (typeof name === "string" || typeof domain === "string") {
144
- sql += ` WHERE `;
145
- if (typeof name === "string") {
146
- sql += `name = '${name}'`;
147
- if (typeof domain === "string") {
148
- sql += ` AND `;
149
- }
150
- }
151
- if (typeof domain === "string") {
152
- sql += `host_key LIKE '${domain}';`;
153
- }
154
- }
155
- return doSqliteQuery1(file, sql);
156
- }
157
-
158
- /**
159
- *
160
- * @returns {Promise<string>}
161
- */
162
- async function getChromePassword() {
163
- return execSimple(
164
- 'security find-generic-password -w -s "Chrome Safe Storage"'
165
- );
166
- }
167
-
168
- /**
169
- *
170
- * @param {string} password
171
- * @param {Buffer} encryptedData
172
- * @returns {Promise<string>}
173
- */
174
- async function decrypt(password, encryptedData) {
175
- if (typeof password !== "string") {
176
- throw new Error("password must be a string: " + password);
177
- }
178
- let encryptedData1;
179
- encryptedData1 = encryptedData;
180
- if (encryptedData1 == null || typeof encryptedData1 !== "object") {
181
- throw new Error("encryptedData must be a object: " + encryptedData1);
182
- }
183
- if (!(encryptedData1 instanceof Buffer)) {
184
- if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
185
- [encryptedData1] = encryptedData1;
186
- if (env.VERBOSE) {
187
- console.log(
188
- `encryptedData is an array of buffers, selected first: ${encryptedData1}`
189
- );
190
- }
191
- } else {
192
- throw new Error("encryptedData must be a Buffer: " + encryptedData1);
193
- }
194
- encryptedData1 = Buffer.from(encryptedData1);
195
- }
196
- if (env.VERBOSE) {
197
- console.log(`Trying to decrypt with password ${password}`);
198
- }
199
- return new Promise((resolve, reject) => {
200
- crypto.pbkdf2(password, "saltysalt", 1003, 16, "sha1", (error, buffer) => {
201
- try {
202
- if (error) {
203
- if (env.VERBOSE) {
204
- console.log("Error doing pbkdf2", error);
205
- }
206
- reject(error);
207
- return;
208
- }
209
-
210
- if (buffer.length !== 16) {
211
- if (env.VERBOSE) {
212
- console.log(
213
- "Error doing pbkdf2, buffer length is not 16",
214
- buffer.length
215
- );
216
- }
217
- reject(new Error("Buffer length is not 16"));
218
- return;
219
- }
220
-
221
- const iv = new Buffer.from(new Array(17).join(" "), "binary");
222
- const decipher = crypto.createDecipheriv("aes-128-cbc", buffer, iv);
223
- decipher.setAutoPadding(false);
224
-
225
- if (encryptedData1 && encryptedData1.slice) {
226
- encryptedData1 = encryptedData1.slice(3);
227
- }
228
-
229
- if (encryptedData1.length % 16 !== 0) {
230
- if (env.VERBOSE) {
231
- console.log(
232
- "Error doing pbkdf2, encryptedData length is not a multiple of 16",
233
- encryptedData1.length
234
- );
235
- }
236
- reject(new Error("encryptedData length is not a multiple of 16"));
237
- return;
238
- }
239
-
240
- let decoded = decipher.update(encryptedData1);
241
- try {
242
- decipher.final("utf-8");
243
- } catch (e) {
244
- if (env.VERBOSE) {
245
- console.log("Error doing decipher.final()", e);
246
- }
247
- reject(e);
248
- return;
249
- }
250
-
251
- const padding = decoded[decoded.length - 1];
252
- if (padding) {
253
- decoded = decoded.slice(0, 0 - padding);
254
- }
255
- // noinspection JSCheckFunctionSignatures
256
- decoded = decoded.toString("utf8");
257
- resolve(decoded);
258
- } catch (e) {
259
- reject(e);
260
- }
261
- });
262
- });
263
- }