@adtl/gateway 0.5.0 → 0.6.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/README.md +105 -12
- package/dist/approvals.d.ts +22 -6
- package/dist/approvals.d.ts.map +1 -1
- package/dist/approvals.js +56 -26
- package/dist/approvals.js.map +1 -1
- package/dist/audit.d.ts +8 -0
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/auditCli.d.ts +2 -0
- package/dist/auditCli.d.ts.map +1 -1
- package/dist/auditCli.js +13 -10
- package/dist/auditCli.js.map +1 -1
- package/dist/cli.js +17 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +285 -21
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +49 -1
- package/dist/config.js.map +1 -1
- package/dist/elicitation.d.ts +25 -0
- package/dist/elicitation.d.ts.map +1 -0
- package/dist/elicitation.js +46 -0
- package/dist/elicitation.js.map +1 -0
- package/dist/gateway.d.ts +26 -1
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +85 -8
- package/dist/gateway.js.map +1 -1
- package/dist/hooks/claudeCode.d.ts +4 -0
- package/dist/hooks/claudeCode.d.ts.map +1 -1
- package/dist/hooks/claudeCode.js +8 -1
- package/dist/hooks/claudeCode.js.map +1 -1
- package/dist/hooks/run.d.ts.map +1 -1
- package/dist/hooks/run.js +6 -3
- package/dist/hooks/run.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/init/presets.d.ts.map +1 -1
- package/dist/init/presets.js +54 -0
- package/dist/init/presets.js.map +1 -1
- package/dist/invoker.d.ts +21 -1
- package/dist/invoker.d.ts.map +1 -1
- package/dist/invoker.js +19 -2
- package/dist/invoker.js.map +1 -1
- package/dist/ledgerCli.d.ts +1 -1
- package/dist/ledgerCli.d.ts.map +1 -1
- package/dist/ledgerCli.js +9 -3
- package/dist/ledgerCli.js.map +1 -1
- package/dist/ntfy.d.ts +3 -1
- package/dist/ntfy.d.ts.map +1 -1
- package/dist/ntfy.js +2 -1
- package/dist/ntfy.js.map +1 -1
- package/dist/playgroundCli.d.ts +8 -0
- package/dist/playgroundCli.d.ts.map +1 -0
- package/dist/playgroundCli.js +57 -0
- package/dist/playgroundCli.js.map +1 -0
- package/dist/receipt.d.ts +62 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +117 -0
- package/dist/receipt.js.map +1 -0
- package/dist/receiptCli.d.ts +8 -0
- package/dist/receiptCli.d.ts.map +1 -0
- package/dist/receiptCli.js +87 -0
- package/dist/receiptCli.js.map +1 -0
- package/dist/roles.d.ts +9 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +41 -0
- package/dist/roles.js.map +1 -0
- package/dist/startup.d.ts +9 -0
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +69 -7
- package/dist/startup.js.map +1 -1
- package/package.json +5 -4
- package/playground/fake-github.mjs +98 -0
- package/playground/fake-memory.mjs +130 -0
- package/playground/index.html +411 -0
- package/playground/playground.yaml +40 -0
- package/playground/server.mjs +290 -0
- package/src/approvals.ts +63 -26
- package/src/audit.ts +5 -0
- package/src/auditCli.ts +11 -8
- package/src/cli.ts +16 -1
- package/src/config.ts +54 -1
- package/src/elicitation.ts +69 -0
- package/src/gateway.ts +96 -9
- package/src/hooks/claudeCode.ts +13 -2
- package/src/hooks/run.ts +6 -3
- package/src/index.ts +5 -0
- package/src/init/presets.ts +54 -0
- package/src/invoker.ts +39 -2
- package/src/ledgerCli.ts +9 -3
- package/src/ntfy.ts +4 -1
- package/src/playgroundCli.ts +60 -0
- package/src/receipt.ts +148 -0
- package/src/receiptCli.ts +85 -0
- package/src/roles.ts +55 -0
- package/src/startup.ts +68 -6
package/README.md
CHANGED
|
@@ -13,6 +13,16 @@ An MCP gateway for AI agents. It sits between your agent (Claude Code, Cursor, a
|
|
|
13
13
|
|
|
14
14
|
It stops attacks like the 2025 GitHub MCP exfiltration, where a public issue told an agent to post private repository contents in a comment.
|
|
15
15
|
|
|
16
|
+
## Playground
|
|
17
|
+
|
|
18
|
+
Try it without setting anything up:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npx -y @adtl/gateway playground # opens http://localhost:4317
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
It runs the real gateway in front of fake GitHub and memory servers, with a page that walks through three test cases (a prompt injection in an issue, an injection remembered in memory and triggered in a later session, and ordinary work), with ADTL on and off. It shows every call's label and decision, the session label, what reached GitHub, approvals as Allow / Deny buttons, the config (edit it and press Apply) and the audit log. It listens on 127.0.0.1 only and keeps everything in a temporary folder. `--port <n>` picks another port; `--no-open` doesn't open the browser.
|
|
25
|
+
|
|
16
26
|
## Quick start
|
|
17
27
|
|
|
18
28
|
1. Describe your tools in `adtl.yaml`:
|
|
@@ -61,6 +71,7 @@ One file holds both the labels and the gateway settings.
|
|
|
61
71
|
| `egress` | `none`, `internal`, `external`, `public` | Where *calling* the tool can send data. |
|
|
62
72
|
| `labelInto` | an argument name, e.g. `metadata` | Write tools of servers that store an object per record: stamp the session label into it. See [labels on records](#labels-on-records). |
|
|
63
73
|
| `readLabels` | `true` | Read tools of those servers: join the stamps found on returned records into the result's label. |
|
|
74
|
+
| `scopeInto` | a scope-id argument, e.g. `run_id` | Write tools of servers that resolve references from earlier messages of the same scope (such as Mem0): give a tainted session's writes a scope of their own. |
|
|
64
75
|
|
|
65
76
|
Tools without a rule use the defaults (`untrusted`, `private`, egress `unknown`). That's safe but strict, so describe the tools you use. Resources and prompts use the server's `defaults`.
|
|
66
77
|
|
|
@@ -75,7 +86,7 @@ adtl-gateway init -c adtl.yaml --write # update the file (original kept as adt
|
|
|
75
86
|
|
|
76
87
|
`init` connects to each upstream and lists its tools.
|
|
77
88
|
|
|
78
|
-
- **Presets** cover the GitHub (`github-mcp-server`), filesystem and fetch servers. A preset says what the server's reads and writes mean; the server's `readOnlyHint` decides which tools are reads, so new tools are covered too.
|
|
89
|
+
- **Presets** cover the GitHub (`github-mcp-server`), filesystem and fetch servers, Slack (`@modelcontextprotocol/server-slack` and `slack-mcp-server`) and Gmail (`@gongrzhe/server-gmail-autoauth-mcp`). A preset says what the server's reads and writes mean; the server's `readOnlyHint` decides which tools are reads, so new tools are covered too. For servers without hints, the preset lists their tools by name. Slack messages and email are untrusted and private; posting to Slack is external egress, and so are sending email and Gmail filters, which can forward mail.
|
|
79
90
|
- **Other servers** get rules from their MCP tool hints. Hints only set egress, because they are the server's own claims: every result stays `untrusted` and `private` until you change it. These lines are marked `review`.
|
|
80
91
|
- Entries already in the file are never changed, so you can re-run `init` after a server adds tools. Tools the server no longer offers are reported, not removed.
|
|
81
92
|
- Edits keep your comments, but the YAML may be reformatted slightly (e.g. `[a]` becomes `[ a ]`).
|
|
@@ -115,16 +126,36 @@ Rules match on `session.integrity`, `session.confidentiality`, `tool.egress` and
|
|
|
115
126
|
| `gateway.approvals` | Ask a human about `ask` decisions. See [Approvals](#approvals). |
|
|
116
127
|
| `gateway.interceptorTimeoutMs` | Timeout per interceptor call (default 5000) |
|
|
117
128
|
| `gateway.interceptors` | Custom interceptor server command (default: the bundled `@adtl/interceptors`) |
|
|
129
|
+
| `gateway.principal` | Who runs this gateway: `{ id, type?, claims? }`. `${VAR}` works in `id`. Default: anonymous. See [Authorization](#authorization). |
|
|
130
|
+
| `gateway.roles` | Who may use which tools, prompts and resources. Deny by default once present. See [Authorization](#authorization). |
|
|
118
131
|
|
|
119
132
|
#### Checking the audit log
|
|
120
133
|
|
|
134
|
+
Each entry lists the interceptors that ran for the call, with the execution policy each ran under, as SEP-2624 asks: `interceptors: [{ name, phase, mode, modeFrom, failOpen, failOpenFrom, failed? }]`, where `*From` is `override` (set by the gateway, e.g. `--audit`) or `default` (declared by the interceptor).
|
|
135
|
+
|
|
121
136
|
Each line carries `prev`, the hash of the line before, and `hash`, over the line itself. Check the chain with:
|
|
122
137
|
|
|
123
138
|
```sh
|
|
124
139
|
adtl-gateway audit verify -c adtl.yaml # or: adtl-gateway audit verify ./adtl-audit.jsonl
|
|
125
140
|
```
|
|
126
141
|
|
|
127
|
-
It exits 1 if a line was edited, removed, reordered or inserted, and prints the hash of the last line. Cutting lines off the end is not detected, so note that hash somewhere else if you need to check later. Lines written by 0.4 or earlier are reported as written before the chain. The gateway and the Claude Code hook append through a lock file (`<log>.lock`), so their lines stay in one chain. The hook blocks `Write`/`Edit` on the log and asks before shell commands that mention it. The chain has no secret: an agent with shell access could still rewrite the whole file consistently.
|
|
142
|
+
It exits 1 if a line was edited, removed, reordered or inserted, and prints the hash of the last line. Cutting lines off the end is not detected, so note that hash somewhere else if you need to check later. Lines written by 0.4 or earlier are reported as written before the chain. The gateway and the Claude Code hook append through a lock file (`<log>.lock`), so their lines stay in one chain. The hook blocks `Write`/`Edit` on the log and asks before shell commands that mention it. The chain has no secret: an agent with shell access could still rewrite the whole file consistently. A signed receipt fixes what a session did at the time you take it (see below).
|
|
143
|
+
|
|
144
|
+
#### Session receipts
|
|
145
|
+
|
|
146
|
+
A receipt is a signed summary of every call one session made through the gateway and the Claude Code hooks, taken from the audit log: for each call the tool, outcome, decision, approver, call hash, result label and the hash of its log line, plus counts. Arguments and results are never included.
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
adtl-gateway receipt list -c adtl.yaml # sessions in the log, newest first
|
|
150
|
+
adtl-gateway receipt <session> -c adtl.yaml -o receipt.json
|
|
151
|
+
adtl-gateway receipt verify receipt.json --log adtl-audit.jsonl --key ed25519:...
|
|
152
|
+
adtl-gateway key # this machine's public key
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- Receipts are signed with this machine's Ed25519 key, `~/.adtl/signing-key.pem` (or `ADTL_SIGNING_KEY`), created on first use and readable only by you. `adtl-gateway key` prints the public key to give to whoever checks your receipts.
|
|
156
|
+
- `verify` checks the signature; with `--key`, that your key made it; with `--log`, that the log is intact and the receipt lists every line the session wrote between its first and last call, so a call can't be left out.
|
|
157
|
+
- A receipt is refused if the audit log's chain is broken.
|
|
158
|
+
- The same key signs ledger entries and declassifications (see [Label ledger](#label-ledger)). The Claude Code hook blocks `Write`/`Edit` on the key file and asks before shell commands that mention it. A signature shows which key signed and that nothing changed since; it doesn't stop someone who can read the key, such as an agent with shell access as you, from signing records of their own. Keep the key out of the agent's reach if that matters, for example by pointing `ADTL_SIGNING_KEY` at a file the agent's user can't read.
|
|
128
159
|
|
|
129
160
|
`${VAR}` in commands, args, env, URLs and headers is read from the environment, so secrets stay out of the file.
|
|
130
161
|
|
|
@@ -157,7 +188,49 @@ How it works and what to know:
|
|
|
157
188
|
- **Nothing sensitive is sent.** Notifications and emails name the tool, the server and the reason, never the call's arguments or results.
|
|
158
189
|
- **Email never contains approve links,** because mail security scanners open links automatically and would approve calls by themselves.
|
|
159
190
|
- **Mind client timeouts.** Many MCP clients give up on a request after 60 seconds, so the default timeout is 50. The gateway sends progress notifications while it waits, which lets clients that reset their timeout on progress wait longer.
|
|
160
|
-
- **Every outcome is logged.** The audit log records `approval: { outcome, channel, waitedMs }` for each call that was asked.
|
|
191
|
+
- **Every outcome is logged.** The audit log records `approval: { outcome, channel, waitedMs }` for each call that was asked, and `approver` when the answer came from a named approver.
|
|
192
|
+
|
|
193
|
+
#### Approving in the MCP client
|
|
194
|
+
|
|
195
|
+
Clients that support MCP elicitation (a server asking the user a question in the client) can show the request in place, so you don't need a phone:
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
gateway:
|
|
199
|
+
approvals:
|
|
200
|
+
elicitation: true # off by default
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- The request appears in the client with Allow and Deny. Allow runs that one call; Deny, declining, dismissing or no answer stops it. The answer goes through the same signed token as a push button, so it can only answer the call it was asked about.
|
|
204
|
+
- If ntfy is configured too, both are asked and the first answer wins. Clients without elicitation support are simply not asked.
|
|
205
|
+
- It's off by default because a client that accepts elicitations by itself, without a person, would approve calls. Turn it on only for clients that always show the prompt to you.
|
|
206
|
+
- It's never used for rules with named approvers: the client can't say who answered.
|
|
207
|
+
|
|
208
|
+
#### Who may approve
|
|
209
|
+
|
|
210
|
+
By default, whoever can read the shared topic can approve, and ADTL can't tell who did. A policy rule can name the roles whose members may answer instead. Each member gets their own topic:
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
policy:
|
|
214
|
+
rules:
|
|
215
|
+
- name: no-public-write-after-untrusted
|
|
216
|
+
when: { session.integrity: untrusted, tool.egress: [public, external, unknown] }
|
|
217
|
+
then: ask
|
|
218
|
+
approvers: [security-leads] # roles from gateway.roles
|
|
219
|
+
|
|
220
|
+
gateway:
|
|
221
|
+
roles:
|
|
222
|
+
security-leads: { members: [alice, bob], tools: ["*.*"] }
|
|
223
|
+
approvals:
|
|
224
|
+
approvers:
|
|
225
|
+
alice: { ntfy: { topic: "${ALICE_NTFY_TOPIC}" } }
|
|
226
|
+
bob: { ntfy: { topic: "${BOB_NTFY_TOPIC}" } }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- The request goes only to the approvers' own topics, never to the shared one. Each person's buttons carry tokens signed for them, and an answer counts only if it comes back on that person's reply topic, so the audit log records who approved (`approver: alice`) and nobody else can. Email notices still go out.
|
|
230
|
+
- If several matching rules name approvers, a person may answer only if they are in one of the roles of every rule.
|
|
231
|
+
- The gateway refuses to start if a rule names a role that isn't under `gateway.roles`, a role with member `"*"`, or a member without a channel under `gateway.approvals.approvers`.
|
|
232
|
+
- Rules without `approvers:` use the shared topic as before. One approval is enough; requiring two, and stopping people from approving their own calls, are not supported yet.
|
|
233
|
+
- Under Claude Code's hooks an "ask" goes to Claude Code's own permission prompt, which doesn't know about approvers.
|
|
161
234
|
|
|
162
235
|
### Trying a policy safely
|
|
163
236
|
|
|
@@ -192,7 +265,7 @@ What it does:
|
|
|
192
265
|
- **It shares one session label with the gateway.** Claude Code gives the gateway its session id (`CLAUDE_CODE_SESSION_ID`), and both keep the label in `~/.adtl/sessions/<id>.jsonl` (set `ADTL_SESSIONS_DIR` to change the directory). A `WebFetch` taints the session for `github__add_comment`, and a private file read through the gateway counts against a later `Bash` call. Labels last for the whole Claude Code session, including `--resume`.
|
|
193
266
|
- **It covers MCP servers that skip the gateway**, such as connectors added in claude.ai. Their tools are labeled as their server name (`mcp__<server>__<tool>` → server `<server>`), so you can describe them under `servers:` like any upstream. The gateway's own tools (`mcp__adtl__*`) are skipped; pass `--gateway-server <name>` if you registered the gateway under another name.
|
|
194
267
|
- **It fails closed.** If the hook can't decide, it exits with code 2, which stops the call.
|
|
195
|
-
- **It writes to the same audit log** (`gateway.audit`) with `"via": "claude-code-hook"`, and follows `gateway.mode: audit`. The config needs no `gateway:` section if you only use the hooks.
|
|
268
|
+
- **It writes to the same audit log** (`gateway.audit`) with `"via": "claude-code-hook"`, and follows `gateway.mode: audit`. Calls made inside a subagent (the Task tool) carry `"agent": { id, type }`. Their labels count toward the session id Claude Code gives the hook; if that is the parent's id, as the hook docs suggest, a subagent that reads untrusted content taints the parent too. The config needs no `gateway:` section if you only use the hooks.
|
|
196
269
|
|
|
197
270
|
Built-in labels, which you can override under `servers.claude-code.tools`:
|
|
198
271
|
|
|
@@ -228,7 +301,7 @@ Look after it with:
|
|
|
228
301
|
|
|
229
302
|
```sh
|
|
230
303
|
adtl-gateway ledger stats
|
|
231
|
-
adtl-gateway ledger verify # exit 1 if an entry was changed or
|
|
304
|
+
adtl-gateway ledger verify # exit 1 if an entry was changed, removed, or signed by another key
|
|
232
305
|
adtl-gateway ledger declassify notes.md --reason "checked by hand, no instructions"
|
|
233
306
|
# after checking it: clear the label of its current content
|
|
234
307
|
adtl-gateway ledger declassified # recent declassifications: when, who, what and why
|
|
@@ -236,7 +309,7 @@ adtl-gateway ledger declassified # recent declassifications: when, wh
|
|
|
236
309
|
|
|
237
310
|
`--reason` is required. Declassifying is where taint tracking tends to erode, as it turns into a reflex, so each one is kept with who did it, the file and the reason, for review with `declassified`.
|
|
238
311
|
|
|
239
|
-
Matching is by normalized chunks (whole text, paragraphs, lines of 32+ characters), so text copied into a bigger file still matches, and text the agent rewrote was already recorded with its session's label. Text a person rewrites by hand, binary files, and content an MCP server returns in another encoding (such as base64) are not matched. The Claude Code hook blocks `Write`/`Edit` on the ledger file and asks before shell commands that mention it or `ledger declassify`. Entries are hash-chained,
|
|
312
|
+
Matching is by normalized chunks (whole text, paragraphs, lines of 32+ characters), so text copied into a bigger file still matches, and text the agent rewrote was already recorded with its session's label. Text a person rewrites by hand, binary files, and content an MCP server returns in another encoding (such as base64) are not matched. The Claude Code hook blocks `Write`/`Edit` on the ledger file and asks before shell commands that mention it or `ledger declassify`. Entries are hash-chained and, from 0.6, signed with this machine's key (the same one as [receipts](#session-receipts)), declassifications included, so a reader elsewhere can check who labeled content. `verify` reports entries signed by another key, bad signatures, and unsigned entries after signed ones; entries written before signing are counted, not refused. Someone who can read the key can still sign a rebuilt file.
|
|
240
313
|
|
|
241
314
|
See [the delayed-injection demo](../../examples/delayed-injection-demo) (`npm run demo:ledger`).
|
|
242
315
|
|
|
@@ -248,13 +321,16 @@ Some memory servers extract facts with a model on write, or merge records, so wh
|
|
|
248
321
|
servers:
|
|
249
322
|
memory:
|
|
250
323
|
tools:
|
|
251
|
-
add_memory: { egress: internal, labelInto: metadata }
|
|
324
|
+
add_memory: { egress: internal, labelInto: metadata, scopeInto: run_id }
|
|
325
|
+
update_memory: { egress: internal, labelInto: metadata }
|
|
252
326
|
search_memory: { integrity: trusted, confidentiality: public, egress: none, readLabels: true }
|
|
253
327
|
```
|
|
254
328
|
|
|
255
|
-
- On every `add_memory` call the gateway sets `metadata["adtl/record-label"]` to `{ integrity, confidentiality, writer }` from the session,
|
|
256
|
-
- On `search_memory`, stamps anywhere in the result (also inside JSON text) are joined into its label, with the origin `adtl-record` / `session:<id>`, and the audit entry gets `records: { stamps, writers }`. Anyone who can write to the store can write a stamp, so stamps only make a label stricter. A malformed stamp counts as the global defaults.
|
|
257
|
-
-
|
|
329
|
+
- **Write.** On every `add_memory` and `update_memory` call the gateway sets `metadata["adtl/record-label/<session>"]` to `{ integrity, confidentiality, writer }` from the session, and drops any stamp keys the agent put there. Each session stamps under its own key, so on a server that lays an update's metadata over the stored metadata (as Mem0 does), a clean session's update adds a stamp instead of replacing a tainted one.
|
|
330
|
+
- **Read.** On `search_memory`, stamps anywhere in the result (also inside JSON text) are joined into its label, with the origin `adtl-record` / `session:<id>`, and the audit entry gets `records: { stamps, writers }`. Anyone who can write to the store can write a stamp, so stamps only make a label stricter. A malformed stamp counts as the global defaults.
|
|
331
|
+
- **Scope.** Some servers also keep recent raw messages per scope, without metadata, and resolve references in a new message from them: Mem0 keeps the last 10 per `user_id`/`agent_id`/`run_id` and asks its extractor to resolve pronouns from them. A clean session writing under a tainted session's ids could then store a fact built from the tainted text, with a clean stamp. With `scopeInto: run_id`, a tainted session's writes get a run id named after its label (`<run_id>:adtl-untrusted-public`, or just `adtl-untrusted-public`), so the server keeps its messages apart. Clean sessions keep their ids, minus any label mark the agent added, so they can't join a tainted scope. Mem0 filters searches by the ids a call sets: a read that names the agent's own `run_id` doesn't see the tainted session's records, and one by `user_id` alone sees them all, with their stamps.
|
|
332
|
+
- **Tools that return text without metadata** can't carry the label, and the ledger can't recognize extracted text. Give them a strict rule, such as `integrity: untrusted`. In Mem0 that is `history()`, and on Mem0's hosted MCP server `list_events` and `get_event_status`.
|
|
333
|
+
- **Servers that drop or replace metadata** (when they consolidate records, or an update overwrites the metadata instead of merging it) can't carry the label either. Give their read tools a strict rule.
|
|
258
334
|
|
|
259
335
|
## How it works
|
|
260
336
|
|
|
@@ -268,7 +344,24 @@ agent ──▶ adtl-gateway ──▶ upstream MCP servers
|
|
|
268
344
|
|
|
269
345
|
### Authorization
|
|
270
346
|
|
|
271
|
-
Labels and the policy decide whether a call may run given what the session has seen.
|
|
347
|
+
Labels and the policy decide whether a call may run given what the session has seen. Authorization decides before that whether the caller may use the tool, resource or prompt at all.
|
|
348
|
+
|
|
349
|
+
Roles in the config:
|
|
350
|
+
|
|
351
|
+
```yaml
|
|
352
|
+
gateway:
|
|
353
|
+
principal: { id: "${USER}" } # who runs this gateway
|
|
354
|
+
roles:
|
|
355
|
+
maintainers: { members: [alice, bob], tools: ["github.*"] }
|
|
356
|
+
readers: { members: ["*"], tools: ["github.read_*", "github.get_*"], resources: ["github://acme/website/*"] }
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
- A call is allowed if a role the caller belongs to grants it. Once `roles:` is present, everything else is denied.
|
|
360
|
+
- `tools` and `prompts` are `server.name` patterns with `*`, as in policy rules; `resources` are URI patterns. Members are principal ids; `"*"` means everyone, including an anonymous caller.
|
|
361
|
+
- Over stdio the caller is whoever started the gateway, set with `gateway.principal`. Without it the caller is anonymous and only roles with `members: ["*"]` apply (the gateway warns at startup).
|
|
362
|
+
- `tools/list`, `prompts/list` and `resources/list` leave out what the caller may not use, so the agent doesn't try it. Hiding is a convenience, not the security boundary: a call to a hidden tool is still checked and denied. Lists are checked without arguments, and nothing is hidden in audit mode. Resource templates are always listed.
|
|
363
|
+
|
|
364
|
+
Or your own `Authorizer` in code:
|
|
272
365
|
|
|
273
366
|
```ts
|
|
274
367
|
import { AdtlGateway, type Authorizer } from "@adtl/gateway";
|
|
@@ -296,7 +389,7 @@ The gateway fails closed. If an interceptor errors or times out, the call is sto
|
|
|
296
389
|
- Tools, resources and prompts are proxied. Resource subscriptions, `list_changed` notifications and completions are not passed through yet.
|
|
297
390
|
- Resources and prompts are labeled by server defaults (`servers.<id>.defaults`) or the global defaults; there are no per-resource or per-prompt rules yet. A resource URI that two upstreams could serve is refused rather than guessed.
|
|
298
391
|
- Approvals come from one push channel (ntfy). Other channels, approvals inside the MCP client, and approver roles are planned.
|
|
299
|
-
- Session labels last for one agent session: one gateway process, or under Claude Code one Claude Code session (shared with the hooks). The [label ledger](#label-ledger) carries them into files, memory servers and sub-agents by content, but not across machines or other agents yet (v0.
|
|
392
|
+
- Session labels last for one agent session: one gateway process, or under Claude Code one Claude Code session (shared with the hooks). The [label ledger](#label-ledger) carries them into files, memory servers and sub-agents by content, but not across machines or other agents yet (v0.7). Memory servers that extract facts or merge records on write return text the ledger cannot recognize; use [labels on records](#labels-on-records), or a strict rule for servers that don't keep metadata.
|
|
300
393
|
- The Claude Code hooks label tools by name, not by what they touched: a `Read` of a downloaded file is as trusted as a `Read` of your own code.
|
|
301
394
|
- Labels are set by your config. Servers' own trust claims can only make labels stricter, unless you set `acceptServerClaims: true`.
|
|
302
395
|
|
package/dist/approvals.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface ApprovalRequest {
|
|
|
10
10
|
matched: string[];
|
|
11
11
|
/** The session label that triggered the rule (no arguments or results). */
|
|
12
12
|
sessionLabel?: unknown;
|
|
13
|
+
/** People who may answer, reached on their own channels. Absent: the shared channels. */
|
|
14
|
+
approvers?: string[];
|
|
13
15
|
createdAt: Date;
|
|
14
16
|
expiresAt: Date;
|
|
15
17
|
}
|
|
@@ -53,7 +55,8 @@ export declare function errorText(err: unknown): string;
|
|
|
53
55
|
export declare class ApprovalTokens {
|
|
54
56
|
private readonly secret;
|
|
55
57
|
constructor(secret?: Buffer);
|
|
56
|
-
|
|
58
|
+
/** A token answering `request` with `decision`; with `approver`, valid only as that person's answer. */
|
|
59
|
+
sign(request: Pick<ApprovalRequest, "id" | "callHash" | "expiresAt">, decision: ApprovalDecision, approver?: string): string;
|
|
57
60
|
/** Parse a token without verifying it. */
|
|
58
61
|
static parse(token: string): {
|
|
59
62
|
id: string;
|
|
@@ -62,11 +65,13 @@ export declare class ApprovalTokens {
|
|
|
62
65
|
sig: string;
|
|
63
66
|
} | undefined;
|
|
64
67
|
/** Verify a token against the pending request it claims to answer. */
|
|
65
|
-
verify(token: string, request: Pick<ApprovalRequest, "id" | "callHash">, now: Date): ApprovalDecision | undefined;
|
|
68
|
+
verify(token: string, request: Pick<ApprovalRequest, "id" | "callHash">, now: Date, approver?: string): ApprovalDecision | undefined;
|
|
66
69
|
private mac;
|
|
67
70
|
}
|
|
68
71
|
export interface ApprovalBrokerOptions {
|
|
69
72
|
notifiers: Notifier[];
|
|
73
|
+
/** Each approver's own channels, by principal id. */
|
|
74
|
+
approverChannels?: Map<string, Notifier[]>;
|
|
70
75
|
timeoutMs: number;
|
|
71
76
|
tokens?: ApprovalTokens;
|
|
72
77
|
now?: () => Date;
|
|
@@ -76,18 +81,29 @@ export interface ApprovalBrokerOptions {
|
|
|
76
81
|
}
|
|
77
82
|
export declare class ApprovalBroker {
|
|
78
83
|
private readonly notifiers;
|
|
84
|
+
private readonly approverChannels;
|
|
79
85
|
private readonly timeoutMs;
|
|
80
86
|
private readonly tokens;
|
|
81
87
|
private readonly now;
|
|
82
88
|
private readonly log;
|
|
83
89
|
private readonly pending;
|
|
84
90
|
constructor(opts: ApprovalBrokerOptions);
|
|
85
|
-
/** True if at least one
|
|
91
|
+
/** True if at least one channel can carry answers. Without one, "ask" stays a block. */
|
|
86
92
|
get canApprove(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Where a request goes: the named approvers' own channels plus notice-only
|
|
95
|
+
* ones, or else every shared channel plus `extra` (channels for this request
|
|
96
|
+
* only, such as the client's elicitation).
|
|
97
|
+
*/
|
|
98
|
+
private targets;
|
|
87
99
|
/** Ask for approval and wait for the answer, the timeout, or a failure to reach anyone. */
|
|
88
|
-
request(input: Omit<ApprovalRequest, "id" | "createdAt" | "expiresAt"
|
|
89
|
-
/**
|
|
90
|
-
|
|
100
|
+
request(input: Omit<ApprovalRequest, "id" | "createdAt" | "expiresAt">, extra?: Notifier[]): Promise<ApprovalResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Handle an answer token from a channel; `approver` is whose own channel it
|
|
103
|
+
* came on, `reportedBy` who a shared channel says answered (recorded, not
|
|
104
|
+
* trusted for anything). Returns true if it settled a pending approval.
|
|
105
|
+
*/
|
|
106
|
+
answer(token: string, channel: string, approver?: string, reportedBy?: string): boolean;
|
|
91
107
|
/** Tell notifiers a call was blocked. Fire and forget. */
|
|
92
108
|
blocked(event: Omit<Extract<ApprovalEvent, {
|
|
93
109
|
kind: "blocked";
|
package/dist/approvals.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"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"}
|
package/dist/approvals.js
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* decision and an expiry. A token only ever answers the one pending call it
|
|
11
11
|
* was made for, and only once. Anyone who can read the push channel can still
|
|
12
12
|
* use the tokens sent on it, so that channel must stay private.
|
|
13
|
+
*
|
|
14
|
+
* A request can name its approvers (policy rule `approvers:`). It then goes
|
|
15
|
+
* only to those people's own channels, each with tokens signed for that
|
|
16
|
+
* person, and an answer counts only if it comes back on the same person's
|
|
17
|
+
* channel. So the result names who approved, and nobody else can.
|
|
13
18
|
*/
|
|
14
19
|
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
15
20
|
const TOKEN_PREFIX = "adtl1";
|
|
@@ -26,9 +31,10 @@ export class ApprovalTokens {
|
|
|
26
31
|
constructor(secret = randomBytes(32)) {
|
|
27
32
|
this.secret = secret;
|
|
28
33
|
}
|
|
29
|
-
|
|
34
|
+
/** A token answering `request` with `decision`; with `approver`, valid only as that person's answer. */
|
|
35
|
+
sign(request, decision, approver) {
|
|
30
36
|
const exp = Math.floor(request.expiresAt.getTime() / 1000);
|
|
31
|
-
return `${TOKEN_PREFIX}.${request.id}.${decision}.${exp}.${this.mac(request.id, request.callHash, decision, exp)}`;
|
|
37
|
+
return `${TOKEN_PREFIX}.${request.id}.${decision}.${exp}.${this.mac(request.id, request.callHash, decision, exp, approver)}`;
|
|
32
38
|
}
|
|
33
39
|
/** Parse a token without verifying it. */
|
|
34
40
|
static parse(token) {
|
|
@@ -43,24 +49,26 @@ export class ApprovalTokens {
|
|
|
43
49
|
return { id, decision, exp: Number(exp), sig };
|
|
44
50
|
}
|
|
45
51
|
/** Verify a token against the pending request it claims to answer. */
|
|
46
|
-
verify(token, request, now) {
|
|
52
|
+
verify(token, request, now, approver) {
|
|
47
53
|
const t = ApprovalTokens.parse(token);
|
|
48
54
|
if (!t || t.id !== request.id)
|
|
49
55
|
return undefined;
|
|
50
56
|
if (now.getTime() > t.exp * 1000)
|
|
51
57
|
return undefined;
|
|
52
|
-
const expected = Buffer.from(this.mac(t.id, request.callHash, t.decision, t.exp));
|
|
58
|
+
const expected = Buffer.from(this.mac(t.id, request.callHash, t.decision, t.exp, approver));
|
|
53
59
|
const given = Buffer.from(t.sig);
|
|
54
60
|
if (given.length !== expected.length || !timingSafeEqual(given, expected))
|
|
55
61
|
return undefined;
|
|
56
62
|
return t.decision;
|
|
57
63
|
}
|
|
58
|
-
mac(id, callHash, decision, exp) {
|
|
59
|
-
|
|
64
|
+
mac(id, callHash, decision, exp, approver) {
|
|
65
|
+
const signed = `${id}\n${callHash}\n${decision}\n${exp}${approver === undefined ? "" : `\napprover:${approver}`}`;
|
|
66
|
+
return createHmac("sha256", this.secret).update(signed).digest("base64url");
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
69
|
export class ApprovalBroker {
|
|
63
70
|
notifiers;
|
|
71
|
+
approverChannels;
|
|
64
72
|
timeoutMs;
|
|
65
73
|
tokens;
|
|
66
74
|
now;
|
|
@@ -68,17 +76,30 @@ export class ApprovalBroker {
|
|
|
68
76
|
pending = new Map();
|
|
69
77
|
constructor(opts) {
|
|
70
78
|
this.notifiers = opts.notifiers;
|
|
79
|
+
this.approverChannels = opts.approverChannels ?? new Map();
|
|
71
80
|
this.timeoutMs = opts.timeoutMs;
|
|
72
81
|
this.tokens = opts.tokens ?? new ApprovalTokens();
|
|
73
82
|
this.now = opts.now ?? (() => new Date());
|
|
74
83
|
this.log = opts.log ?? { warn: () => { } };
|
|
75
84
|
}
|
|
76
|
-
/** True if at least one
|
|
85
|
+
/** True if at least one channel can carry answers. Without one, "ask" stays a block. */
|
|
77
86
|
get canApprove() {
|
|
78
|
-
return this.notifiers.some((n) => n.listen);
|
|
87
|
+
return this.notifiers.some((n) => n.listen) || [...this.approverChannels.values()].some((ns) => ns.some((n) => n.listen));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Where a request goes: the named approvers' own channels plus notice-only
|
|
91
|
+
* ones, or else every shared channel plus `extra` (channels for this request
|
|
92
|
+
* only, such as the client's elicitation).
|
|
93
|
+
*/
|
|
94
|
+
targets(request, extra) {
|
|
95
|
+
if (!request.approvers)
|
|
96
|
+
return [...this.notifiers, ...extra].map((notifier) => ({ notifier }));
|
|
97
|
+
const own = request.approvers.flatMap((approver) => (this.approverChannels.get(approver) ?? []).map((notifier) => ({ notifier, approver })));
|
|
98
|
+
// Shared channels that can answer would let anyone who reads them approve; notice-only ones (email) are fine.
|
|
99
|
+
return [...own, ...this.notifiers.filter((n) => !n.listen).map((notifier) => ({ notifier }))];
|
|
79
100
|
}
|
|
80
101
|
/** Ask for approval and wait for the answer, the timeout, or a failure to reach anyone. */
|
|
81
|
-
async request(input) {
|
|
102
|
+
async request(input, extra = []) {
|
|
82
103
|
const createdAt = this.now();
|
|
83
104
|
const request = {
|
|
84
105
|
...input,
|
|
@@ -104,46 +125,55 @@ export class ApprovalBroker {
|
|
|
104
125
|
request,
|
|
105
126
|
settle: (decision, channel, approver) => finish({ outcome: decision === "allow" ? "approved" : "denied", channel, ...(approver ? { approver } : {}), waitedMs: waited() }),
|
|
106
127
|
});
|
|
107
|
-
// Start listening before anyone can answer, then send the request
|
|
108
|
-
const
|
|
109
|
-
for (const n of
|
|
110
|
-
n.listen(request, (token,
|
|
128
|
+
// Start listening before anyone can answer, then send the request to every target.
|
|
129
|
+
const targets = this.targets(request, extra);
|
|
130
|
+
for (const { notifier: n, approver } of targets.filter((t) => t.notifier.listen)) {
|
|
131
|
+
n.listen(request, (token, reportedBy) => this.answer(token, n.name, approver, reportedBy), abort.signal).catch((err) => this.log.warn(`${n.name}: stopped listening for approvals: ${errorText(err)}`));
|
|
111
132
|
}
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
const sent = targets.map(({ notifier: n, approver }) => {
|
|
134
|
+
const tokens = { allow: this.tokens.sign(request, "allow", approver), deny: this.tokens.sign(request, "deny", approver) };
|
|
135
|
+
return n.notify({ kind: "pending", request, tokens }).then(() => true, (err) => {
|
|
136
|
+
this.log.warn(`${n.name}: could not send approval request: ${errorText(err)}`);
|
|
137
|
+
return false;
|
|
138
|
+
});
|
|
139
|
+
});
|
|
117
140
|
// Nobody who could answer was reached: deny now rather than wait out the timeout.
|
|
118
141
|
// Notify-only channels (email) are not waited for.
|
|
119
|
-
void Promise.all(sent.filter((_, i) =>
|
|
142
|
+
void Promise.all(sent.filter((_, i) => targets[i].notifier.listen)).then((ok) => {
|
|
120
143
|
if (!ok.includes(true))
|
|
121
144
|
finish({ outcome: "undeliverable", waitedMs: waited() });
|
|
122
145
|
});
|
|
123
146
|
const r = await result;
|
|
124
|
-
this.broadcast({ kind: "resolved", request, result: r });
|
|
147
|
+
this.broadcast({ kind: "resolved", request, result: r }, [...new Set(targets.map((t) => t.notifier))]);
|
|
125
148
|
return r;
|
|
126
149
|
}
|
|
127
|
-
/**
|
|
128
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Handle an answer token from a channel; `approver` is whose own channel it
|
|
152
|
+
* came on, `reportedBy` who a shared channel says answered (recorded, not
|
|
153
|
+
* trusted for anything). Returns true if it settled a pending approval.
|
|
154
|
+
*/
|
|
155
|
+
answer(token, channel, approver, reportedBy) {
|
|
129
156
|
const parsed = ApprovalTokens.parse(token);
|
|
130
157
|
const p = parsed && this.pending.get(parsed.id);
|
|
131
158
|
if (!p)
|
|
132
159
|
return false; // not ours, already answered, or garbage
|
|
133
|
-
|
|
160
|
+
// A request with approvers takes answers only on their own channels.
|
|
161
|
+
if (p.request.approvers && (approver === undefined || !p.request.approvers.includes(approver)))
|
|
162
|
+
return false;
|
|
163
|
+
const decision = this.tokens.verify(token, p.request, this.now(), approver);
|
|
134
164
|
if (!decision) {
|
|
135
165
|
this.log.warn(`${channel}: ignored an approval answer with a bad or expired signature`);
|
|
136
166
|
return false;
|
|
137
167
|
}
|
|
138
|
-
p.settle(decision, channel, approver);
|
|
168
|
+
p.settle(decision, channel, approver ?? reportedBy);
|
|
139
169
|
return true;
|
|
140
170
|
}
|
|
141
171
|
/** Tell notifiers a call was blocked. Fire and forget. */
|
|
142
172
|
blocked(event) {
|
|
143
173
|
this.broadcast({ kind: "blocked", ...event });
|
|
144
174
|
}
|
|
145
|
-
broadcast(event) {
|
|
146
|
-
for (const n of
|
|
175
|
+
broadcast(event, notifiers = this.notifiers) {
|
|
176
|
+
for (const n of notifiers) {
|
|
147
177
|
n.notify(event).catch((err) => this.log.warn(`${n.name}: could not send ${event.kind} notice: ${errorText(err)}`));
|
|
148
178
|
}
|
|
149
179
|
}
|
package/dist/approvals.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approvals.js","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"approvals.js","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InterceptorRun } from "./invoker.js";
|
|
1
2
|
export interface AuditEntry {
|
|
2
3
|
ts: string;
|
|
3
4
|
sessionId: string;
|
|
@@ -11,6 +12,11 @@ export interface AuditEntry {
|
|
|
11
12
|
outcome: "forwarded" | "blocked" | "asked" | "upstream_error";
|
|
12
13
|
/** Present when the entry was written by the Claude Code hook rather than the gateway. */
|
|
13
14
|
via?: "claude-code-hook";
|
|
15
|
+
/** Present when a Claude Code subagent made the call: its id and type (e.g. "Explore"). */
|
|
16
|
+
agent?: {
|
|
17
|
+
id: string;
|
|
18
|
+
type?: string;
|
|
19
|
+
};
|
|
14
20
|
/** The policy's decision. In audit mode a call can be forwarded with decision "ask" or "block". */
|
|
15
21
|
decision?: string;
|
|
16
22
|
/** Present when the gateway ran in audit mode: nothing was stopped or changed. */
|
|
@@ -36,6 +42,8 @@ export interface AuditEntry {
|
|
|
36
42
|
/** Present when the result carried record label stamps: how many, and which sessions wrote them. */
|
|
37
43
|
records?: unknown;
|
|
38
44
|
sessionLabel?: unknown;
|
|
45
|
+
/** Every interceptor invocation for this call, with its resolved mode and failOpen and where each came from (SEP-2624). */
|
|
46
|
+
interceptors?: InterceptorRun[];
|
|
39
47
|
warnings?: string[];
|
|
40
48
|
durationMs: number;
|
|
41
49
|
}
|
package/dist/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,GAAG,gBAAgB,GAAG,aAAa,CAAC;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,gBAAgB,CAAC;IAC9D,0FAA0F;IAC1F,GAAG,CAAC,EAAE,kBAAkB,CAAC;IACzB,2FAA2F;IAC3F,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2GAA2G;IAC3G,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,2FAA2F;IAC3F,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8HAA8H;IAC9H,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oGAAoG;IACpG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2HAA2H;IAC3H,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC;AAED,yCAAyC;AACzC,eAAO,MAAM,aAAa,QAA6B,CAAC;AAsExD,qBAAa,aAAc,YAAW,SAAS;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAIjC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAc/B;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CA4B9D;AAED,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAM;IACpC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAG/B"}
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7H,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/auditCli.d.ts
CHANGED
|
@@ -3,5 +3,7 @@ export interface AuditCommandResult {
|
|
|
3
3
|
stdout: string;
|
|
4
4
|
exitCode: number;
|
|
5
5
|
}
|
|
6
|
+
/** The audit log: `file` if given, else `gateway.audit` from `config`. */
|
|
7
|
+
export declare function resolveAuditPath(file: string | undefined, config: string | undefined, usage: string): Promise<string>;
|
|
6
8
|
export declare function runAuditCommand(argv: string[]): Promise<AuditCommandResult>;
|
|
7
9
|
//# sourceMappingURL=auditCli.d.ts.map
|
package/dist/auditCli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auditCli.d.ts","sourceRoot":"","sources":["../src/auditCli.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW,kYAMO,CAAC;AAEhC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"auditCli.d.ts","sourceRoot":"","sources":["../src/auditCli.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW,kYAMO,CAAC;AAEhC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3H;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoBjF"}
|