@mherod/get-cookie 2.0.0-rc.29 → 2.0.0-rc.30
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/dist/cli.js +1 -974
- package/dist/index.js +2 -1024
- package/dist/module.js +289 -296
- package/dist/module.js.map +1 -1
- package/dist/prompt.2b8c61c0.js +40 -0
- package/dist/prompt.536a2c51.js +40 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/jest.config.ts +14 -0
- package/package-lock.json +67 -150
- package/package.json +22 -16
- package/src/ExportedCookie.ts +12 -0
- package/src/FetchResponse.ts +1 -2
- package/src/FileCookieStore.ts +1 -0
- package/src/StringToRegex.ts +10 -0
- package/src/argv.ts +10 -0
- package/src/browsers/ChromeApplicationSupport.ts +10 -0
- package/src/browsers/ChromeCookieQueryStrategy.ts +29 -171
- package/src/browsers/DoSqliteQueryWithTransform.ts +85 -0
- package/src/browsers/FirefoxCookieQueryStrategy.ts +4 -47
- package/src/browsers/decrypt.ts +118 -0
- package/src/browsers/getChromePassword.ts +9 -0
- package/src/execSimple.ts +13 -0
- package/src/findAllFiles.ts +24 -51
- package/src/getChromeCookie.ts +1 -1
- package/src/global.ts +1 -1
- package/src/index.ts +1 -2
- package/src/listChromeProfiles.ts +45 -0
- package/src/util/flatMapAsync.test.ts +55 -0
- package/src/util/flatMapAsync.ts +37 -0
- package/src/IsExportedCookie.ts +0 -9
- package/src/doSqliteQuery1Params.ts +0 -8
- package/src/utils.ts +0 -25
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import CookieQueryStrategy from "./CookieQueryStrategy";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import fs, { existsSync } from "fs";
|
|
2
|
+
import { env } from "../global";
|
|
3
|
+
import { existsSync } from "fs";
|
|
5
4
|
import { findAllFiles } from "../findAllFiles";
|
|
6
|
-
import
|
|
7
|
-
import * as path from "path";
|
|
5
|
+
import { join } from "path";
|
|
8
6
|
import { merge } from "lodash";
|
|
9
7
|
import { isCookieRow } from "../IsCookieRow";
|
|
10
|
-
import { isExportedCookie } from "../
|
|
8
|
+
import { isExportedCookie } from "../ExportedCookie";
|
|
11
9
|
import CookieRow from "../CookieRow";
|
|
12
10
|
import ExportedCookie from "../ExportedCookie";
|
|
13
11
|
import { stringToRegex } from "../StringToRegex";
|
|
14
12
|
import { parsedArgs } from "../argv";
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
13
|
+
import consola from "consola";
|
|
14
|
+
import { getChromePassword } from "./getChromePassword";
|
|
15
|
+
import { chromeApplicationSupport } from "./ChromeApplicationSupport";
|
|
16
|
+
import { decrypt } from "./decrypt";
|
|
17
|
+
import { doSqliteQueryWithTransform } from "./DoSqliteQueryWithTransform";
|
|
17
18
|
|
|
18
19
|
export default class ChromeCookieQueryStrategy implements CookieQueryStrategy {
|
|
19
20
|
browserName = "Chrome";
|
|
@@ -55,7 +56,7 @@ async function getPromise1(
|
|
|
55
56
|
async function getPromise(name: string, domain: string): Promise<CookieRow[]> {
|
|
56
57
|
try {
|
|
57
58
|
const files: string[] = await findAllFiles({
|
|
58
|
-
path:
|
|
59
|
+
path: chromeApplicationSupport,
|
|
59
60
|
name: "Cookies",
|
|
60
61
|
});
|
|
61
62
|
const promises: Promise<CookieRow[]>[] = files.map((file) =>
|
|
@@ -131,18 +132,10 @@ Promise<ExportedCookie[]> {
|
|
|
131
132
|
return results;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
const chromeLocal = path.join(
|
|
135
|
-
HOME,
|
|
136
|
-
"Library",
|
|
137
|
-
"Application Support",
|
|
138
|
-
"Google",
|
|
139
|
-
"Chrome"
|
|
140
|
-
);
|
|
141
|
-
|
|
142
135
|
async function getEncryptedChromeCookie({
|
|
143
136
|
name,
|
|
144
137
|
domain,
|
|
145
|
-
file =
|
|
138
|
+
file = join(chromeApplicationSupport, "Default", "Cookies"),
|
|
146
139
|
}: //
|
|
147
140
|
{
|
|
148
141
|
name: string;
|
|
@@ -154,38 +147,47 @@ async function getEncryptedChromeCookie({
|
|
|
154
147
|
}
|
|
155
148
|
if (parsedArgs.verbose) {
|
|
156
149
|
const s = file.split("/").slice(-3).join("/");
|
|
157
|
-
|
|
150
|
+
consola.start(
|
|
151
|
+
`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`
|
|
152
|
+
);
|
|
158
153
|
}
|
|
159
154
|
let sql;
|
|
160
155
|
//language=SQL
|
|
161
|
-
sql = "SELECT
|
|
162
|
-
//
|
|
163
|
-
|
|
156
|
+
sql = "SELECT encrypted_value, name, host_key, expires_utc FROM cookies";
|
|
157
|
+
// Define a regular expression to match wildcard characters
|
|
164
158
|
const wildcardRegexp = /^([*%])$/i;
|
|
159
|
+
// Check if the name does not contain wildcard characters
|
|
165
160
|
const specifiedName = name.match(wildcardRegexp) == null;
|
|
161
|
+
// Check if the domain does not contain wildcard characters
|
|
166
162
|
const specifiedDomain = domain.match(wildcardRegexp) == null;
|
|
163
|
+
// Check if the domain contains wildcard characters
|
|
167
164
|
const wildcardDomain = domain.match(/[%*]/) != null;
|
|
165
|
+
// Determine if we should query the domain based on the previous checks
|
|
168
166
|
const queryDomain = specifiedDomain && !wildcardDomain;
|
|
169
167
|
// if we have a wildcard domain, we need to use a regexp
|
|
168
|
+
// If the name is specified or we need to query the domain, we add a WHERE clause to the SQL query
|
|
170
169
|
if (specifiedName || queryDomain) {
|
|
171
170
|
sql += ` WHERE `;
|
|
171
|
+
// If the name is specified, we add a condition to the SQL query to match the name
|
|
172
172
|
if (specifiedName) {
|
|
173
173
|
sql += `name = '${name}'`;
|
|
174
|
+
// If we also need to query the domain, we add an AND operator to the SQL query
|
|
174
175
|
if (queryDomain) {
|
|
175
176
|
sql += ` AND `;
|
|
176
177
|
}
|
|
177
178
|
}
|
|
179
|
+
// If we need to query the domain, we add a condition to the SQL query to match the domain
|
|
178
180
|
if (queryDomain) {
|
|
179
|
-
// leading dot replaced with % to match subdomains
|
|
181
|
+
// The leading dot is replaced with % to match subdomains
|
|
180
182
|
const sqlEmbedDomain = domain.replace(/^[.%]?/, "%");
|
|
181
183
|
sql += `host_key LIKE '${sqlEmbedDomain}';`;
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
if (parsedArgs.verbose) {
|
|
185
|
-
|
|
187
|
+
consola.info("Querying:", sql);
|
|
186
188
|
}
|
|
187
189
|
const domainRegexp: RegExp = stringToRegex(domain);
|
|
188
|
-
const sqliteQuery1: CookieRow[] = await
|
|
190
|
+
const sqliteQuery1: CookieRow[] = await doSqliteQueryWithTransform({
|
|
189
191
|
file: file,
|
|
190
192
|
sql: sql,
|
|
191
193
|
rowFilter: (row) => {
|
|
@@ -199,157 +201,13 @@ async function getEncryptedChromeCookie({
|
|
|
199
201
|
value: row["encrypted_value"],
|
|
200
202
|
};
|
|
201
203
|
if (parsedArgs.verbose) {
|
|
202
|
-
|
|
204
|
+
consola.info("Found", cookieRow);
|
|
203
205
|
}
|
|
204
206
|
return cookieRow;
|
|
205
207
|
},
|
|
206
208
|
});
|
|
207
209
|
return sqliteQuery1.filter((row) => {
|
|
210
|
+
// TODO: is this needed?
|
|
208
211
|
return row.domain.match(domainRegexp) != null;
|
|
209
212
|
});
|
|
210
213
|
}
|
|
211
|
-
|
|
212
|
-
async function getChromePassword(): Promise<string> {
|
|
213
|
-
return execSimple(
|
|
214
|
-
'security find-generic-password -w -s "Chrome Safe Storage"'
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
async function decrypt(
|
|
219
|
-
password: crypto.BinaryLike,
|
|
220
|
-
encryptedData: Buffer
|
|
221
|
-
): Promise<string> {
|
|
222
|
-
if (typeof password !== "string") {
|
|
223
|
-
throw new Error("password must be a string: " + password);
|
|
224
|
-
}
|
|
225
|
-
let encryptedData1: any;
|
|
226
|
-
encryptedData1 = encryptedData;
|
|
227
|
-
if (encryptedData1 == null || typeof encryptedData1 !== "object") {
|
|
228
|
-
throw new Error("encryptedData must be a object: " + encryptedData1);
|
|
229
|
-
}
|
|
230
|
-
if (!(encryptedData1 instanceof Buffer)) {
|
|
231
|
-
if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
|
|
232
|
-
[encryptedData1] = encryptedData1;
|
|
233
|
-
if (parsedArgs.verbose) {
|
|
234
|
-
console.log(
|
|
235
|
-
`encryptedData is an array of buffers, selected first: ${encryptedData1}`
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
throw new Error("encryptedData must be a Buffer: " + encryptedData1);
|
|
240
|
-
}
|
|
241
|
-
encryptedData1 = Buffer.from(encryptedData1);
|
|
242
|
-
}
|
|
243
|
-
if (parsedArgs.verbose) {
|
|
244
|
-
console.log(`Trying to decrypt with password ${password}`);
|
|
245
|
-
}
|
|
246
|
-
return new Promise((resolve, reject) => {
|
|
247
|
-
crypto.pbkdf2(password, "saltysalt", 1003, 16, "sha1", (error, buffer) => {
|
|
248
|
-
try {
|
|
249
|
-
if (error) {
|
|
250
|
-
if (parsedArgs.verbose) {
|
|
251
|
-
console.log("Error doing pbkdf2", error);
|
|
252
|
-
}
|
|
253
|
-
reject(error);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (buffer.length !== 16) {
|
|
258
|
-
if (parsedArgs.verbose) {
|
|
259
|
-
console.log(
|
|
260
|
-
"Error doing pbkdf2, buffer length is not 16",
|
|
261
|
-
buffer.length
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
reject(new Error("Buffer length is not 16"));
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const str = new Array(17).join(" ");
|
|
269
|
-
const iv = Buffer.from(str, "binary");
|
|
270
|
-
const decipher = crypto.createDecipheriv("aes-128-cbc", buffer, iv);
|
|
271
|
-
decipher.setAutoPadding(false);
|
|
272
|
-
|
|
273
|
-
if (encryptedData1 && encryptedData1.slice) {
|
|
274
|
-
encryptedData1 = encryptedData1.slice(3);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (encryptedData1.length % 16 !== 0) {
|
|
278
|
-
if (parsedArgs.verbose) {
|
|
279
|
-
console.log(
|
|
280
|
-
"Error doing pbkdf2, encryptedData length is not a multiple of 16",
|
|
281
|
-
encryptedData1.length
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
reject(new Error("encryptedData length is not a multiple of 16"));
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
let decoded = decipher.update(encryptedData1);
|
|
289
|
-
try {
|
|
290
|
-
decipher.final("utf-8");
|
|
291
|
-
} catch (e) {
|
|
292
|
-
if (parsedArgs.verbose) {
|
|
293
|
-
console.log("Error doing decipher.final()", e);
|
|
294
|
-
}
|
|
295
|
-
reject(e);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const padding = decoded[decoded.length - 1];
|
|
300
|
-
if (padding) {
|
|
301
|
-
decoded = decoded.slice(0, 0 - padding);
|
|
302
|
-
}
|
|
303
|
-
// noinspection JSCheckFunctionSignatures
|
|
304
|
-
const decodedString = decoded.toString("utf8");
|
|
305
|
-
resolve(decodedString);
|
|
306
|
-
} catch (e) {
|
|
307
|
-
reject(e);
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
export async function doChromeSqliteQuery1({
|
|
314
|
-
file,
|
|
315
|
-
sql,
|
|
316
|
-
rowFilter = () => true,
|
|
317
|
-
rowTransform,
|
|
318
|
-
}: DoSqliteQuery1Params): Promise<CookieRow[]> {
|
|
319
|
-
if (!file || (file && !fs.existsSync(file))) {
|
|
320
|
-
throw new Error(`doSqliteQuery1: file ${file} does not exist`);
|
|
321
|
-
}
|
|
322
|
-
const db = new sqlite3.Database(file);
|
|
323
|
-
return new Promise((resolve, reject) => {
|
|
324
|
-
db.all(sql, (err: Error, rows: any[]) => {
|
|
325
|
-
if (err) {
|
|
326
|
-
reject(err);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
const rows1: any[] = rows;
|
|
330
|
-
if (rows1 == null || rows1.length === 0) {
|
|
331
|
-
resolve([]);
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
if (Array.isArray(rows1)) {
|
|
335
|
-
const cookieRows: CookieRow[] = rows1
|
|
336
|
-
.filter(rowFilter)
|
|
337
|
-
.map((row: any) => {
|
|
338
|
-
const newVar = {
|
|
339
|
-
meta: {
|
|
340
|
-
file: file,
|
|
341
|
-
},
|
|
342
|
-
};
|
|
343
|
-
const cookieRow: CookieRow = rowTransform(row);
|
|
344
|
-
return merge(newVar, cookieRow);
|
|
345
|
-
});
|
|
346
|
-
resolve(cookieRows);
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (parsedArgs.verbose) {
|
|
350
|
-
console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
|
|
351
|
-
}
|
|
352
|
-
resolve([rows1]);
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
}
|
|
@@ -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 "consola";
|
|
7
|
+
|
|
8
|
+
export interface DoSqliteQueryWithTransformOptions {
|
|
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) {
|
|
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 doSqliteQueryWithTransform(
|
|
47
|
+
//
|
|
48
|
+
{
|
|
49
|
+
//
|
|
50
|
+
file,
|
|
51
|
+
sql,
|
|
52
|
+
rowFilter = () => true,
|
|
53
|
+
rowTransform,
|
|
54
|
+
}: DoSqliteQueryWithTransformOptions
|
|
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
|
+
}
|
|
@@ -1,57 +1,14 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
import CookieQueryStrategy from "./CookieQueryStrategy";
|
|
3
3
|
import { HOME } from "../global";
|
|
4
|
-
import
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
5
|
import { findAllFiles } from "../findAllFiles";
|
|
6
6
|
import ExportedCookie from "../ExportedCookie";
|
|
7
7
|
import CookieRow from "../CookieRow";
|
|
8
8
|
import CookieSpec from "../CookieSpec";
|
|
9
9
|
import { specialCases } from "../SpecialCases";
|
|
10
10
|
import { parsedArgs } from "../argv";
|
|
11
|
-
import {
|
|
12
|
-
import * as sqlite3 from "sqlite3";
|
|
13
|
-
import { merge } from "lodash";
|
|
14
|
-
|
|
15
|
-
export async function doSqliteQuery1({
|
|
16
|
-
file,
|
|
17
|
-
sql,
|
|
18
|
-
rowTransform,
|
|
19
|
-
}: DoSqliteQuery1Params): Promise<CookieRow[]> {
|
|
20
|
-
if (!file || (file && !fs.existsSync(file))) {
|
|
21
|
-
throw new Error(`doSqliteQuery1: file ${file} does not exist`);
|
|
22
|
-
}
|
|
23
|
-
const db = new sqlite3.Database(file);
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
db.all(sql, (err: Error, rows: any[]) => {
|
|
26
|
-
if (err) {
|
|
27
|
-
reject(err);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const rows1: any[] = rows;
|
|
31
|
-
if (rows1 == null || rows1.length === 0) {
|
|
32
|
-
resolve([]);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (Array.isArray(rows1)) {
|
|
36
|
-
const cookieRows: CookieRow[] = rows1.map((row: any) => {
|
|
37
|
-
const newVar = {
|
|
38
|
-
meta: {
|
|
39
|
-
file: file,
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
const cookieRow: CookieRow = rowTransform(row);
|
|
43
|
-
return merge(newVar, cookieRow);
|
|
44
|
-
});
|
|
45
|
-
resolve(cookieRows);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (parsedArgs.verbose) {
|
|
49
|
-
console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
|
|
50
|
-
}
|
|
51
|
-
resolve([rows1]);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
}
|
|
11
|
+
import { doSqliteQueryWithTransform } from "./DoSqliteQueryWithTransform";
|
|
55
12
|
|
|
56
13
|
export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
57
14
|
browserName = "Firefox";
|
|
@@ -81,7 +38,7 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
81
38
|
async #getFirefoxCookie(
|
|
82
39
|
{ name, domain }: CookieSpec //
|
|
83
40
|
) {
|
|
84
|
-
const files: string[] =
|
|
41
|
+
const files: string[] = findAllFiles({
|
|
85
42
|
path: path.join(
|
|
86
43
|
HOME,
|
|
87
44
|
"Library",
|
|
@@ -133,7 +90,7 @@ export default class FirefoxCookieQueryStrategy implements CookieQueryStrategy {
|
|
|
133
90
|
};
|
|
134
91
|
};
|
|
135
92
|
try {
|
|
136
|
-
return await
|
|
93
|
+
return await doSqliteQueryWithTransform({
|
|
137
94
|
file,
|
|
138
95
|
sql,
|
|
139
96
|
rowTransform,
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { BinaryLike, createDecipheriv, pbkdf2 } from "crypto";
|
|
2
|
+
import { parsedArgs } from "../argv";
|
|
3
|
+
import consola from "consola";
|
|
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,9 @@
|
|
|
1
|
+
import { execSimple } from "../execSimple";
|
|
2
|
+
|
|
3
|
+
const chromePassword: Promise<string> = execSimple(
|
|
4
|
+
'security find-generic-password -w -s "Chrome Safe Storage"'
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
export async function getChromePassword(): Promise<string> {
|
|
8
|
+
return await chromePassword;
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
|
|
3
|
+
export async function execSimple(command: string): Promise<string> {
|
|
4
|
+
try {
|
|
5
|
+
const stdout = execSync(command, {
|
|
6
|
+
encoding: "binary",
|
|
7
|
+
maxBuffer: 5 * 1024,
|
|
8
|
+
});
|
|
9
|
+
return stdout.trim();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/findAllFiles.ts
CHANGED
|
@@ -1,65 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { readdir } from "fs/promises";
|
|
1
|
+
import { existsSync } from "fs";
|
|
3
2
|
import { parsedArgs } from "./argv";
|
|
4
3
|
import consola from "consola";
|
|
4
|
+
import { sync } from "fast-glob";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
path,
|
|
8
|
-
name,
|
|
9
|
-
maxDepth = 2,
|
|
10
|
-
}: //
|
|
11
|
-
{
|
|
6
|
+
type FindFilesOptions = {
|
|
12
7
|
path: string;
|
|
13
8
|
name: string;
|
|
14
9
|
maxDepth?: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
consola.error(`Error reading ${path}`, e);
|
|
25
|
-
}
|
|
26
|
-
return [];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function findAllFiles(
|
|
13
|
+
//
|
|
14
|
+
{ path, name, maxDepth = 2 }: FindFilesOptions
|
|
15
|
+
): //
|
|
16
|
+
string[] {
|
|
17
|
+
if (!existsSync(path)) {
|
|
18
|
+
throw new Error(`Path ${path} does not exist`);
|
|
27
19
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
stat = fs.statSync(filePath);
|
|
33
|
-
} catch (e) {
|
|
34
|
-
if (parsedArgs.verbose) {
|
|
35
|
-
consola.error(`Error getting stat for ${filePath}`, e);
|
|
36
|
-
}
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (stat.isDirectory()) {
|
|
40
|
-
if (filePath.split("/").length < rootSegments + maxDepth) {
|
|
41
|
-
try {
|
|
42
|
-
const subFiles = await findAllFiles({
|
|
43
|
-
path: filePath,
|
|
44
|
-
name: name,
|
|
45
|
-
maxDepth: 2,
|
|
46
|
-
});
|
|
47
|
-
files.push(...subFiles);
|
|
48
|
-
} catch (e) {
|
|
49
|
-
if (parsedArgs.verbose) {
|
|
50
|
-
consola.error(e);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
} else if (file === name) {
|
|
55
|
-
files.push(filePath);
|
|
56
|
-
}
|
|
20
|
+
|
|
21
|
+
if (parsedArgs.verbose) {
|
|
22
|
+
consola.start(`Searching for ${name} files in ${path}`);
|
|
57
23
|
}
|
|
24
|
+
|
|
25
|
+
const files: string[] = sync(`${path}/**/${name}`, {
|
|
26
|
+
onlyFiles: true,
|
|
27
|
+
deep: maxDepth,
|
|
28
|
+
});
|
|
29
|
+
|
|
58
30
|
if (parsedArgs.verbose) {
|
|
59
31
|
if (files.length > 0) {
|
|
60
|
-
consola.
|
|
61
|
-
consola.
|
|
32
|
+
consola.success(`Found ${files.length} ${name} files`);
|
|
33
|
+
consola.info(files);
|
|
62
34
|
}
|
|
63
35
|
}
|
|
36
|
+
|
|
64
37
|
return files;
|
|
65
38
|
}
|
package/src/getChromeCookie.ts
CHANGED
|
@@ -2,7 +2,7 @@ import CookieSpec from "./CookieSpec";
|
|
|
2
2
|
import ExportedCookie from "./ExportedCookie";
|
|
3
3
|
import { queryCookies } from "./queryCookies";
|
|
4
4
|
import ChromeCookieQueryStrategy from "./browsers/ChromeCookieQueryStrategy";
|
|
5
|
-
import { isExportedCookie } from "./
|
|
5
|
+
import { isExportedCookie } from "./ExportedCookie";
|
|
6
6
|
|
|
7
7
|
export async function getChromeCookie(
|
|
8
8
|
params: CookieSpec
|
package/src/global.ts
CHANGED
package/src/index.ts
CHANGED