@puffle/cookiejar 0.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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/cli.js +151 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/access.js +39 -0
- package/dist/core/access.js.map +1 -0
- package/dist/core/audit.js +26 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/browsers/chromium.js +241 -0
- package/dist/core/browsers/chromium.js.map +1 -0
- package/dist/core/browsers/firefox.js +53 -0
- package/dist/core/browsers/firefox.js.map +1 -0
- package/dist/core/browsers/index.js +37 -0
- package/dist/core/browsers/index.js.map +1 -0
- package/dist/core/browsers/safari.js +79 -0
- package/dist/core/browsers/safari.js.map +1 -0
- package/dist/core/browsers/sqlite.js +38 -0
- package/dist/core/browsers/sqlite.js.map +1 -0
- package/dist/core/bundles.js +116 -0
- package/dist/core/bundles.js.map +1 -0
- package/dist/core/crypto.js +51 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/paths.js +20 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/vault.js +108 -0
- package/dist/core/vault.js.map +1 -0
- package/dist/mcp/server.js +139 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/server/index.js +442 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/util.js +55 -0
- package/dist/server/util.js.map +1 -0
- package/dist/ui/assets/index-DJtWXRmR.css +1 -0
- package/dist/ui/assets/index-Ddqx44Bn.js +43 -0
- package/dist/ui/index.html +15 -0
- package/package.json +56 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { withCookieDb } from './sqlite.js';
|
|
5
|
+
function profileRoots() {
|
|
6
|
+
const home = os.homedir();
|
|
7
|
+
return process.platform === 'darwin'
|
|
8
|
+
? [path.join(home, 'Library/Application Support/Firefox/Profiles')]
|
|
9
|
+
: [path.join(home, '.mozilla/firefox'), path.join(home, 'snap/firefox/common/.mozilla/firefox')];
|
|
10
|
+
}
|
|
11
|
+
export function discoverFirefoxProfiles() {
|
|
12
|
+
const profiles = [];
|
|
13
|
+
for (const root of profileRoots()) {
|
|
14
|
+
if (!fs.existsSync(root))
|
|
15
|
+
continue;
|
|
16
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
17
|
+
if (!entry.isDirectory())
|
|
18
|
+
continue;
|
|
19
|
+
const cookies = path.join(root, entry.name, 'cookies.sqlite');
|
|
20
|
+
if (!fs.existsSync(cookies))
|
|
21
|
+
continue;
|
|
22
|
+
const name = entry.name.replace(/^[a-z0-9]+\./i, '');
|
|
23
|
+
profiles.push({
|
|
24
|
+
browser: 'firefox',
|
|
25
|
+
id: `firefox:${entry.name}`,
|
|
26
|
+
label: `Firefox — ${name || entry.name}`,
|
|
27
|
+
path: cookies,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return profiles;
|
|
32
|
+
}
|
|
33
|
+
const sameSiteFromFirefox = (value) => value === 0 ? 'None' : value === 1 ? 'Lax' : value === 2 ? 'Strict' : 'Unspecified';
|
|
34
|
+
export function readFirefoxCookies(profile) {
|
|
35
|
+
return withCookieDb(profile.path, (db) => {
|
|
36
|
+
const rows = db
|
|
37
|
+
.prepare(`SELECT host, name, value, path, expiry, isSecure, isHttpOnly, sameSite FROM moz_cookies ORDER BY host, name`)
|
|
38
|
+
.all();
|
|
39
|
+
return rows.map((row) => ({
|
|
40
|
+
name: String(row.name),
|
|
41
|
+
value: String(row.value ?? ''),
|
|
42
|
+
domain: String(row.host),
|
|
43
|
+
path: String(row.path ?? '/'),
|
|
44
|
+
expires: Number(row.expiry ?? 0),
|
|
45
|
+
secure: Boolean(row.isSecure),
|
|
46
|
+
httpOnly: Boolean(row.isHttpOnly),
|
|
47
|
+
sameSite: sameSiteFromFirefox(Number(row.sameSite ?? -1)),
|
|
48
|
+
profileId: profile.id,
|
|
49
|
+
browser: profile.browser,
|
|
50
|
+
}));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=firefox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firefox.js","sourceRoot":"","sources":["../../../src/core/browsers/firefox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { discoverChromiumProfiles, readChromiumCookies } from './chromium.js';
|
|
2
|
+
import { discoverFirefoxProfiles, readFirefoxCookies } from './firefox.js';
|
|
3
|
+
import { discoverSafariProfiles, readSafariCookies } from './safari.js';
|
|
4
|
+
export function discoverProfiles() {
|
|
5
|
+
return [...discoverChromiumProfiles(), ...discoverFirefoxProfiles(), ...discoverSafariProfiles()];
|
|
6
|
+
}
|
|
7
|
+
export function findProfile(profileId) {
|
|
8
|
+
const profile = discoverProfiles().find((p) => p.id === profileId);
|
|
9
|
+
if (!profile)
|
|
10
|
+
throw new Error(`no such browser profile: ${profileId}`);
|
|
11
|
+
return profile;
|
|
12
|
+
}
|
|
13
|
+
export function readCookies(profile) {
|
|
14
|
+
if (profile.browser === 'firefox')
|
|
15
|
+
return readFirefoxCookies(profile);
|
|
16
|
+
if (profile.browser === 'safari')
|
|
17
|
+
return readSafariCookies(profile);
|
|
18
|
+
return readChromiumCookies(profile);
|
|
19
|
+
}
|
|
20
|
+
/** Reads every discovered profile, reporting per-profile failures instead of throwing. */
|
|
21
|
+
export function readAllProfiles(profileIds) {
|
|
22
|
+
return discoverProfiles()
|
|
23
|
+
.filter((profile) => !profileIds || profileIds.includes(profile.id))
|
|
24
|
+
.map((profile) => {
|
|
25
|
+
try {
|
|
26
|
+
return { profile, cookies: readCookies(profile) };
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return { profile, cookies: [], error: error instanceof Error ? error.message : String(error) };
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function toMeta(cookie) {
|
|
34
|
+
const { value, ...rest } = cookie;
|
|
35
|
+
return { ...rest, valueLength: value.length };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/browsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGxE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,GAAG,wBAAwB,EAAE,EAAE,GAAG,uBAAuB,EAAE,EAAE,GAAG,sBAAsB,EAAE,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;IACnE,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,SAAS,EAAE,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAuB;IACjD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACpE,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC;AAQD,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,UAAqB;IACnD,OAAO,gBAAgB,EAAE;SACtB,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,UAAU,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACnE,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACf,IAAI,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACjG,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,MAAqB;IAC1C,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAClC,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const MAC_EPOCH_OFFSET = 978_307_200; // 2001-01-01 → 1970-01-01
|
|
5
|
+
export class FullDiskAccessError extends Error {
|
|
6
|
+
constructor(file) {
|
|
7
|
+
super(`cannot read ${file}. macOS protects Safari's cookies: grant Full Disk Access to the terminal (or app) running cookiejar in System Settings → Privacy & Security → Full Disk Access.`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function discoverSafariProfiles() {
|
|
11
|
+
if (process.platform !== 'darwin')
|
|
12
|
+
return [];
|
|
13
|
+
const home = os.homedir();
|
|
14
|
+
const candidates = [
|
|
15
|
+
path.join(home, 'Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies'),
|
|
16
|
+
path.join(home, 'Library/Cookies/Cookies.binarycookies'),
|
|
17
|
+
];
|
|
18
|
+
const file = candidates.find((p) => fs.existsSync(p));
|
|
19
|
+
if (!file)
|
|
20
|
+
return [];
|
|
21
|
+
return [{ browser: 'safari', id: 'safari:default', label: 'Safari', path: file }];
|
|
22
|
+
}
|
|
23
|
+
/** Reads Apple's undocumented-but-stable `Cookies.binarycookies` container. */
|
|
24
|
+
export function parseBinaryCookies(buf, profile) {
|
|
25
|
+
if (buf.subarray(0, 4).toString('latin1') !== 'cook')
|
|
26
|
+
throw new Error('not a binarycookies file');
|
|
27
|
+
const pageCount = buf.readUInt32BE(4);
|
|
28
|
+
const pageSizes = [];
|
|
29
|
+
for (let i = 0; i < pageCount; i++)
|
|
30
|
+
pageSizes.push(buf.readUInt32BE(8 + i * 4));
|
|
31
|
+
const cookies = [];
|
|
32
|
+
let cursor = 8 + pageCount * 4;
|
|
33
|
+
for (const size of pageSizes) {
|
|
34
|
+
const page = buf.subarray(cursor, cursor + size);
|
|
35
|
+
cursor += size;
|
|
36
|
+
const cookieCount = page.readUInt32LE(4);
|
|
37
|
+
for (let i = 0; i < cookieCount; i++) {
|
|
38
|
+
const offset = page.readUInt32LE(8 + i * 4);
|
|
39
|
+
const record = page.subarray(offset);
|
|
40
|
+
const flags = record.readUInt32LE(8);
|
|
41
|
+
const readString = (at) => {
|
|
42
|
+
const end = record.indexOf(0, at);
|
|
43
|
+
return record.subarray(at, end === -1 ? undefined : end).toString('utf8');
|
|
44
|
+
};
|
|
45
|
+
const domain = readString(record.readUInt32LE(16));
|
|
46
|
+
const name = readString(record.readUInt32LE(20));
|
|
47
|
+
const cookiePath = readString(record.readUInt32LE(24));
|
|
48
|
+
const value = readString(record.readUInt32LE(28));
|
|
49
|
+
const expires = Math.max(0, Math.floor(record.readDoubleLE(40) + MAC_EPOCH_OFFSET));
|
|
50
|
+
cookies.push({
|
|
51
|
+
name,
|
|
52
|
+
value,
|
|
53
|
+
domain,
|
|
54
|
+
path: cookiePath || '/',
|
|
55
|
+
expires,
|
|
56
|
+
secure: (flags & 0x1) !== 0,
|
|
57
|
+
httpOnly: (flags & 0x4) !== 0,
|
|
58
|
+
sameSite: 'Unspecified',
|
|
59
|
+
profileId: profile.id,
|
|
60
|
+
browser: 'safari',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return cookies;
|
|
65
|
+
}
|
|
66
|
+
export function readSafariCookies(profile) {
|
|
67
|
+
let buf;
|
|
68
|
+
try {
|
|
69
|
+
buf = fs.readFileSync(profile.path);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error.code === 'EPERM' || error.code === 'EACCES') {
|
|
73
|
+
throw new FullDiskAccessError(profile.path);
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return parseBinaryCookies(buf, profile);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=safari.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safari.js","sourceRoot":"","sources":["../../../src/core/browsers/safari.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
6
|
+
/**
|
|
7
|
+
* Browsers hold a write lock on their cookie stores while running, so read from
|
|
8
|
+
* a throwaway copy (including the WAL, or recent cookies go missing).
|
|
9
|
+
*/
|
|
10
|
+
export function withCookieDb(dbPath, fn) {
|
|
11
|
+
const scratch = fs.mkdtempSync(path.join(os.tmpdir(), `cookiejar-${crypto.randomBytes(4).toString('hex')}-`));
|
|
12
|
+
const copy = path.join(scratch, path.basename(dbPath));
|
|
13
|
+
try {
|
|
14
|
+
fs.copyFileSync(dbPath, copy);
|
|
15
|
+
for (const suffix of ['-wal', '-shm']) {
|
|
16
|
+
if (fs.existsSync(dbPath + suffix))
|
|
17
|
+
fs.copyFileSync(dbPath + suffix, copy + suffix);
|
|
18
|
+
}
|
|
19
|
+
const db = new DatabaseSync(copy, { readOnly: false });
|
|
20
|
+
try {
|
|
21
|
+
return fn(db);
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
db.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
fs.rmSync(scratch, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Chromium stores timestamps as microseconds since 1601-01-01. */
|
|
32
|
+
export function webkitToUnix(micros) {
|
|
33
|
+
const value = typeof micros === 'bigint' ? Number(micros) : micros;
|
|
34
|
+
if (!value)
|
|
35
|
+
return 0;
|
|
36
|
+
return Math.max(0, Math.floor(value / 1_000_000 - 11_644_473_600));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../../src/core/browsers/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAI,MAAc,EAAE,EAA2B;IACzE,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9G,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC9B,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACtC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;gBAAE,EAAE,CAAC,YAAY,CAAC,MAAM,GAAG,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAAC,MAAuB;IAClD,MAAM,KAAK,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readAllProfiles } from './browsers/index.js';
|
|
2
|
+
export const bareDomain = (domain) => domain.replace(/^\./, '').toLowerCase();
|
|
3
|
+
/** True when `host` is the selector domain or a subdomain of it. */
|
|
4
|
+
export function domainCovers(selectorDomain, cookieDomain) {
|
|
5
|
+
const target = bareDomain(selectorDomain);
|
|
6
|
+
const host = bareDomain(cookieDomain);
|
|
7
|
+
return host === target || host.endsWith(`.${target}`);
|
|
8
|
+
}
|
|
9
|
+
/** Cookie-spec host matching: `.example.com` cookies are sent to subdomains. */
|
|
10
|
+
export function cookieAppliesToHost(cookie, host) {
|
|
11
|
+
const cookieHost = bareDomain(cookie.domain);
|
|
12
|
+
const target = host.toLowerCase();
|
|
13
|
+
if (cookie.domain.startsWith('.'))
|
|
14
|
+
return target === cookieHost || target.endsWith(`.${cookieHost}`);
|
|
15
|
+
return target === cookieHost;
|
|
16
|
+
}
|
|
17
|
+
export function cookieAppliesToPath(cookie, pathname) {
|
|
18
|
+
const cookiePath = cookie.path || '/';
|
|
19
|
+
if (cookiePath === '/')
|
|
20
|
+
return true;
|
|
21
|
+
if (!pathname.startsWith(cookiePath))
|
|
22
|
+
return false;
|
|
23
|
+
return cookiePath.endsWith('/') || pathname.length === cookiePath.length || pathname[cookiePath.length] === '/';
|
|
24
|
+
}
|
|
25
|
+
export const isExpired = (cookie, now = Date.now() / 1000) => cookie.expires !== 0 && cookie.expires < now;
|
|
26
|
+
function matchesSelector(selector, cookie) {
|
|
27
|
+
if (cookie.profileId !== selector.profileId)
|
|
28
|
+
return false;
|
|
29
|
+
if (!domainCovers(selector.domain, cookie.domain))
|
|
30
|
+
return false;
|
|
31
|
+
return selector.names.length === 0 || selector.names.includes(cookie.name);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reads the bundle's cookies live from the browsers. Nothing is cached, so a
|
|
35
|
+
* bundle always reflects the current login state and re-authenticating in the
|
|
36
|
+
* browser is enough to "refresh" an agent's access.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveBundle(bundle, opts = {}) {
|
|
39
|
+
const profileIds = [...new Set(bundle.selectors.map((s) => s.profileId))];
|
|
40
|
+
const reads = readAllProfiles(profileIds);
|
|
41
|
+
const available = reads.flatMap((read) => read.cookies);
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
const cookies = [];
|
|
44
|
+
const emptySelectors = [];
|
|
45
|
+
for (const selector of bundle.selectors) {
|
|
46
|
+
const matched = available.filter((cookie) => matchesSelector(selector, cookie));
|
|
47
|
+
if (matched.length === 0)
|
|
48
|
+
emptySelectors.push(selector);
|
|
49
|
+
for (const cookie of matched) {
|
|
50
|
+
if (!opts.includeExpired && isExpired(cookie))
|
|
51
|
+
continue;
|
|
52
|
+
const key = `${cookie.profileId}|${cookie.domain}|${cookie.path}|${cookie.name}`;
|
|
53
|
+
if (seen.has(key))
|
|
54
|
+
continue;
|
|
55
|
+
seen.add(key);
|
|
56
|
+
cookies.push(cookie);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
cookies,
|
|
61
|
+
emptySelectors,
|
|
62
|
+
errors: reads.filter((r) => r.error).map((r) => ({ profileId: r.profile.id, error: r.error })),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** `curl -b` / yt-dlp / requests compatible cookie jar. */
|
|
66
|
+
export function toNetscape(cookies) {
|
|
67
|
+
const lines = ['# Netscape HTTP Cookie File', '# Generated by cookiejar. Treat this file as a password.', ''];
|
|
68
|
+
for (const cookie of cookies) {
|
|
69
|
+
const includeSubdomains = cookie.domain.startsWith('.') ? 'TRUE' : 'FALSE';
|
|
70
|
+
const prefix = cookie.httpOnly ? '#HttpOnly_' : '';
|
|
71
|
+
lines.push([
|
|
72
|
+
`${prefix}${cookie.domain}`,
|
|
73
|
+
includeSubdomains,
|
|
74
|
+
cookie.path || '/',
|
|
75
|
+
cookie.secure ? 'TRUE' : 'FALSE',
|
|
76
|
+
String(cookie.expires),
|
|
77
|
+
cookie.name,
|
|
78
|
+
cookie.value,
|
|
79
|
+
].join('\t'));
|
|
80
|
+
}
|
|
81
|
+
return `${lines.join('\n')}\n`;
|
|
82
|
+
}
|
|
83
|
+
/** Playwright / Puppeteer `storageState` shape. */
|
|
84
|
+
export function toStorageState(cookies) {
|
|
85
|
+
return JSON.stringify({
|
|
86
|
+
cookies: cookies.map((cookie) => ({
|
|
87
|
+
name: cookie.name,
|
|
88
|
+
value: cookie.value,
|
|
89
|
+
domain: cookie.domain,
|
|
90
|
+
path: cookie.path || '/',
|
|
91
|
+
expires: cookie.expires === 0 ? -1 : cookie.expires,
|
|
92
|
+
httpOnly: cookie.httpOnly,
|
|
93
|
+
secure: cookie.secure,
|
|
94
|
+
sameSite: cookie.sameSite === 'Unspecified' ? 'Lax' : cookie.sameSite,
|
|
95
|
+
})),
|
|
96
|
+
origins: [],
|
|
97
|
+
}, null, 2);
|
|
98
|
+
}
|
|
99
|
+
export function cookieHeaderFor(cookies, url) {
|
|
100
|
+
const target = new URL(url);
|
|
101
|
+
const https = target.protocol === 'https:';
|
|
102
|
+
const applicable = cookies
|
|
103
|
+
.filter((cookie) => cookieAppliesToHost(cookie, target.hostname))
|
|
104
|
+
.filter((cookie) => cookieAppliesToPath(cookie, target.pathname))
|
|
105
|
+
.filter((cookie) => (cookie.secure ? https : true))
|
|
106
|
+
.filter((cookie) => !isExpired(cookie));
|
|
107
|
+
// A bundle can hold the same cookie name from two browsers; a request may only
|
|
108
|
+
// carry one, so the first (longest path wins, as browsers do) is used.
|
|
109
|
+
const byName = new Map();
|
|
110
|
+
for (const cookie of [...applicable].sort((a, b) => (b.path || '/').length - (a.path || '/').length)) {
|
|
111
|
+
if (!byName.has(cookie.name))
|
|
112
|
+
byName.set(cookie.name, cookie);
|
|
113
|
+
}
|
|
114
|
+
return [...byName.values()].map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=bundles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundles.js","sourceRoot":"","sources":["../../src/core/bundles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
const SCRYPT_PARAMS = { N: 2 ** 17, r: 8, p: 1, maxmem: 256 * 1024 * 1024 };
|
|
3
|
+
const KEY_BYTES = 32;
|
|
4
|
+
// Unicode form used before hashing, so the same typed passphrase always derives the same key.
|
|
5
|
+
const NORMALIZATION = 'NFKC';
|
|
6
|
+
export function deriveKey(passphrase, salt) {
|
|
7
|
+
return crypto.scryptSync(passphrase.normalize(NORMALIZATION), salt, KEY_BYTES, SCRYPT_PARAMS);
|
|
8
|
+
}
|
|
9
|
+
export function seal(plaintext, key, salt) {
|
|
10
|
+
const iv = crypto.randomBytes(12);
|
|
11
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
12
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
13
|
+
return {
|
|
14
|
+
kdf: 'scrypt',
|
|
15
|
+
N: SCRYPT_PARAMS.N,
|
|
16
|
+
r: SCRYPT_PARAMS.r,
|
|
17
|
+
p: SCRYPT_PARAMS.p,
|
|
18
|
+
salt: salt.toString('base64'),
|
|
19
|
+
iv: iv.toString('base64'),
|
|
20
|
+
tag: cipher.getAuthTag().toString('base64'),
|
|
21
|
+
ciphertext: ciphertext.toString('base64'),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function open(box, passphrase) {
|
|
25
|
+
const salt = Buffer.from(box.salt, 'base64');
|
|
26
|
+
const key = crypto.scryptSync(passphrase.normalize(NORMALIZATION), salt, KEY_BYTES, {
|
|
27
|
+
N: box.N,
|
|
28
|
+
r: box.r,
|
|
29
|
+
p: box.p,
|
|
30
|
+
maxmem: SCRYPT_PARAMS.maxmem,
|
|
31
|
+
});
|
|
32
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, Buffer.from(box.iv, 'base64'));
|
|
33
|
+
decipher.setAuthTag(Buffer.from(box.tag, 'base64'));
|
|
34
|
+
const plaintext = Buffer.concat([decipher.update(Buffer.from(box.ciphertext, 'base64')), decipher.final()]).toString('utf8');
|
|
35
|
+
return { plaintext, key, salt };
|
|
36
|
+
}
|
|
37
|
+
export function newSalt() {
|
|
38
|
+
return crypto.randomBytes(16);
|
|
39
|
+
}
|
|
40
|
+
export function newToken(prefix = 'cjr') {
|
|
41
|
+
return `${prefix}_${crypto.randomBytes(24).toString('base64url')}`;
|
|
42
|
+
}
|
|
43
|
+
export function hashToken(token) {
|
|
44
|
+
return crypto.createHash('sha256').update(token).digest('hex');
|
|
45
|
+
}
|
|
46
|
+
export function tokenMatches(token, hash) {
|
|
47
|
+
const a = Buffer.from(hashToken(token), 'hex');
|
|
48
|
+
const b = Buffer.from(hash, 'hex');
|
|
49
|
+
return a.length === b.length && crypto.timingSafeEqual(a, b);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/core/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
/** Everything cookiejar persists lives under this directory, and nowhere else. */
|
|
5
|
+
export function configDir() {
|
|
6
|
+
return process.env.COOKIEJAR_HOME ?? path.join(homedir(), '.cookiejar');
|
|
7
|
+
}
|
|
8
|
+
export function vaultPath() {
|
|
9
|
+
return path.join(configDir(), 'vault.json');
|
|
10
|
+
}
|
|
11
|
+
export function auditPath() {
|
|
12
|
+
return path.join(configDir(), 'audit.log');
|
|
13
|
+
}
|
|
14
|
+
export function ensureConfigDir() {
|
|
15
|
+
const dir = configDir();
|
|
16
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
17
|
+
fs.chmodSync(dir, 0o700);
|
|
18
|
+
return dir;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,kFAAkF;AAClF,MAAM,UAAU,SAAS;IACvB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,YAAY,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,WAAW,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACzB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import { ensureConfigDir, vaultPath } from './paths.js';
|
|
5
|
+
import { deriveKey, newSalt, open, seal } from './crypto.js';
|
|
6
|
+
const EMPTY = { version: 1, bundles: [] };
|
|
7
|
+
export class VaultLockedError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('vault is locked');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class BadPasswordError extends Error {
|
|
13
|
+
constructor() {
|
|
14
|
+
super('wrong master password');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The encrypted store of bundle definitions and grants. Cookie *values* are
|
|
19
|
+
* never kept here: they are read live from the browsers on each access, so a
|
|
20
|
+
* stolen vault file leaks nothing but bundle names and domains.
|
|
21
|
+
*/
|
|
22
|
+
export class Vault {
|
|
23
|
+
key = null;
|
|
24
|
+
salt = null;
|
|
25
|
+
data = null;
|
|
26
|
+
get exists() {
|
|
27
|
+
return fs.existsSync(vaultPath());
|
|
28
|
+
}
|
|
29
|
+
get unlocked() {
|
|
30
|
+
return this.key !== null;
|
|
31
|
+
}
|
|
32
|
+
create(password) {
|
|
33
|
+
if (this.exists)
|
|
34
|
+
throw new Error('vault already exists');
|
|
35
|
+
this.salt = newSalt();
|
|
36
|
+
this.key = deriveKey(password, this.salt);
|
|
37
|
+
this.data = structuredClone(EMPTY);
|
|
38
|
+
this.persist();
|
|
39
|
+
}
|
|
40
|
+
unlock(password) {
|
|
41
|
+
const file = JSON.parse(fs.readFileSync(vaultPath(), 'utf8'));
|
|
42
|
+
let opened;
|
|
43
|
+
try {
|
|
44
|
+
opened = open(file.box, password);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new BadPasswordError();
|
|
48
|
+
}
|
|
49
|
+
this.data = JSON.parse(opened.plaintext);
|
|
50
|
+
this.key = opened.key;
|
|
51
|
+
this.salt = opened.salt;
|
|
52
|
+
}
|
|
53
|
+
lock() {
|
|
54
|
+
this.key?.fill(0);
|
|
55
|
+
this.key = null;
|
|
56
|
+
this.salt = null;
|
|
57
|
+
this.data = null;
|
|
58
|
+
}
|
|
59
|
+
read() {
|
|
60
|
+
if (!this.data)
|
|
61
|
+
throw new VaultLockedError();
|
|
62
|
+
return this.data;
|
|
63
|
+
}
|
|
64
|
+
/** Mutates the decrypted data and re-seals the file atomically. */
|
|
65
|
+
write(mutate) {
|
|
66
|
+
const data = this.read();
|
|
67
|
+
mutate(data);
|
|
68
|
+
this.persist();
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
bundle(id) {
|
|
72
|
+
const found = this.read().bundles.find((b) => b.id === id);
|
|
73
|
+
if (!found)
|
|
74
|
+
throw new Error(`no such bundle: ${id}`);
|
|
75
|
+
return found;
|
|
76
|
+
}
|
|
77
|
+
changePassword(current, next) {
|
|
78
|
+
this.unlock(current);
|
|
79
|
+
const data = this.read();
|
|
80
|
+
this.salt = newSalt();
|
|
81
|
+
this.key = deriveKey(next, this.salt);
|
|
82
|
+
this.data = data;
|
|
83
|
+
this.persist();
|
|
84
|
+
}
|
|
85
|
+
persist() {
|
|
86
|
+
if (!this.key || !this.salt || !this.data)
|
|
87
|
+
throw new VaultLockedError();
|
|
88
|
+
ensureConfigDir();
|
|
89
|
+
const file = {
|
|
90
|
+
format: 'cookiejar-vault',
|
|
91
|
+
createdAt: new Date().toISOString(),
|
|
92
|
+
box: seal(JSON.stringify(this.data), this.key, this.salt),
|
|
93
|
+
};
|
|
94
|
+
const target = vaultPath();
|
|
95
|
+
const tmp = path.join(path.dirname(target), `.vault.${crypto.randomUUID()}.tmp`);
|
|
96
|
+
fs.writeFileSync(tmp, JSON.stringify(file, null, 2), { mode: 0o600 });
|
|
97
|
+
fs.renameSync(tmp, target);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function newBundleId(name) {
|
|
101
|
+
const slug = name
|
|
102
|
+
.toLowerCase()
|
|
103
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
104
|
+
.replace(/^-|-$/g, '')
|
|
105
|
+
.slice(0, 32);
|
|
106
|
+
return `${slug || 'bundle'}-${crypto.randomBytes(3).toString('hex')}`;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=vault.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.js","sourceRoot":"","sources":["../../src/core/vault.ts"],"names":[],"mappings":"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"}
|