@agenttrail/guardrails 0.2.0 → 0.2.1
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/dist/{chunk-3NDBRXP3.js → chunk-SLWHBH7H.js} +103 -26
- package/dist/chunk-SLWHBH7H.js.map +1 -0
- package/dist/guardrails.cjs +102 -25
- package/dist/guardrails.cjs.map +1 -1
- package/dist/guardrails.d.cts +8 -5
- package/dist/guardrails.d.ts +8 -5
- package/dist/guardrails.js +1 -1
- package/dist/index.cjs +102 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +44 -29
- package/dist/chunk-3NDBRXP3.js.map +0 -1
package/dist/guardrails.cjs
CHANGED
|
@@ -205,7 +205,7 @@ var acInstructionFileEdit = {
|
|
|
205
205
|
severity: "high",
|
|
206
206
|
defaultAction: "require_approval",
|
|
207
207
|
title: "The agent editing its standing instructions",
|
|
208
|
-
description: "Holds a file tool opening the instructions a coding agent loads at the start of every session: `CLAUDE.md`, `CLAUDE.local.md` and `.claude/rules/`; `AGENTS.md` and `AGENTS.override.md`, which Codex, Cursor, Windsurf, Copilot and Cline all read; `GEMINI.md`; `.cursorrules`; Windsurf's `.windsurfrules`, `.windsurf/rules/` and `.devin/rules/`; Cline's `.clinerules` file or directory and its global `Cline/Rules/` folder; Copilot's `.github/copilot-instructions.md` and `.github/instructions/**/*.instructions.md`; and `.aider.conf.yml`, which sets the files Aider reads on every launch. A line written into one of these is followed in every later session. Matched in any directory and in any letter case. File tools carry a path and no content, so it cannot see what was written
|
|
208
|
+
description: "Holds a file tool opening the instructions a coding agent loads at the start of every session: `CLAUDE.md`, `CLAUDE.local.md` and `.claude/rules/`; `AGENTS.md` and `AGENTS.override.md`, which Codex, Cursor, Windsurf, Copilot and Cline all read; `GEMINI.md`; `.cursorrules`; Windsurf's `.windsurfrules`, `.windsurf/rules/` and `.devin/rules/`; Cline's `.clinerules` file or directory and its global `Cline/Rules/` folder; Copilot's `.github/copilot-instructions.md` and `.github/instructions/**/*.instructions.md`; and `.aider.conf.yml`, which sets the files Aider reads on every launch. A line written into one of these is followed in every later session. Matched in any directory and in any letter case. Reading is excluded \u2014 the `Read` and `Grep` tools never match \u2014 so an agent opening its instructions to read them is not held; every other file tool is, including one this corpus does not know. File tools carry a path and no content, so it still cannot see what was written. Does not cover `.cursor/rules/`, which `fs.agent-self-config` holds. Misses a context file renamed through Gemini's `context.fileName` or Codex's `project_doc_fallback_filenames`, Aider's `CONVENTIONS.md`, which Aider loads only when asked and which is too common a name to match, and any of these files written by a shell command instead of a file tool.",
|
|
209
209
|
match: {
|
|
210
210
|
any_of: [
|
|
211
211
|
{
|
|
@@ -221,6 +221,11 @@ var acInstructionFileEdit = {
|
|
|
221
221
|
{ kind: "execute_tool", file_glob: "**/.github/copilot-instructions.md" },
|
|
222
222
|
{ kind: "execute_tool", file_glob: "**/.github/instructions/**/*.instructions.md" },
|
|
223
223
|
{ kind: "execute_tool", file_glob: "**/.aider.conf.yml" }
|
|
224
|
+
],
|
|
225
|
+
none_of: [
|
|
226
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
227
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
228
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
224
229
|
]
|
|
225
230
|
},
|
|
226
231
|
fixtures: {
|
|
@@ -255,7 +260,9 @@ var acInstructionFileEdit = {
|
|
|
255
260
|
file(".github/workflows/ci.yml"),
|
|
256
261
|
file(".cursor/rules/style.mdc"),
|
|
257
262
|
file(".claude/settings.json"),
|
|
258
|
-
file(".aider.chat.history.md")
|
|
263
|
+
file(".aider.chat.history.md"),
|
|
264
|
+
file("CLAUDE.md", "Read"),
|
|
265
|
+
file(".github/copilot-instructions.md", "Grep")
|
|
259
266
|
]
|
|
260
267
|
}
|
|
261
268
|
};
|
|
@@ -269,7 +276,7 @@ var acMcpServerAdd = {
|
|
|
269
276
|
severity: "high",
|
|
270
277
|
defaultAction: "require_approval",
|
|
271
278
|
title: "Adding an MCP server to an agent",
|
|
272
|
-
description: "Holds the commands that register an MCP server with a coding agent \u2014 `claude mcp add`, `claude mcp add-json`, `claude mcp add-from-claude-desktop`, `codex mcp add` and `gemini mcp add` \u2014 and `claude --mcp-config`, which attaches servers to a single session. Each gives an agent a new set of tools, often a program fetched and started on the spot, with nobody reviewing the change. Deliberately NOT matched: listing or removing servers (`claude mcp list`, `claude mcp remove`), and the MCP Inspector (`npx @modelcontextprotocol/inspector`), which is a debugging tool rather than a registration. Cursor has no command for this, so its `.cursor/mcp.json`, in a project or the home directory, is matched as a file instead; a project's `.mcp.json` is held by `fs.agent-self-config`. Misses servers written into `~/.claude.json` or Gemini's `settings.json` with a file tool, and a global flag whose value is quoted when it sits before `mcp`. A quoted MENTION is not a use: a search, a `git commit -m` message, an `echo` or a `curl --data` body that only names this command is left alone, as long as every shell metacharacter stays inside the quotes.",
|
|
279
|
+
description: "Holds the commands that register an MCP server with a coding agent \u2014 `claude mcp add`, `claude mcp add-json`, `claude mcp add-from-claude-desktop`, `codex mcp add` and `gemini mcp add` \u2014 and `claude --mcp-config`, which attaches servers to a single session. Each gives an agent a new set of tools, often a program fetched and started on the spot, with nobody reviewing the change. Deliberately NOT matched: listing or removing servers (`claude mcp list`, `claude mcp remove`), and the MCP Inspector (`npx @modelcontextprotocol/inspector`), which is a debugging tool rather than a registration. Cursor has no command for this, so its `.cursor/mcp.json`, in a project or the home directory, is matched as a file instead \u2014 written, not read: the `Read` and `Grep` tools never match it, and every other file tool does, including one this corpus does not know; a project's `.mcp.json` is held by `fs.agent-self-config`. Misses servers written into `~/.claude.json` or Gemini's `settings.json` with a file tool, and a global flag whose value is quoted when it sits before `mcp`. A quoted MENTION is not a use: a search, a `git commit -m` message, an `echo` or a `curl --data` body that only names this command is left alone, as long as every shell metacharacter stays inside the quotes.",
|
|
273
280
|
match: {
|
|
274
281
|
any_of: [
|
|
275
282
|
{
|
|
@@ -284,7 +291,14 @@ var acMcpServerAdd = {
|
|
|
284
291
|
// Cursor has no `mcp add` command; its servers are added by editing this file.
|
|
285
292
|
{ kind: "execute_tool", file_glob: "**/.cursor/mcp.json" }
|
|
286
293
|
],
|
|
287
|
-
none_of: [
|
|
294
|
+
none_of: [
|
|
295
|
+
// A read is not an addition. This covers the `.cursor/mcp.json` arm — the
|
|
296
|
+
// shell arm already pins its own tool family. Named as a deny-list rather
|
|
297
|
+
// than an `any_of` over the write tools, so an unknown file tool on that
|
|
298
|
+
// path still holds.
|
|
299
|
+
{ kind: "execute_tool", label: "{Read,Grep}" },
|
|
300
|
+
...QUOTED_MENTION
|
|
301
|
+
]
|
|
288
302
|
},
|
|
289
303
|
fixtures: {
|
|
290
304
|
block: [
|
|
@@ -309,7 +323,9 @@ var acMcpServerAdd = {
|
|
|
309
323
|
bash("npx @modelcontextprotocol/inspector"),
|
|
310
324
|
file("config/mcp.json"),
|
|
311
325
|
file(".cursor/rules/style.mdc"),
|
|
312
|
-
bash("pnpm run test")
|
|
326
|
+
bash("pnpm run test"),
|
|
327
|
+
file(".cursor/mcp.json", "Read"),
|
|
328
|
+
file("/Users/dev/.cursor/mcp.json", "Grep")
|
|
313
329
|
]
|
|
314
330
|
}
|
|
315
331
|
};
|
|
@@ -321,7 +337,7 @@ var acMemoryStoreEdit = {
|
|
|
321
337
|
severity: "high",
|
|
322
338
|
defaultAction: "require_approval",
|
|
323
339
|
title: "The agent editing its own memory",
|
|
324
|
-
description: "Holds a file tool opening the memory a coding agent carries between sessions: Claude Code's auto memory under `.claude/projects/<project>/memory/`, and its sub-agent memory in `.claude/agent-memory/` and `.claude/agent-memory-local/`; Codex's `.codex/memories/`; Gemini's private `.gemini/tmp/<project>/memory/`; Windsurf's `.codeium/windsurf/memories/`, including `global_rules.md`; and `.cursor/memory/`. A false fact saved here is recalled as true in every later session. A `MEMORY.md` outside those directories deliberately does NOT match, and neither does a project's own `docs/memory/` folder: the name alone is not an agent's memory.
|
|
340
|
+
description: "Holds a file tool opening the memory a coding agent carries between sessions: Claude Code's auto memory under `.claude/projects/<project>/memory/`, and its sub-agent memory in `.claude/agent-memory/` and `.claude/agent-memory-local/`; Codex's `.codex/memories/`; Gemini's private `.gemini/tmp/<project>/memory/`; Windsurf's `.codeium/windsurf/memories/`, including `global_rules.md`; and `.cursor/memory/`. A false fact saved here is recalled as true in every later session. A `MEMORY.md` outside those directories deliberately does NOT match, and neither does a project's own `docs/memory/` folder: the name alone is not an agent's memory. Reading is excluded \u2014 the `Read` and `Grep` tools never match \u2014 so an agent recalling a memory by reading its file is not asked; every other file tool is, including one this corpus does not know. File tools carry a path and no content, so it still cannot see what was written. Misses a memory directory moved with Claude Code's `autoMemoryDirectory` setting, and Cursor memories kept anywhere other than `.cursor/memory/`, since Cursor does not document where it stores them. Gemini memories saved into `GEMINI.md` are held by `ac.instruction-file-edit` instead.",
|
|
325
341
|
match: {
|
|
326
342
|
any_of: [
|
|
327
343
|
{ kind: "execute_tool", file_glob: "**/.claude/projects/*/memory/**" },
|
|
@@ -330,6 +346,11 @@ var acMemoryStoreEdit = {
|
|
|
330
346
|
{ kind: "execute_tool", file_glob: "**/.gemini/tmp/*/memory/**" },
|
|
331
347
|
{ kind: "execute_tool", file_glob: "**/.codeium/windsurf/memories/**" },
|
|
332
348
|
{ kind: "execute_tool", file_glob: "**/.cursor/memory/**" }
|
|
349
|
+
],
|
|
350
|
+
none_of: [
|
|
351
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
352
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
353
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
333
354
|
]
|
|
334
355
|
},
|
|
335
356
|
fixtures: {
|
|
@@ -353,7 +374,9 @@ var acMemoryStoreEdit = {
|
|
|
353
374
|
file("packages/memory-store/src/index.ts"),
|
|
354
375
|
file("/Users/dev/.claude/projects/-Users-dev-shop/transcript.jsonl"),
|
|
355
376
|
file("/home/dev/.gemini/settings.json"),
|
|
356
|
-
file(".claude/settings.json")
|
|
377
|
+
file(".claude/settings.json"),
|
|
378
|
+
file(".claude/agent-memory/reviewer/MEMORY.md", "Read"),
|
|
379
|
+
file("/home/dev/.codex/memories/project.md", "Grep")
|
|
357
380
|
]
|
|
358
381
|
}
|
|
359
382
|
};
|
|
@@ -423,7 +446,7 @@ var acSkillInstall = {
|
|
|
423
446
|
severity: "high",
|
|
424
447
|
defaultAction: "require_approval",
|
|
425
448
|
title: "The agent installing a skill, command or sub-agent",
|
|
426
|
-
description: "Holds a file tool opening a skill, slash command, sub-agent or output style that a coding agent loads by name: Claude Code's `.claude/skills/`, `.claude/commands/`, `.claude/agents/` and `.claude/output-styles/`; the shared `.agents/skills/`; Codex's `.codex/skills/`, `.codex/prompts/` and `.codex/agents/`; Gemini's `.gemini/commands/`, `.gemini/skills/` and `.gemini/agents/`; Cursor's `.cursor/skills/`, `.cursor/agents/` and `.cursor/commands/`; Windsurf's `.windsurf/workflows/` and `.windsurf/skills/` and their global copies under `.codeium/windsurf/`; Cline's `.cline/skills/`; and a `SKILL.md` anywhere, which is how a plugin ships a skill. Each becomes a reusable instruction the agent may follow later, often with a script beside it that the agent runs. Matched at project or home level and in any letter case. File tools carry a path and no content, so it cannot see what was written
|
|
449
|
+
description: "Holds a file tool opening a skill, slash command, sub-agent or output style that a coding agent loads by name: Claude Code's `.claude/skills/`, `.claude/commands/`, `.claude/agents/` and `.claude/output-styles/`; the shared `.agents/skills/`; Codex's `.codex/skills/`, `.codex/prompts/` and `.codex/agents/`; Gemini's `.gemini/commands/`, `.gemini/skills/` and `.gemini/agents/`; Cursor's `.cursor/skills/`, `.cursor/agents/` and `.cursor/commands/`; Windsurf's `.windsurf/workflows/` and `.windsurf/skills/` and their global copies under `.codeium/windsurf/`; Cline's `.cline/skills/`; and a `SKILL.md` anywhere, which is how a plugin ships a skill. Each becomes a reusable instruction the agent may follow later, often with a script beside it that the agent runs. Matched at project or home level and in any letter case. Reading is excluded \u2014 the `Read` and `Grep` tools never match \u2014 so an agent opening an installed skill to read it is not held; every other file tool is, including one this corpus does not know. File tools carry a path and no content, so it still cannot see what was written. Deliberately NOT matched: an ordinary `skills/`, `commands/` or `agents/` folder in a project's source, and `.clinerules/skills/`, which `ac.instruction-file-edit` holds. Misses a skill copied into one of these folders by a shell command such as `cp` or `git clone`, and the managed system-wide workflow folders.",
|
|
427
450
|
match: {
|
|
428
451
|
any_of: [
|
|
429
452
|
{ kind: "execute_tool", file_glob: "**/.claude/{skills,commands,agents,output-styles}/**" },
|
|
@@ -435,6 +458,11 @@ var acSkillInstall = {
|
|
|
435
458
|
{ kind: "execute_tool", file_glob: "**/.codeium/windsurf/{skills,global_workflows}/**" },
|
|
436
459
|
{ kind: "execute_tool", file_glob: "**/.cline/skills/**" },
|
|
437
460
|
{ kind: "execute_tool", file_glob: "**/SKILL.md" }
|
|
461
|
+
],
|
|
462
|
+
none_of: [
|
|
463
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
464
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
465
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
438
466
|
]
|
|
439
467
|
},
|
|
440
468
|
fixtures: {
|
|
@@ -464,7 +492,9 @@ var acSkillInstall = {
|
|
|
464
492
|
file(".claude/settings.json"),
|
|
465
493
|
file(".github/workflows/release.yml"),
|
|
466
494
|
file("scripts/release.sh"),
|
|
467
|
-
file(".gemini/settings.json")
|
|
495
|
+
file(".gemini/settings.json"),
|
|
496
|
+
file(".claude/skills/deploy/SKILL.md", "Read"),
|
|
497
|
+
file(".claude/commands/deploy.md", "Grep")
|
|
468
498
|
]
|
|
469
499
|
}
|
|
470
500
|
};
|
|
@@ -1056,7 +1086,7 @@ var fsAgentSelfConfig = {
|
|
|
1056
1086
|
severity: "high",
|
|
1057
1087
|
defaultAction: "require_approval",
|
|
1058
1088
|
title: "The agent editing its own configuration",
|
|
1059
|
-
description: "Holds a file tool opening the files that define what the agent itself is allowed to do \u2014 Claude Code settings and hooks, an MCP server list, a plugin manifest, Cursor rules, a Codex config, and the guard's own config.json and guardrails.json. An agent that can edit these can widen its own reach with nobody reviewing the change. Does NOT match ordinary project files, or `.claude/commands/*.md`, which are prompts rather than permissions. Bounded to WELL-KNOWN paths: no working directory or project root reaches the guard, so it can only match names it already knows.",
|
|
1089
|
+
description: "Holds a file tool opening the files that define what the agent itself is allowed to do \u2014 Claude Code settings and hooks, an MCP server list, a plugin manifest, Cursor rules, a Codex config, and the guard's own config.json and guardrails.json. An agent that can edit these can widen its own reach with nobody reviewing the change. Reading is excluded \u2014 the `Read` and `Grep` tools never match \u2014 so opening one of these files to read it is not held; every other file tool is, including one this corpus does not know. Does NOT match ordinary project files, or `.claude/commands/*.md`, which are prompts rather than permissions. Bounded to WELL-KNOWN paths: no working directory or project root reaches the guard, so it can only match names it already knows.",
|
|
1060
1090
|
match: {
|
|
1061
1091
|
any_of: [
|
|
1062
1092
|
{ kind: "execute_tool", file_glob: "**/.claude/settings*.json" },
|
|
@@ -1067,6 +1097,11 @@ var fsAgentSelfConfig = {
|
|
|
1067
1097
|
{ kind: "execute_tool", file_glob: "**/.codex/config.toml" },
|
|
1068
1098
|
{ kind: "execute_tool", file_glob: "**/.agenttrail/guard/config.json" },
|
|
1069
1099
|
{ kind: "execute_tool", file_glob: "**/.agenttrail/guard/guardrails.json" }
|
|
1100
|
+
],
|
|
1101
|
+
none_of: [
|
|
1102
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
1103
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
1104
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
1070
1105
|
]
|
|
1071
1106
|
},
|
|
1072
1107
|
fixtures: {
|
|
@@ -1080,7 +1115,9 @@ var fsAgentSelfConfig = {
|
|
|
1080
1115
|
file(".claude/commands/deploy.md"),
|
|
1081
1116
|
file("src/index.ts"),
|
|
1082
1117
|
file("package.json"),
|
|
1083
|
-
file("README.md")
|
|
1118
|
+
file("README.md"),
|
|
1119
|
+
file(".claude/settings.json", "Read"),
|
|
1120
|
+
file(".mcp.json", "Grep")
|
|
1084
1121
|
]
|
|
1085
1122
|
}
|
|
1086
1123
|
};
|
|
@@ -1092,7 +1129,7 @@ var fsCiDefinition = {
|
|
|
1092
1129
|
severity: "medium",
|
|
1093
1130
|
defaultAction: "require_approval",
|
|
1094
1131
|
title: "Editing the CI pipeline definition",
|
|
1095
|
-
description: "Holds a file tool opening a CI definition \u2014 GitHub Actions workflows and composite actions, .gitlab-ci.yml, a Jenkinsfile, CircleCI, Azure Pipelines, Buildkite or Bitbucket pipelines. This is where the checks that gate every merge are written down, and where a new step would run with the repository's secrets; both edits look like an ordinary diff. Does NOT match other files under .github/ (CODEOWNERS, issue templates), which gate nothing. It MISSES a CI system whose definition lives outside the repository.",
|
|
1132
|
+
description: "Holds a file tool opening a CI definition \u2014 GitHub Actions workflows and composite actions, .gitlab-ci.yml, a Jenkinsfile, CircleCI, Azure Pipelines, Buildkite or Bitbucket pipelines. This is where the checks that gate every merge are written down, and where a new step would run with the repository's secrets; both edits look like an ordinary diff. Reading is excluded \u2014 the Read and Grep tools never match \u2014 so opening a CI definition to read it is not held; every other file tool is, including one this corpus does not know. Does NOT match other files under .github/ (CODEOWNERS, issue templates), which gate nothing. It MISSES a CI system whose definition lives outside the repository.",
|
|
1096
1133
|
match: {
|
|
1097
1134
|
any_of: [
|
|
1098
1135
|
{ kind: "execute_tool", file_glob: "**/.github/workflows/**" },
|
|
@@ -1103,6 +1140,11 @@ var fsCiDefinition = {
|
|
|
1103
1140
|
{ kind: "execute_tool", file_glob: "**/azure-pipelines.yml" },
|
|
1104
1141
|
{ kind: "execute_tool", file_glob: "**/.buildkite/**" },
|
|
1105
1142
|
{ kind: "execute_tool", file_glob: "**/bitbucket-pipelines.yml" }
|
|
1143
|
+
],
|
|
1144
|
+
none_of: [
|
|
1145
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
1146
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
1147
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
1106
1148
|
]
|
|
1107
1149
|
},
|
|
1108
1150
|
fixtures: {
|
|
@@ -1116,7 +1158,9 @@ var fsCiDefinition = {
|
|
|
1116
1158
|
file(".github/CODEOWNERS"),
|
|
1117
1159
|
file(".github/PULL_REQUEST_TEMPLATE.md"),
|
|
1118
1160
|
file("package.json"),
|
|
1119
|
-
file("README.md")
|
|
1161
|
+
file("README.md"),
|
|
1162
|
+
file(".github/workflows/ci.yml", "Read"),
|
|
1163
|
+
file(".gitlab-ci.yml", "Grep")
|
|
1120
1164
|
]
|
|
1121
1165
|
}
|
|
1122
1166
|
};
|
|
@@ -1128,7 +1172,7 @@ var fsSystemPaths = {
|
|
|
1128
1172
|
severity: "high",
|
|
1129
1173
|
defaultAction: "require_approval",
|
|
1130
1174
|
title: "Writing to a system directory",
|
|
1131
|
-
description: "Holds a file tool
|
|
1175
|
+
description: "Holds a file tool writing to a path under a system directory \u2014 /etc, /bin, /sbin, /usr/bin, /usr/local/bin, /boot, /System, /Library/LaunchDaemons, or Windows/System32. Reading is excluded \u2014 the Read and Grep tools never match \u2014 so reading /etc/hosts is not held; every other file tool is, including one this corpus does not know. HONEST CEILING: this is a list of well-known ABSOLUTE paths and it cannot be anything else. No working directory and no project root reaches the guard, so the rule you would actually want \u2014 'the agent wrote outside the project' \u2014 is inexpressible, and would match everything or nothing. It therefore MISSES a write anywhere else outside your repository, including another project on the same machine.",
|
|
1132
1176
|
match: {
|
|
1133
1177
|
any_of: [
|
|
1134
1178
|
{ kind: "execute_tool", file_glob: "/etc/**" },
|
|
@@ -1140,6 +1184,11 @@ var fsSystemPaths = {
|
|
|
1140
1184
|
{ kind: "execute_tool", file_glob: "/System/**" },
|
|
1141
1185
|
{ kind: "execute_tool", file_glob: "/Library/LaunchDaemons/**" },
|
|
1142
1186
|
{ kind: "execute_tool", file_glob: "**/Windows/System32/**" }
|
|
1187
|
+
],
|
|
1188
|
+
none_of: [
|
|
1189
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
1190
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
1191
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
1143
1192
|
]
|
|
1144
1193
|
},
|
|
1145
1194
|
fixtures: {
|
|
@@ -1153,7 +1202,9 @@ var fsSystemPaths = {
|
|
|
1153
1202
|
file("src/index.ts"),
|
|
1154
1203
|
file("/home/dev/project/src/main.rs"),
|
|
1155
1204
|
file("/tmp/scratch.txt", "Write"),
|
|
1156
|
-
file("docs/etc-notes.md")
|
|
1205
|
+
file("docs/etc-notes.md"),
|
|
1206
|
+
file("/etc/hosts", "Read"),
|
|
1207
|
+
file("/usr/local/bin/app", "Grep")
|
|
1157
1208
|
]
|
|
1158
1209
|
}
|
|
1159
1210
|
};
|
|
@@ -1165,7 +1216,7 @@ var fsVcsInternals = {
|
|
|
1165
1216
|
severity: "high",
|
|
1166
1217
|
defaultAction: "require_approval",
|
|
1167
1218
|
title: "Editing git's internals directly",
|
|
1168
|
-
description: "Holds a file tool opening git's own bookkeeping \u2014 .git/config, .git/hooks/, .git/refs/, .git/HEAD, .git/info/exclude \u2014 where a change alters what future git commands do rather than what the repository contains. Deliberately NARROW: all of .git/** would include COMMIT_EDITMSG and the index, which change during every ordinary commit, so the rule would fire constantly and be switched off. It does NOT match .gitignore, .gitattributes or anything under .github/, which are tracked project files.",
|
|
1219
|
+
description: "Holds a file tool opening git's own bookkeeping \u2014 .git/config, .git/hooks/, .git/refs/, .git/HEAD, .git/info/exclude \u2014 where a change alters what future git commands do rather than what the repository contains. Deliberately NARROW: all of .git/** would include COMMIT_EDITMSG and the index, which change during every ordinary commit, so the rule would fire constantly and be switched off. Reading is excluded \u2014 the Read and Grep tools never match \u2014 so opening .git/config to read it is not held; every other file tool is, including one this corpus does not know. It does NOT match .gitignore, .gitattributes or anything under .github/, which are tracked project files.",
|
|
1169
1220
|
match: {
|
|
1170
1221
|
any_of: [
|
|
1171
1222
|
{ kind: "execute_tool", file_glob: "**/.git/config" },
|
|
@@ -1173,6 +1224,11 @@ var fsVcsInternals = {
|
|
|
1173
1224
|
{ kind: "execute_tool", file_glob: "**/.git/refs/**" },
|
|
1174
1225
|
{ kind: "execute_tool", file_glob: "**/.git/HEAD" },
|
|
1175
1226
|
{ kind: "execute_tool", file_glob: "**/.git/info/exclude" }
|
|
1227
|
+
],
|
|
1228
|
+
none_of: [
|
|
1229
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
1230
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
1231
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
1176
1232
|
]
|
|
1177
1233
|
},
|
|
1178
1234
|
fixtures: {
|
|
@@ -1186,7 +1242,9 @@ var fsVcsInternals = {
|
|
|
1186
1242
|
file(".gitignore"),
|
|
1187
1243
|
file(".gitattributes"),
|
|
1188
1244
|
file(".github/CODEOWNERS"),
|
|
1189
|
-
file("src/index.ts")
|
|
1245
|
+
file("src/index.ts"),
|
|
1246
|
+
file(".git/config", "Read"),
|
|
1247
|
+
file(".git/hooks/pre-commit", "Grep")
|
|
1190
1248
|
]
|
|
1191
1249
|
}
|
|
1192
1250
|
};
|
|
@@ -1497,7 +1555,7 @@ var blockProdConfigEdit = {
|
|
|
1497
1555
|
severity: "high",
|
|
1498
1556
|
defaultAction: "require_approval",
|
|
1499
1557
|
title: "Approve production config edits",
|
|
1500
|
-
description: "Routes edits to production configuration files to human approval. Matched by path: `*.prod.*` and `*.production.*`, the bare `prod.*` / `production.*` spellings, and any file under a `prod/` or `production/` directory. Prose is excluded \u2014 `.md`, `.mdx` and `.txt` never match \u2014 so writing a runbook under `docs/production/` does not ask for approval to change production. It matches the PATH only: it cannot tell a real production config from a file that merely spells prod in its name, and it MISSES a production config named something else entirely, such as `values-live.yaml`.",
|
|
1558
|
+
description: "Routes edits to production configuration files to human approval. Matched by path: `*.prod.*` and `*.production.*`, the bare `prod.*` / `production.*` spellings, and any file under a `prod/` or `production/` directory. Reading is excluded \u2014 the `Read` and `Grep` tools never match \u2014 so opening a production config to look at it does not ask for approval; every other file tool does, including one this corpus does not know. Prose is excluded too \u2014 `.md`, `.mdx` and `.txt` never match \u2014 so writing a runbook under `docs/production/` does not ask for approval to change production. It matches the PATH only: it cannot tell a real production config from a file that merely spells prod in its name, and it MISSES a production config named something else entirely, such as `values-live.yaml`.",
|
|
1501
1559
|
match: {
|
|
1502
1560
|
any_of: [
|
|
1503
1561
|
{ kind: "execute_tool", file_glob: "**/*.prod.*" },
|
|
@@ -1508,6 +1566,9 @@ var blockProdConfigEdit = {
|
|
|
1508
1566
|
{ kind: "execute_tool", file_glob: "**/production/**" }
|
|
1509
1567
|
],
|
|
1510
1568
|
none_of: [
|
|
1569
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
1570
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
1571
|
+
{ kind: "execute_tool", label: "{Read,Grep}" },
|
|
1511
1572
|
{ kind: "execute_tool", file_glob: "**/*.md" },
|
|
1512
1573
|
{ kind: "execute_tool", file_glob: "**/*.mdx" },
|
|
1513
1574
|
{ kind: "execute_tool", file_glob: "**/*.txt" }
|
|
@@ -1525,7 +1586,9 @@ var blockProdConfigEdit = {
|
|
|
1525
1586
|
file("docs/production/README.md"),
|
|
1526
1587
|
file("config/database.dev.yml"),
|
|
1527
1588
|
file("src/index.ts"),
|
|
1528
|
-
file("package.json")
|
|
1589
|
+
file("package.json"),
|
|
1590
|
+
file("config/database.prod.yml", "Read"),
|
|
1591
|
+
file("k8s/production/deployment.yaml", "Grep")
|
|
1529
1592
|
]
|
|
1530
1593
|
}
|
|
1531
1594
|
};
|
|
@@ -2498,7 +2561,7 @@ var requireAuthOnPiiEndpoints = {
|
|
|
2498
2561
|
severity: "medium",
|
|
2499
2562
|
defaultAction: "require_approval",
|
|
2500
2563
|
title: "Review API endpoint changes for auth",
|
|
2501
|
-
description: "Routes edits to API route/handler SOURCE files to human approval so a reviewer can confirm authentication is present on new or changed endpoints. Matched by path: a source file under routes/, handlers/ or controllers/; an api/ directory nested inside a source tree (src/api/, app/api/, pages/api/); Next's route.ts convention; and the <name>.controller.* / <name>.routes.* spellings. HEURISTIC: a path signal only \u2014 it cannot inspect the edit for a missing auth check or exposed PII, so treat a match as confirm auth on this endpoint, not as a finding. It deliberately does NOT match every file in a package merely NAMED api, nor a client-side router table such as routes.tsx, which defines no endpoint; and it MISSES endpoints declared inline in a server file or by a framework convention not listed above.",
|
|
2564
|
+
description: "Routes edits to API route/handler SOURCE files to human approval so a reviewer can confirm authentication is present on new or changed endpoints. Matched by path: a source file under routes/, handlers/ or controllers/; an api/ directory nested inside a source tree (src/api/, app/api/, pages/api/); Next's route.ts convention; and the <name>.controller.* / <name>.routes.* spellings. Reading is excluded \u2014 the Read and Grep tools never match \u2014 so opening an endpoint's source to read it does not ask for approval; every other file tool does, including one this corpus does not know. HEURISTIC: a path signal only \u2014 it cannot inspect the edit for a missing auth check or exposed PII, so treat a match as confirm auth on this endpoint, not as a finding. It deliberately does NOT match every file in a package merely NAMED api, nor a client-side router table such as routes.tsx, which defines no endpoint; and it MISSES endpoints declared inline in a server file or by a framework convention not listed above.",
|
|
2502
2565
|
match: {
|
|
2503
2566
|
any_of: [
|
|
2504
2567
|
{ kind: "execute_tool", file_glob: `**/routes/**/*.${SOURCE}` },
|
|
@@ -2510,6 +2573,11 @@ var requireAuthOnPiiEndpoints = {
|
|
|
2510
2573
|
{ kind: "execute_tool", file_glob: `**/route.${SOURCE}` },
|
|
2511
2574
|
{ kind: "execute_tool", file_glob: `**/*.controller.${SOURCE}` },
|
|
2512
2575
|
{ kind: "execute_tool", file_glob: `**/*.routes.${SOURCE}` }
|
|
2576
|
+
],
|
|
2577
|
+
none_of: [
|
|
2578
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
2579
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
2580
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
2513
2581
|
]
|
|
2514
2582
|
},
|
|
2515
2583
|
fixtures: {
|
|
@@ -2523,7 +2591,9 @@ var requireAuthOnPiiEndpoints = {
|
|
|
2523
2591
|
file("apps/api/README.md"),
|
|
2524
2592
|
file("apps/api/package.json"),
|
|
2525
2593
|
file("apps/api/src/lib/logger.ts"),
|
|
2526
|
-
file("apps/web/src/app/routes.tsx")
|
|
2594
|
+
file("apps/web/src/app/routes.tsx"),
|
|
2595
|
+
file("src/api/users.ts", "Read"),
|
|
2596
|
+
file("src/controllers/payments.ts", "Grep")
|
|
2527
2597
|
]
|
|
2528
2598
|
}
|
|
2529
2599
|
};
|
|
@@ -3080,7 +3150,7 @@ var tiTestConfigEdit = {
|
|
|
3080
3150
|
severity: "medium",
|
|
3081
3151
|
defaultAction: "require_approval",
|
|
3082
3152
|
title: "Editing a test runner or coverage configuration",
|
|
3083
|
-
description: "Holds a file tool opening a test runner's or coverage tool's own configuration: Jest's `jest.config.*`, Vitest's `vitest.config.*` and legacy `vitest.workspace.*`, `pytest.ini`, `pytest.toml` and their dotted forms, `tox.ini`, Mocha's `.mocharc.*`, PHPUnit's `phpunit.xml`, `phpunit.xml.dist` and `phpunit.dist.xml`, `codecov.yml`, nyc's `.nycrc*` and `nyc.config.*`, `.c8rc`, coverage.py's `.coveragerc`, `karma.conf.*`, and the Playwright and Cypress configs. One line in any of these can exclude a failing file, lower a coverage threshold or retry a flaky test until it passes. File tools carry a path and no content, so it cannot tell a harmless edit from a weakening one
|
|
3153
|
+
description: "Holds a file tool opening a test runner's or coverage tool's own configuration: Jest's `jest.config.*`, Vitest's `vitest.config.*` and legacy `vitest.workspace.*`, `pytest.ini`, `pytest.toml` and their dotted forms, `tox.ini`, Mocha's `.mocharc.*`, PHPUnit's `phpunit.xml`, `phpunit.xml.dist` and `phpunit.dist.xml`, `codecov.yml`, nyc's `.nycrc*` and `nyc.config.*`, `.c8rc`, coverage.py's `.coveragerc`, `karma.conf.*`, and the Playwright and Cypress configs. One line in any of these can exclude a failing file, lower a coverage threshold or retry a flaky test until it passes. Reading is excluded \u2014 the `Read` and `Grep` tools never match \u2014 so opening one of these configs to read it is not held; every other file tool is, including one this corpus does not know. File tools carry a path and no content, so it still cannot tell a harmless edit from a weakening one. Deliberately NOT matched: general files that can also hold test settings \u2014 `pyproject.toml`, `setup.cfg`, `package.json`, `vite.config.*` \u2014 and test files themselves, since editing a test is how a test gets fixed. Misses test settings kept in those general files, and a config at a path passed with `--config`.",
|
|
3084
3154
|
match: {
|
|
3085
3155
|
any_of: [
|
|
3086
3156
|
{ kind: "execute_tool", file_glob: "**/jest.config.{js,ts,mjs,mts,cjs,cts,json}" },
|
|
@@ -3105,6 +3175,11 @@ var tiTestConfigEdit = {
|
|
|
3105
3175
|
kind: "execute_tool",
|
|
3106
3176
|
file_glob: "**/{playwright,cypress}.config.{js,ts,mjs,mts,cjs,cts}"
|
|
3107
3177
|
}
|
|
3178
|
+
],
|
|
3179
|
+
none_of: [
|
|
3180
|
+
// A read is not an edit. Named as a deny-list rather than an `any_of` over
|
|
3181
|
+
// the write tools, so an unknown file tool on a matched path still holds.
|
|
3182
|
+
{ kind: "execute_tool", label: "{Read,Grep}" }
|
|
3108
3183
|
]
|
|
3109
3184
|
},
|
|
3110
3185
|
fixtures: {
|
|
@@ -3137,7 +3212,9 @@ var tiTestConfigEdit = {
|
|
|
3137
3212
|
file("jest.setup.js"),
|
|
3138
3213
|
file(".env.test"),
|
|
3139
3214
|
file("docs/testing.md"),
|
|
3140
|
-
file("biome.json")
|
|
3215
|
+
file("biome.json"),
|
|
3216
|
+
file("vitest.config.ts", "Read"),
|
|
3217
|
+
file("jest.config.js", "Grep")
|
|
3141
3218
|
]
|
|
3142
3219
|
}
|
|
3143
3220
|
};
|
|
@@ -3623,8 +3700,8 @@ function isPack(value) {
|
|
|
3623
3700
|
}
|
|
3624
3701
|
|
|
3625
3702
|
// src/stamp.ts
|
|
3626
|
-
var CATALOG_VERSION = "0.2.
|
|
3627
|
-
var CATALOG_PUBLISHED_AT = "2026-09-
|
|
3703
|
+
var CATALOG_VERSION = "0.2.1";
|
|
3704
|
+
var CATALOG_PUBLISHED_AT = "2026-09-29T03:49:22Z";
|
|
3628
3705
|
|
|
3629
3706
|
// src/rules.ts
|
|
3630
3707
|
var RULES_BY_PACK = {
|