sigild 0.0.2 → 0.0.3
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/README.md +40 -4
- package/THREAT_MODEL.md +18 -7
- package/dist/src/bin/sigil-mcp.js +4 -1
- package/dist/src/bin/sigil-mcp.js.map +1 -1
- package/dist/src/cli/main.d.ts.map +1 -1
- package/dist/src/cli/main.js +47 -2
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/paths.d.ts +1 -0
- package/dist/src/cli/paths.d.ts.map +1 -1
- package/dist/src/cli/paths.js +1 -0
- package/dist/src/cli/paths.js.map +1 -1
- package/dist/src/cli/portal.d.ts +9 -0
- package/dist/src/cli/portal.d.ts.map +1 -1
- package/dist/src/cli/portal.js +24 -5
- package/dist/src/cli/portal.js.map +1 -1
- package/dist/src/daemon/methods.d.ts +7 -0
- package/dist/src/daemon/methods.d.ts.map +1 -1
- package/dist/src/daemon/methods.js +37 -0
- package/dist/src/daemon/methods.js.map +1 -1
- package/dist/src/policy/evaluate.d.ts +13 -0
- package/dist/src/policy/evaluate.d.ts.map +1 -0
- package/dist/src/policy/evaluate.js +73 -0
- package/dist/src/policy/evaluate.js.map +1 -0
- package/dist/src/policy/index.d.ts +5 -0
- package/dist/src/policy/index.d.ts.map +1 -0
- package/dist/src/policy/index.js +5 -0
- package/dist/src/policy/index.js.map +1 -0
- package/dist/src/policy/loader.d.ts +33 -0
- package/dist/src/policy/loader.d.ts.map +1 -0
- package/dist/src/policy/loader.js +170 -0
- package/dist/src/policy/loader.js.map +1 -0
- package/dist/src/policy/template.d.ts +10 -0
- package/dist/src/policy/template.d.ts.map +1 -0
- package/dist/src/policy/template.js +69 -0
- package/dist/src/policy/template.js.map +1 -0
- package/dist/src/policy/types.d.ts +62 -0
- package/dist/src/policy/types.d.ts.map +1 -0
- package/dist/src/policy/types.js +10 -0
- package/dist/src/policy/types.js.map +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`sigil` is a local signing tool and Claude Code integration that lets agentic coding tools use private keys without ever putting key material in the model's context window.
|
|
6
6
|
|
|
7
|
-
**Status:** pre-alpha
|
|
7
|
+
**Status:** pre-alpha. The MCP server, CLI, unlock flow, ward hooks, and policy engine (static checks) all work end-to-end. Out-of-band confirmation, rolling-window value caps, and EIP-712 domain allowlists are not yet implemented. Until they land — and until the supply-chain attestations promised for v0.1.0 ship — **do not use this with real funds yet.** Build plan lives in the [tracking issue](https://github.com/cdrn/sigil/issues/9).
|
|
8
8
|
|
|
9
9
|
## What it is
|
|
10
10
|
|
|
@@ -22,7 +22,7 @@ Sign methods exposed today: EIP-191 personal_sign, EIP-1559 + legacy transaction
|
|
|
22
22
|
|
|
23
23
|
- Not a hardware wallet replacement. If you can use a Ledger or YubiKey, do that.
|
|
24
24
|
- Not a custody solution. It runs on your laptop or VPS and protects you from one specific class of failure: leaking key material through an LLM agent.
|
|
25
|
-
-
|
|
25
|
+
- A first cut of *bounding signing authority* via the policy engine — but not the full thing. v1 covers static checks (chain ID, destination allowlist, per-tx value cap, function-selector allowlist, on/off toggles for personal_sign and EIP-712). Rolling-window caps, EIP-712 domain allowlists, and out-of-band human confirmation are tracked in [#3](https://github.com/cdrn/sigil/issues/3) + [#4](https://github.com/cdrn/sigil/issues/4) and will land incrementally.
|
|
26
26
|
|
|
27
27
|
## Install
|
|
28
28
|
|
|
@@ -43,7 +43,12 @@ sigil init
|
|
|
43
43
|
# 2. Encrypt a private key into sigil's keystore. Source key is deleted by default.
|
|
44
44
|
# Accepts either 32 raw bytes or 64 hex chars (with optional 0x prefix).
|
|
45
45
|
sigil portal add eth:bot --key-file ./bot.key
|
|
46
|
-
# → prompts for a passphrase, derives the address, writes
|
|
46
|
+
# → prompts for a passphrase, derives the address, writes
|
|
47
|
+
# ~/.sigil/keys/eth:bot.sigil AND ~/.sigil/policy/eth:bot.toml (permissive)
|
|
48
|
+
#
|
|
49
|
+
# Pass --strict to start with a locked-down policy template you fill in
|
|
50
|
+
# before any sign succeeds:
|
|
51
|
+
# sigil portal add eth:bot --key-file ./bot.key --strict
|
|
47
52
|
|
|
48
53
|
# 3. Open Claude Code. It spawns sigil-mcp automatically via your MCP config.
|
|
49
54
|
# sigil-mcp boots locked — the first sign attempt will return DAEMON_LOCKED.
|
|
@@ -68,11 +73,18 @@ sigil init [--user]
|
|
|
68
73
|
.claude/settings.json. With --user, writes ~/.claude/settings.json
|
|
69
74
|
instead. Idempotent — preserves your unrelated settings.
|
|
70
75
|
|
|
71
|
-
sigil portal add <handle> --key-file <path> [--no-remove-source]
|
|
76
|
+
sigil portal add <handle> --key-file <path> [--no-remove-source] [--strict]
|
|
72
77
|
Encrypt the key with your passphrase and store it at
|
|
73
78
|
~/.sigil/keys/<handle>.sigil (mode 0600). Handle format is
|
|
74
79
|
<kind>:<name> where kind is "eth". The source key file is deleted
|
|
75
80
|
by default — pass --no-remove-source to keep it.
|
|
81
|
+
Also writes ~/.sigil/policy/<handle>.toml — permissive by default
|
|
82
|
+
(signs anything), or use --strict for a locked-down template you
|
|
83
|
+
fill in before signs succeed.
|
|
84
|
+
|
|
85
|
+
sigil policy show <handle>
|
|
86
|
+
Print the current policy file for a portal. Validates schema; exits
|
|
87
|
+
1 if the file is missing or malformed.
|
|
76
88
|
|
|
77
89
|
sigil portal list
|
|
78
90
|
List the encrypted keyfiles on disk with their derived addresses.
|
|
@@ -105,6 +117,30 @@ Each Claude Code window spawns its own `sigil-mcp`. They share the on-disk keyfi
|
|
|
105
117
|
|
|
106
118
|
OS-keychain integration (planned, v0.3) will make unlock zero-touch for users who set it up.
|
|
107
119
|
|
|
120
|
+
## Policy engine
|
|
121
|
+
|
|
122
|
+
Once a portal is unlocked, signing authority over its key is real. To bound the blast radius of a successful prompt injection, every portal has a policy file at `~/.sigil/policy/<handle>.toml`. Two modes:
|
|
123
|
+
|
|
124
|
+
**Permissive** (default for `sigil portal add`): no rules. Sign anything the agent asks. The key isolation guarantees still hold — your key never enters the agent's context — but the unlocked portal can be made to sign whatever an attacker can get the agent to ask for. Useful for: testnet bots, demo flows, anyone who only cares about the context-window protection.
|
|
125
|
+
|
|
126
|
+
**Strict** (opt in with `--strict`): every sign request is checked. Generated template:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
mode = "strict"
|
|
130
|
+
|
|
131
|
+
chain_ids = [1] # allowed chain IDs
|
|
132
|
+
allow_to = [] # allowed destination addresses (lowercase 0x)
|
|
133
|
+
max_value_wei = "0" # per-tx cap, in wei, as decimal string
|
|
134
|
+
allowed_selectors = [] # 4-byte function selectors, e.g. "0xa9059cbb"
|
|
135
|
+
|
|
136
|
+
allow_message_signing = false # EIP-191 personal_sign (e.g. SIWE)
|
|
137
|
+
allow_typed_data = false # EIP-712 (Permit, OpenSea — can be financial)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
A failed rule throws `POLICY_DENIED` (-32001) back to the agent with the human-readable reason ("tx denied — value X exceeds max_value_wei Y"), and the deny is appended to the hash-chained audit log alongside allows. Denies are forensically the more interesting half — they're the prompt-injection canary.
|
|
141
|
+
|
|
142
|
+
What's deferred to follow-up PRs (still in [#3](https://github.com/cdrn/sigil/issues/3)): rolling-window value caps (e.g. 1 ETH/day per portal), EIP-712 domain + primary-type allowlists, decoded-calldata arg checks, and the `require_confirm_above_wei` outcome that hooks into the OOB push gate ([#4](https://github.com/cdrn/sigil/issues/4)).
|
|
143
|
+
|
|
108
144
|
## Supply chain posture
|
|
109
145
|
|
|
110
146
|
Key-management libraries die from supply chain compromise, not from clever attacks on the code. Given the npm ecosystem in 2026 (Mini Shai-Hulud, Axios, pgserve, TanStack), `sigil` commits to:
|
package/THREAT_MODEL.md
CHANGED
|
@@ -48,13 +48,24 @@ Defenses:
|
|
|
48
48
|
|
|
49
49
|
A defended key still loses you money if the agent can be tricked into signing the wrong payload. Prompt injection through transaction calldata, web content, or repo files can redirect signing intent.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
51
|
+
The policy engine (per-portal `~/.sigil/policy/<handle>.toml`) is the layer that bounds this. v1 ships static checks; further work is staged in [#3](https://github.com/cdrn/sigil/issues/3) and [#4](https://github.com/cdrn/sigil/issues/4).
|
|
52
|
+
|
|
53
|
+
**Shipped (v1):**
|
|
54
|
+
- Two-mode policy. `permissive` mode (default for `sigil portal add`) does no checks — useful for users who only want the key-isolation guarantee. `strict` mode enforces every rule below.
|
|
55
|
+
- Destination allowlist (`allow_to`).
|
|
56
|
+
- Chain ID allowlist (`chain_ids`).
|
|
57
|
+
- Per-tx value cap (`max_value_wei`).
|
|
58
|
+
- Function selector allowlist (`allowed_selectors`) — 4-byte selector match, no calldata decoding.
|
|
59
|
+
- On/off toggles for personal_sign (`allow_message_signing`) and EIP-712 typed data (`allow_typed_data`).
|
|
60
|
+
- Append-only hash-chained audit log records both allow AND deny decisions. Denies are the prompt-injection canary.
|
|
61
|
+
- Runtime fail-closed when the policy file is missing for a portal — keys can't be used without an explicit decision.
|
|
62
|
+
|
|
63
|
+
**Planned:**
|
|
64
|
+
- Rolling-window value caps (`max_value_per_hour_wei`, etc.) backed by a per-portal ledger with flock for multi-instance safety.
|
|
65
|
+
- EIP-712 domain + primary-type allowlists (today `allow_typed_data` is binary; OpenSea-style approvals deserve finer control).
|
|
66
|
+
- Decoded-calldata arg checks ("allow `transfer(addr, amt)` only when `amt <= 100e18` and `addr in list`"). Needs a BYO-ABI registry per portal.
|
|
67
|
+
- Out-of-band human confirmation above a configurable value threshold (push to ntfy / Pushover / Telegram / Apple Push), [#4](https://github.com/cdrn/sigil/issues/4).
|
|
68
|
+
- `allow_contract_creation` toggle — currently strict mode always denies tx with `to: null`.
|
|
58
69
|
|
|
59
70
|
### 3. Supply chain compromise of `sigil` itself
|
|
60
71
|
|
|
@@ -5,6 +5,7 @@ import { resolvePaths } from '../cli/paths.js';
|
|
|
5
5
|
import { startControlServer } from '../control/index.js';
|
|
6
6
|
import { HandleTable } from '../daemon/handles.js';
|
|
7
7
|
import { runMcpStdio } from '../mcp/server.js';
|
|
8
|
+
import { FileSystemPolicyResolver } from '../policy/index.js';
|
|
8
9
|
/**
|
|
9
10
|
* sigil-mcp: single-process MCP server for sigil. Spawned by Claude Code per
|
|
10
11
|
* session via the .claude/settings.json mcpServers entry.
|
|
@@ -27,8 +28,10 @@ async function main() {
|
|
|
27
28
|
const paths = resolvePaths(process.env);
|
|
28
29
|
mkdirSync(paths.home, { recursive: true, mode: 0o700 });
|
|
29
30
|
mkdirSync(paths.keysDir, { recursive: true, mode: 0o700 });
|
|
31
|
+
mkdirSync(paths.policyDir, { recursive: true, mode: 0o700 });
|
|
30
32
|
const handles = new HandleTable();
|
|
31
33
|
const audit = new AuditWriter(paths.auditLog);
|
|
34
|
+
const policy = new FileSystemPolicyResolver(paths.policyDir);
|
|
32
35
|
let controlClosed = false;
|
|
33
36
|
let control = null;
|
|
34
37
|
try {
|
|
@@ -67,7 +70,7 @@ async function main() {
|
|
|
67
70
|
process.on('SIGINT', () => process.exit(0));
|
|
68
71
|
process.on('SIGTERM', () => process.exit(0));
|
|
69
72
|
await runMcpStdio({
|
|
70
|
-
context: { handles, audit },
|
|
73
|
+
context: { handles, audit, policy },
|
|
71
74
|
stdin: process.stdin,
|
|
72
75
|
stdout: process.stdout,
|
|
73
76
|
onLog: (e) => process.stderr.write(JSON.stringify(e) + '\n'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sigil-mcp.js","sourceRoot":"","sources":["../../../src/bin/sigil-mcp.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sigil-mcp.js","sourceRoot":"","sources":["../../../src/bin/sigil-mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AA8BpD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5C,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAkJ/D"}
|
package/dist/src/cli/main.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { readPassphrase } from '../daemon/passphrase.js';
|
|
2
4
|
import { installInto } from '../hooks/install.js';
|
|
5
|
+
import { parsePolicy } from '../policy/index.js';
|
|
3
6
|
import { ArgsError, parseSubcommand } from './args.js';
|
|
4
7
|
import { resolvePaths } from './paths.js';
|
|
5
8
|
import { portalAdd, portalListFromDisk, portalRemove } from './portal.js';
|
|
@@ -10,14 +13,17 @@ const USAGE = `sigil — local signing control for Claude Code
|
|
|
10
13
|
Usage:
|
|
11
14
|
sigil init [--user]
|
|
12
15
|
sigil status
|
|
13
|
-
sigil portal add <handle> --key-file <path> [--no-remove-source]
|
|
16
|
+
sigil portal add <handle> --key-file <path> [--no-remove-source] [--strict]
|
|
14
17
|
sigil portal list
|
|
15
18
|
sigil portal remove <handle>
|
|
19
|
+
sigil policy show <handle>
|
|
16
20
|
sigil unlock
|
|
17
21
|
sigil lock
|
|
18
22
|
|
|
19
23
|
"sigil init" writes the MCP server registration + tool hooks into
|
|
20
24
|
.claude/settings.json (or ~/.claude/settings.json with --user).
|
|
25
|
+
"sigil portal add" writes a permissive policy by default; pass --strict
|
|
26
|
+
to get a locked-down template you fill in before any sign succeeds.
|
|
21
27
|
"sigil unlock" prompts for the passphrase and pushes it to the running
|
|
22
28
|
sigil-mcp process (spawned by Claude Code) over the control socket.
|
|
23
29
|
Set SIGIL_HOME to override ~/.sigil.
|
|
@@ -79,6 +85,7 @@ export async function runCli(opts) {
|
|
|
79
85
|
options: {
|
|
80
86
|
'key-file': { type: 'string' },
|
|
81
87
|
'no-remove-source': { type: 'boolean' },
|
|
88
|
+
'strict': { type: 'boolean' },
|
|
82
89
|
},
|
|
83
90
|
},
|
|
84
91
|
list: { options: {} },
|
|
@@ -92,16 +99,22 @@ export async function runCli(opts) {
|
|
|
92
99
|
if (typeof keyFile !== 'string' || keyFile.length === 0) {
|
|
93
100
|
throw new ArgsError('portal add: --key-file is required');
|
|
94
101
|
}
|
|
102
|
+
const policyMode = sub.options['strict'] === true ? 'strict' : 'permissive';
|
|
95
103
|
const passphrase = await askPassphrase();
|
|
96
104
|
try {
|
|
97
|
-
const { address, keyfilePath } = portalAdd(paths, {
|
|
105
|
+
const { address, keyfilePath, policyPath } = portalAdd(paths, {
|
|
98
106
|
handle,
|
|
99
107
|
keyFile,
|
|
100
108
|
passphrase,
|
|
109
|
+
policyMode,
|
|
101
110
|
...(sub.options['no-remove-source'] === true ? { removeSource: false } : {}),
|
|
102
111
|
...(opts.kdfParams ? { kdfParams: opts.kdfParams } : {}),
|
|
103
112
|
});
|
|
104
113
|
out.write(`added ${handle} (${address}) → ${keyfilePath}\n`);
|
|
114
|
+
out.write(`policy: ${policyMode} → ${policyPath}\n`);
|
|
115
|
+
if (policyMode === 'strict') {
|
|
116
|
+
out.write(`note: strict policy denies everything until you edit ${policyPath}\n`);
|
|
117
|
+
}
|
|
105
118
|
}
|
|
106
119
|
finally {
|
|
107
120
|
passphrase.fill(0);
|
|
@@ -137,6 +150,38 @@ export async function runCli(opts) {
|
|
|
137
150
|
return { code: result.removed ? 0 : 1 };
|
|
138
151
|
}
|
|
139
152
|
}
|
|
153
|
+
if (head === 'policy') {
|
|
154
|
+
const sub = parseSubcommand(rest, { show: { options: {} } });
|
|
155
|
+
if (sub.command === 'show') {
|
|
156
|
+
const handle = sub.positionals[0];
|
|
157
|
+
if (!handle)
|
|
158
|
+
throw new ArgsError('policy show: missing handle');
|
|
159
|
+
const policyPath = join(paths.policyDir, `${handle}.toml`);
|
|
160
|
+
let source;
|
|
161
|
+
try {
|
|
162
|
+
source = readFileSync(policyPath, 'utf8');
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
if (e.code === 'ENOENT') {
|
|
166
|
+
err.write(`policy: no file at ${policyPath}\n`);
|
|
167
|
+
return { code: 1 };
|
|
168
|
+
}
|
|
169
|
+
throw e;
|
|
170
|
+
}
|
|
171
|
+
// Validate by parsing — surface schema errors as exit 1.
|
|
172
|
+
try {
|
|
173
|
+
parsePolicy(source);
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
err.write(`policy: ${e.message}\n`);
|
|
177
|
+
err.write(`(file at ${policyPath} is on disk but doesn't parse — fix it before signing)\n`);
|
|
178
|
+
return { code: 1 };
|
|
179
|
+
}
|
|
180
|
+
out.write(source);
|
|
181
|
+
out.write(`# ${policyPath}\n`);
|
|
182
|
+
return { code: 0 };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
140
185
|
throw new ArgsError(`unknown subcommand "${head}"`);
|
|
141
186
|
}
|
|
142
187
|
catch (e) {
|
package/dist/src/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAkB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,KAAK,GAAG
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
package/dist/src/cli/paths.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/cli/paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/cli/paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAS7E"}
|
package/dist/src/cli/paths.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/cli/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/cli/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAmBjC,MAAM,UAAU,YAAY,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC/D,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC5D,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QAC3B,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;QAC/B,aAAa,EAAE,GAAG,CAAC,oBAAoB,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;QACtE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;KAClC,CAAC;AACJ,CAAC"}
|
package/dist/src/cli/portal.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type KdfParams, SecretBuffer } from '../crypto/index.js';
|
|
2
|
+
import { type PolicyMode } from '../policy/index.js';
|
|
2
3
|
import type { SigilPaths } from './paths.js';
|
|
3
4
|
export interface PortalAddOpts {
|
|
4
5
|
handle: string;
|
|
@@ -16,6 +17,13 @@ export interface PortalAddOpts {
|
|
|
16
17
|
* the suite fast. The CLI binary never sets this.
|
|
17
18
|
*/
|
|
18
19
|
kdfParams?: KdfParams;
|
|
20
|
+
/**
|
|
21
|
+
* Policy template to write at provisioning time. Defaults to "permissive"
|
|
22
|
+
* (signs anything the agent asks — same UX as today, key still protected
|
|
23
|
+
* from context). Pass "strict" to write a locked-down template the user
|
|
24
|
+
* must edit before signing succeeds.
|
|
25
|
+
*/
|
|
26
|
+
policyMode?: PolicyMode;
|
|
19
27
|
}
|
|
20
28
|
/**
|
|
21
29
|
* Reads a private key from disk, encrypts it with the passphrase, writes it
|
|
@@ -29,6 +37,7 @@ export interface PortalAddOpts {
|
|
|
29
37
|
export declare function portalAdd(paths: SigilPaths, opts: PortalAddOpts): {
|
|
30
38
|
address: string;
|
|
31
39
|
keyfilePath: string;
|
|
40
|
+
policyPath: string;
|
|
32
41
|
};
|
|
33
42
|
export interface PortalInfo {
|
|
34
43
|
handle: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../src/cli/portal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAW,YAAY,EAAa,MAAM,oBAAoB,CAAC;AAGtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../src/cli/portal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAW,YAAY,EAAa,MAAM,oBAAoB,CAAC;AAGtF,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,aAAa,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAqC9D;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,EAAE,CAgBtF;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAalF;AAqBD,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/src/cli/portal.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { sealKey, SecretBuffer, unsealKey } from '../crypto/index.js';
|
|
4
4
|
import { addressFromPrivateKey } from '../eth/index.js';
|
|
5
5
|
import { HandleTable } from '../daemon/handles.js';
|
|
6
|
+
import { policyTemplate } from '../policy/index.js';
|
|
6
7
|
/**
|
|
7
8
|
* Reads a private key from disk, encrypts it with the passphrase, writes it
|
|
8
9
|
* to the keys directory under <handle>.sigil, and (by default) deletes the
|
|
@@ -15,10 +16,15 @@ import { HandleTable } from '../daemon/handles.js';
|
|
|
15
16
|
export function portalAdd(paths, opts) {
|
|
16
17
|
HandleTable.parseHandle(opts.handle); // validates format
|
|
17
18
|
mkdirSync(paths.keysDir, { recursive: true, mode: 0o700 });
|
|
19
|
+
mkdirSync(paths.policyDir, { recursive: true, mode: 0o700 });
|
|
18
20
|
const destPath = join(paths.keysDir, `${opts.handle}.sigil`);
|
|
19
21
|
if (existsSync(destPath)) {
|
|
20
22
|
throw new Error(`portal "${opts.handle}" already exists at ${destPath}; remove it first`);
|
|
21
23
|
}
|
|
24
|
+
const policyPath = join(paths.policyDir, `${opts.handle}.toml`);
|
|
25
|
+
if (existsSync(policyPath)) {
|
|
26
|
+
throw new Error(`policy file for "${opts.handle}" already exists at ${policyPath}; remove it first`);
|
|
27
|
+
}
|
|
22
28
|
const raw = readFileSync(opts.keyFile);
|
|
23
29
|
const priv = normalizePrivateKey(raw);
|
|
24
30
|
let address;
|
|
@@ -32,13 +38,16 @@ export function portalAdd(paths, opts) {
|
|
|
32
38
|
finally {
|
|
33
39
|
priv.fill(0);
|
|
34
40
|
}
|
|
41
|
+
// Write the policy file alongside the keyfile. Mode 0o600 — it's not secret
|
|
42
|
+
// per se, but it does describe what this key can sign, which is sensitive.
|
|
43
|
+
writeFileSync(policyPath, policyTemplate(opts.policyMode ?? 'permissive'), { mode: 0o600 });
|
|
35
44
|
if (opts.removeSource !== false) {
|
|
36
45
|
try {
|
|
37
46
|
unlinkSync(opts.keyFile);
|
|
38
47
|
}
|
|
39
48
|
catch { /* best-effort cleanup; file may already be gone */ }
|
|
40
49
|
}
|
|
41
|
-
return { address, keyfilePath: destPath };
|
|
50
|
+
return { address, keyfilePath: destPath, policyPath };
|
|
42
51
|
}
|
|
43
52
|
/**
|
|
44
53
|
* Lists portals by reading the keys directory and decrypting each keyfile
|
|
@@ -68,10 +77,20 @@ export function portalListFromDisk(paths, passphrase) {
|
|
|
68
77
|
export function portalRemove(paths, handle) {
|
|
69
78
|
HandleTable.parseHandle(handle); // validates format
|
|
70
79
|
const destPath = join(paths.keysDir, `${handle}.sigil`);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
const policyPath = join(paths.policyDir, `${handle}.toml`);
|
|
81
|
+
const keyfileExisted = existsSync(destPath);
|
|
82
|
+
if (keyfileExisted)
|
|
83
|
+
unlinkSync(destPath);
|
|
84
|
+
// Best-effort policy cleanup. We don't fail the remove if the keyfile is
|
|
85
|
+
// missing but the policy file isn't, or vice versa — better to err on the
|
|
86
|
+
// side of cleaning up orphans.
|
|
87
|
+
if (existsSync(policyPath)) {
|
|
88
|
+
try {
|
|
89
|
+
unlinkSync(policyPath);
|
|
90
|
+
}
|
|
91
|
+
catch { /* ignore */ }
|
|
92
|
+
}
|
|
93
|
+
return { removed: keyfileExisted, path: destPath };
|
|
75
94
|
}
|
|
76
95
|
// ---------------------------------------------------------------------------
|
|
77
96
|
// Helpers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.js","sourceRoot":"","sources":["../../../src/cli/portal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAY,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAkB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"portal.js","sourceRoot":"","sources":["../../../src/cli/portal.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AuditWriter } from '../audit/index.js';
|
|
2
|
+
import { type PolicyResolver } from '../policy/index.js';
|
|
2
3
|
import { type HandleTable } from './handles.js';
|
|
3
4
|
export declare const RPC_INVALID_PARAMS = -32602;
|
|
4
5
|
export declare const RPC_METHOD_NOT_FOUND = -32601;
|
|
@@ -14,6 +15,12 @@ export declare class RpcMethodError extends Error {
|
|
|
14
15
|
export interface MethodContext {
|
|
15
16
|
handles: HandleTable;
|
|
16
17
|
audit: AuditWriter;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the per-portal policy at evaluation time. Wrapped in an
|
|
20
|
+
* interface so tests can inject a constant policy without touching the
|
|
21
|
+
* filesystem.
|
|
22
|
+
*/
|
|
23
|
+
policy: PolicyResolver;
|
|
17
24
|
}
|
|
18
25
|
export type MethodHandler = (params: unknown, ctx: MethodContext) => unknown;
|
|
19
26
|
export declare const METHODS: Readonly<Record<string, MethodHandler>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../../../src/daemon/methods.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAE3C,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAC1C,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAExC,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBACX,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;CAM1D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../../../src/daemon/methods.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAE3C,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAC1C,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAExC,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBACX,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;CAM1D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC;IACnB;;;;OAIG;IACH,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;AA4P7E,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAK1D,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAMrF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { personalSign, signTransaction, signTypedData, } from '../eth/index.js';
|
|
2
|
+
import { evaluate, PolicyLoadError, } from '../policy/index.js';
|
|
2
3
|
// Error codes — these match JSON-RPC 2.0 standard error codes plus a
|
|
3
4
|
// sigil-specific range (-32000..-32099). They flow through the MCP wire
|
|
4
5
|
// unchanged so the client sees the exact diagnosis.
|
|
@@ -51,6 +52,35 @@ function requirePortal(handles, handle) {
|
|
|
51
52
|
}
|
|
52
53
|
return sb.bytes();
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the portal's policy and evaluate the request. On Deny — or on any
|
|
57
|
+
* PolicyLoadError (missing file, malformed TOML) — appends a deny entry to
|
|
58
|
+
* the audit log AND throws RPC_POLICY_DENIED. Returns silently on Allow.
|
|
59
|
+
*
|
|
60
|
+
* The order is: require unlocked + portal first, then policy. That way the
|
|
61
|
+
* caller's mistakes (wrong handle, daemon locked) error before we touch the
|
|
62
|
+
* policy file at all.
|
|
63
|
+
*/
|
|
64
|
+
function gatePolicy(ctx, handle, kind, payload, request) {
|
|
65
|
+
let reason;
|
|
66
|
+
try {
|
|
67
|
+
const policy = ctx.policy.resolve(handle);
|
|
68
|
+
const decision = evaluate(request, policy);
|
|
69
|
+
if (decision.allow)
|
|
70
|
+
return;
|
|
71
|
+
reason = decision.reason;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err instanceof PolicyLoadError) {
|
|
75
|
+
reason = err.message;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
ctx.audit.append({ kind, portal: handle, payload, decision: 'deny', reason });
|
|
82
|
+
throw new RpcMethodError(RPC_POLICY_DENIED, reason);
|
|
83
|
+
}
|
|
54
84
|
// ---------------------------------------------------------------------------
|
|
55
85
|
// Methods
|
|
56
86
|
// ---------------------------------------------------------------------------
|
|
@@ -63,6 +93,9 @@ const sigil_eth_sign_message = (params, ctx) => {
|
|
|
63
93
|
const messageHex = asString(obj, 'message', 'eth_sign_message');
|
|
64
94
|
const message = hexToBuf(messageHex, 'eth_sign_message', 'message');
|
|
65
95
|
const priv = requirePortal(ctx.handles, portal);
|
|
96
|
+
gatePolicy(ctx, portal, 'eth_sign_message', { message: messageHex }, {
|
|
97
|
+
kind: 'message', messageBytes: message,
|
|
98
|
+
});
|
|
66
99
|
const sig = personalSign(message, priv);
|
|
67
100
|
const sigHex = ('0x' + sig.toString('hex'));
|
|
68
101
|
ctx.audit.append({
|
|
@@ -175,6 +208,7 @@ const sigil_eth_sign_transaction = (params, ctx) => {
|
|
|
175
208
|
}
|
|
176
209
|
const tx = asTx(txObj);
|
|
177
210
|
const priv = requirePortal(ctx.handles, portal);
|
|
211
|
+
gatePolicy(ctx, portal, 'eth_sign_transaction', { tx: txObj }, { kind: 'transaction', tx });
|
|
178
212
|
const signed = signTransaction(tx, priv);
|
|
179
213
|
ctx.audit.append({
|
|
180
214
|
kind: 'eth_sign_transaction',
|
|
@@ -195,6 +229,9 @@ const sigil_eth_sign_typed_data = (params, ctx) => {
|
|
|
195
229
|
// We trust the typed-data shape minimally — sign-typed.ts will throw
|
|
196
230
|
// on missing fields; we wrap that as INVALID_PARAMS for the caller.
|
|
197
231
|
const priv = requirePortal(ctx.handles, portal);
|
|
232
|
+
gatePolicy(ctx, portal, 'eth_sign_typed_data', { typedData: td }, {
|
|
233
|
+
kind: 'typed_data', typedData: td,
|
|
234
|
+
});
|
|
198
235
|
let sig;
|
|
199
236
|
try {
|
|
200
237
|
sig = signTypedData(td, priv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../../../src/daemon/methods.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,YAAY,EAEZ,eAAe,EACf,aAAa,GAEd,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../../../src/daemon/methods.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,YAAY,EAEZ,eAAe,EACf,aAAa,GAEd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,QAAQ,EACR,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAG5B,qEAAqE;AACrE,wEAAwE;AACxE,oDAAoD;AACpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAK,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAK,CAAC;AAC3C,kBAAkB;AAClB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAK,CAAC;AAC3C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC;AACxC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAK,CAAC;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC;AAExC,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,IAAI,CAAS;IACb,IAAI,CAAU;IACvB,YAAY,IAAY,EAAE,OAAe,EAAE,IAAc;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAeD,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,QAAQ,CAAC,MAAe,EAAE,UAAkB;IACnD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,GAAG,UAAU,4BAA4B,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED,SAAS,QAAQ,CAAC,GAA4B,EAAE,GAAW,EAAE,UAAkB;IAC7E,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,GAAG,UAAU,KAAK,GAAG,mBAAmB,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,UAAkB,EAAE,GAAW;IAC1D,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,GAAG,UAAU,KAAK,GAAG,0BAA0B,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,aAAa,CAAC,OAAoB,EAAE,MAAc;IACzD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QAC1B,MAAM,IAAI,cAAc,CACtB,iBAAiB,EACjB,iEAAiE,CAClE,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,cAAc,CAAC,oBAAoB,EAAE,WAAW,MAAM,aAAa,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;AACpB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,UAAU,CACjB,GAAkB,EAClB,MAAc,EACd,IAAY,EACZ,OAAgB,EAChB,OAAsB;IAEtB,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3C,IAAI,QAAQ,CAAC,KAAK;YAAE,OAAO;QAC3B,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9E,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,kBAAkB,GAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;IACzD,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;AACzC,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAC5D,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,kBAAkB,EAAE,SAAS,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE;QACnE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO;KACvC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAQ,CAAC;IACnD,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,kBAAkB;QACxB,MAAM;QACN,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE;QAChC,QAAQ,EAAE,OAAO;QACjB,GAAG,EAAE,MAAM;KACZ,CAAC,CAAC;IACH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,SAAS,IAAI,CAAC,GAA4B;IACxC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5C,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,wCAAwC,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,GAAG,GAAG,CAAC,GAAW,EAAU,EAAE;QAClC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,OAAO,GAAG,4BAA4B,CAAC,CAAC;YACvF,CAAC;QACH,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,OAAO,GAAG,iCAAiC,CAAC,CAAC;YAC5F,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;QACD,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,OAAO,GAAG,mCAAmC,CAAC,CAAC;IAC9F,CAAC,CAAC;IAEF,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,IAAI,EAAE,KAAK,IAAI,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,mCAAmC,CAAC,CAAC;IACpF,CAAC;IACD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,4CAA4C,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,kCAAkC,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,EAAE,GAAa;YACnB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC;YACvB,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;YACnB,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC;YACzB,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC;YACzB,EAAE,EAAE,EAA0B;YAC9B,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;YACnB,IAAI,EAAE,IAAqB;SAC5B,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,UAAU;IACV,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;IACxC,IAAI,UAAmC,CAAC;IACxC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,UAAU,GAAG,EAAE,CAAC;IAClB,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACxC,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrE,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;YACtF,CAAC;YACD,MAAM,EAAE,GAAG,IAA+B,CAAC;YAC3C,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC;YAC/B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClE,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;YAC/E,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,6BAA6B,CAAC,CAAC;YACjG,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC5D,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;gBACtF,CAAC;YACH,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAqB,EAAE,WAAW,EAAE,IAAuB,EAAE,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,yCAAyC,CAAC,CAAC;IAC1F,CAAC;IACD,MAAM,EAAE,GAAc;QACpB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC;QACvB,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;QACnB,oBAAoB,EAAE,GAAG,CAAC,sBAAsB,CAAC;QACjD,YAAY,EAAE,GAAG,CAAC,cAAc,CAAC;QACjC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC;QACzB,EAAE,EAAE,EAA0B;QAC9B,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;QACnB,IAAI,EAAE,IAAqB;QAC3B,UAAU;KACX,CAAC;IACF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,0BAA0B,GAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAChE,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,sBAAsB,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,4CAA4C,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAgC,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,sBAAsB,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,sBAAsB;QAC5B,MAAM;QACN,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE;QACtB,QAAQ,EAAE,OAAO;QACjB,GAAG,EAAE,MAAM;KACZ,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAC/D,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IAC9D,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;IAC5B,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,cAAc,CAAC,kBAAkB,EAAE,+CAA+C,CAAC,CAAC;IAChG,CAAC;IACD,qEAAqE;IACrE,oEAAoE;IACpE,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,qBAAqB,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE;QAChE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAe;KAC/C,CAAC,CAAC;IACH,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,EAAe,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,cAAc,CACtB,kBAAkB,EAClB,wBAAyB,GAAa,CAAC,OAAO,EAAE,CACjD,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAQ,CAAC;IACnD,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,qBAAqB;QAC3B,MAAM;QACN,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QAC1B,QAAQ,EAAE,OAAO;QACjB,GAAG,EAAE,MAAM;KACZ,CAAC,CAAC;IACH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAA4C,MAAM,CAAC,MAAM,CAAC;IAC5E,kBAAkB;IAClB,sBAAsB;IACtB,0BAA0B;IAC1B,yBAAyB;CAC1B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,MAAe,EAAE,GAAkB;IAC1E,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,cAAc,CAAC,oBAAoB,EAAE,qBAAqB,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Policy, PolicyDecision, PolicyRequest } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pure evaluator. Takes a (request, policy), returns Allow or Deny(reason).
|
|
4
|
+
*
|
|
5
|
+
* The reason string is what the caller writes into the audit log and surfaces
|
|
6
|
+
* as the RPC_POLICY_DENIED error message — write it for a human.
|
|
7
|
+
*
|
|
8
|
+
* Permissive mode short-circuits to Allow for everything. Strict mode walks
|
|
9
|
+
* the rules in a fixed order and returns the first failing one — the order
|
|
10
|
+
* doesn't matter for correctness, only for which reason the user sees first.
|
|
11
|
+
*/
|
|
12
|
+
export declare function evaluate(request: PolicyRequest, policy: Policy): PolicyDecision;
|
|
13
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../../src/policy/evaluate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAoB/E"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure evaluator. Takes a (request, policy), returns Allow or Deny(reason).
|
|
3
|
+
*
|
|
4
|
+
* The reason string is what the caller writes into the audit log and surfaces
|
|
5
|
+
* as the RPC_POLICY_DENIED error message — write it for a human.
|
|
6
|
+
*
|
|
7
|
+
* Permissive mode short-circuits to Allow for everything. Strict mode walks
|
|
8
|
+
* the rules in a fixed order and returns the first failing one — the order
|
|
9
|
+
* doesn't matter for correctness, only for which reason the user sees first.
|
|
10
|
+
*/
|
|
11
|
+
export function evaluate(request, policy) {
|
|
12
|
+
if (policy.mode === 'permissive')
|
|
13
|
+
return { allow: true };
|
|
14
|
+
switch (request.kind) {
|
|
15
|
+
case 'transaction':
|
|
16
|
+
return evaluateTransaction(request.tx, policy);
|
|
17
|
+
case 'message':
|
|
18
|
+
return policy.allowMessageSigning
|
|
19
|
+
? { allow: true }
|
|
20
|
+
: {
|
|
21
|
+
allow: false,
|
|
22
|
+
reason: 'personal_sign denied — strict mode + allow_message_signing=false',
|
|
23
|
+
};
|
|
24
|
+
case 'typed_data':
|
|
25
|
+
return policy.allowTypedData
|
|
26
|
+
? { allow: true }
|
|
27
|
+
: {
|
|
28
|
+
allow: false,
|
|
29
|
+
reason: 'EIP-712 typed-data denied — strict mode + allow_typed_data=false',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function evaluateTransaction(tx, policy) {
|
|
34
|
+
// 1. chain ID
|
|
35
|
+
if (!policy.chainIds.includes(Number(tx.chainId))) {
|
|
36
|
+
return {
|
|
37
|
+
allow: false,
|
|
38
|
+
reason: `tx denied — chain ${tx.chainId} not in chain_ids ${JSON.stringify(policy.chainIds)}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// 2. contract creation (to: null) — denied by default; future "allow_contract_creation" toggle
|
|
42
|
+
if (tx.to === null) {
|
|
43
|
+
return { allow: false, reason: 'tx denied — contract creation not allowed' };
|
|
44
|
+
}
|
|
45
|
+
// 3. destination allowlist (case-insensitive; allow_to is pre-lowercased)
|
|
46
|
+
const to = tx.to.toLowerCase();
|
|
47
|
+
if (!policy.allowTo.includes(to)) {
|
|
48
|
+
return { allow: false, reason: `tx denied — destination ${to} not in allow_to` };
|
|
49
|
+
}
|
|
50
|
+
// 4. per-tx value cap
|
|
51
|
+
if (tx.value > policy.maxValueWei) {
|
|
52
|
+
return {
|
|
53
|
+
allow: false,
|
|
54
|
+
reason: `tx denied — value ${tx.value} wei exceeds max_value_wei ${policy.maxValueWei}`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// 5. function selector allowlist (only for txs with calldata)
|
|
58
|
+
const dataHex = typeof tx.data === 'string' ? tx.data : ('0x' + tx.data.toString('hex'));
|
|
59
|
+
if (dataHex.length > 2) {
|
|
60
|
+
if (dataHex.length < 10) {
|
|
61
|
+
return { allow: false, reason: `tx denied — calldata too short to extract selector` };
|
|
62
|
+
}
|
|
63
|
+
const selector = dataHex.slice(0, 10).toLowerCase();
|
|
64
|
+
if (!policy.allowedSelectors.includes(selector)) {
|
|
65
|
+
return {
|
|
66
|
+
allow: false,
|
|
67
|
+
reason: `tx denied — selector ${selector} not in allowed_selectors`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { allow: true };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../../src/policy/evaluate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { type Policy, type PolicyRequest, type PolicyDecision, type PolicyResolver, PolicyLoadError, } from './types.js';
|
|
2
|
+
export { parsePolicy, FileSystemPolicyResolver, permissivePolicyResolver, } from './loader.js';
|
|
3
|
+
export { evaluate } from './evaluate.js';
|
|
4
|
+
export { type PolicyMode, PERMISSIVE_TEMPLATE, STRICT_TEMPLATE, policyTemplate, } from './template.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,KAAK,UAAU,EACf,mBAAmB,EACnB,eAAe,EACf,cAAc,GACf,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { PolicyLoadError, } from './types.js';
|
|
2
|
+
export { parsePolicy, FileSystemPolicyResolver, permissivePolicyResolver, } from './loader.js';
|
|
3
|
+
export { evaluate } from './evaluate.js';
|
|
4
|
+
export { PERMISSIVE_TEMPLATE, STRICT_TEMPLATE, policyTemplate, } from './template.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAEL,mBAAmB,EACnB,eAAe,EACf,cAAc,GACf,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Policy, type PolicyResolver } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse + validate + normalize a TOML policy file. Throws PolicyLoadError on
|
|
4
|
+
* any schema violation with a message pointing at the offending field.
|
|
5
|
+
*
|
|
6
|
+
* Normalization:
|
|
7
|
+
* - addresses lowercased so allowlist membership is case-insensitive
|
|
8
|
+
* - selectors lowercased likewise
|
|
9
|
+
* - max_value_wei string → bigint
|
|
10
|
+
*
|
|
11
|
+
* Permissive mode tolerates missing strict fields (they're ignored anyway).
|
|
12
|
+
* Strict mode applies defaults: chain_ids must be present; everything else
|
|
13
|
+
* defaults to a closed/zero value.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parsePolicy(source: string): Policy;
|
|
16
|
+
/**
|
|
17
|
+
* A PolicyResolver that returns the same permissive policy for every handle.
|
|
18
|
+
* Useful for tests that want to exercise sign methods without provisioning
|
|
19
|
+
* a TOML file per portal.
|
|
20
|
+
*/
|
|
21
|
+
export declare function permissivePolicyResolver(): PolicyResolver;
|
|
22
|
+
/**
|
|
23
|
+
* File-backed PolicyResolver: reads ~/.sigil/policy/<handle>.toml every time
|
|
24
|
+
* resolve() is called. Sign calls are human-paced so the read is cheap; the
|
|
25
|
+
* fresh read also means policy edits take effect immediately (no daemon
|
|
26
|
+
* restart).
|
|
27
|
+
*/
|
|
28
|
+
export declare class FileSystemPolicyResolver implements PolicyResolver {
|
|
29
|
+
#private;
|
|
30
|
+
constructor(policyDir: string);
|
|
31
|
+
resolve(handle: string): Policy;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/policy/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAmB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAM/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA+DlD;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,cAAc,CAWzD;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,cAAc;;gBAEjD,SAAS,EAAE,MAAM;IAG7B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAehC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import toml from '@iarna/toml';
|
|
4
|
+
import { PolicyLoadError } from './types.js';
|
|
5
|
+
const ADDR_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
6
|
+
const SELECTOR_RE = /^0x[0-9a-fA-F]{8}$/;
|
|
7
|
+
const DEC_RE = /^[0-9]+$/;
|
|
8
|
+
/**
|
|
9
|
+
* Parse + validate + normalize a TOML policy file. Throws PolicyLoadError on
|
|
10
|
+
* any schema violation with a message pointing at the offending field.
|
|
11
|
+
*
|
|
12
|
+
* Normalization:
|
|
13
|
+
* - addresses lowercased so allowlist membership is case-insensitive
|
|
14
|
+
* - selectors lowercased likewise
|
|
15
|
+
* - max_value_wei string → bigint
|
|
16
|
+
*
|
|
17
|
+
* Permissive mode tolerates missing strict fields (they're ignored anyway).
|
|
18
|
+
* Strict mode applies defaults: chain_ids must be present; everything else
|
|
19
|
+
* defaults to a closed/zero value.
|
|
20
|
+
*/
|
|
21
|
+
export function parsePolicy(source) {
|
|
22
|
+
let raw;
|
|
23
|
+
try {
|
|
24
|
+
raw = toml.parse(source);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
throw new PolicyLoadError(`policy: invalid TOML — ${err.message}`, err);
|
|
28
|
+
}
|
|
29
|
+
const mode = raw['mode'];
|
|
30
|
+
if (mode !== 'permissive' && mode !== 'strict') {
|
|
31
|
+
throw new PolicyLoadError(`policy.mode must be "permissive" or "strict" (got ${JSON.stringify(mode)})`);
|
|
32
|
+
}
|
|
33
|
+
if (mode === 'permissive') {
|
|
34
|
+
return {
|
|
35
|
+
mode: 'permissive',
|
|
36
|
+
chainIds: [],
|
|
37
|
+
allowTo: [],
|
|
38
|
+
maxValueWei: 0n,
|
|
39
|
+
allowedSelectors: [],
|
|
40
|
+
allowMessageSigning: true,
|
|
41
|
+
allowTypedData: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// strict mode — every field is consulted; apply defaults for absent ones.
|
|
45
|
+
const chainIds = asNumberArray(raw['chain_ids'], 'chain_ids', { required: true });
|
|
46
|
+
for (const id of chainIds) {
|
|
47
|
+
if (!Number.isInteger(id) || id < 0) {
|
|
48
|
+
throw new PolicyLoadError(`policy.chain_ids[*] must be non-negative integers (got ${id})`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const allowToRaw = asStringArray(raw['allow_to'], 'allow_to');
|
|
52
|
+
const allowTo = allowToRaw.map((s, i) => {
|
|
53
|
+
if (!ADDR_RE.test(s)) {
|
|
54
|
+
throw new PolicyLoadError(`policy.allow_to[${i}] must be 0x-prefixed 20-byte address`);
|
|
55
|
+
}
|
|
56
|
+
return s.toLowerCase();
|
|
57
|
+
});
|
|
58
|
+
const maxValueWei = parseMaxValue(raw['max_value_wei']);
|
|
59
|
+
const selectorsRaw = asStringArray(raw['allowed_selectors'], 'allowed_selectors');
|
|
60
|
+
const allowedSelectors = selectorsRaw.map((s, i) => {
|
|
61
|
+
if (!SELECTOR_RE.test(s)) {
|
|
62
|
+
throw new PolicyLoadError(`policy.allowed_selectors[${i}] must be 0x + 4 hex bytes (got ${JSON.stringify(s)})`);
|
|
63
|
+
}
|
|
64
|
+
return s.toLowerCase();
|
|
65
|
+
});
|
|
66
|
+
const allowMessageSigning = asBool(raw['allow_message_signing'], 'allow_message_signing', false);
|
|
67
|
+
const allowTypedData = asBool(raw['allow_typed_data'], 'allow_typed_data', false);
|
|
68
|
+
return {
|
|
69
|
+
mode: 'strict',
|
|
70
|
+
chainIds,
|
|
71
|
+
allowTo,
|
|
72
|
+
maxValueWei,
|
|
73
|
+
allowedSelectors,
|
|
74
|
+
allowMessageSigning,
|
|
75
|
+
allowTypedData,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A PolicyResolver that returns the same permissive policy for every handle.
|
|
80
|
+
* Useful for tests that want to exercise sign methods without provisioning
|
|
81
|
+
* a TOML file per portal.
|
|
82
|
+
*/
|
|
83
|
+
export function permissivePolicyResolver() {
|
|
84
|
+
const policy = {
|
|
85
|
+
mode: 'permissive',
|
|
86
|
+
chainIds: [],
|
|
87
|
+
allowTo: [],
|
|
88
|
+
maxValueWei: 0n,
|
|
89
|
+
allowedSelectors: [],
|
|
90
|
+
allowMessageSigning: true,
|
|
91
|
+
allowTypedData: true,
|
|
92
|
+
};
|
|
93
|
+
return { resolve: () => policy };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* File-backed PolicyResolver: reads ~/.sigil/policy/<handle>.toml every time
|
|
97
|
+
* resolve() is called. Sign calls are human-paced so the read is cheap; the
|
|
98
|
+
* fresh read also means policy edits take effect immediately (no daemon
|
|
99
|
+
* restart).
|
|
100
|
+
*/
|
|
101
|
+
export class FileSystemPolicyResolver {
|
|
102
|
+
#policyDir;
|
|
103
|
+
constructor(policyDir) {
|
|
104
|
+
this.#policyDir = policyDir;
|
|
105
|
+
}
|
|
106
|
+
resolve(handle) {
|
|
107
|
+
const path = join(this.#policyDir, `${handle}.toml`);
|
|
108
|
+
let source;
|
|
109
|
+
try {
|
|
110
|
+
source = readFileSync(path, 'utf8');
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (err.code === 'ENOENT') {
|
|
114
|
+
throw new PolicyLoadError(`policy: no policy file for portal "${handle}" at ${path} — run "sigil portal add" to provision`);
|
|
115
|
+
}
|
|
116
|
+
throw new PolicyLoadError(`policy: failed to read ${path}`, err);
|
|
117
|
+
}
|
|
118
|
+
return parsePolicy(source);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Helpers
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
function asNumberArray(v, name, opts = {}) {
|
|
125
|
+
if (v === undefined) {
|
|
126
|
+
if (opts.required)
|
|
127
|
+
throw new PolicyLoadError(`policy.${name} is required in strict mode`);
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
if (!Array.isArray(v))
|
|
131
|
+
throw new PolicyLoadError(`policy.${name} must be an array`);
|
|
132
|
+
return v.map((item, i) => {
|
|
133
|
+
if (typeof item !== 'number') {
|
|
134
|
+
throw new PolicyLoadError(`policy.${name}[${i}] must be a number (got ${typeof item})`);
|
|
135
|
+
}
|
|
136
|
+
return item;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function asStringArray(v, name) {
|
|
140
|
+
if (v === undefined)
|
|
141
|
+
return [];
|
|
142
|
+
if (!Array.isArray(v))
|
|
143
|
+
throw new PolicyLoadError(`policy.${name} must be an array`);
|
|
144
|
+
return v.map((item, i) => {
|
|
145
|
+
if (typeof item !== 'string') {
|
|
146
|
+
throw new PolicyLoadError(`policy.${name}[${i}] must be a string (got ${typeof item})`);
|
|
147
|
+
}
|
|
148
|
+
return item;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function asBool(v, name, def) {
|
|
152
|
+
if (v === undefined)
|
|
153
|
+
return def;
|
|
154
|
+
if (typeof v !== 'boolean') {
|
|
155
|
+
throw new PolicyLoadError(`policy.${name} must be a boolean (got ${typeof v})`);
|
|
156
|
+
}
|
|
157
|
+
return v;
|
|
158
|
+
}
|
|
159
|
+
function parseMaxValue(v) {
|
|
160
|
+
if (v === undefined)
|
|
161
|
+
return 0n;
|
|
162
|
+
if (typeof v !== 'string') {
|
|
163
|
+
throw new PolicyLoadError(`policy.max_value_wei must be a decimal string (e.g. "100000000000000000"); got ${typeof v}`);
|
|
164
|
+
}
|
|
165
|
+
if (!DEC_RE.test(v)) {
|
|
166
|
+
throw new PolicyLoadError(`policy.max_value_wei must be a decimal integer string (got ${JSON.stringify(v)})`);
|
|
167
|
+
}
|
|
168
|
+
return BigInt(v);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/policy/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOML template strings written by `sigil portal add`. Hand-written rather
|
|
3
|
+
* than serialized so we can include explanatory comments — TOML serializers
|
|
4
|
+
* generally strip those.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PERMISSIVE_TEMPLATE = "# sigil policy file \u2014 permissive mode (default)\n#\n# This portal will sign anything the agent asks for. Your private key is\n# still encrypted at rest and never enters Claude's context, but signing\n# authority is unbounded.\n#\n# To restrict what this portal can sign, run:\n#\n# sigil portal remove <handle>\n# sigil portal add <handle> --key-file <path> --strict\n#\n# ...and edit the resulting policy file. Or just change \"permissive\" below\n# to \"strict\" and add the rules in the commented template at:\n# https://github.com/cdrn/sigil#policy-engine\n\nmode = \"permissive\"\n";
|
|
7
|
+
export declare const STRICT_TEMPLATE = "# sigil policy file \u2014 strict mode\n#\n# Every sign request is checked against the rules below before the key is\n# used. Edit the values to fit your portal. Anything you leave at the\n# default-zero/empty state will deny.\n\nmode = \"strict\"\n\n# Allowed chain IDs (decimal). At least one is required.\n# 1 = ethereum mainnet\n# 8453 = base\n# 42161 = arbitrum one\n# 10 = optimism\n# 11155111 = sepolia testnet\nchain_ids = [1]\n\n# Allowed destination addresses (lowercase 0x-prefixed). Empty = no tx allowed.\n# Example:\n# allow_to = [\"0x000000000000000000000000000000000000dead\"]\nallow_to = []\n\n# Per-tx value cap in wei. Default 0 = no ETH sends allowed at all.\n# 0.1 ether = \"100000000000000000\"\n# 1 ether = \"1000000000000000000\"\n# Quoted because uint256 doesn't fit in a TOML integer.\nmax_value_wei = \"0\"\n\n# 4-byte function selectors that are callable. Empty = pure ETH sends only.\n# Common selectors:\n# \"0xa9059cbb\" ERC-20 transfer(address,uint256)\n# \"0x095ea7b3\" ERC-20 approve(address,uint256)\n# \"0x23b872dd\" ERC-20 transferFrom(address,address,uint256)\nallowed_selectors = []\n\n# EIP-191 personal_sign \u2014 typically safe (used by Sign-In With Ethereum and\n# similar login flows). Set true to permit.\nallow_message_signing = false\n\n# EIP-712 typed data \u2014 CAN authorize off-chain financial actions (Permit,\n# OpenSea orders, gasless approvals). Treat with the same care as signing\n# transactions. Set true to permit.\nallow_typed_data = false\n";
|
|
8
|
+
export type PolicyMode = 'permissive' | 'strict';
|
|
9
|
+
export declare function policyTemplate(mode: PolicyMode): string;
|
|
10
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/policy/template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,mBAAmB,wlBAgB/B,CAAC;AAEF,eAAO,MAAM,eAAe,2/CA0C3B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEjD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOML template strings written by `sigil portal add`. Hand-written rather
|
|
3
|
+
* than serialized so we can include explanatory comments — TOML serializers
|
|
4
|
+
* generally strip those.
|
|
5
|
+
*/
|
|
6
|
+
export const PERMISSIVE_TEMPLATE = `# sigil policy file — permissive mode (default)
|
|
7
|
+
#
|
|
8
|
+
# This portal will sign anything the agent asks for. Your private key is
|
|
9
|
+
# still encrypted at rest and never enters Claude's context, but signing
|
|
10
|
+
# authority is unbounded.
|
|
11
|
+
#
|
|
12
|
+
# To restrict what this portal can sign, run:
|
|
13
|
+
#
|
|
14
|
+
# sigil portal remove <handle>
|
|
15
|
+
# sigil portal add <handle> --key-file <path> --strict
|
|
16
|
+
#
|
|
17
|
+
# ...and edit the resulting policy file. Or just change "permissive" below
|
|
18
|
+
# to "strict" and add the rules in the commented template at:
|
|
19
|
+
# https://github.com/cdrn/sigil#policy-engine
|
|
20
|
+
|
|
21
|
+
mode = "permissive"
|
|
22
|
+
`;
|
|
23
|
+
export const STRICT_TEMPLATE = `# sigil policy file — strict mode
|
|
24
|
+
#
|
|
25
|
+
# Every sign request is checked against the rules below before the key is
|
|
26
|
+
# used. Edit the values to fit your portal. Anything you leave at the
|
|
27
|
+
# default-zero/empty state will deny.
|
|
28
|
+
|
|
29
|
+
mode = "strict"
|
|
30
|
+
|
|
31
|
+
# Allowed chain IDs (decimal). At least one is required.
|
|
32
|
+
# 1 = ethereum mainnet
|
|
33
|
+
# 8453 = base
|
|
34
|
+
# 42161 = arbitrum one
|
|
35
|
+
# 10 = optimism
|
|
36
|
+
# 11155111 = sepolia testnet
|
|
37
|
+
chain_ids = [1]
|
|
38
|
+
|
|
39
|
+
# Allowed destination addresses (lowercase 0x-prefixed). Empty = no tx allowed.
|
|
40
|
+
# Example:
|
|
41
|
+
# allow_to = ["0x000000000000000000000000000000000000dead"]
|
|
42
|
+
allow_to = []
|
|
43
|
+
|
|
44
|
+
# Per-tx value cap in wei. Default 0 = no ETH sends allowed at all.
|
|
45
|
+
# 0.1 ether = "100000000000000000"
|
|
46
|
+
# 1 ether = "1000000000000000000"
|
|
47
|
+
# Quoted because uint256 doesn't fit in a TOML integer.
|
|
48
|
+
max_value_wei = "0"
|
|
49
|
+
|
|
50
|
+
# 4-byte function selectors that are callable. Empty = pure ETH sends only.
|
|
51
|
+
# Common selectors:
|
|
52
|
+
# "0xa9059cbb" ERC-20 transfer(address,uint256)
|
|
53
|
+
# "0x095ea7b3" ERC-20 approve(address,uint256)
|
|
54
|
+
# "0x23b872dd" ERC-20 transferFrom(address,address,uint256)
|
|
55
|
+
allowed_selectors = []
|
|
56
|
+
|
|
57
|
+
# EIP-191 personal_sign — typically safe (used by Sign-In With Ethereum and
|
|
58
|
+
# similar login flows). Set true to permit.
|
|
59
|
+
allow_message_signing = false
|
|
60
|
+
|
|
61
|
+
# EIP-712 typed data — CAN authorize off-chain financial actions (Permit,
|
|
62
|
+
# OpenSea orders, gasless approvals). Treat with the same care as signing
|
|
63
|
+
# transactions. Set true to permit.
|
|
64
|
+
allow_typed_data = false
|
|
65
|
+
`;
|
|
66
|
+
export function policyTemplate(mode) {
|
|
67
|
+
return mode === 'permissive' ? PERMISSIVE_TEMPLATE : STRICT_TEMPLATE;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/policy/template.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;CAgBlC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0C9B,CAAC;AAIF,MAAM,UAAU,cAAc,CAAC,IAAgB;IAC7C,OAAO,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,eAAe,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SignableTx, TypedData } from '../eth/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* A loaded + validated per-portal policy.
|
|
4
|
+
*
|
|
5
|
+
* Stored on disk at ~/.sigil/policy/<handle>.toml; loaded once per sign call
|
|
6
|
+
* (cheap — file is small, sign calls are human-paced).
|
|
7
|
+
*
|
|
8
|
+
* Two modes:
|
|
9
|
+
* - "permissive": evaluator returns Allow for everything. The other fields
|
|
10
|
+
* are ignored. This is the default written by `sigil portal add`; the
|
|
11
|
+
* user is opting into key isolation without any signing constraints.
|
|
12
|
+
* - "strict": every field below is enforced. `sigil portal add --strict`
|
|
13
|
+
* writes a template with conservative defaults the user fills in.
|
|
14
|
+
*
|
|
15
|
+
* `max_value_wei` is a string in the TOML and is parsed to bigint at load
|
|
16
|
+
* time — uint256 doesn't fit in TOML's int64.
|
|
17
|
+
*/
|
|
18
|
+
export interface Policy {
|
|
19
|
+
mode: 'permissive' | 'strict';
|
|
20
|
+
chainIds: readonly number[];
|
|
21
|
+
/** Lowercase 0x-prefixed addresses, normalized at load time. */
|
|
22
|
+
allowTo: readonly string[];
|
|
23
|
+
/** Per-tx value cap, in wei. 0n means "no value sends allowed at all". */
|
|
24
|
+
maxValueWei: bigint;
|
|
25
|
+
/** 4-byte function selectors, lowercase 0x-prefixed, normalized at load time. */
|
|
26
|
+
allowedSelectors: readonly string[];
|
|
27
|
+
allowMessageSigning: boolean;
|
|
28
|
+
allowTypedData: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** What the evaluator gets asked about. */
|
|
31
|
+
export type PolicyRequest = {
|
|
32
|
+
kind: 'transaction';
|
|
33
|
+
tx: SignableTx;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'message';
|
|
36
|
+
messageBytes: Buffer;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'typed_data';
|
|
39
|
+
typedData: TypedData;
|
|
40
|
+
};
|
|
41
|
+
/** Evaluator output. Reason is human-readable + audit-loggable on Deny. */
|
|
42
|
+
export type PolicyDecision = {
|
|
43
|
+
allow: true;
|
|
44
|
+
} | {
|
|
45
|
+
allow: false;
|
|
46
|
+
reason: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Resolves a portal handle to its current policy. Wrapped in an interface so
|
|
50
|
+
* tests can inject a constant policy without going through the filesystem.
|
|
51
|
+
*
|
|
52
|
+
* Throws PolicyLoadError if the file is missing or malformed. Sign methods
|
|
53
|
+
* treat any throw as a hard Deny + audit it.
|
|
54
|
+
*/
|
|
55
|
+
export interface PolicyResolver {
|
|
56
|
+
resolve(handle: string): Policy;
|
|
57
|
+
}
|
|
58
|
+
export declare class PolicyLoadError extends Error {
|
|
59
|
+
readonly cause?: unknown;
|
|
60
|
+
constructor(message: string, cause?: unknown);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/policy/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,YAAY,GAAG,QAAQ,CAAC;IAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,gEAAgE;IAChE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GACtB;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAC;gBACtB,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAK7C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/policy/types.ts"],"names":[],"mappings":"AAqDA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACtB,KAAK,CAAW;IAClC,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9C,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigild",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"mcpName": "io.github.cdrn/sigil",
|
|
5
5
|
"description": "Claude can sign, but never see. MCP server + CLI that keeps private keys out of the LLM's context window.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"test:watch": "node --watch --test 'dist/test/**/*.test.js'"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
+
"@iarna/toml": "2.2.5",
|
|
56
57
|
"@noble/ciphers": "1.3.0",
|
|
57
58
|
"@noble/hashes": "1.8.0",
|
|
58
59
|
"@noble/secp256k1": "3.1.0"
|