@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DevelopIQ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# cookiejar
|
|
2
|
+
|
|
3
|
+
**Local-only cookie bundles for coding agents.**
|
|
4
|
+
|
|
5
|
+
You are already logged into GitHub, Linear, Notion, your admin panels. Your agent is not, and half of
|
|
6
|
+
those things have no MCP server. cookiejar lets you pick the exact cookies you are willing to share,
|
|
7
|
+
group them into a **bundle**, and hand that bundle to an agent with a revocable token — without the
|
|
8
|
+
agent (or anyone else) getting a copy of your whole browser.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
browsers cookiejar agents
|
|
12
|
+
┌──────────────┐ ┌──────────────────┐ ┌────────────────────┐
|
|
13
|
+
│ Chrome │ │ ~/.cookiejar │ │ Devin / Claude │
|
|
14
|
+
│ Safari │─────▶│ bundles, tokens │◀──MCP──│ Codex / curl │
|
|
15
|
+
│ Firefox … │ live │ (encrypted) │ HTTP │ │
|
|
16
|
+
└──────────────┘ read └──────────────────┘ └────────────────────┘
|
|
17
|
+
everything stays on 127.0.0.1
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Local only.** No account, no cloud, no telemetry. The server binds to `127.0.0.1` and the only
|
|
21
|
+
outbound requests it ever makes are the ones an agent explicitly proxies.
|
|
22
|
+
- **Password protected.** Bundle definitions live in `~/.cookiejar/vault.json`, encrypted with
|
|
23
|
+
scrypt + AES-256-GCM. Agent tokens only work while the jar is unlocked.
|
|
24
|
+
- **Values are never stored.** A bundle records *which* cookies to use. The values are read live from
|
|
25
|
+
the browser on every access, so re-logging in the browser is all it takes to refresh an agent, and a
|
|
26
|
+
stolen vault file leaks nothing but bundle names.
|
|
27
|
+
- **Per-bundle tokens.** Expiry, revocation, an access counter, and an append-only audit log. A token
|
|
28
|
+
can be marked *proxy only*, so the agent can make authenticated requests but never sees a cookie.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
Requires Node 22.5+ (for the built-in SQLite reader).
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g @puffle/cookiejar
|
|
36
|
+
cookiejar ui --open
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or from source:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/DevelopIQ-ai/cookiejar
|
|
43
|
+
cd cookiejar
|
|
44
|
+
npm install && npm run build
|
|
45
|
+
node dist/cli.js ui --open
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Open http://127.0.0.1:4088, choose a master password, and pick your cookies.
|
|
49
|
+
|
|
50
|
+
## Using a bundle from an agent
|
|
51
|
+
|
|
52
|
+
Issue a token in **Bundles → Agent access**, then give the agent one of these.
|
|
53
|
+
|
|
54
|
+
**MCP (Devin, Claude Code, Cursor, Codex …)**
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"cookiejar-ticket-triage": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["-y", "@puffle/cookiejar", "mcp"],
|
|
62
|
+
"env": { "COOKIEJAR_TOKEN": "cjr_…" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Tools: `describe_bundle`, `get_cookie_header`, `export_cookies`, `http_request`.
|
|
69
|
+
|
|
70
|
+
**CLI**
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export COOKIEJAR_TOKEN=cjr_…
|
|
74
|
+
cookiejar export --format netscape --out cookies.txt # curl -b cookies.txt …
|
|
75
|
+
cookiejar export --format storage-state # Playwright / Puppeteer
|
|
76
|
+
cookiejar header --url-target https://linear.app/team # a single Cookie header
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**HTTP** (the daemon, `Authorization: Bearer cjr_…`)
|
|
80
|
+
|
|
81
|
+
| Endpoint | Purpose |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `GET /agent/bundle` | Name, hosts, permissions — no secrets |
|
|
84
|
+
| `GET /agent/cookies?format=netscape\|storage-state\|json` | The jar |
|
|
85
|
+
| `GET /agent/cookies?format=header&url=…` | One `Cookie` header |
|
|
86
|
+
| `POST /agent/fetch` | Proxy a request with the bundle's cookies attached |
|
|
87
|
+
|
|
88
|
+
`POST /agent/fetch` is the safest option: cookies never leave the machine, and the request is refused
|
|
89
|
+
unless the target host is one the bundle actually holds cookies for.
|
|
90
|
+
|
|
91
|
+
## Browser support
|
|
92
|
+
|
|
93
|
+
| Browser | macOS | Linux | Notes |
|
|
94
|
+
| --- | --- | --- | --- |
|
|
95
|
+
| Chrome, Chromium, Brave, Edge, Arc | ✅ | ✅ | Key from the login Keychain / keyring |
|
|
96
|
+
| Firefox | ✅ | ✅ | Plaintext store |
|
|
97
|
+
| Safari | ✅ | — | Needs **Full Disk Access** for your terminal |
|
|
98
|
+
| Windows Chromium | — | — | App-bound (`v20`) cookies are not supported yet |
|
|
99
|
+
|
|
100
|
+
`cookiejar doctor` prints what can and cannot be read on this machine, and why.
|
|
101
|
+
|
|
102
|
+
Browsers hold a lock on their cookie stores, so cookiejar reads from a temporary copy (including the
|
|
103
|
+
WAL) and never writes to them.
|
|
104
|
+
|
|
105
|
+
## Security model
|
|
106
|
+
|
|
107
|
+
- The vault protects bundle *definitions*, not cookie values — values live in your browser only.
|
|
108
|
+
- An agent token is worth exactly one bundle, only while the app is unlocked, and only until you hit
|
|
109
|
+
**Revoke** (or the jar auto-locks after 30 idle minutes).
|
|
110
|
+
- Every access is appended to `~/.cookiejar/audit.log` with the bundle, token label, and what was
|
|
111
|
+
asked for. Values are never logged.
|
|
112
|
+
- Prefer *proxy only* tokens and per-site bundles. Cookies are bearer credentials: anything you put in
|
|
113
|
+
a bundle, the agent can act as you with.
|
|
114
|
+
- `Set-Cookie` responses from proxied requests are dropped, so an agent cannot rewrite your session.
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm run dev # daemon with tsx
|
|
120
|
+
npm --prefix ui run dev # Vite UI on :4089, proxying the daemon
|
|
121
|
+
npm test # node:test, includes an end-to-end daemon test
|
|
122
|
+
npm run lint && npm run typecheck
|
|
123
|
+
node scripts/seed-demo-profile.mjs /tmp/cookiejar-demo # fake browser data for UI work
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Layout: `src/core` (crypto, vault, browser readers, bundle resolution), `src/server` (local HTTP API),
|
|
127
|
+
`src/mcp` (stdio MCP server), `ui` (React app), `test`.
|
|
128
|
+
|
|
129
|
+
MIT licensed. Issues and PRs welcome.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { startServer } from './server/index.js';
|
|
7
|
+
import { runMcpServer } from './mcp/server.js';
|
|
8
|
+
import { readAllProfiles } from './core/browsers/index.js';
|
|
9
|
+
import { bareDomain } from './core/bundles.js';
|
|
10
|
+
// node:sqlite is how we read cookie stores; its experimental banner is noise here.
|
|
11
|
+
process.removeAllListeners('warning');
|
|
12
|
+
process.on('warning', (warning) => {
|
|
13
|
+
if (warning.name !== 'ExperimentalWarning')
|
|
14
|
+
console.warn(`${warning.name}: ${warning.message}`);
|
|
15
|
+
});
|
|
16
|
+
const DEFAULT_PORT = Number(process.env.COOKIEJAR_PORT ?? 4088);
|
|
17
|
+
const DEFAULT_URL = process.env.COOKIEJAR_URL ?? `http://127.0.0.1:${DEFAULT_PORT}`;
|
|
18
|
+
function parseArgs(argv) {
|
|
19
|
+
const flags = new Map();
|
|
20
|
+
const [command = 'ui', ...rest] = argv;
|
|
21
|
+
for (let i = 0; i < rest.length; i++) {
|
|
22
|
+
const arg = rest[i];
|
|
23
|
+
if (!arg.startsWith('--'))
|
|
24
|
+
continue;
|
|
25
|
+
const [name, inline] = arg.slice(2).split('=', 2);
|
|
26
|
+
if (inline !== undefined)
|
|
27
|
+
flags.set(name, inline);
|
|
28
|
+
else if (rest[i + 1] && !rest[i + 1].startsWith('--'))
|
|
29
|
+
flags.set(name, rest[++i]);
|
|
30
|
+
else
|
|
31
|
+
flags.set(name, true);
|
|
32
|
+
}
|
|
33
|
+
return { command, flags };
|
|
34
|
+
}
|
|
35
|
+
const flagString = (args, name, fallback) => {
|
|
36
|
+
const value = args.flags.get(name);
|
|
37
|
+
return typeof value === 'string' ? value : fallback;
|
|
38
|
+
};
|
|
39
|
+
function openBrowser(url) {
|
|
40
|
+
const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'explorer' : 'xdg-open';
|
|
41
|
+
execFile(opener, [url], () => {
|
|
42
|
+
// Opening a browser is a convenience; the URL is printed regardless.
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function uiDir() {
|
|
46
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
47
|
+
for (const candidate of [path.join(here, 'ui'), path.join(here, '..', 'ui', 'dist'), path.join(here, '..', 'dist', 'ui')]) {
|
|
48
|
+
if (fs.existsSync(path.join(candidate, 'index.html')))
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
return path.join(here, 'ui');
|
|
52
|
+
}
|
|
53
|
+
function requireToken(args) {
|
|
54
|
+
const token = flagString(args, 'token') ?? process.env.COOKIEJAR_TOKEN;
|
|
55
|
+
if (!token) {
|
|
56
|
+
console.error('A bundle token is required. Pass --token or set COOKIEJAR_TOKEN.');
|
|
57
|
+
process.exit(2);
|
|
58
|
+
}
|
|
59
|
+
return token;
|
|
60
|
+
}
|
|
61
|
+
async function agentGet(args, path) {
|
|
62
|
+
const base = flagString(args, 'url', DEFAULT_URL);
|
|
63
|
+
const response = await fetch(new URL(path, base), { headers: { authorization: `Bearer ${requireToken(args)}` } });
|
|
64
|
+
const text = await response.text();
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
console.error(text);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
const HELP = `cookiejar — local-only cookie bundles for coding agents
|
|
72
|
+
|
|
73
|
+
Usage:
|
|
74
|
+
cookiejar ui [--port ${DEFAULT_PORT}] [--open] [--auto-lock <minutes>]
|
|
75
|
+
Start the local app (127.0.0.1 only) to manage cookies and bundles.
|
|
76
|
+
|
|
77
|
+
cookiejar mcp [--token <token>] [--url ${DEFAULT_URL}]
|
|
78
|
+
Speak MCP over stdio so an agent can use one bundle. Token via
|
|
79
|
+
--token or COOKIEJAR_TOKEN.
|
|
80
|
+
|
|
81
|
+
cookiejar export [--format netscape|storage-state|json] [--out <file>]
|
|
82
|
+
Write a bundle's cookies to a jar file.
|
|
83
|
+
|
|
84
|
+
cookiejar header --url-target <url>
|
|
85
|
+
Print the Cookie header for a URL covered by the bundle.
|
|
86
|
+
|
|
87
|
+
cookiejar doctor
|
|
88
|
+
Show which browser profiles cookiejar can read on this machine.
|
|
89
|
+
`;
|
|
90
|
+
async function main() {
|
|
91
|
+
const args = parseArgs(process.argv.slice(2));
|
|
92
|
+
switch (args.command) {
|
|
93
|
+
case 'ui': {
|
|
94
|
+
const port = Number(flagString(args, 'port', String(DEFAULT_PORT)));
|
|
95
|
+
const autoLock = Number(flagString(args, 'auto-lock', '30'));
|
|
96
|
+
const { url } = await startServer({ port, uiDir: uiDir(), autoLockMinutes: autoLock });
|
|
97
|
+
console.log(`cookiejar is running at ${url}`);
|
|
98
|
+
console.log(`vault: ${process.env.COOKIEJAR_HOME ?? '~/.cookiejar'} · auto-lock: ${autoLock || 'off'}`);
|
|
99
|
+
if (args.flags.get('open'))
|
|
100
|
+
openBrowser(url);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
case 'mcp': {
|
|
104
|
+
runMcpServer({ daemonUrl: flagString(args, 'url', DEFAULT_URL), token: requireToken(args) });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
case 'export': {
|
|
108
|
+
const format = flagString(args, 'format', 'netscape');
|
|
109
|
+
const body = await agentGet(args, `/agent/cookies?format=${encodeURIComponent(format)}`);
|
|
110
|
+
const out = flagString(args, 'out');
|
|
111
|
+
if (out) {
|
|
112
|
+
fs.writeFileSync(out, body, { mode: 0o600 });
|
|
113
|
+
console.log(`wrote ${out}`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
process.stdout.write(body);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
case 'header': {
|
|
121
|
+
const target = flagString(args, 'url-target');
|
|
122
|
+
if (!target) {
|
|
123
|
+
console.error('--url-target <url> is required');
|
|
124
|
+
process.exit(2);
|
|
125
|
+
}
|
|
126
|
+
const body = await agentGet(args, `/agent/cookies?format=header&url=${encodeURIComponent(target)}`);
|
|
127
|
+
console.log(JSON.parse(body).cookie);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
case 'doctor': {
|
|
131
|
+
const reads = readAllProfiles();
|
|
132
|
+
if (reads.length === 0)
|
|
133
|
+
console.log('No browser profiles found.');
|
|
134
|
+
for (const read of reads) {
|
|
135
|
+
const sites = new Set(read.cookies.map((c) => bareDomain(c.domain))).size;
|
|
136
|
+
console.log(read.error
|
|
137
|
+
? `✗ ${read.profile.label} (${read.profile.id})\n ${read.error}`
|
|
138
|
+
: `✓ ${read.profile.label} (${read.profile.id}) — ${read.cookies.length} cookies across ${sites} sites`);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
console.log(HELP);
|
|
144
|
+
process.exit(args.command === 'help' || args.command === '--help' ? 0 : 2);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
void main().catch((error) => {
|
|
148
|
+
console.error(error instanceof Error ? error.message : error);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { audit } from './audit.js';
|
|
2
|
+
import { tokenMatches } from './crypto.js';
|
|
3
|
+
export class AccessDeniedError extends Error {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a bearer token to the bundle it unlocks. Requires the vault to be
|
|
7
|
+
* unlocked: agent tokens are useless while the jar is closed.
|
|
8
|
+
*/
|
|
9
|
+
export function authorize(vault, token) {
|
|
10
|
+
if (!token)
|
|
11
|
+
throw new AccessDeniedError('missing bundle token');
|
|
12
|
+
if (!vault.unlocked)
|
|
13
|
+
throw new AccessDeniedError('cookiejar is locked; unlock it in the app first');
|
|
14
|
+
const now = Date.now() / 1000;
|
|
15
|
+
for (const bundle of vault.read().bundles) {
|
|
16
|
+
for (const grant of bundle.grants) {
|
|
17
|
+
if (!tokenMatches(token, grant.tokenHash))
|
|
18
|
+
continue;
|
|
19
|
+
if (grant.revokedAt)
|
|
20
|
+
throw new AccessDeniedError('this token was revoked');
|
|
21
|
+
if (grant.expiresAt && grant.expiresAt < now)
|
|
22
|
+
throw new AccessDeniedError('this token has expired');
|
|
23
|
+
return { bundle, grant };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
throw new AccessDeniedError('unknown bundle token');
|
|
27
|
+
}
|
|
28
|
+
export function noteUse(vault, grantee, event, detail) {
|
|
29
|
+
vault.write((data) => {
|
|
30
|
+
const bundle = data.bundles.find((b) => b.id === grantee.bundle.id);
|
|
31
|
+
const grant = bundle?.grants.find((g) => g.tokenHash === grantee.grant.tokenHash);
|
|
32
|
+
if (!grant)
|
|
33
|
+
return;
|
|
34
|
+
grant.lastUsedAt = new Date().toISOString();
|
|
35
|
+
grant.useCount += 1;
|
|
36
|
+
});
|
|
37
|
+
audit({ event, bundleId: grantee.bundle.id, grantLabel: grantee.grant.label, detail });
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/core/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAO/C;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAY,EAAE,KAAyB;IAC/D,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,iBAAiB,CAAC,sBAAsB,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK,CAAC,QAAQ;QAAE,MAAM,IAAI,iBAAiB,CAAC,iDAAiD,CAAC,CAAC;IACpG,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAC9B,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;gBAAE,SAAS;YACpD,IAAI,KAAK,CAAC,SAAS;gBAAE,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;YAC3E,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG;gBAAE,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;YACpG,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,MAAM,IAAI,iBAAiB,CAAC,sBAAsB,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAY,EAAE,OAAgB,EAAE,KAAqC,EAAE,MAAe;IAC5G,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAClF,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,KAAK,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC5C,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { auditPath, ensureConfigDir } from './paths.js';
|
|
3
|
+
/** Append-only local log of every access. Cookie values are never recorded. */
|
|
4
|
+
export function audit(entry) {
|
|
5
|
+
ensureConfigDir();
|
|
6
|
+
fs.appendFileSync(auditPath(), `${JSON.stringify({ at: new Date().toISOString(), ...entry })}\n`, { mode: 0o600 });
|
|
7
|
+
}
|
|
8
|
+
export function readAudit(limit = 200) {
|
|
9
|
+
if (!fs.existsSync(auditPath()))
|
|
10
|
+
return [];
|
|
11
|
+
return fs
|
|
12
|
+
.readFileSync(auditPath(), 'utf8')
|
|
13
|
+
.split('\n')
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.slice(-limit)
|
|
16
|
+
.reverse()
|
|
17
|
+
.flatMap((line) => {
|
|
18
|
+
try {
|
|
19
|
+
return [JSON.parse(line)];
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGxD,+EAA+E;AAC/E,MAAM,UAAU,KAAK,CAAC,KAA6B;IACjD,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,cAAc,CAAC,SAAS,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACrH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAK,GAAG,GAAG;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3C,OAAO,EAAE;SACN,YAAY,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC;SACjC,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,CAAC,KAAK,CAAC;SACb,OAAO,EAAE;SACT,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChB,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import { webkitToUnix, withCookieDb } from './sqlite.js';
|
|
7
|
+
const FLAVOURS = [
|
|
8
|
+
{
|
|
9
|
+
browser: 'chrome',
|
|
10
|
+
label: 'Chrome',
|
|
11
|
+
dirs: { darwin: 'Library/Application Support/Google/Chrome', linux: '.config/google-chrome' },
|
|
12
|
+
keychainService: 'Chrome Safe Storage',
|
|
13
|
+
linuxKeyringApp: 'chrome',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
browser: 'chrome-beta',
|
|
17
|
+
label: 'Chrome Beta',
|
|
18
|
+
dirs: { darwin: 'Library/Application Support/Google/Chrome Beta', linux: '.config/google-chrome-beta' },
|
|
19
|
+
keychainService: 'Chrome Safe Storage',
|
|
20
|
+
linuxKeyringApp: 'chrome',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
browser: 'chromium',
|
|
24
|
+
label: 'Chromium',
|
|
25
|
+
dirs: { darwin: 'Library/Application Support/Chromium', linux: '.config/chromium' },
|
|
26
|
+
keychainService: 'Chromium Safe Storage',
|
|
27
|
+
linuxKeyringApp: 'chromium',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
browser: 'brave',
|
|
31
|
+
label: 'Brave',
|
|
32
|
+
dirs: {
|
|
33
|
+
darwin: 'Library/Application Support/BraveSoftware/Brave-Browser',
|
|
34
|
+
linux: '.config/BraveSoftware/Brave-Browser',
|
|
35
|
+
},
|
|
36
|
+
keychainService: 'Brave Safe Storage',
|
|
37
|
+
linuxKeyringApp: 'brave',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
browser: 'edge',
|
|
41
|
+
label: 'Edge',
|
|
42
|
+
dirs: { darwin: 'Library/Application Support/Microsoft Edge', linux: '.config/microsoft-edge' },
|
|
43
|
+
keychainService: 'Microsoft Edge Safe Storage',
|
|
44
|
+
linuxKeyringApp: 'chromium',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
browser: 'arc',
|
|
48
|
+
label: 'Arc',
|
|
49
|
+
dirs: { darwin: 'Library/Application Support/Arc/User Data' },
|
|
50
|
+
keychainService: 'Arc Safe Storage',
|
|
51
|
+
linuxKeyringApp: 'chromium',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
function userDataDir(flavour) {
|
|
55
|
+
const rel = process.platform === 'darwin' ? flavour.dirs.darwin : flavour.dirs.linux;
|
|
56
|
+
if (!rel)
|
|
57
|
+
return null;
|
|
58
|
+
const dir = path.join(os.homedir(), rel);
|
|
59
|
+
return fs.existsSync(dir) ? dir : null;
|
|
60
|
+
}
|
|
61
|
+
function profileNames(userDir) {
|
|
62
|
+
const names = new Map();
|
|
63
|
+
try {
|
|
64
|
+
const state = JSON.parse(fs.readFileSync(path.join(userDir, 'Local State'), 'utf8'));
|
|
65
|
+
for (const [dir, info] of Object.entries(state.profile?.info_cache ?? {})) {
|
|
66
|
+
if (info.name)
|
|
67
|
+
names.set(dir, info.name);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// A missing or half-written Local State just costs us pretty profile names.
|
|
72
|
+
}
|
|
73
|
+
return names;
|
|
74
|
+
}
|
|
75
|
+
export function discoverChromiumProfiles() {
|
|
76
|
+
const profiles = [];
|
|
77
|
+
for (const flavour of FLAVOURS) {
|
|
78
|
+
const userDir = userDataDir(flavour);
|
|
79
|
+
if (!userDir)
|
|
80
|
+
continue;
|
|
81
|
+
const pretty = profileNames(userDir);
|
|
82
|
+
for (const entry of fs.readdirSync(userDir, { withFileTypes: true })) {
|
|
83
|
+
if (!entry.isDirectory())
|
|
84
|
+
continue;
|
|
85
|
+
const cookies = [
|
|
86
|
+
path.join(userDir, entry.name, 'Cookies'),
|
|
87
|
+
path.join(userDir, entry.name, 'Network', 'Cookies'),
|
|
88
|
+
].find((p) => fs.existsSync(p));
|
|
89
|
+
if (!cookies)
|
|
90
|
+
continue;
|
|
91
|
+
profiles.push({
|
|
92
|
+
browser: flavour.browser,
|
|
93
|
+
id: `${flavour.browser}:${entry.name}`,
|
|
94
|
+
label: `${flavour.label} — ${pretty.get(entry.name) ?? entry.name}`,
|
|
95
|
+
path: cookies,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return profiles;
|
|
100
|
+
}
|
|
101
|
+
function keychainPassword(service) {
|
|
102
|
+
try {
|
|
103
|
+
return execFileSync('security', ['find-generic-password', '-w', '-s', service], {
|
|
104
|
+
encoding: 'utf8',
|
|
105
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
106
|
+
}).trim();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function linuxKeyringPassword(app) {
|
|
113
|
+
for (const args of [
|
|
114
|
+
['lookup', 'application', app],
|
|
115
|
+
['lookup', 'application', 'chrome'],
|
|
116
|
+
]) {
|
|
117
|
+
try {
|
|
118
|
+
const out = execFileSync('secret-tool', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
119
|
+
if (out)
|
|
120
|
+
return out.trim();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// secret-tool missing or the entry is absent; fall through to "peanuts".
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/** Chromium derives its AES key from a password with fixed, published parameters. */
|
|
129
|
+
function legacyKey(password, iterations) {
|
|
130
|
+
return crypto.pbkdf2Sync(password, 'saltysalt', iterations, 16, 'sha1');
|
|
131
|
+
}
|
|
132
|
+
function keyringFor(flavour) {
|
|
133
|
+
if (process.platform === 'darwin') {
|
|
134
|
+
const password = keychainPassword(flavour.keychainService);
|
|
135
|
+
return { cbcKeys: password ? [legacyKey(password, 1003)] : [], gcmKey: null };
|
|
136
|
+
}
|
|
137
|
+
const keys = [legacyKey('peanuts', 1)];
|
|
138
|
+
const fromKeyring = linuxKeyringPassword(flavour.linuxKeyringApp);
|
|
139
|
+
if (fromKeyring)
|
|
140
|
+
keys.unshift(legacyKey(fromKeyring, 1));
|
|
141
|
+
return { cbcKeys: keys, gcmKey: null };
|
|
142
|
+
}
|
|
143
|
+
export class UnavailableKeyError extends Error {
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Recent Chrome versions bind a cookie to its host by prefixing the plaintext
|
|
147
|
+
* with sha256(host_key), so strip that prefix when it is really there.
|
|
148
|
+
*/
|
|
149
|
+
function stripDomainPrefix(plaintext, host) {
|
|
150
|
+
if (plaintext.length <= 32)
|
|
151
|
+
return plaintext;
|
|
152
|
+
const prefix = plaintext.subarray(0, 32);
|
|
153
|
+
for (const candidate of host ? [host, host.replace(/^\./, '')] : []) {
|
|
154
|
+
if (crypto.createHash('sha256').update(candidate).digest().equals(prefix))
|
|
155
|
+
return plaintext.subarray(32);
|
|
156
|
+
}
|
|
157
|
+
// Unknown hash: fall back to "a value would not contain control bytes".
|
|
158
|
+
return prefix.some((byte) => byte < 0x20) ? plaintext.subarray(32) : plaintext;
|
|
159
|
+
}
|
|
160
|
+
function decryptCbc(blob, key, host) {
|
|
161
|
+
const iv = Buffer.alloc(16, 0x20);
|
|
162
|
+
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
|
|
163
|
+
const out = Buffer.concat([decipher.update(blob), decipher.final()]);
|
|
164
|
+
return stripDomainPrefix(out, host).toString('utf8');
|
|
165
|
+
}
|
|
166
|
+
function decryptGcm(blob, key, host) {
|
|
167
|
+
const nonce = blob.subarray(0, 12);
|
|
168
|
+
const tag = blob.subarray(blob.length - 16);
|
|
169
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
|
|
170
|
+
decipher.setAuthTag(tag);
|
|
171
|
+
const out = Buffer.concat([decipher.update(blob.subarray(12, blob.length - 16)), decipher.final()]);
|
|
172
|
+
return stripDomainPrefix(out, host).toString('utf8');
|
|
173
|
+
}
|
|
174
|
+
export function decryptChromiumValue(encrypted, plain, keyring, host) {
|
|
175
|
+
if (plain)
|
|
176
|
+
return plain;
|
|
177
|
+
if (encrypted.length === 0)
|
|
178
|
+
return '';
|
|
179
|
+
const version = encrypted.subarray(0, 3).toString('latin1');
|
|
180
|
+
const body = encrypted.subarray(3);
|
|
181
|
+
if (version === 'v20') {
|
|
182
|
+
// App-bound encryption (Windows only); the key lives behind an elevation service.
|
|
183
|
+
throw new UnavailableKeyError('app-bound encrypted cookie (v20) is not supported');
|
|
184
|
+
}
|
|
185
|
+
if (version === 'v10' || version === 'v11') {
|
|
186
|
+
if (keyring.gcmKey)
|
|
187
|
+
return decryptGcm(body, keyring.gcmKey, host);
|
|
188
|
+
let lastError;
|
|
189
|
+
for (const key of keyring.cbcKeys) {
|
|
190
|
+
try {
|
|
191
|
+
return decryptCbc(body, key, host);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
lastError = error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (keyring.cbcKeys.length === 0)
|
|
198
|
+
throw new UnavailableKeyError('no decryption key available for this browser');
|
|
199
|
+
throw lastError instanceof Error ? lastError : new Error('failed to decrypt cookie');
|
|
200
|
+
}
|
|
201
|
+
return encrypted.toString('utf8');
|
|
202
|
+
}
|
|
203
|
+
const sameSiteFromChromium = (value) => value === 0 ? 'None' : value === 1 ? 'Lax' : value === 2 ? 'Strict' : 'Unspecified';
|
|
204
|
+
export function readChromiumCookies(profile) {
|
|
205
|
+
const flavour = FLAVOURS.find((f) => f.browser === profile.browser);
|
|
206
|
+
if (!flavour)
|
|
207
|
+
throw new Error(`unknown chromium flavour: ${profile.browser}`);
|
|
208
|
+
const keyring = keyringFor(flavour);
|
|
209
|
+
return withCookieDb(profile.path, (db) => {
|
|
210
|
+
const statement = db.prepare(`SELECT host_key, name, value, encrypted_value, path, expires_utc, is_secure, is_httponly, samesite
|
|
211
|
+
FROM cookies ORDER BY host_key, name`);
|
|
212
|
+
// Chromium expiry timestamps overflow a JS number, so read integers as BigInt.
|
|
213
|
+
statement.setReadBigInts(true);
|
|
214
|
+
const rows = statement.all();
|
|
215
|
+
const cookies = [];
|
|
216
|
+
for (const row of rows) {
|
|
217
|
+
const encrypted = row.encrypted_value instanceof Uint8Array ? Buffer.from(row.encrypted_value) : Buffer.alloc(0);
|
|
218
|
+
let value;
|
|
219
|
+
try {
|
|
220
|
+
value = decryptChromiumValue(encrypted, String(row.value ?? ''), keyring, String(row.host_key ?? ''));
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
continue; // Skip cookies we cannot read rather than failing the whole profile.
|
|
224
|
+
}
|
|
225
|
+
cookies.push({
|
|
226
|
+
name: String(row.name),
|
|
227
|
+
value,
|
|
228
|
+
domain: String(row.host_key),
|
|
229
|
+
path: String(row.path ?? '/'),
|
|
230
|
+
expires: webkitToUnix(row.expires_utc),
|
|
231
|
+
secure: Boolean(row.is_secure),
|
|
232
|
+
httpOnly: Boolean(row.is_httponly),
|
|
233
|
+
sameSite: sameSiteFromChromium(Number(row.samesite ?? -1)),
|
|
234
|
+
profileId: profile.id,
|
|
235
|
+
browser: profile.browser,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return cookies;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=chromium.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chromium.js","sourceRoot":"","sources":["../../../src/core/browsers/chromium.ts"],"names":[],"mappings":"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"}
|