@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
|
@@ -7,81 +7,116 @@ import { stringToRegex } from "../StringToRegex";
|
|
|
7
7
|
import CookieRow from "../CookieRow";
|
|
8
8
|
import consola from "../logger";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
name,
|
|
12
|
-
domain,
|
|
13
|
-
file = join(chromeApplicationSupport, "Default", "Cookies"),
|
|
14
|
-
}: {
|
|
10
|
+
interface GetEncryptedChromeCookieParams {
|
|
15
11
|
name: string;
|
|
16
12
|
domain: string;
|
|
17
|
-
file
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
file?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface SqlQueryBuilderInterface {
|
|
17
|
+
build(): string;
|
|
18
|
+
addCondition(field: string, value: string, operator?: string): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class SqlQueryBuilder implements SqlQueryBuilderInterface {
|
|
22
|
+
private baseQuery: string;
|
|
23
|
+
private conditions: string[];
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
baseQuery: string = "SELECT encrypted_value, name, host_key, expires_utc FROM cookies",
|
|
27
|
+
) {
|
|
28
|
+
this.baseQuery = baseQuery;
|
|
29
|
+
this.conditions = [];
|
|
21
30
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
|
|
32
|
+
addCondition(field: string, value: string, operator: string = "="): void {
|
|
33
|
+
const wildcardRegexp = /^([*%])$/i;
|
|
34
|
+
if (!wildcardRegexp.test(value)) {
|
|
35
|
+
const condition = `${field} ${operator} '${value}'`;
|
|
36
|
+
this.conditions.push(condition);
|
|
37
|
+
}
|
|
27
38
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
}
|
|
39
|
+
|
|
40
|
+
build(): string {
|
|
41
|
+
let sql = this.baseQuery;
|
|
42
|
+
if (this.conditions.length > 0) {
|
|
43
|
+
sql += ` WHERE ${this.conditions.join(" AND ")}`;
|
|
52
44
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
return sql;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface LoggerInterface {
|
|
50
|
+
log(file: string, name: string, domain: string, sql: string): void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class VerboseLogger implements LoggerInterface {
|
|
54
|
+
log(file: string, name: string, domain: string, sql: string): void {
|
|
55
|
+
if (parsedArgs.verbose) {
|
|
56
|
+
const s = file.split("/").slice(-3).join("/");
|
|
57
|
+
consola.start(
|
|
58
|
+
`Trying Chrome (at ${s}) cookie ${name} for domain ${domain}`,
|
|
59
|
+
);
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface RowTransformerInterface {
|
|
65
|
+
transform(row: any): CookieRow;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class RowTransformer implements RowTransformerInterface {
|
|
69
|
+
transform(row: any): CookieRow {
|
|
70
|
+
const cookieRow: CookieRow = {
|
|
71
|
+
expiry: (row["expires_utc"] / 1000000 - 11644473600) * 1000,
|
|
72
|
+
domain: row["host_key"],
|
|
73
|
+
name: row["name"],
|
|
74
|
+
value: row["encrypted_value"],
|
|
75
|
+
};
|
|
76
|
+
return cookieRow;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function getEncryptedChromeCookie({
|
|
81
|
+
name,
|
|
82
|
+
domain,
|
|
83
|
+
file = join(chromeApplicationSupport, "Default", "Cookies"),
|
|
84
|
+
}: GetEncryptedChromeCookieParams): Promise<CookieRow[]> {
|
|
85
|
+
if (!existsSync(file)) {
|
|
86
|
+
throw new Error(`File ${file} does not exist`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const sqlQueryBuilder: SqlQueryBuilderInterface = new SqlQueryBuilder();
|
|
90
|
+
sqlQueryBuilder.addCondition("name", name);
|
|
91
|
+
const wildcardDomain = /[%*]/.test(domain);
|
|
92
|
+
if (!wildcardDomain) {
|
|
93
|
+
const sqlEmbedDomain = domain.replace(/^[.%]?/, "%");
|
|
94
|
+
sqlQueryBuilder.addCondition("host_key", sqlEmbedDomain, "LIKE");
|
|
62
95
|
}
|
|
96
|
+
const sql: string = sqlQueryBuilder.build();
|
|
97
|
+
|
|
98
|
+
const verboseLogger: LoggerInterface = new VerboseLogger();
|
|
99
|
+
verboseLogger.log(file, name, domain, sql);
|
|
100
|
+
|
|
63
101
|
const domainRegexp: RegExp = stringToRegex(domain);
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return cookieRow;
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
return sqliteQuery1.filter((row) => {
|
|
84
|
-
// TODO: is this needed?
|
|
102
|
+
const rowTransformer: RowTransformerInterface = new RowTransformer();
|
|
103
|
+
let sqliteQuery1: CookieRow[];
|
|
104
|
+
try {
|
|
105
|
+
sqliteQuery1 = await querySqliteThenTransform({
|
|
106
|
+
file: file,
|
|
107
|
+
sql: sql,
|
|
108
|
+
rowFilter: (row) => {
|
|
109
|
+
return row["host_key"].match(domainRegexp) != null;
|
|
110
|
+
},
|
|
111
|
+
rowTransform: rowTransformer.transform.bind(rowTransformer),
|
|
112
|
+
});
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const filteredResults = sqliteQuery1.filter((row) => {
|
|
85
118
|
return row.domain.match(domainRegexp) != null;
|
|
86
119
|
});
|
|
120
|
+
|
|
121
|
+
return filteredResults;
|
|
87
122
|
}
|
|
@@ -4,15 +4,19 @@ import CookieQueryStrategy from "../CookieQueryStrategy";
|
|
|
4
4
|
export default class MockCookieQueryStrategy implements CookieQueryStrategy {
|
|
5
5
|
browserName: string = "mock";
|
|
6
6
|
|
|
7
|
-
private cookies: ExportedCookie[]
|
|
7
|
+
private cookies: ExportedCookie[];
|
|
8
8
|
|
|
9
9
|
constructor(cookies: ExportedCookie[]) {
|
|
10
10
|
this.cookies = cookies;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const filteredCookies: ExportedCookie[] = [];
|
|
15
|
+
for (const cookie of this.cookies) {
|
|
16
|
+
if (cookie.name === name && cookie.domain === domain) {
|
|
17
|
+
filteredCookies.push(cookie);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return filteredCookies;
|
|
17
21
|
}
|
|
18
22
|
}
|
|
@@ -1,11 +1,42 @@
|
|
|
1
1
|
import CookieQueryStrategy from "../CookieQueryStrategy";
|
|
2
2
|
import ExportedCookie from "../../ExportedCookie";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { decodeBinaryCookies } from "../../decodeBinaryCookies"; // Assuming you have a module to decode binary cookies
|
|
5
|
+
import CookieRow from "../../CookieRow";
|
|
3
6
|
|
|
4
7
|
export default class SafariCookieQueryStrategy implements CookieQueryStrategy {
|
|
5
|
-
browserName = "Safari";
|
|
8
|
+
browserName: string = "Safari";
|
|
6
9
|
|
|
7
10
|
async queryCookies(name: string, domain: string): Promise<ExportedCookie[]> {
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
const homeDir: string | undefined = process.env.HOME;
|
|
12
|
+
if (!homeDir) {
|
|
13
|
+
throw new Error("HOME environment variable is not set");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const cookieDbPath: string = join(
|
|
17
|
+
homeDir,
|
|
18
|
+
"Library",
|
|
19
|
+
"Cookies",
|
|
20
|
+
"Cookies.binarycookies",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const cookies: CookieRow[] = await decodeBinaryCookies(cookieDbPath);
|
|
25
|
+
|
|
26
|
+
const filteredCookies: CookieRow[] = cookies.filter(
|
|
27
|
+
(cookie: CookieRow) => cookie.name === name && cookie.domain.includes(domain)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const exportedCookies: ExportedCookie[] = filteredCookies.map((cookie: CookieRow) => ({
|
|
31
|
+
domain: cookie.domain,
|
|
32
|
+
name: cookie.name,
|
|
33
|
+
value: cookie.value.toString("utf8"),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
return exportedCookies;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.error(`Error decoding ${cookieDbPath}`, e);
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
10
41
|
}
|
|
11
42
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { argv, parsedArgs } from "./argv";
|
|
4
4
|
import { fetchWithCookies } from "./fetchWithCookies";
|
|
@@ -10,13 +10,19 @@ import { cliQueryCookies } from "./cliQueryCookies";
|
|
|
10
10
|
|
|
11
11
|
async function main() {
|
|
12
12
|
if (parsedArgs["help"] || parsedArgs["h"]) {
|
|
13
|
-
logger.log(`Usage: ${argv[1]} [name] [domain] [options]
|
|
13
|
+
logger.log(`Usage: ${argv[1]} [name] [domain] [options]`);
|
|
14
14
|
logger.log(`Options:`);
|
|
15
|
-
logger.log(` -h, --help: Show this help`);
|
|
16
|
-
logger.log(` -v, --verbose:
|
|
15
|
+
logger.log(` -h, --help: Show this help message`);
|
|
16
|
+
logger.log(` -v, --verbose: Enable verbose output`);
|
|
17
17
|
logger.log(` -d, --dump: Dump all results`);
|
|
18
18
|
logger.log(` -D, --dump-grouped: Dump all results, grouped by profile`);
|
|
19
19
|
logger.log(` -r, --render: Render all results`);
|
|
20
|
+
logger.log(` -F, --fetch <url>: Fetch data from the specified URL`);
|
|
21
|
+
logger.log(` -H <header>: Specify headers for the fetch request`);
|
|
22
|
+
logger.log(
|
|
23
|
+
` --dump-response-headers: Dump response headers from fetch request`,
|
|
24
|
+
);
|
|
25
|
+
logger.log(` --dump-response-body: Dump response body from fetch request`);
|
|
20
26
|
return;
|
|
21
27
|
}
|
|
22
28
|
|
|
@@ -26,7 +32,7 @@ async function main() {
|
|
|
26
32
|
if (fetchUrl) {
|
|
27
33
|
let url: URL;
|
|
28
34
|
try {
|
|
29
|
-
url = new URL(
|
|
35
|
+
url = new URL(fetchUrl);
|
|
30
36
|
} catch (e) {
|
|
31
37
|
logger.error("Invalid URL", fetchUrl);
|
|
32
38
|
return;
|
|
@@ -34,34 +40,25 @@ async function main() {
|
|
|
34
40
|
logger.start("Fetching", url.href);
|
|
35
41
|
const headerArgs: string[] | string = parsedArgs["H"];
|
|
36
42
|
const headers = unpackHeaders(headerArgs);
|
|
37
|
-
const onfulfilled = (res: Response) => {
|
|
43
|
+
const onfulfilled = async (res: Response) => {
|
|
38
44
|
if (parsedArgs["dump-response-headers"]) {
|
|
39
|
-
|
|
45
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
40
46
|
logger.log(`${key}: ${value}`);
|
|
41
|
-
}
|
|
47
|
+
}
|
|
42
48
|
}
|
|
43
49
|
if (parsedArgs["dump-response-body"]) {
|
|
44
|
-
res.text()
|
|
45
|
-
|
|
46
|
-
});
|
|
50
|
+
const responseBody = await res.text();
|
|
51
|
+
logger.log(responseBody);
|
|
47
52
|
}
|
|
48
|
-
return;
|
|
49
53
|
};
|
|
50
|
-
|
|
51
|
-
url,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
(res) => {
|
|
59
|
-
logger.debug("Response", res);
|
|
60
|
-
onfulfilled(res);
|
|
61
|
-
},
|
|
62
|
-
logger.error,
|
|
63
|
-
//
|
|
64
|
-
);
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetchWithCookies(url, { headers });
|
|
56
|
+
logger.debug("Response", res);
|
|
57
|
+
await onfulfilled(res);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error(error);
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
65
62
|
}
|
|
66
63
|
|
|
67
64
|
const cookieSpecs: CookieSpec[] = [];
|
|
@@ -81,7 +78,17 @@ async function main() {
|
|
|
81
78
|
logger.log("cookieSpecs", cookieSpecs);
|
|
82
79
|
}
|
|
83
80
|
|
|
84
|
-
|
|
81
|
+
try {
|
|
82
|
+
await cliQueryCookies(cookieSpecs);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error(error);
|
|
85
|
+
}
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
main().then(
|
|
88
|
+
main().then(
|
|
89
|
+
() => process.exit(0),
|
|
90
|
+
(error) => {
|
|
91
|
+
logger.error(error);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
},
|
|
94
|
+
);
|
package/src/cliQueryCookies.ts
CHANGED
|
@@ -10,26 +10,29 @@ export async function cliQueryCookies(
|
|
|
10
10
|
cookieSpec: CookieSpec | CookieSpec[],
|
|
11
11
|
limit?: number,
|
|
12
12
|
removeExpired?: boolean,
|
|
13
|
-
//
|
|
14
13
|
) {
|
|
15
14
|
try {
|
|
16
15
|
const results: ExportedCookie[] = await comboQueryCookieSpec(cookieSpec, {
|
|
17
16
|
limit,
|
|
18
17
|
removeExpired,
|
|
19
18
|
});
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
if (!results || results.length === 0) {
|
|
21
21
|
logger.error("No results");
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
+
|
|
24
25
|
if (parsedArgs["dump"] || parsedArgs["d"]) {
|
|
25
26
|
logger.log(results);
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
29
|
+
|
|
28
30
|
if (parsedArgs["dump-grouped"] || parsedArgs["D"]) {
|
|
29
31
|
const groupedByFile = groupBy(results, (r) => r.meta?.file);
|
|
30
32
|
logger.log(JSON.stringify(groupedByFile, null, 2));
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
35
|
+
|
|
33
36
|
if (
|
|
34
37
|
parsedArgs["render"] ||
|
|
35
38
|
parsedArgs["render-merged"] ||
|
|
@@ -38,14 +41,21 @@ export async function cliQueryCookies(
|
|
|
38
41
|
logger.log(resultsRendered(results));
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
44
|
+
|
|
41
45
|
if (parsedArgs["render-grouped"] || parsedArgs["R"]) {
|
|
42
46
|
const groupedByFile = groupBy(results, (r) => r.meta?.file);
|
|
43
47
|
for (const file of Object.keys(groupedByFile)) {
|
|
44
|
-
|
|
45
|
-
logger.log(file
|
|
48
|
+
const fileResults = groupedByFile[file];
|
|
49
|
+
logger.log(`${file}: ${resultsRendered(fileResults)}`);
|
|
46
50
|
}
|
|
47
51
|
return;
|
|
48
52
|
}
|
|
53
|
+
|
|
54
|
+
if (parsedArgs["output"] === "json") {
|
|
55
|
+
logger.log(JSON.stringify(results, null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
for (const result of results) {
|
|
50
60
|
logger.log(result.value);
|
|
51
61
|
}
|
|
@@ -12,18 +12,15 @@ export async function comboQueryCookieSpec(
|
|
|
12
12
|
): Promise<ExportedCookie[]> {
|
|
13
13
|
const optsWithDefaults: CookieQueryOptions<CookieQueryStrategy> =
|
|
14
14
|
mergedWithDefaults(options);
|
|
15
|
-
const
|
|
15
|
+
const queryFn = async (cs: CookieSpec): Promise<ExportedCookie[]> =>
|
|
16
|
+
queryCookies(cs, optsWithDefaults);
|
|
16
17
|
|
|
18
|
+
let cookies: ExportedCookie[];
|
|
17
19
|
if (Array.isArray(cookieSpec)) {
|
|
18
|
-
|
|
19
|
-
cookieSpec,
|
|
20
|
-
async (cs: CookieSpec) => {
|
|
21
|
-
return await fn(cs);
|
|
22
|
-
},
|
|
23
|
-
);
|
|
24
|
-
return processBeforeReturn(cookiesForMultiSpec, options);
|
|
20
|
+
cookies = await flatMapAsync(cookieSpec, queryFn);
|
|
25
21
|
} else {
|
|
26
|
-
|
|
27
|
-
return processBeforeReturn(cookiesForSingleSpec, options);
|
|
22
|
+
cookies = await queryFn(cookieSpec);
|
|
28
23
|
}
|
|
24
|
+
|
|
25
|
+
return processBeforeReturn(cookies, options);
|
|
29
26
|
}
|
|
@@ -11,10 +11,17 @@ export type CookieQueryOptions<T extends CookieQueryStrategy> = {
|
|
|
11
11
|
export const defaultCookieQueryOptions: CookieQueryOptions<CookieQueryStrategy> =
|
|
12
12
|
{
|
|
13
13
|
strategy: new CompositeCookieQueryStrategy(),
|
|
14
|
+
limit: undefined,
|
|
15
|
+
removeExpired: undefined,
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export function mergedWithDefaults<T extends CookieQueryStrategy>(
|
|
17
19
|
options?: CookieQueryOptions<T>,
|
|
18
20
|
): CookieQueryOptions<T> {
|
|
19
|
-
|
|
21
|
+
const mergedOptions: CookieQueryOptions<T> = merge(
|
|
22
|
+
{},
|
|
23
|
+
defaultCookieQueryOptions,
|
|
24
|
+
options,
|
|
25
|
+
);
|
|
26
|
+
return mergedOptions;
|
|
20
27
|
}
|
|
@@ -13,15 +13,61 @@ export function cookieSpecsFromUrl(url: URL | string): CookieSpec[] {
|
|
|
13
13
|
return [];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const urlObj =
|
|
17
|
-
const hostnameParts = urlObj.hostname
|
|
18
|
-
const topLevelDomain = hostnameParts
|
|
16
|
+
const urlObj: URL = parseUrl(url);
|
|
17
|
+
const hostnameParts: string[] = splitHostname(urlObj.hostname);
|
|
18
|
+
const topLevelDomain: string = getTopLevelDomain(hostnameParts);
|
|
19
19
|
|
|
20
|
-
const cookieSpecs: CookieSpec[] =
|
|
20
|
+
const cookieSpecs: CookieSpec[] = createCookieSpecs(
|
|
21
|
+
urlObj.hostname,
|
|
22
|
+
topLevelDomain,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return uniqBy(
|
|
26
|
+
cookieSpecs,
|
|
27
|
+
(spec: CookieSpec) => `${spec.name}:${spec.domain}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parses the input URL string or URL object and returns a URL object.
|
|
33
|
+
* @param url - The URL to parse.
|
|
34
|
+
* @returns A URL object.
|
|
35
|
+
*/
|
|
36
|
+
function parseUrl(url: URL | string): URL {
|
|
37
|
+
return typeof url === "string" ? new URL(url) : url;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Splits the hostname into its constituent parts.
|
|
42
|
+
* @param hostname - The hostname to split.
|
|
43
|
+
* @returns An array of strings representing the parts of the hostname.
|
|
44
|
+
*/
|
|
45
|
+
function splitHostname(hostname: string): string[] {
|
|
46
|
+
return hostname.split(".");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Extracts the top-level domain from the hostname parts.
|
|
51
|
+
* @param hostnameParts - The parts of the hostname.
|
|
52
|
+
* @returns A string representing the top-level domain.
|
|
53
|
+
*/
|
|
54
|
+
function getTopLevelDomain(hostnameParts: string[]): string {
|
|
55
|
+
return hostnameParts.slice(-2).join(".");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates cookie specifications based on the hostname and top-level domain.
|
|
60
|
+
* @param hostname - The full hostname.
|
|
61
|
+
* @param topLevelDomain - The top-level domain.
|
|
62
|
+
* @returns An array of CookieSpec objects.
|
|
63
|
+
*/
|
|
64
|
+
function createCookieSpecs(
|
|
65
|
+
hostname: string,
|
|
66
|
+
topLevelDomain: string,
|
|
67
|
+
): CookieSpec[] {
|
|
68
|
+
return [
|
|
21
69
|
{ name: "%", domain: `%.${topLevelDomain}` },
|
|
22
|
-
{ name: "%", domain:
|
|
70
|
+
{ name: "%", domain: hostname },
|
|
23
71
|
{ name: "%", domain: topLevelDomain },
|
|
24
72
|
];
|
|
25
|
-
|
|
26
|
-
return uniqBy(cookieSpecs, JSON.stringify);
|
|
27
73
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import CookieRow from "./CookieRow";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
|
|
4
|
+
export const decodeBinaryCookies = async (cookieDbPath: string): Promise<CookieRow[]> => {
|
|
5
|
+
try {
|
|
6
|
+
// Check if file exists
|
|
7
|
+
await fs.access(cookieDbPath);
|
|
8
|
+
} catch {
|
|
9
|
+
// If file doesn't exist, return empty array
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Magic bytes: "COOK" = 0x636F6F6B
|
|
14
|
+
const magicBytes: Buffer = Buffer.from([0x63, 0x6F, 0x6B, 0x6B]);
|
|
15
|
+
const buffer: Buffer = await fs.readFile(cookieDbPath);
|
|
16
|
+
|
|
17
|
+
if (!buffer.slice(0, 4).equals(magicBytes)) {
|
|
18
|
+
throw new Error("Not a cookie file");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const count: number = buffer.readUInt32BE(4);
|
|
22
|
+
const cookies: CookieRow[] = [];
|
|
23
|
+
|
|
24
|
+
let offset: number = 8;
|
|
25
|
+
for (let i: number = 0; i < count; i++) {
|
|
26
|
+
const pageSize: number = buffer.readUInt32BE(offset);
|
|
27
|
+
offset += 4;
|
|
28
|
+
const page: Buffer = buffer.slice(offset, offset + pageSize);
|
|
29
|
+
offset += pageSize;
|
|
30
|
+
|
|
31
|
+
if (!page.slice(0, 4).equals(Buffer.from([0x00, 0x00, 0x01, 0x00]))) {
|
|
32
|
+
throw new Error("Bad page header");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cookieCount: number = page.readUInt32LE(4);
|
|
36
|
+
let pageOffset: number = 8;
|
|
37
|
+
for (let j: number = 0; j < cookieCount; j++) {
|
|
38
|
+
const cookieOffset: number = page.readUInt32LE(pageOffset);
|
|
39
|
+
pageOffset += 4;
|
|
40
|
+
const cookieLength: number = page.readUInt32LE(cookieOffset);
|
|
41
|
+
const cookie: Buffer = page.slice(cookieOffset, cookieOffset + cookieLength);
|
|
42
|
+
|
|
43
|
+
const flags: number = cookie.readUInt32LE(8);
|
|
44
|
+
const urlOffset: number = cookie.readUInt32LE(16);
|
|
45
|
+
const nameOffset: number = cookie.readUInt32LE(20);
|
|
46
|
+
const valueOffset: number = cookie.readUInt32LE(28);
|
|
47
|
+
const expiry: number = cookie.readDoubleLE(40) + 978307200;
|
|
48
|
+
|
|
49
|
+
const url: string = cookie.slice(urlOffset, nameOffset).toString('utf8').replace(/\0/g, '');
|
|
50
|
+
const name: string = cookie.slice(nameOffset, valueOffset).toString('utf8').replace(/\0/g, '');
|
|
51
|
+
const value: string = cookie.slice(valueOffset, cookie.length).toString('utf8').replace(/\0/g, '');
|
|
52
|
+
|
|
53
|
+
cookies.push({
|
|
54
|
+
domain: url,
|
|
55
|
+
name: name,
|
|
56
|
+
value: Buffer.from(value, 'utf8'),
|
|
57
|
+
expiry: new Date(expiry * 1000).getTime(),
|
|
58
|
+
meta: {
|
|
59
|
+
path: cookie.slice(cookie.readUInt32LE(24), valueOffset).toString('utf8').replace(/\0/g, ''),
|
|
60
|
+
httpOnly: (flags & 0x04) === 0x04,
|
|
61
|
+
secure: (flags & 0x01) === 0x01
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!page.slice(cookieCount * 4 + 8, cookieCount * 4 + 12).equals(Buffer.from([0x00, 0x00, 0x00, 0x00]))) {
|
|
67
|
+
throw new Error("Bad page trailer");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return cookies;
|
|
72
|
+
};
|
package/src/execSimple.ts
CHANGED