@mherod/get-cookie 2.0.0-rc.8 → 2.1.0
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/.idea/runConfigurations/build.xml +12 -0
- package/.parcelrc +12 -0
- package/.prettierignore +5 -0
- package/.prettierrc +12 -0
- package/.terserrc +26 -0
- package/README.md +2 -2
- package/build-indexes.ts +108 -0
- package/dist/cli.js +2 -0
- package/dist/index.js +1 -823
- package/dist/index.js.map +1 -0
- package/dist/module.js +1446 -0
- package/dist/module.js.map +1 -0
- package/dist/prompt.2b8c61c0.js +40 -0
- package/dist/prompt.536a2c51.js +40 -0
- package/dist/prompt.fb2c7dad.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -0
- package/jest.config.ts +14 -0
- package/package-lock.json +3862 -8098
- package/package.json +49 -35
- package/src/CookieRow.ts +8 -0
- package/src/CookieSpec.ts +20 -0
- package/src/CookieStore.ts +24 -7
- package/src/ExportedCookie.ts +12 -0
- package/src/FetchResponse.ts +1 -3
- package/src/FileCookieStore.ts +178 -0
- package/src/StringToRegex.ts +10 -0
- package/src/argv.ts +27 -2
- package/src/browsers/CompositeCookieQueryStrategy.ts +36 -38
- package/src/browsers/CookieQueryStrategy.ts +1 -0
- package/src/browsers/CookieStoreQueryStrategy.ts +29 -22
- package/src/browsers/QuerySqliteThenTransform.ts +85 -0
- package/src/browsers/chrome/ChromeApplicationSupport.ts +10 -0
- package/src/browsers/chrome/ChromeCookieQueryStrategy.ts +131 -0
- package/src/browsers/chrome/decrypt.ts +118 -0
- package/src/browsers/chrome/getChromePassword.ts +11 -0
- package/src/browsers/{FirefoxCookieQueryStrategy.ts → firefox/FirefoxCookieQueryStrategy.ts} +19 -16
- package/src/browsers/getEncryptedChromeCookie.ts +87 -0
- package/src/browsers/index.ts +12 -0
- package/src/browsers/mock/MockCookieQueryStrategy.ts +18 -0
- package/src/browsers/{SafariCookieQueryStrategy.ts → safari/SafariCookieQueryStrategy.ts} +3 -2
- package/src/cli.ts +43 -67
- package/src/cliQueryCookies.ts +55 -0
- package/src/comboQueryCookieSpec.test.ts +92 -0
- package/src/comboQueryCookieSpec.ts +29 -0
- package/src/cookieQueryOptions.ts +20 -0
- package/src/cookieSpecsFromUrl.test.ts +65 -0
- package/src/cookieSpecsFromUrl.ts +27 -0
- package/src/execSimple.ts +13 -0
- package/src/fetchWithCookies.test.ts +32 -0
- package/src/fetchWithCookies.ts +124 -47
- package/src/findAllFiles.ts +25 -49
- package/src/getChromeCookie.ts +6 -4
- package/src/getCookie.ts +6 -3
- package/src/getFirefoxCookie.ts +6 -4
- package/src/getGroupedRenderedCookies.ts +7 -17
- package/src/getMergedRenderedCookies.test.ts +43 -0
- package/src/getMergedRenderedCookies.ts +17 -0
- package/src/global.ts +1 -1
- package/src/index.ts +2 -7
- package/src/isValidJwt.ts +2 -2
- package/src/listChromeProfiles.ts +45 -0
- package/src/logger.ts +13 -0
- package/src/processBeforeReturn.ts +26 -0
- package/src/queryCookies.ts +11 -3
- package/src/util/flatMapAsync.test.ts +55 -0
- package/src/util/flatMapAsync.ts +37 -0
- package/src/util/index.ts +1 -0
- package/tsconfig.json +14 -11
- package/.github/dependabot.yml +0 -6
- package/.prettierrc.json +0 -1
- package/src/IsCookieRow.ts +0 -9
- package/src/IsExportedCookie.ts +0 -9
- package/src/browsers/ChromeCookieQueryStrategy.ts +0 -343
- package/src/doSqliteQuery1.ts +0 -47
- package/src/doSqliteQuery1Params.ts +0 -7
- package/src/utils.ts +0 -25
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import CookieRow from "../CookieRow";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { Database } from "sqlite3";
|
|
4
|
+
import { merge } from "lodash";
|
|
5
|
+
import { parsedArgs } from "../argv";
|
|
6
|
+
import consola from "../logger";
|
|
7
|
+
|
|
8
|
+
interface FnOptions {
|
|
9
|
+
file: string;
|
|
10
|
+
sql: string;
|
|
11
|
+
rowFilter?: (row: any) => boolean;
|
|
12
|
+
rowTransform: (row: any) => CookieRow;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function checkFileExistence(file: string) {
|
|
16
|
+
if (!file || !fs.existsSync(file)) {
|
|
17
|
+
throw new Error(`File ${file} does not exist`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createDatabase(file: string): Database {
|
|
22
|
+
return new Database(file);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function transformRows(
|
|
26
|
+
//
|
|
27
|
+
rows: any[],
|
|
28
|
+
rowFilter: (row: any) => boolean,
|
|
29
|
+
rowTransform: (row: any) => CookieRow,
|
|
30
|
+
file: string,
|
|
31
|
+
//
|
|
32
|
+
): CookieRow[] {
|
|
33
|
+
return rows.filter(rowFilter).map((row: any) => {
|
|
34
|
+
const metaData = {
|
|
35
|
+
meta: {
|
|
36
|
+
file: file,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const cookieRow: CookieRow = rowTransform(row);
|
|
41
|
+
|
|
42
|
+
return merge(metaData, cookieRow);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function querySqliteThenTransform(
|
|
47
|
+
//
|
|
48
|
+
{
|
|
49
|
+
//
|
|
50
|
+
file,
|
|
51
|
+
sql,
|
|
52
|
+
rowFilter = () => true,
|
|
53
|
+
rowTransform,
|
|
54
|
+
}: FnOptions,
|
|
55
|
+
): //
|
|
56
|
+
Promise<CookieRow[]> {
|
|
57
|
+
checkFileExistence(file);
|
|
58
|
+
|
|
59
|
+
const db: Database = createDatabase(file);
|
|
60
|
+
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
db.all(sql, (err: Error, rows: any[]) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
return reject(err);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!rows || rows.length === 0) {
|
|
68
|
+
return resolve([]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cookieRows: CookieRow[] = transformRows(
|
|
72
|
+
rows,
|
|
73
|
+
rowFilter,
|
|
74
|
+
rowTransform,
|
|
75
|
+
file,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (parsedArgs.verbose) {
|
|
79
|
+
consola.log(`Rows: ${JSON.stringify(rows)}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return resolve(cookieRows);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import CookieQueryStrategy from "../CookieQueryStrategy";
|
|
2
|
+
import CookieRow, { isCookieRow } from "../../CookieRow";
|
|
3
|
+
import ExportedCookie from "../../ExportedCookie";
|
|
4
|
+
import { chromeApplicationSupport } from "./ChromeApplicationSupport";
|
|
5
|
+
import { decrypt } from "./decrypt";
|
|
6
|
+
import { env } from "../../global";
|
|
7
|
+
import { findAllFiles } from "../../findAllFiles";
|
|
8
|
+
import { getChromePassword } from "./getChromePassword";
|
|
9
|
+
import { isExportedCookie } from "../../ExportedCookie";
|
|
10
|
+
import { merge } from "lodash";
|
|
11
|
+
import { parsedArgs } from "../../argv";
|
|
12
|
+
import { flatMapAsync } from "../../util/flatMapAsync";
|
|
13
|
+
import { getEncryptedChromeCookie } from "../getEncryptedChromeCookie";
|
|
14
|
+
import logger from "../../logger";
|
|
15
|
+
|
|
16
|
+
export const consola = logger.withTag("ChromeCookieQueryStrategy");
|
|
17
|
+
|
|
18
|
+
export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
|
|
19
|
+
browserName = "Chrome";
|
|
20
|
+
|
|
21
|
+
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
22
|
+
if (process.platform !== "darwin") {
|
|
23
|
+
throw new Error("This only works on macOS");
|
|
24
|
+
}
|
|
25
|
+
if (env.FIREFOX_ONLY) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return getChromeCookies({
|
|
29
|
+
requireJwt: false,
|
|
30
|
+
name,
|
|
31
|
+
domain,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function getPromise1(
|
|
37
|
+
name: string,
|
|
38
|
+
domain: string,
|
|
39
|
+
file: string,
|
|
40
|
+
): Promise<CookieRow[]> {
|
|
41
|
+
try {
|
|
42
|
+
return await getEncryptedChromeCookie({
|
|
43
|
+
name: name,
|
|
44
|
+
domain: domain,
|
|
45
|
+
file: file,
|
|
46
|
+
});
|
|
47
|
+
} catch (e) {
|
|
48
|
+
if (parsedArgs.verbose) {
|
|
49
|
+
console.log("Error getting encrypted cookie", e);
|
|
50
|
+
}
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function getPromise(name: string, domain: string): Promise<CookieRow[]> {
|
|
56
|
+
try {
|
|
57
|
+
const files: string[] = findAllFiles({
|
|
58
|
+
path: chromeApplicationSupport,
|
|
59
|
+
name: "Cookies",
|
|
60
|
+
});
|
|
61
|
+
const results1: CookieRow[] = await flatMapAsync(files, async (file) => {
|
|
62
|
+
return await getPromise1(name, domain, file);
|
|
63
|
+
});
|
|
64
|
+
return results1.filter(isCookieRow);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (parsedArgs.verbose) {
|
|
67
|
+
console.log("error", error);
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function decryptValue(password: string, encryptedValue: Buffer) {
|
|
74
|
+
let d: string | null;
|
|
75
|
+
try {
|
|
76
|
+
d = await decrypt(password, encryptedValue);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
if (parsedArgs.verbose) {
|
|
79
|
+
console.log("Error decrypting cookie", e);
|
|
80
|
+
}
|
|
81
|
+
d = null;
|
|
82
|
+
}
|
|
83
|
+
return d ?? encryptedValue.toString("utf-8");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function getChromeCookies({
|
|
87
|
+
name,
|
|
88
|
+
domain = "%",
|
|
89
|
+
requireJwt = false,
|
|
90
|
+
}: {
|
|
91
|
+
name: string;
|
|
92
|
+
domain: string;
|
|
93
|
+
requireJwt: boolean | undefined;
|
|
94
|
+
//
|
|
95
|
+
}): //
|
|
96
|
+
Promise<ExportedCookie[]> {
|
|
97
|
+
const encryptedDataItems: CookieRow[] = await getPromise(name, domain);
|
|
98
|
+
const password: string = await getChromePassword();
|
|
99
|
+
const decrypted: Promise<ExportedCookie | null>[] = encryptedDataItems
|
|
100
|
+
.filter(({ value }) => value != null && value.length > 0)
|
|
101
|
+
.map(async (cookieRow: CookieRow) => {
|
|
102
|
+
const encryptedValue: Buffer = cookieRow.value;
|
|
103
|
+
const decryptedValue = await decryptValue(password, encryptedValue);
|
|
104
|
+
const meta = {};
|
|
105
|
+
merge(meta, cookieRow.meta ?? {});
|
|
106
|
+
const exportedCookie: ExportedCookie = {
|
|
107
|
+
domain: cookieRow.domain,
|
|
108
|
+
name: cookieRow.name,
|
|
109
|
+
value: decryptedValue,
|
|
110
|
+
meta: meta,
|
|
111
|
+
};
|
|
112
|
+
const expiry = cookieRow.expiry;
|
|
113
|
+
const mergeExpiry =
|
|
114
|
+
expiry != null && expiry > 0
|
|
115
|
+
? {
|
|
116
|
+
expiry: new Date(expiry),
|
|
117
|
+
}
|
|
118
|
+
: {
|
|
119
|
+
expiry: "Infinity",
|
|
120
|
+
};
|
|
121
|
+
merge(exportedCookie, mergeExpiry);
|
|
122
|
+
return exportedCookie;
|
|
123
|
+
});
|
|
124
|
+
const results: ExportedCookie[] = (await Promise.all(decrypted)).filter(
|
|
125
|
+
isExportedCookie,
|
|
126
|
+
);
|
|
127
|
+
if (parsedArgs.verbose) {
|
|
128
|
+
console.log("results", results);
|
|
129
|
+
}
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { BinaryLike, createDecipheriv, pbkdf2 } from "crypto";
|
|
2
|
+
import { parsedArgs } from "../../argv";
|
|
3
|
+
import consola from "../../logger";
|
|
4
|
+
|
|
5
|
+
// Function to decrypt encrypted data using a password
|
|
6
|
+
export async function decrypt(
|
|
7
|
+
password: BinaryLike, // The password to use for decryption
|
|
8
|
+
encryptedData: Buffer, // The data to decrypt
|
|
9
|
+
): Promise<string> {
|
|
10
|
+
// Returns a promise that resolves with the decrypted string
|
|
11
|
+
// Check if password is a string
|
|
12
|
+
if (typeof password !== "string") {
|
|
13
|
+
throw new Error("password must be a string: " + password);
|
|
14
|
+
}
|
|
15
|
+
let encryptedData1: any;
|
|
16
|
+
encryptedData1 = encryptedData;
|
|
17
|
+
// Check if encryptedData is an object
|
|
18
|
+
if (encryptedData1 == null || typeof encryptedData1 !== "object") {
|
|
19
|
+
throw new Error("encryptedData must be a object: " + encryptedData1);
|
|
20
|
+
}
|
|
21
|
+
// Check if encryptedData is a Buffer or an array of Buffers
|
|
22
|
+
if (!(encryptedData1 instanceof Buffer)) {
|
|
23
|
+
if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
|
|
24
|
+
[encryptedData1] = encryptedData1;
|
|
25
|
+
// Log if encryptedData is an array of buffers
|
|
26
|
+
if (parsedArgs.verbose) {
|
|
27
|
+
console.log(
|
|
28
|
+
`encryptedData is an array of buffers, selected first: ${encryptedData1}`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
throw new Error("encryptedData must be a Buffer: " + encryptedData1);
|
|
33
|
+
}
|
|
34
|
+
encryptedData1 = Buffer.from(encryptedData1);
|
|
35
|
+
}
|
|
36
|
+
// Log the password being used for decryption
|
|
37
|
+
if (parsedArgs.verbose) {
|
|
38
|
+
consola.start(`Trying to decrypt with password: ${password}`);
|
|
39
|
+
}
|
|
40
|
+
// Return a promise that resolves with the decrypted string
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
// Use pbkdf2 to derive a key from the password
|
|
43
|
+
pbkdf2(password, "saltysalt", 1003, 16, "sha1", (error, buffer) => {
|
|
44
|
+
try {
|
|
45
|
+
// Handle any errors from pbkdf2
|
|
46
|
+
if (error) {
|
|
47
|
+
if (parsedArgs.verbose) {
|
|
48
|
+
console.log("Error doing pbkdf2", error);
|
|
49
|
+
}
|
|
50
|
+
reject(error);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check if the buffer length is 16
|
|
55
|
+
if (buffer.length !== 16) {
|
|
56
|
+
if (parsedArgs.verbose) {
|
|
57
|
+
console.log(
|
|
58
|
+
"Error doing pbkdf2, buffer length is not 16",
|
|
59
|
+
buffer.length,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
reject(new Error("Buffer length is not 16"));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Create an initialization vector
|
|
67
|
+
const str = new Array(17).join(" ");
|
|
68
|
+
const iv = Buffer.from(str, "binary");
|
|
69
|
+
// Create a decipher using the derived key and initialization vector
|
|
70
|
+
const decipher = createDecipheriv("aes-128-cbc", buffer, iv);
|
|
71
|
+
decipher.setAutoPadding(false);
|
|
72
|
+
|
|
73
|
+
// Remove the first 3 bytes from the encrypted data
|
|
74
|
+
if (encryptedData1 && encryptedData1.slice) {
|
|
75
|
+
encryptedData1 = encryptedData1.slice(3);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if the encrypted data length is a multiple of 16
|
|
79
|
+
if (encryptedData1.length % 16 !== 0) {
|
|
80
|
+
if (parsedArgs.verbose) {
|
|
81
|
+
console.log(
|
|
82
|
+
"Error doing pbkdf2, encryptedData length is not a multiple of 16",
|
|
83
|
+
encryptedData1.length,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
reject(new Error("encryptedData length is not a multiple of 16"));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Update the decipher with the encrypted data
|
|
91
|
+
let decoded = decipher.update(encryptedData1);
|
|
92
|
+
try {
|
|
93
|
+
// Finalize the decipher
|
|
94
|
+
decipher.final("utf-8");
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (parsedArgs.verbose) {
|
|
97
|
+
console.log("Error doing decipher.final()", e);
|
|
98
|
+
}
|
|
99
|
+
reject(e);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Remove padding from the decoded data
|
|
104
|
+
const padding = decoded[decoded.length - 1];
|
|
105
|
+
if (padding) {
|
|
106
|
+
decoded = decoded.slice(0, 0 - padding);
|
|
107
|
+
}
|
|
108
|
+
// Convert the decoded data to a string
|
|
109
|
+
const decodedString = decoded.toString("utf8");
|
|
110
|
+
// Resolve the promise with the decrypted string
|
|
111
|
+
resolve(decodedString);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// Reject the promise if there is an error
|
|
114
|
+
reject(e);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { execSimple } from "../../execSimple";
|
|
2
|
+
|
|
3
|
+
// top level so it's only called once and cached
|
|
4
|
+
const chromePassword: Promise<string> =
|
|
5
|
+
process.platform == "darwin"
|
|
6
|
+
? execSimple('security find-generic-password -w -s "Chrome Safe Storage"')
|
|
7
|
+
: Promise.reject(new Error("This only works on macOS"));
|
|
8
|
+
|
|
9
|
+
export async function getChromePassword(): Promise<string> {
|
|
10
|
+
return await chromePassword;
|
|
11
|
+
}
|
package/src/browsers/{FirefoxCookieQueryStrategy.ts → firefox/FirefoxCookieQueryStrategy.ts}
RENAMED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
|
-
import CookieQueryStrategy from "
|
|
3
|
-
import {
|
|
2
|
+
import CookieQueryStrategy from "../CookieQueryStrategy";
|
|
3
|
+
import { HOME } from "../../global";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
|
-
import { findAllFiles } from "
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
5
|
+
import { findAllFiles } from "../../findAllFiles";
|
|
6
|
+
import ExportedCookie from "../../ExportedCookie";
|
|
7
|
+
import CookieRow from "../../CookieRow";
|
|
8
|
+
import CookieSpec from "../../CookieSpec";
|
|
9
|
+
import { specialCases } from "../../SpecialCases";
|
|
10
|
+
import { parsedArgs } from "../../argv";
|
|
11
|
+
import { querySqliteThenTransform } from "../QuerySqliteThenTransform";
|
|
11
12
|
|
|
12
13
|
export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
13
14
|
browserName = "Firefox";
|
|
14
15
|
|
|
15
16
|
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
16
17
|
if (process.platform !== "darwin") {
|
|
17
|
-
|
|
18
|
+
// TODO: implement
|
|
19
|
+
return [];
|
|
18
20
|
}
|
|
19
|
-
if (
|
|
21
|
+
if (parsedArgs.browser !== "firefox") {
|
|
20
22
|
return [];
|
|
21
23
|
}
|
|
22
24
|
const cookies = await this.#getFirefoxCookie({ name, domain });
|
|
@@ -34,29 +36,30 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
async #getFirefoxCookie(
|
|
37
|
-
{ name, domain }: CookieSpec //
|
|
39
|
+
{ name, domain }: CookieSpec, //
|
|
38
40
|
) {
|
|
39
|
-
const files: string[] =
|
|
41
|
+
const files: string[] = findAllFiles({
|
|
40
42
|
path: path.join(
|
|
41
43
|
HOME,
|
|
42
44
|
"Library",
|
|
43
45
|
"Application Support",
|
|
44
46
|
"Firefox",
|
|
45
|
-
"Profiles"
|
|
47
|
+
"Profiles",
|
|
46
48
|
),
|
|
47
49
|
name: "cookies.sqlite",
|
|
48
50
|
});
|
|
49
51
|
const fn: (file: string) => Promise<CookieRow[]> = async (file: string) => {
|
|
50
52
|
return await this.#queryCookiesDb(file, name, domain);
|
|
51
53
|
};
|
|
52
|
-
const all:
|
|
54
|
+
const all: CookieRow[][] = await Promise.all(files.map(fn));
|
|
55
|
+
// flatten
|
|
53
56
|
return all.flat();
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
async #queryCookiesDb(
|
|
57
60
|
file: string,
|
|
58
61
|
name: string,
|
|
59
|
-
domain: string
|
|
62
|
+
domain: string,
|
|
60
63
|
): Promise<CookieRow[]> {
|
|
61
64
|
if (file && !existsSync(file)) {
|
|
62
65
|
throw new Error(`File ${file} does not exist`);
|
|
@@ -87,7 +90,7 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
87
90
|
};
|
|
88
91
|
};
|
|
89
92
|
try {
|
|
90
|
-
return await
|
|
93
|
+
return await querySqliteThenTransform({
|
|
91
94
|
file,
|
|
92
95
|
sql,
|
|
93
96
|
rowTransform,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { chromeApplicationSupport } from "./chrome/ChromeApplicationSupport";
|
|
2
|
+
import { querySqliteThenTransform } from "./QuerySqliteThenTransform";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { parsedArgs } from "../argv";
|
|
6
|
+
import { stringToRegex } from "../StringToRegex";
|
|
7
|
+
import CookieRow from "../CookieRow";
|
|
8
|
+
import consola from "../logger";
|
|
9
|
+
|
|
10
|
+
export async function getEncryptedChromeCookie({
|
|
11
|
+
name,
|
|
12
|
+
domain,
|
|
13
|
+
file = join(chromeApplicationSupport, "Default", "Cookies"),
|
|
14
|
+
}: {
|
|
15
|
+
name: string;
|
|
16
|
+
domain: string;
|
|
17
|
+
file: string;
|
|
18
|
+
}): Promise<CookieRow[]> {
|
|
19
|
+
if (!existsSync(file)) {
|
|
20
|
+
throw new Error(`File ${file} does not exist`);
|
|
21
|
+
}
|
|
22
|
+
if (parsedArgs.verbose) {
|
|
23
|
+
const s = file.split("/").slice(-3).join("/");
|
|
24
|
+
consola.start(
|
|
25
|
+
`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
let sql;
|
|
29
|
+
//language=SQL
|
|
30
|
+
sql = "SELECT encrypted_value, name, host_key, expires_utc FROM cookies";
|
|
31
|
+
// Define a regular expression to match wildcard characters
|
|
32
|
+
const wildcardRegexp = /^([*%])$/i;
|
|
33
|
+
// Check if the name does not contain wildcard characters
|
|
34
|
+
const specifiedName = name.match(wildcardRegexp) == null;
|
|
35
|
+
// Check if the domain does not contain wildcard characters
|
|
36
|
+
const specifiedDomain = domain.match(wildcardRegexp) == null;
|
|
37
|
+
// Check if the domain contains wildcard characters
|
|
38
|
+
const wildcardDomain = domain.match(/[%*]/) != null;
|
|
39
|
+
// Determine if we should query the domain based on the previous checks
|
|
40
|
+
const queryDomain = specifiedDomain && !wildcardDomain;
|
|
41
|
+
// if we have a wildcard domain, we need to use a regexp
|
|
42
|
+
// If the name is specified or we need to query the domain, we add a WHERE clause to the SQL query
|
|
43
|
+
if (specifiedName || queryDomain) {
|
|
44
|
+
sql += ` WHERE `;
|
|
45
|
+
// If the name is specified, we add a condition to the SQL query to match the name
|
|
46
|
+
if (specifiedName) {
|
|
47
|
+
sql += `name = '${name}'`;
|
|
48
|
+
// If we also need to query the domain, we add an AND operator to the SQL query
|
|
49
|
+
if (queryDomain) {
|
|
50
|
+
sql += ` AND `;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// If we need to query the domain, we add a condition to the SQL query to match the domain
|
|
54
|
+
if (queryDomain) {
|
|
55
|
+
// The leading dot is replaced with % to match subdomains
|
|
56
|
+
const sqlEmbedDomain = domain.replace(/^[.%]?/, "%");
|
|
57
|
+
sql += `host_key LIKE '${sqlEmbedDomain}';`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (parsedArgs.verbose) {
|
|
61
|
+
consola.info("Querying:", sql);
|
|
62
|
+
}
|
|
63
|
+
const domainRegexp: RegExp = stringToRegex(domain);
|
|
64
|
+
const sqliteQuery1: CookieRow[] = await querySqliteThenTransform({
|
|
65
|
+
file: file,
|
|
66
|
+
sql: sql,
|
|
67
|
+
rowFilter: (row) => {
|
|
68
|
+
return row["host_key"].match(domainRegexp) != null;
|
|
69
|
+
},
|
|
70
|
+
rowTransform: (row) => {
|
|
71
|
+
const cookieRow = {
|
|
72
|
+
expiry: (row["expires_utc"] / 1000000 - 11644473600) * 1000,
|
|
73
|
+
domain: row["host_key"],
|
|
74
|
+
name: row["name"],
|
|
75
|
+
value: row["encrypted_value"],
|
|
76
|
+
};
|
|
77
|
+
if (parsedArgs.verbose) {
|
|
78
|
+
consola.info("Found", cookieRow);
|
|
79
|
+
}
|
|
80
|
+
return cookieRow;
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
return sqliteQuery1.filter((row) => {
|
|
84
|
+
// TODO: is this needed?
|
|
85
|
+
return row.domain.match(domainRegexp) != null;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./chrome/ChromeApplicationSupport";
|
|
2
|
+
export * from "./chrome/ChromeCookieQueryStrategy";
|
|
3
|
+
export * from "./CompositeCookieQueryStrategy";
|
|
4
|
+
export * from "./CookieQueryStrategy";
|
|
5
|
+
export * from "./CookieStoreQueryStrategy";
|
|
6
|
+
export * from "./QuerySqliteThenTransform";
|
|
7
|
+
export * from "./firefox/FirefoxCookieQueryStrategy";
|
|
8
|
+
export * from "./mock/MockCookieQueryStrategy";
|
|
9
|
+
export * from "./safari/SafariCookieQueryStrategy";
|
|
10
|
+
export * from "./chrome/decrypt";
|
|
11
|
+
export * from "./chrome/getChromePassword";
|
|
12
|
+
export * from "./getEncryptedChromeCookie";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import ExportedCookie from "../../ExportedCookie";
|
|
2
|
+
import CookieQueryStrategy from "../CookieQueryStrategy";
|
|
3
|
+
|
|
4
|
+
export default class MockCookieQueryStrategy implements CookieQueryStrategy {
|
|
5
|
+
browserName: string = "mock";
|
|
6
|
+
|
|
7
|
+
private cookies: ExportedCookie[] = [];
|
|
8
|
+
|
|
9
|
+
constructor(cookies: ExportedCookie[]) {
|
|
10
|
+
this.cookies = cookies;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
14
|
+
return this.cookies.filter((c) => {
|
|
15
|
+
return c.name === name && c.domain === domain;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import CookieQueryStrategy from "
|
|
2
|
-
import ExportedCookie from "
|
|
1
|
+
import CookieQueryStrategy from "../CookieQueryStrategy";
|
|
2
|
+
import ExportedCookie from "../../ExportedCookie";
|
|
3
3
|
|
|
4
4
|
export default class SafariCookieQueryStrategy implements CookieQueryStrategy {
|
|
5
5
|
browserName = "Safari";
|
|
6
6
|
|
|
7
7
|
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
8
|
+
// TODO: implement
|
|
8
9
|
return [];
|
|
9
10
|
}
|
|
10
11
|
}
|