@mherod/get-cookie 2.1.1 → 2.1.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.
- package/.parcelrc +2 -7
- package/bun.lockb +0 -0
- package/dist/cli.js +18319 -2
- package/dist/index.js +24754 -1300
- package/dist/types.d.ts +361 -30
- package/package-lock.json +3997 -6448
- package/package.json +8 -52
- package/src/CookieRow.ts +35 -7
- package/src/CookieSpec.ts +32 -14
- package/src/SpecialCases.ts +6 -5
- package/src/StringToRegex.ts +4 -4
- package/src/browsers/CompositeCookieQueryStrategy.ts +34 -28
- package/src/browsers/CookieQueryStrategy.ts +1 -1
- package/src/browsers/CookieStoreQueryStrategy.ts +71 -55
- package/src/browsers/QuerySqliteThenTransform.ts +27 -64
- package/src/browsers/chrome/ChromeApplicationSupport.ts +4 -0
- package/src/browsers/chrome/ChromeCookieQueryStrategy.ts +118 -91
- package/src/browsers/chrome/decrypt.ts +133 -97
- package/src/browsers/chrome/getChromePassword.ts +22 -5
- package/src/browsers/firefox/FirefoxCookieQueryStrategy.ts +19 -13
- package/src/browsers/getEncryptedChromeCookie.ts +101 -66
- package/src/browsers/mock/MockCookieQueryStrategy.ts +8 -4
- package/src/browsers/safari/SafariCookieQueryStrategy.ts +34 -3
- package/src/cli.ts +36 -29
- package/src/cliQueryCookies.ts +14 -4
- package/src/comboQueryCookieSpec.ts +7 -10
- package/src/cookieQueryOptions.ts +8 -1
- package/src/cookieSpecsFromUrl.ts +53 -7
- package/src/decodeBinaryCookies.ts +72 -0
- package/src/execSimple.ts +1 -2
- package/src/fetchWithCookies.ts +172 -144
- package/src/findAllFiles.ts +10 -0
- package/src/getChromeCookie.ts +6 -8
- package/src/getCookie.ts +8 -8
- package/src/getFirefoxCookie.ts +13 -9
- package/src/getGroupedRenderedCookies.ts +29 -6
- package/src/getMergedRenderedCookies.ts +4 -2
- package/src/global.ts +2 -2
- package/src/index.ts +16 -6
- package/src/isValidJwt.ts +6 -4
- package/src/listChromeProfiles.ts +18 -10
- package/src/logger.ts +0 -1
- package/src/processBeforeReturn.ts +15 -12
- package/src/queryCookies.ts +22 -17
- package/src/resultsRendered.ts +20 -7
- package/src/unpackHeaders.ts +10 -8
- package/src/util/flatMapAsync.ts +20 -16
- package/tsconfig.json +4 -4
- package/dist/index.js.map +0 -1
- package/dist/module.js +0 -1446
- package/dist/module.js.map +0 -1
- package/dist/prompt.2b8c61c0.js +0 -40
- package/dist/prompt.536a2c51.js +0 -40
- package/dist/prompt.fb2c7dad.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/src/CookieStore.ts +0 -27
- package/src/FileCookieStore.ts +0 -178
|
@@ -16,116 +16,143 @@ import logger from "../../logger";
|
|
|
16
16
|
export const consola = logger.withTag("ChromeCookieQueryStrategy");
|
|
17
17
|
|
|
18
18
|
export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
|
|
19
|
-
browserName = "Chrome";
|
|
19
|
+
browserName: string = "Chrome";
|
|
20
20
|
|
|
21
21
|
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
22
|
-
|
|
23
|
-
throw new Error("This only works on macOS");
|
|
24
|
-
}
|
|
22
|
+
this.ensurePlatformIsMacOS();
|
|
25
23
|
if (env.FIREFOX_ONLY) {
|
|
26
24
|
return [];
|
|
27
25
|
}
|
|
28
|
-
return getChromeCookies({
|
|
26
|
+
return this.getChromeCookies({
|
|
29
27
|
requireJwt: false,
|
|
30
28
|
name,
|
|
31
29
|
domain,
|
|
32
30
|
});
|
|
33
31
|
}
|
|
34
|
-
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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);
|
|
33
|
+
private ensurePlatformIsMacOS(): void {
|
|
34
|
+
if (process.platform !== "darwin") {
|
|
35
|
+
throw new Error("This only works on macOS");
|
|
50
36
|
}
|
|
51
|
-
return [];
|
|
52
37
|
}
|
|
53
|
-
}
|
|
54
38
|
|
|
55
|
-
async
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
39
|
+
private async getChromeCookies({
|
|
40
|
+
name,
|
|
41
|
+
domain = "%",
|
|
42
|
+
requireJwt = false,
|
|
43
|
+
}: {
|
|
44
|
+
name: string;
|
|
45
|
+
domain: string;
|
|
46
|
+
requireJwt: boolean | undefined;
|
|
47
|
+
}): Promise<ExportedCookie[]> {
|
|
48
|
+
const encryptedDataItems: CookieRow[] = await this.getEncryptedCookies(
|
|
49
|
+
name,
|
|
50
|
+
domain,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const password: string = await getChromePassword();
|
|
54
|
+
const decryptedCookies: ExportedCookie[] = await this.decryptCookies(
|
|
55
|
+
encryptedDataItems,
|
|
56
|
+
password,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return decryptedCookies;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private async getEncryptedCookies(
|
|
63
|
+
name: string,
|
|
64
|
+
domain: string,
|
|
65
|
+
): Promise<CookieRow[]> {
|
|
66
|
+
try {
|
|
67
|
+
const files: string[] = findAllFiles({
|
|
68
|
+
path: chromeApplicationSupport,
|
|
69
|
+
name: "Cookies",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const results: CookieRow[] = await flatMapAsync(files, async (file) => {
|
|
73
|
+
return await this.getCookiesFromFile(name, domain, file);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return results.filter(isCookieRow);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
consola.warn("Error finding encrypted cookies", error);
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async getCookiesFromFile(
|
|
84
|
+
name: string,
|
|
85
|
+
domain: string,
|
|
86
|
+
file: string,
|
|
87
|
+
): Promise<CookieRow[]> {
|
|
88
|
+
try {
|
|
89
|
+
return await getEncryptedChromeCookie({
|
|
90
|
+
name: name,
|
|
91
|
+
domain: domain,
|
|
92
|
+
file: file,
|
|
93
|
+
});
|
|
94
|
+
} catch (e) {
|
|
95
|
+
consola.warn("Error getting encrypted cookie from file", e);
|
|
96
|
+
return [];
|
|
68
97
|
}
|
|
69
|
-
return [];
|
|
70
98
|
}
|
|
71
|
-
}
|
|
72
99
|
|
|
73
|
-
async
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
100
|
+
private async decryptCookies(
|
|
101
|
+
encryptedDataItems: CookieRow[],
|
|
102
|
+
password: string,
|
|
103
|
+
): Promise<ExportedCookie[]> {
|
|
104
|
+
const decrypted: Promise<ExportedCookie | null>[] = encryptedDataItems
|
|
105
|
+
.filter(({ value }) => value != null && value.length > 0)
|
|
106
|
+
.map(async (cookieRow: CookieRow) => {
|
|
107
|
+
const encryptedValue: Uint8Array | Buffer = cookieRow.value;
|
|
108
|
+
const decryptedValue = await this.decryptValue(
|
|
109
|
+
password,
|
|
110
|
+
encryptedValue,
|
|
111
|
+
);
|
|
112
|
+
return this.createExportedCookie(cookieRow, decryptedValue);
|
|
113
|
+
});
|
|
114
|
+
return (await Promise.all(decrypted)).filter(isExportedCookie);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private async decryptValue(
|
|
118
|
+
password: string,
|
|
119
|
+
encryptedValue: Uint8Array | Buffer,
|
|
120
|
+
): Promise<string> {
|
|
121
|
+
let decrypted: string | null;
|
|
122
|
+
try {
|
|
123
|
+
const bufferValue = Buffer.isBuffer(encryptedValue)
|
|
124
|
+
? encryptedValue
|
|
125
|
+
: Buffer.from(encryptedValue);
|
|
126
|
+
decrypted = await decrypt(password, bufferValue);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
consola.warn("Error decrypting cookie", e);
|
|
129
|
+
decrypted = null;
|
|
80
130
|
}
|
|
81
|
-
|
|
131
|
+
return decrypted ?? encryptedValue.toString("utf-8");
|
|
82
132
|
}
|
|
83
|
-
return d ?? encryptedValue.toString("utf-8");
|
|
84
|
-
}
|
|
85
133
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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);
|
|
134
|
+
private createExportedCookie(
|
|
135
|
+
cookieRow: CookieRow,
|
|
136
|
+
decryptedValue: string,
|
|
137
|
+
): ExportedCookie {
|
|
138
|
+
const meta = {};
|
|
139
|
+
merge(meta, cookieRow.meta ?? {});
|
|
140
|
+
const exportedCookie: ExportedCookie = {
|
|
141
|
+
domain: cookieRow.domain,
|
|
142
|
+
name: cookieRow.name,
|
|
143
|
+
value: decryptedValue,
|
|
144
|
+
meta: meta,
|
|
145
|
+
};
|
|
146
|
+
const expiry = cookieRow.expiry;
|
|
147
|
+
const mergeExpiry =
|
|
148
|
+
expiry != null && expiry > 0
|
|
149
|
+
? {
|
|
150
|
+
expiry: new Date(expiry),
|
|
151
|
+
}
|
|
152
|
+
: {
|
|
153
|
+
expiry: "Infinity",
|
|
154
|
+
};
|
|
155
|
+
merge(exportedCookie, mergeExpiry);
|
|
156
|
+
return exportedCookie;
|
|
129
157
|
}
|
|
130
|
-
return results;
|
|
131
158
|
}
|
|
@@ -1,118 +1,154 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createDecipheriv, pbkdf2 } from "crypto";
|
|
2
2
|
import { parsedArgs } from "../../argv";
|
|
3
3
|
import consola from "../../logger";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
interface Decryptor {
|
|
6
|
+
decrypt(password: string, encryptedData: Buffer): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class BufferDecryptor implements Decryptor {
|
|
10
|
+
async decrypt(password: string, encryptedData: Buffer): Promise<string> {
|
|
11
|
+
this.validatePassword(password);
|
|
12
|
+
const preparedData: Buffer =
|
|
13
|
+
this.validateAndPrepareEncryptedData(encryptedData);
|
|
14
|
+
|
|
15
|
+
if (parsedArgs.verbose) {
|
|
16
|
+
consola.start(`Trying to decrypt with password: ${password}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const decryptedData: string = await this.performDecryption(
|
|
20
|
+
password,
|
|
21
|
+
preparedData,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (parsedArgs.verbose) {
|
|
25
|
+
consola.success(`Decryption successful: ${decryptedData}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return decryptedData;
|
|
14
29
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
|
|
31
|
+
private validatePassword(password: string): void {
|
|
32
|
+
if (typeof password !== "string") {
|
|
33
|
+
throw new Error("password must be a string: " + password);
|
|
34
|
+
}
|
|
20
35
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
|
|
37
|
+
private validateAndPrepareEncryptedData(encryptedData: Buffer): Buffer {
|
|
38
|
+
if (
|
|
39
|
+
encryptedData == null ||
|
|
40
|
+
!(
|
|
41
|
+
encryptedData instanceof Buffer ||
|
|
42
|
+
(Array.isArray(encryptedData) && Buffer.isBuffer(encryptedData[0]))
|
|
43
|
+
)
|
|
44
|
+
) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"encryptedData must be a Buffer or an array of Buffers: " +
|
|
47
|
+
encryptedData,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(encryptedData) && Buffer.isBuffer(encryptedData[0])) {
|
|
52
|
+
encryptedData = encryptedData[0];
|
|
26
53
|
if (parsedArgs.verbose) {
|
|
27
|
-
|
|
28
|
-
`encryptedData is an array of buffers, selected first: ${
|
|
54
|
+
consola.info(
|
|
55
|
+
`encryptedData is an array of buffers, selected first: ${encryptedData}`,
|
|
29
56
|
);
|
|
30
57
|
}
|
|
31
|
-
} else {
|
|
32
|
-
throw new Error("encryptedData must be a Buffer: " + encryptedData1);
|
|
33
58
|
}
|
|
34
|
-
|
|
59
|
+
|
|
60
|
+
return Buffer.from(encryptedData);
|
|
35
61
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
62
|
+
|
|
63
|
+
private performDecryption(
|
|
64
|
+
password: string,
|
|
65
|
+
encryptedData: Buffer,
|
|
66
|
+
): Promise<string> {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
this.deriveKey(password)
|
|
69
|
+
.then((key) => this.decryptData(key, encryptedData))
|
|
70
|
+
.then((decrypted) => resolve(decrypted))
|
|
71
|
+
.catch((error) => reject(error));
|
|
72
|
+
});
|
|
39
73
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
74
|
+
|
|
75
|
+
private deriveKey(password: string): Promise<Buffer> {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
pbkdf2(
|
|
78
|
+
password,
|
|
79
|
+
"saltysalt",
|
|
80
|
+
1003,
|
|
81
|
+
16,
|
|
82
|
+
"sha1",
|
|
83
|
+
(error: Error | null, buffer: Buffer) => {
|
|
84
|
+
if (error) {
|
|
85
|
+
this.logError("Error doing pbkdf2", error);
|
|
86
|
+
reject(error);
|
|
87
|
+
return;
|
|
49
88
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Check if the buffer length is 16
|
|
55
|
-
if (buffer.length !== 16) {
|
|
56
|
-
if (parsedArgs.verbose) {
|
|
57
|
-
console.log(
|
|
89
|
+
|
|
90
|
+
if (buffer.length !== 16) {
|
|
91
|
+
this.logError(
|
|
58
92
|
"Error doing pbkdf2, buffer length is not 16",
|
|
59
93
|
buffer.length,
|
|
60
94
|
);
|
|
95
|
+
reject(new Error("Buffer length is not 16"));
|
|
96
|
+
return;
|
|
61
97
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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);
|
|
98
|
+
|
|
99
|
+
resolve(buffer);
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private decryptData(key: Buffer, encryptedData: Buffer): Promise<string> {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const iv: Buffer = Buffer.from(new Array(17).join(" "), "binary");
|
|
108
|
+
const decipher = createDecipheriv("aes-128-cbc", key, iv);
|
|
109
|
+
decipher.setAutoPadding(false);
|
|
110
|
+
|
|
111
|
+
const slicedData: Buffer = encryptedData.slice(3);
|
|
112
|
+
|
|
113
|
+
if (slicedData.length % 16 !== 0) {
|
|
114
|
+
this.logError(
|
|
115
|
+
"Error, encryptedData length is not a multiple of 16",
|
|
116
|
+
slicedData.length,
|
|
117
|
+
);
|
|
118
|
+
reject(new Error("encryptedData length is not a multiple of 16"));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let decoded: Buffer = decipher.update(slicedData);
|
|
123
|
+
try {
|
|
124
|
+
decipher.final("utf-8");
|
|
112
125
|
} catch (e) {
|
|
113
|
-
|
|
126
|
+
this.logError("Error doing decipher.final()", e);
|
|
114
127
|
reject(e);
|
|
128
|
+
return;
|
|
115
129
|
}
|
|
130
|
+
|
|
131
|
+
const padding: number = decoded[decoded.length - 1];
|
|
132
|
+
if (padding) {
|
|
133
|
+
decoded = decoded.slice(0, 0 - padding);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
resolve(decoded.toString("utf8"));
|
|
116
137
|
});
|
|
117
|
-
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private logError(message: string, error: any): void {
|
|
141
|
+
if (parsedArgs.verbose) {
|
|
142
|
+
consola.error(message, error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const decryptor: Decryptor = new BufferDecryptor();
|
|
148
|
+
|
|
149
|
+
export async function decrypt(
|
|
150
|
+
password: string,
|
|
151
|
+
encryptedData: Buffer,
|
|
152
|
+
): Promise<string> {
|
|
153
|
+
return decryptor.decrypt(password, encryptedData);
|
|
118
154
|
}
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { execSimple } from "../../execSimple";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
interface PasswordRetriever {
|
|
4
|
+
retrievePassword(): Promise<string>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
class MacOSPasswordRetriever implements PasswordRetriever {
|
|
8
|
+
async retrievePassword(): Promise<string> {
|
|
9
|
+
return execSimple(
|
|
10
|
+
'security find-generic-password -w -s "Chrome Safe Storage"',
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class UnsupportedPlatformPasswordRetriever implements PasswordRetriever {
|
|
16
|
+
async retrievePassword(): Promise<string> {
|
|
17
|
+
return Promise.reject(new Error("This only works on macOS"));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const passwordRetriever: PasswordRetriever =
|
|
5
22
|
process.platform == "darwin"
|
|
6
|
-
?
|
|
7
|
-
:
|
|
23
|
+
? new MacOSPasswordRetriever()
|
|
24
|
+
: new UnsupportedPlatformPasswordRetriever();
|
|
8
25
|
|
|
9
26
|
export async function getChromePassword(): Promise<string> {
|
|
10
|
-
return await
|
|
27
|
+
return await passwordRetriever.retrievePassword();
|
|
11
28
|
}
|
|
@@ -11,7 +11,7 @@ import { parsedArgs } from "../../argv";
|
|
|
11
11
|
import { querySqliteThenTransform } from "../QuerySqliteThenTransform";
|
|
12
12
|
|
|
13
13
|
export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
14
|
-
browserName = "Firefox";
|
|
14
|
+
browserName: string = "Firefox";
|
|
15
15
|
|
|
16
16
|
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
17
17
|
if (process.platform !== "darwin") {
|
|
@@ -21,13 +21,15 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
21
21
|
if (parsedArgs.browser !== "firefox") {
|
|
22
22
|
return [];
|
|
23
23
|
}
|
|
24
|
-
const cookies = await this
|
|
24
|
+
const cookies: CookieRow[] = await this.getFirefoxCookie({ name, domain });
|
|
25
25
|
if (Array.isArray(cookies)) {
|
|
26
26
|
return cookies.map((cookie: CookieRow) => {
|
|
27
27
|
return {
|
|
28
28
|
domain: cookie.domain,
|
|
29
29
|
name: cookie.name,
|
|
30
|
-
value: cookie.value
|
|
30
|
+
value: Buffer.isBuffer(cookie.value)
|
|
31
|
+
? cookie.value.toString("utf8")
|
|
32
|
+
: Buffer.from(cookie.value).toString("utf8"),
|
|
31
33
|
};
|
|
32
34
|
});
|
|
33
35
|
} else {
|
|
@@ -35,9 +37,12 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
async
|
|
40
|
+
private async getFirefoxCookie(
|
|
39
41
|
{ name, domain }: CookieSpec, //
|
|
40
|
-
) {
|
|
42
|
+
): Promise<CookieRow[]> {
|
|
43
|
+
if (!HOME) {
|
|
44
|
+
throw new Error("HOME environment variable is not set");
|
|
45
|
+
}
|
|
41
46
|
const files: string[] = findAllFiles({
|
|
42
47
|
path: path.join(
|
|
43
48
|
HOME,
|
|
@@ -48,23 +53,24 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
48
53
|
),
|
|
49
54
|
name: "cookies.sqlite",
|
|
50
55
|
});
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
const all: CookieRow[][] = await Promise.all(
|
|
57
|
+
files.map(async (file: string) => {
|
|
58
|
+
return await this.queryCookiesDb(file, name, domain);
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
55
61
|
// flatten
|
|
56
62
|
return all.flat();
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
async
|
|
65
|
+
private async queryCookiesDb(
|
|
60
66
|
file: string,
|
|
61
67
|
name: string,
|
|
62
68
|
domain: string,
|
|
63
69
|
): Promise<CookieRow[]> {
|
|
64
|
-
if (
|
|
70
|
+
if (!existsSync(file)) {
|
|
65
71
|
throw new Error(`File ${file} does not exist`);
|
|
66
72
|
}
|
|
67
|
-
let sql;
|
|
73
|
+
let sql: string;
|
|
68
74
|
//language=SQL
|
|
69
75
|
sql = "SELECT value, name, host FROM moz_cookies";
|
|
70
76
|
const { specifiedName, specifiedDomain } = specialCases({ name, domain });
|
|
@@ -80,7 +86,7 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
80
86
|
sql += `host LIKE '${domain}';`;
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
|
-
const rowTransform = (row: any) => {
|
|
89
|
+
const rowTransform = (row: any): CookieRow => {
|
|
84
90
|
// row is object key by column name
|
|
85
91
|
const value = row.value as string;
|
|
86
92
|
return {
|