@orygn/opa-mcp 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +62 -23
  3. package/dist/config.d.ts +1 -1
  4. package/dist/config.js +2 -2
  5. package/dist/constants.d.ts +8 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/constants.js +8 -0
  8. package/dist/constants.js.map +1 -0
  9. package/dist/lib/errors.d.ts +1 -1
  10. package/dist/lib/logger.js +2 -2
  11. package/dist/lib/logger.js.map +1 -1
  12. package/dist/lib/opa-cli.d.ts +3 -3
  13. package/dist/lib/opa-cli.js +4 -4
  14. package/dist/lib/opa-client.d.ts +1 -1
  15. package/dist/lib/regal-cli.d.ts +4 -4
  16. package/dist/lib/regal-cli.js +5 -5
  17. package/dist/lib/subprocess.d.ts +1 -1
  18. package/dist/lib/subprocess.js +2 -2
  19. package/dist/lib/tool-helpers.d.ts +1 -1
  20. package/dist/prompts/index.js +6 -6
  21. package/dist/prompts/index.js.map +1 -1
  22. package/dist/resources/index.d.ts +4 -4
  23. package/dist/resources/patterns.d.ts +1 -1
  24. package/dist/resources/patterns.d.ts.map +1 -1
  25. package/dist/resources/patterns.js +7 -7
  26. package/dist/resources/style-guide.d.ts +1 -1
  27. package/dist/resources/style-guide.d.ts.map +1 -1
  28. package/dist/resources/style-guide.js +3 -3
  29. package/dist/server.d.ts +2 -2
  30. package/dist/server.d.ts.map +1 -1
  31. package/dist/server.js +4 -4
  32. package/dist/server.js.map +1 -1
  33. package/dist/tools/authoring/capabilities.d.ts +2 -0
  34. package/dist/tools/authoring/capabilities.d.ts.map +1 -1
  35. package/dist/tools/authoring/capabilities.js +24 -4
  36. package/dist/tools/authoring/capabilities.js.map +1 -1
  37. package/dist/tools/authoring/check.js +3 -3
  38. package/dist/tools/authoring/check.js.map +1 -1
  39. package/dist/tools/authoring/deps.js +2 -2
  40. package/dist/tools/authoring/deps.js.map +1 -1
  41. package/dist/tools/authoring/format.js +1 -1
  42. package/dist/tools/authoring/index.d.ts +1 -1
  43. package/dist/tools/authoring/inspect.js +2 -2
  44. package/dist/tools/authoring/inspect.js.map +1 -1
  45. package/dist/tools/authoring/lint.js +1 -1
  46. package/dist/tools/authoring/parse.js +1 -1
  47. package/dist/tools/bundles/build.js +1 -1
  48. package/dist/tools/bundles/index.d.ts +1 -1
  49. package/dist/tools/bundles/sign.js +1 -1
  50. package/dist/tools/evaluation/_shared.d.ts +1 -1
  51. package/dist/tools/evaluation/_shared.js +1 -1
  52. package/dist/tools/evaluation/bench.js +1 -1
  53. package/dist/tools/evaluation/compile.d.ts +2 -2
  54. package/dist/tools/evaluation/compile.js +1 -1
  55. package/dist/tools/evaluation/compile.js.map +1 -1
  56. package/dist/tools/evaluation/eval.d.ts +2 -2
  57. package/dist/tools/evaluation/eval.js +1 -1
  58. package/dist/tools/evaluation/eval.js.map +1 -1
  59. package/dist/tools/evaluation/index.d.ts +1 -1
  60. package/dist/tools/evaluation/test.js +1 -1
  61. package/dist/tools/helpers/coverage-gaps.d.ts +29 -0
  62. package/dist/tools/helpers/coverage-gaps.d.ts.map +1 -0
  63. package/dist/tools/helpers/coverage-gaps.js +125 -0
  64. package/dist/tools/helpers/coverage-gaps.js.map +1 -0
  65. package/dist/tools/helpers/describe-policy.js +1 -1
  66. package/dist/tools/helpers/explain-decision.d.ts +1 -1
  67. package/dist/tools/helpers/explain-decision.js +1 -1
  68. package/dist/tools/helpers/explain-decision.js.map +1 -1
  69. package/dist/tools/helpers/generate-test-skeleton.js +3 -3
  70. package/dist/tools/helpers/generate-test-skeleton.js.map +1 -1
  71. package/dist/tools/helpers/index.d.ts +1 -1
  72. package/dist/tools/helpers/index.d.ts.map +1 -1
  73. package/dist/tools/helpers/index.js +4 -0
  74. package/dist/tools/helpers/index.js.map +1 -1
  75. package/dist/tools/helpers/security-audit.d.ts +21 -0
  76. package/dist/tools/helpers/security-audit.d.ts.map +1 -0
  77. package/dist/tools/helpers/security-audit.js +111 -0
  78. package/dist/tools/helpers/security-audit.js.map +1 -0
  79. package/dist/tools/helpers/suggest-fix.js +6 -6
  80. package/dist/tools/helpers/suggest-fix.js.map +1 -1
  81. package/dist/tools/index.d.ts +9 -6
  82. package/dist/tools/index.d.ts.map +1 -1
  83. package/dist/tools/index.js +2 -0
  84. package/dist/tools/index.js.map +1 -1
  85. package/dist/tools/meta/index.d.ts +10 -0
  86. package/dist/tools/meta/index.d.ts.map +1 -0
  87. package/dist/tools/meta/index.js +5 -0
  88. package/dist/tools/meta/index.js.map +1 -0
  89. package/dist/tools/meta/server-info.d.ts +17 -0
  90. package/dist/tools/meta/server-info.d.ts.map +1 -0
  91. package/dist/tools/meta/server-info.js +30 -0
  92. package/dist/tools/meta/server-info.js.map +1 -0
  93. package/dist/tools/server-management/data.js +1 -1
  94. package/dist/tools/server-management/data.js.map +1 -1
  95. package/dist/tools/server-management/decisions.js +1 -1
  96. package/dist/tools/server-management/decisions.js.map +1 -1
  97. package/dist/tools/server-management/index.d.ts +1 -1
  98. package/dist/tools/server-management/policies.js +1 -1
  99. package/dist/tools/server-management/policies.js.map +1 -1
  100. package/dist/tools/server-management/status.js +2 -2
  101. package/dist/tools/server-management/status.js.map +1 -1
  102. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -17,6 +17,53 @@ not part of the public surface and may change in minor releases.
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [0.1.3] - 2026-05-18
21
+
22
+ ### Added
23
+
24
+ - **`rego_coverage_gaps` tool.** Runs `opa test --coverage` and returns a
25
+ per-file breakdown of uncovered line ranges, sorted by coverage ascending
26
+ so the worst-covered files appear first. Accepts an optional `threshold`
27
+ to limit output to files below a target coverage percentage. Surfaces
28
+ `testsPassed`, `testsFailed`, `testsSkipped`, and `overallCoverage` in
29
+ the envelope alongside the gap report.
30
+
31
+ - **`rego_security_audit` tool.** Runs regal lint restricted to the
32
+ `security` and `bugs` categories across one or more policy directories.
33
+ Returns findings grouped by severity (high / medium) with per-finding
34
+ remediation guidance. Designed for fleet-wide periodic sweeps rather
35
+ than per-file style review. Requires regal.
36
+
37
+ - **`mcp_server_info` tool.** Returns the server name, version, resolved
38
+ `opa` and `regal` versions, transport type, and Node.js version in a
39
+ single call. Useful for verifying which server instance an agent is
40
+ talking to and confirming binary paths resolved correctly.
41
+
42
+ - **Claude Code install section** in README with the `claude mcp add --env`
43
+ command. Standing-instructions template (`examples/CLAUDE.md`) and
44
+ PostToolUse hook config (`examples/claude-code-hook.json`) for policy
45
+ repos using Claude Code.
46
+
47
+ - **Node 24 added to CI matrix.** Unit tests now run on Node 20, 22, and 24
48
+ across Ubuntu, macOS, and Windows.
49
+
50
+ ### Changed
51
+
52
+ - All em dashes in source comments replaced with `--` (U+002D pairs).
53
+ No behavior change; cosmetic consistency fix.
54
+
55
+ ## [0.1.2] - 2026-05-18
56
+
57
+ ### Fixed
58
+
59
+ - `rego_capabilities` with `current: true` or a `version` argument now
60
+ returns only builtin names, a count, future keywords, and features by
61
+ default (`names_only: true`). Previously the full spec payload -- type
62
+ signatures, documentation, and metadata for every builtin -- routinely
63
+ exceeded the 100 KB `maxResponseBytes` cap and returned a useless
64
+ `__truncated` envelope. Pass `names_only: false` to retrieve the
65
+ complete payload when type signatures or documentation are needed.
66
+
20
67
  ## [0.1.1] - 2026-05-09
21
68
 
22
69
  ### Fixed
@@ -145,6 +192,8 @@ wrappers end-to-end. CI matrix: Ubuntu, macOS, and Windows on Node
145
192
  20 and 22, plus CodeQL security scanning and weekly Dependabot updates
146
193
  for npm, GitHub Actions, and Docker base images.
147
194
 
148
- [Unreleased]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.1...HEAD
195
+ [Unreleased]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.3...HEAD
196
+ [0.1.3]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.2...v0.1.3
197
+ [0.1.2]: https://github.com/OrygnsCode/opa-mcp-server/releases/tag/v0.1.2
149
198
  [0.1.1]: https://github.com/OrygnsCode/opa-mcp-server/releases/tag/v0.1.1
150
199
  [0.1.0]: https://github.com/OrygnsCode/opa-mcp-server/releases/tag/v0.1.0
package/README.md CHANGED
@@ -17,10 +17,10 @@ environment.
17
17
  +--------------------+ MCP / stdio +-----------------+ spawn / HTTP +------------------+
18
18
  | Claude · Cursor · | ------------> | @orygn/opa-mcp | -------------> | opa · regal · |
19
19
  | VS Code · ... | <------------ | | <------------- | OPA REST API |
20
- +--------------------+ 32 tools +-----------------+ +------------------+
20
+ +--------------------+ 35 tools +-----------------+ +------------------+
21
21
  ```
22
22
 
23
- > **Status:** v0.1.1. Tool surface, error codes, and
23
+ > **Status:** v0.1.3. Tool surface, error codes, and
24
24
  > environment variables follow [SemVer](https://semver.org/) from
25
25
  > v0.1.0 forward.
26
26
 
@@ -100,20 +100,10 @@ recognize the gap this fills.
100
100
  The server runs locally over stdio. Pick the install path that matches
101
101
  your client.
102
102
 
103
- ### Claude Desktop / Claude Code
103
+ ### Claude Desktop
104
104
 
105
- The fastest path is the Smithery one-liner:
106
-
107
- ```bash
108
- npx -y @smithery/cli install @orygn/opa-mcp --client claude
109
- ```
110
-
111
- Or download `opa-mcp.mcpb` from the
112
- [latest release](https://github.com/OrygnsCode/opa-mcp-server/releases/latest)
113
- and double-click it.
114
-
115
- If you prefer to edit `claude_desktop_config.json` by hand, the snippet
116
- lives in [`examples/claude-desktop.json`](./examples/claude-desktop.json):
105
+ Edit `claude_desktop_config.json` directly (or copy from
106
+ [`examples/claude-desktop.json`](./examples/claude-desktop.json)):
117
107
 
118
108
  ```json
119
109
  {
@@ -136,6 +126,47 @@ lives in [`examples/claude-desktop.json`](./examples/claude-desktop.json):
136
126
  > [first-time install gotcha](#-first-time-install-gotcha-read-this-if-you-used-npx-or-the-global-install)
137
127
  > below. Windows users substitute `C:\\path\\to\\opa.exe`.
138
128
 
129
+ Or download `opa-mcp.mcpb` from the
130
+ [latest release](https://github.com/OrygnsCode/opa-mcp-server/releases/latest)
131
+ and double-click it.
132
+
133
+ Alternatively, use the Smithery one-liner:
134
+
135
+ ```bash
136
+ npx -y @smithery/cli install @orygn/opa-mcp --client claude
137
+ ```
138
+
139
+ ### Claude Code (CLI)
140
+
141
+ Register the server for the current project with `claude mcp add`:
142
+
143
+ ```bash
144
+ claude mcp add \
145
+ --env OPA_BINARY=/usr/local/bin/opa \
146
+ --env REGAL_BINARY=/usr/local/bin/regal \
147
+ --env OPA_MCP_ALLOWED_PATHS=/path/to/your/policies \
148
+ opa -- npx -y @orygn/opa-mcp
149
+ ```
150
+
151
+ This writes the config into `.mcp.json` at your project root and is
152
+ picked up automatically on every `claude` session in that directory.
153
+ Add `--scope user` to register it globally instead.
154
+
155
+ > Replace the paths with your real absolute paths (same caveat as
156
+ > Claude Desktop above). On Windows use `C:\path\to\opa.exe` syntax.
157
+
158
+ **Persistent context and auto-checks for policy repos.**
159
+ If you work in an OPA policy repo regularly, two extra files remove
160
+ repetitive setup from every session:
161
+
162
+ - [`examples/CLAUDE.md`](./examples/CLAUDE.md) -- copy to your repo
163
+ root or `.claude/CLAUDE.md`. Claude Code loads it every session,
164
+ so the agent always knows which tools to use and what conventions apply.
165
+ - [`examples/claude-code-hook.json`](./examples/claude-code-hook.json) --
166
+ merge the `hooks` block into `.claude/settings.json`. Runs `opa check`
167
+ automatically after any `.rego` file is written, so syntax errors
168
+ surface immediately without a manual tool call.
169
+
139
170
  ### Cursor
140
171
 
141
172
  Drop [`examples/cursor.json`](./examples/cursor.json) into either
@@ -369,6 +400,14 @@ the tasks agents are actually asked to do.
369
400
  | `rego_generate_test_skeleton` | Given a policy, generate a `_test.rego` skeleton covering each rule. |
370
401
  | `rego_describe_policy` | Summarize what a policy does, its inputs, decisions, and assumptions. |
371
402
  | `rego_suggest_fix` | For a failed `rego_check` or `rego_lint`, propose minimal patches. |
403
+ | `rego_coverage_gaps` | Run `opa test --coverage` and return per-file uncovered line ranges, sorted worst first. Use `threshold` to focus on files below a target percentage. |
404
+ | `rego_security_audit` | Run regal lint restricted to `security` and `bugs` categories across a directory. Returns severity-grouped findings with remediation guidance. |
405
+
406
+ ### Category F: Meta
407
+
408
+ | Tool | What it does |
409
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
410
+ | `mcp_server_info` | Return server name, version, resolved `opa`/`regal` versions, transport type, and Node.js version in one call. Useful for verifying which server instance the agent is connected to. |
372
411
 
373
412
  ## Prompts
374
413
 
@@ -452,7 +491,8 @@ Agent: Done. Policy `rbac` is live on staging at $OPA_URL.
452
491
  │ ├── tools/evaluation/ ─┤ │
453
492
  │ ├── tools/bundles/ ─┼─── lib/opa-cli.ts ──┐ │
454
493
  │ ├── tools/server-management/ ─┤ │ │
455
- │ ├── tools/helpers/ ─┘ │ │
494
+ │ ├── tools/helpers/ ─┤ │ │
495
+ │ ├── tools/meta/ ─┘ │ │
456
496
  │ │ ▼ │
457
497
  │ │ lib/subprocess.ts ──┴── opa │
458
498
  │ │ lib/regal-cli.ts ───── regal│
@@ -554,11 +594,10 @@ upgrade Regal: `brew upgrade regal` or download from the
554
594
 
555
595
  **`directory-package-mismatch` violation when linting inline source.**
556
596
 
557
- When you pass `source` rather than `paths` to `rego_lint`, Regal sees a
558
- randomized temp-file path that can't possibly match your declared package
559
- path. The diagnostic is an artifact of inline linting, not a real issue.
560
- Disable the rule for inline workflows or lint via `paths` against the
561
- real on-disk file when you want canonical signal.
597
+ Since v0.1.1, the server auto-disables this rule for inline-source calls.
598
+ If you see it, you are running an older version -- upgrade to v0.1.1 or
599
+ later. To get canonical signal on this rule, lint via `paths` against the
600
+ real on-disk file instead of passing `source` directly.
562
601
 
563
602
  **Where are the logs?**
564
603
 
@@ -588,8 +627,8 @@ npm run build # compile to dist/
588
627
  ```
589
628
 
590
629
  CI runs lint, typecheck, build, and unit tests on every push and PR
591
- across Ubuntu, macOS, and Windows on Node 20 and 22. Integration tests
592
- run on Linux against pinned `opa` and `regal` releases.
630
+ across Ubuntu, macOS, and Windows on Node 20, 22, and 24. Integration
631
+ tests run on Linux against pinned `opa` and `regal` releases.
593
632
 
594
633
  For the full contributor workflow (adding tools, naming conventions,
595
634
  logging discipline, release process), see [CONTRIBUTING.md](./CONTRIBUTING.md).
package/dist/config.d.ts CHANGED
@@ -15,7 +15,7 @@ declare const ConfigSchema: z.ZodObject<{
15
15
  /**
16
16
  * Allow-listed root directories for file path inputs. Tools that accept
17
17
  * filesystem paths reject anything outside these roots. Empty by
18
- * default file-based tools refuse to read from disk until the
18
+ * default -- file-based tools refuse to read from disk until the
19
19
  * operator explicitly opts in via `OPA_MCP_ALLOWED_PATHS`.
20
20
  */
21
21
  allowedPaths: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Centralized configuration loaded from environment variables.
3
3
  *
4
- * Environment variables are the only configuration surface there is no
4
+ * Environment variables are the only configuration surface -- there is no
5
5
  * config file, no flags. This matches how MCP clients (Claude Desktop,
6
6
  * Cursor, VS Code) pass config via the `env` object in their JSON.
7
7
  */
@@ -24,7 +24,7 @@ const ConfigSchema = z.object({
24
24
  /**
25
25
  * Allow-listed root directories for file path inputs. Tools that accept
26
26
  * filesystem paths reject anything outside these roots. Empty by
27
- * default file-based tools refuse to read from disk until the
27
+ * default -- file-based tools refuse to read from disk until the
28
28
  * operator explicitly opts in via `OPA_MCP_ALLOWED_PATHS`.
29
29
  */
30
30
  allowedPaths: z.array(z.string()).default([]),
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Server identity constants shared between the entry point and the
3
+ * mcp_server_info tool. Defined here to avoid a circular import
4
+ * (server.ts -> tools -> meta -> server.ts).
5
+ */
6
+ export declare const SERVER_NAME = "orygn-opa-mcp";
7
+ export declare const SERVER_VERSION = "0.1.3";
8
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,WAAW,kBAAkB,CAAC;AAC3C,eAAO,MAAM,cAAc,UAAU,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Server identity constants shared between the entry point and the
3
+ * mcp_server_info tool. Defined here to avoid a circular import
4
+ * (server.ts -> tools -> meta -> server.ts).
5
+ */
6
+ export const SERVER_NAME = 'orygn-opa-mcp';
7
+ export const SERVER_VERSION = '0.1.3';
8
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC;AAC3C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Helpers for building structured tool errors.
3
3
  *
4
- * Tools should never throw raw exceptions to the MCP layer every failure
4
+ * Tools should never throw raw exceptions to the MCP layer -- every failure
5
5
  * mode returns a `ToolEnvelope` with `ok: false` and a structured `error`.
6
6
  */
7
7
  import type { ToolEnvelope, ToolErrorCode } from '../types.js';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * File-only logger.
3
3
  *
4
- * Stdout is reserved for MCP protocol traffic writing anywhere else
4
+ * Stdout is reserved for MCP protocol traffic -- writing anywhere else
5
5
  * would corrupt the JSON-RPC stream. This logger appends to a file with
6
6
  * level filtering, and never touches stdout.
7
7
  *
@@ -37,7 +37,7 @@ function write(level, msg, ctx) {
37
37
  }
38
38
  catch {
39
39
  // Logging must never throw. If the log file is unreachable, drop the
40
- // line silently better than crashing the server.
40
+ // line silently -- better than crashing the server.
41
41
  }
42
42
  }
43
43
  export const logger = {
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAIzC,MAAM,cAAc,GAA0B;IAC5C,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;CACV,CAAC;AAOF,kDAAkD;AAClD,IAAI,KAA8B,CAAC;AAEnC,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAY;IACnD,KAAK,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,KAAK,CAAC,KAAY,EAAE,GAAW,EAAE,GAA6B;IACrE,0EAA0E;IAC1E,yDAAyD;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,IAAI,cAAc,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO;IAEnE,MAAM,KAAK,GAAG;QACZ,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG;QACH,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxB,CAAC;IACF,IAAI,CAAC;QACH,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;QACrE,mDAAmD;IACrD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QAC1D,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3B,CAAC;IACD,IAAI,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QACzD,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QACzD,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QAC1D,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAIzC,MAAM,cAAc,GAA0B;IAC5C,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;CACV,CAAC;AAOF,kDAAkD;AAClD,IAAI,KAA8B,CAAC;AAEnC,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAY;IACnD,KAAK,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,KAAK,CAAC,KAAY,EAAE,GAAW,EAAE,GAA6B;IACrE,0EAA0E;IAC1E,yDAAyD;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,IAAI,cAAc,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO;IAEnE,MAAM,KAAK,GAAG;QACZ,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG;QACH,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxB,CAAC;IACF,IAAI,CAAC;QACH,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;QACrE,oDAAoD;IACtD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QAC1D,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3B,CAAC;IACD,IAAI,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QACzD,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QACzD,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,EAAE,CAAC,GAAW,EAAE,GAA6B,EAAQ,EAAE;QAC1D,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF,CAAC"}
@@ -138,7 +138,7 @@ export interface SignInput {
138
138
  /**
139
139
  * Wrapper around the local `opa` binary.
140
140
  *
141
- * Methods do not throw on `opa` errors non-zero exit codes are
141
+ * Methods do not throw on `opa` errors -- non-zero exit codes are
142
142
  * surfaced on the returned `SpawnResult` so tools can map them to
143
143
  * structured error envelopes at their layer. They DO throw on
144
144
  * caller-side bugs (e.g. an empty `paths` array passed where one is
@@ -154,7 +154,7 @@ export declare class OpaCli {
154
154
  version(): Promise<string | null>;
155
155
  /**
156
156
  * Format Rego source. Stdout contains the formatted output. Reports
157
- * `exitCode: 0` even when no changes are needed callers compare
157
+ * `exitCode: 0` even when no changes are needed -- callers compare
158
158
  * input vs output to detect a no-op.
159
159
  */
160
160
  fmt(input: FmtInput): Promise<SpawnResult>;
@@ -162,7 +162,7 @@ export declare class OpaCli {
162
162
  * Type-check Rego. Returns `exitCode: 0` and empty `stdout` when the
163
163
  * policy is valid. On failure, `exitCode` is non-zero and the JSON
164
164
  * error report is written to **stderr** (this is OPA's actual
165
- * behavior tools must read `stderr`, not `stdout`, for `check`
165
+ * behavior -- tools must read `stderr`, not `stdout`, for `check`
166
166
  * diagnostics). Either inline `source` or one or more `paths` must be
167
167
  * provided.
168
168
  */
@@ -10,7 +10,7 @@
10
10
  * The `-` (read-from-stdin) convention some opa subcommands document is
11
11
  * unreliable on Windows, where `-` is treated as a literal filename.
12
12
  *
13
- * The methods do not parse stdout into typed result objects different
13
+ * The methods do not parse stdout into typed result objects -- different
14
14
  * tools need different shapes of the same output and a one-size parser
15
15
  * would force conversions both ways. Stdout is JSON whenever
16
16
  * `--format=json` is set, and tools call `JSON.parse` on it themselves.
@@ -23,7 +23,7 @@ import { runBinary } from './subprocess.js';
23
23
  /**
24
24
  * Wrapper around the local `opa` binary.
25
25
  *
26
- * Methods do not throw on `opa` errors non-zero exit codes are
26
+ * Methods do not throw on `opa` errors -- non-zero exit codes are
27
27
  * surfaced on the returned `SpawnResult` so tools can map them to
28
28
  * structured error envelopes at their layer. They DO throw on
29
29
  * caller-side bugs (e.g. an empty `paths` array passed where one is
@@ -48,7 +48,7 @@ export class OpaCli {
48
48
  // ─── Authoring ───────────────────────────────────────────────────────
49
49
  /**
50
50
  * Format Rego source. Stdout contains the formatted output. Reports
51
- * `exitCode: 0` even when no changes are needed callers compare
51
+ * `exitCode: 0` even when no changes are needed -- callers compare
52
52
  * input vs output to detect a no-op.
53
53
  */
54
54
  async fmt(input) {
@@ -58,7 +58,7 @@ export class OpaCli {
58
58
  * Type-check Rego. Returns `exitCode: 0` and empty `stdout` when the
59
59
  * policy is valid. On failure, `exitCode` is non-zero and the JSON
60
60
  * error report is written to **stderr** (this is OPA's actual
61
- * behavior tools must read `stderr`, not `stdout`, for `check`
61
+ * behavior -- tools must read `stderr`, not `stdout`, for `check`
62
62
  * diagnostics). Either inline `source` or one or more `paths` must be
63
63
  * provided.
64
64
  */
@@ -29,7 +29,7 @@ export interface RequestOptions {
29
29
  body?: unknown;
30
30
  /**
31
31
  * Raw string body sent verbatim. Used for endpoints that accept
32
- * non-JSON content notably `PUT /v1/policies/{id}` which expects
32
+ * non-JSON content -- notably `PUT /v1/policies/{id}` which expects
33
33
  * Rego source as `text/plain`.
34
34
  */
35
35
  rawBody?: string;
@@ -39,7 +39,7 @@ export interface LintInput {
39
39
  /**
40
40
  * Wrapper around the local `regal` binary.
41
41
  *
42
- * Like `OpaCli`, methods do not throw on Regal-side errors the exit
42
+ * Like `OpaCli`, methods do not throw on Regal-side errors -- the exit
43
43
  * code on the returned `SpawnResult` is the signal. Inline source is
44
44
  * always written to a temp file because `regal lint` does not read
45
45
  * from stdin.
@@ -58,9 +58,9 @@ export declare class RegalCli {
58
58
  * provided.
59
59
  *
60
60
  * Regal's exit codes:
61
- * - 0 no findings at or above `failLevel`
62
- * - 3 findings present
63
- * - non-zero other Regal-internal failure (config error, etc.)
61
+ * - 0 -- no findings at or above `failLevel`
62
+ * - 3 -- findings present
63
+ * - non-zero other -- Regal-internal failure (config error, etc.)
64
64
  *
65
65
  * When called with inline `source`, location-bound rules whose
66
66
  * verdict depends on the on-disk path (currently
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Wrapper around the optional `regal` binary (Rego linter, by Styra).
3
3
  *
4
- * Regal is OPTIONAL only the `rego_lint` tool requires it. Other
4
+ * Regal is OPTIONAL -- only the `rego_lint` tool requires it. Other
5
5
  * tools work without Regal installed. If absent, `rego_lint` returns a
6
6
  * structured `REGAL_NOT_FOUND` error with an install hint.
7
7
  */
@@ -21,7 +21,7 @@ export const INLINE_SOURCE_FALSE_POSITIVE_RULES = ['directory-package-mismatch']
21
21
  /**
22
22
  * Wrapper around the local `regal` binary.
23
23
  *
24
- * Like `OpaCli`, methods do not throw on Regal-side errors the exit
24
+ * Like `OpaCli`, methods do not throw on Regal-side errors -- the exit
25
25
  * code on the returned `SpawnResult` is the signal. Inline source is
26
26
  * always written to a temp file because `regal lint` does not read
27
27
  * from stdin.
@@ -48,9 +48,9 @@ export class RegalCli {
48
48
  * provided.
49
49
  *
50
50
  * Regal's exit codes:
51
- * - 0 no findings at or above `failLevel`
52
- * - 3 findings present
53
- * - non-zero other Regal-internal failure (config error, etc.)
51
+ * - 0 -- no findings at or above `failLevel`
52
+ * - 3 -- findings present
53
+ * - non-zero other -- Regal-internal failure (config error, etc.)
54
54
  *
55
55
  * When called with inline `source`, location-bound rules whose
56
56
  * verdict depends on the on-disk path (currently
@@ -19,7 +19,7 @@ export interface SpawnResult {
19
19
  durationMs: number;
20
20
  }
21
21
  /**
22
- * Run a binary and return its captured output. Never throws failures
22
+ * Run a binary and return its captured output. Never throws -- failures
23
23
  * are reflected in `exitCode` / `timedOut`.
24
24
  */
25
25
  export declare function runBinary(binary: string, opts: SpawnOptions): Promise<SpawnResult>;
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * Subprocess wrapper used by `opa-cli.ts` and `regal-cli.ts`.
3
3
  *
4
- * - Uses argv arrays only (never shell strings) prevents injection.
4
+ * - Uses argv arrays only (never shell strings) -- prevents injection.
5
5
  * - Hard timeout per invocation, defaulting to config.subprocessTimeoutMs.
6
6
  * - Captures stdout / stderr / exit code separately.
7
7
  * - Optional stdin payload for piping source code.
8
8
  */
9
9
  import { spawn } from 'node:child_process';
10
10
  /**
11
- * Run a binary and return its captured output. Never throws failures
11
+ * Run a binary and return its captured output. Never throws -- failures
12
12
  * are reflected in `exitCode` / `timedOut`.
13
13
  */
14
14
  export async function runBinary(binary, opts) {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Helpers shared across tool implementations.
3
3
  *
4
- * Each tool follows the same shape validate input, call into a CLI
4
+ * Each tool follows the same shape -- validate input, call into a CLI
5
5
  * wrapper or HTTP client, map errors to a structured envelope, return
6
6
  * the formatted MCP result. The pieces here factor out the parts every
7
7
  * tool repeats so each tool file can stay focused on its own logic.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * MCP Prompts slash-command-like workflow templates.
2
+ * MCP Prompts -- slash-command-like workflow templates.
3
3
  *
4
4
  * Each prompt is a stateless instruction set the agent receives when
5
5
  * the user invokes it. They orient the agent toward a specific
@@ -64,7 +64,7 @@ Return a concise review with: pass/fail per item, recommended diffs.`;
64
64
  const decisionDebuggingWorkflowPrompt = (args) => {
65
65
  return `You are debugging an unexpected Rego decision.
66
66
 
67
- Query: ${args.query ?? '<not provided ask the user>'}
67
+ Query: ${args.query ?? '<not provided -- ask the user>'}
68
68
  User's expectation: ${args.expectation ?? '<ask the user what they expected>'}
69
69
 
70
70
  Workflow:
@@ -77,13 +77,13 @@ Workflow:
77
77
  3. Re-run with \`rego_explain_decision\` to get a structured trace.
78
78
  Identify which rules were evaluated and which fired.
79
79
  4. The cause is one of:
80
- a. **Input mismatch** the policy expected a different input shape.
80
+ a. **Input mismatch** -- the policy expected a different input shape.
81
81
  Use \`rego_describe_policy\` to list the refs the policy reads
82
82
  and confirm each is present in the input.
83
- b. **Rule logic** a guard fired or didn't fire when it should
83
+ b. **Rule logic** -- a guard fired or didn't fire when it should
84
84
  have. Read the trace and explain which rule's body evaluated to
85
85
  true/false and why.
86
- c. **Default decision** no rule produced a value, so the default
86
+ c. **Default decision** -- no rule produced a value, so the default
87
87
  kicked in.
88
88
  5. Propose the smallest fix: either an input correction or a policy
89
89
  change. If a policy change, run \`rego_check\` and \`rego_test\` on the
@@ -115,7 +115,7 @@ export function registerPrompts(server, _config) {
115
115
  source: z
116
116
  .string()
117
117
  .optional()
118
- .describe('Rego source to review. Optional agent can ask for it.'),
118
+ .describe('Rego source to review. Optional -- agent can ask for it.'),
119
119
  },
120
120
  }, (args) => ({
121
121
  messages: [
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,MAAM,8BAA8B,GAAG,CAAC,IAGvC,EAAU,EAAE;IACX,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,gBAAgB,CAAC;IACzD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,IAAI,gCAAgC,CAAC;IAC1E,OAAO;;;;;EAKP,WAAW;;qBAEQ,WAAW;;;;;;;;;;;;;;;gDAegB,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,2BAA2B,GAAG,CAAC,IAAyB,EAAU,EAAE;IACxE,OAAO;;;EAGP,IAAI,CAAC,MAAM,IAAI,8CAA8C;;;;;;;;;;;;;;;;;;;;;qEAqBM,CAAC;AACtE,CAAC,CAAC;AAEF,MAAM,+BAA+B,GAAG,CAAC,IAGxC,EAAU,EAAE;IACX,OAAO;;SAEA,IAAI,CAAC,KAAK,IAAI,+BAA+B;sBAChC,IAAI,CAAC,WAAW,IAAI,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;2CAyBlC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,MAAiB,EAAE,OAAe;IAChE,MAAM,CAAC,cAAc,CACnB,4BAA4B,EAC5B;QACE,KAAK,EAAE,4BAA4B;QACnC,WAAW,EACT,uHAAuH;QACzH,UAAU,EAAE;YACV,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YAChF,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;SACvF;KACF,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,8BAA8B,CAAC,IAAI,CAAC,EAAE;aACtE;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,yBAAyB,EACzB;QACE,KAAK,EAAE,yBAAyB;QAChC,WAAW,EACT,wHAAwH;QAC1H,UAAU,EAAE;YACV,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,yDAAyD,CAAC;SACvE;KACF,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,CAAC,IAAI,CAAC,EAAE;aACnE;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,6BAA6B,EAC7B;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,2IAA2I;QAC7I,UAAU,EAAE;YACV,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,qDAAqD,CAAC;YAClE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;SACjF;KACF,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,CAAC,IAAI,CAAC,EAAE;aACvE;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,MAAM,8BAA8B,GAAG,CAAC,IAGvC,EAAU,EAAE;IACX,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,gBAAgB,CAAC;IACzD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,IAAI,gCAAgC,CAAC;IAC1E,OAAO;;;;;EAKP,WAAW;;qBAEQ,WAAW;;;;;;;;;;;;;;;gDAegB,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,2BAA2B,GAAG,CAAC,IAAyB,EAAU,EAAE;IACxE,OAAO;;;EAGP,IAAI,CAAC,MAAM,IAAI,8CAA8C;;;;;;;;;;;;;;;;;;;;;qEAqBM,CAAC;AACtE,CAAC,CAAC;AAEF,MAAM,+BAA+B,GAAG,CAAC,IAGxC,EAAU,EAAE;IACX,OAAO;;SAEA,IAAI,CAAC,KAAK,IAAI,gCAAgC;sBACjC,IAAI,CAAC,WAAW,IAAI,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;2CAyBlC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,MAAiB,EAAE,OAAe;IAChE,MAAM,CAAC,cAAc,CACnB,4BAA4B,EAC5B;QACE,KAAK,EAAE,4BAA4B;QACnC,WAAW,EACT,uHAAuH;QACzH,UAAU,EAAE;YACV,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YAChF,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;SACvF;KACF,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,8BAA8B,CAAC,IAAI,CAAC,EAAE;aACtE;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,yBAAyB,EACzB;QACE,KAAK,EAAE,yBAAyB;QAChC,WAAW,EACT,wHAAwH;QAC1H,UAAU,EAAE;YACV,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,0DAA0D,CAAC;SACxE;KACF,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,CAAC,IAAI,CAAC,EAAE;aACnE;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,6BAA6B,EAC7B;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,2IAA2I;QAC7I,UAAU,EAAE;YACV,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,qDAAqD,CAAC;YAClE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;SACjF;KACF,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACT,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,CAAC,IAAI,CAAC,EAAE;aACvE;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -1,9 +1,9 @@
1
1
  /**
2
- * MCP Resources read-only references the agent can fetch by URI.
2
+ * MCP Resources -- read-only references the agent can fetch by URI.
3
3
  *
4
- * - opa://builtins derived at read-time from `opa capabilities`.
5
- * - opa://style-guide curated Rego style guide content.
6
- * - opa://patterns pattern library: RBAC, ABAC, K8s admission,
4
+ * - opa://builtins -- derived at read-time from `opa capabilities`.
5
+ * - opa://style-guide -- curated Rego style guide content.
6
+ * - opa://patterns -- pattern library: RBAC, ABAC, K8s admission,
7
7
  * IaC gates, API authz, rate limiting.
8
8
  */
9
9
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
@@ -5,5 +5,5 @@
5
5
  * - A test
6
6
  * - Common pitfalls
7
7
  */
8
- export declare const PATTERNS = "# Rego pattern library\n\nCommon Rego patterns with working examples, tests, and pitfalls. Each\npattern is self-contained \u2014 copy, adapt, and ship.\n\n---\n\n## 1. Role-based access control (RBAC)\n\n**When to use:** the simplest authorization model. Every user has one\nor more roles; each role grants a set of actions on a set of\nresources. Sufficient for ~80% of internal applications.\n\n```rego\npackage rbac\n\nimport rego.v1\n\ndefault allow := false\n\n# Permissions table \u2014 extend as roles evolve.\npermissions := {\n \"admin\": {\"read\", \"write\", \"delete\", \"manage_users\"},\n \"editor\": {\"read\", \"write\"},\n \"viewer\": {\"read\"},\n}\n\nallow if {\n some role in input.user.roles\n input.action in permissions[role]\n}\n\n# Why was it denied? \u2014 useful for audit logs.\ndeny_reasons contains reason if {\n not allow\n input.user\n reason := sprintf(\n \"user %q has roles %v, none grant %q\",\n [input.user.id, input.user.roles, input.action],\n )\n}\n\ndeny_reasons contains \"anonymous request\" if {\n not allow\n not input.user\n}\n```\n\n**Test:**\n\n```rego\npackage rbac_test\n\nimport rego.v1\nimport data.rbac\n\ntest_admin_can_delete if {\n rbac.allow with input as {\n \"user\": {\"id\": \"alice\", \"roles\": [\"admin\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_viewer_cannot_delete if {\n not rbac.allow with input as {\n \"user\": {\"id\": \"bob\", \"roles\": [\"viewer\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_anonymous_denied if {\n not rbac.allow with input as {\"action\": \"read\"}\n \"anonymous request\" in (rbac.deny_reasons with input as {\"action\": \"read\"})\n}\n```\n\n**Pitfalls:**\n- The `permissions` table grows unbounded. Move to data files\n (`data.permissions`) once you have more than ~20 roles.\n- Roles overlap with groups in real auth systems; map at the boundary\n rather than carrying both.\n\n---\n\n## 2. Attribute-based access control (ABAC)\n\n**When to use:** when \"who\" alone isn't enough \u2014 decisions also depend\non resource attributes (ownership, tenant, sensitivity) and context\n(time of day, source IP).\n\n```rego\npackage abac\n\nimport rego.v1\n\ndefault allow := false\n\n# A user can read any resource they own.\nallow if {\n input.action == \"read\"\n input.resource.owner_id == input.user.id\n}\n\n# A user can read shared resources at their organization.\nallow if {\n input.action == \"read\"\n input.resource.shared\n input.resource.org_id == input.user.org_id\n}\n\n# Admins can do anything within their organization.\nallow if {\n \"admin\" in input.user.roles\n input.resource.org_id == input.user.org_id\n}\n\n# Don't show \"secret\" resources to anyone outside the owner's\n# organization, even admins.\nallow := false if {\n input.resource.classification == \"secret\"\n input.resource.org_id != input.user.org_id\n}\n```\n\n**Test:**\n\n```rego\npackage abac_test\n\nimport rego.v1\nimport data.abac\n\ntest_owner_reads_own if {\n abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\"},\n \"resource\": {\"owner_id\": \"u1\", \"org_id\": \"o1\"},\n }\n}\n\ntest_admin_blocked_from_secret_in_other_org if {\n not abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\", \"roles\": [\"admin\"]},\n \"resource\": {\n \"owner_id\": \"u2\",\n \"org_id\": \"o2\",\n \"classification\": \"secret\",\n },\n }\n}\n```\n\n**Pitfalls:**\n- Multiple `allow` rules combine with logical OR. Use explicit\n `allow := false if ...` to *override* an allow.\n- Don't compute attributes inside the policy; compute them at the\n boundary and pass via `input`.\n\n---\n\n## 3. Kubernetes admission control\n\n**When to use:** validate or mutate Kubernetes resources at admission\ntime. Run as a Gatekeeper, OPA-as-a-webhook, or Kyverno-equivalent\npolicy layer.\n\n```rego\npackage k8s.admission\n\nimport rego.v1\n\n# Reject pods without resource limits.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n container := input.request.object.spec.containers[_]\n not container.resources.limits.memory\n msg := sprintf(\n \"Pod %q container %q is missing resources.limits.memory\",\n [input.request.object.metadata.name, container.name],\n )\n}\n\n# Reject privileged containers in production.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n input.request.namespace != \"kube-system\"\n container := input.request.object.spec.containers[_]\n container.securityContext.privileged == true\n msg := sprintf(\n \"privileged containers are not allowed: %q in %q\",\n [container.name, input.request.object.metadata.name],\n )\n}\n```\n\n**Pitfalls:**\n- Use `input.request.object` for the resource being admitted; the\n envelope shape comes from Kubernetes, not your control.\n- For mutations, return a JSON Patch via the `patch` field. Test\n patches with the actual admission webhook in dry-run mode before\n enforcing.\n- Iteration order is undefined; never rely on `containers[0]` to mean\n anything specific.\n\n---\n\n## 4. Infrastructure-as-Code gates (Terraform)\n\n**When to use:** validate Terraform plans before apply. Catch overly\npermissive IAM, public S3 buckets, missing encryption.\n\n```rego\npackage terraform\n\nimport rego.v1\n\n# Reject S3 buckets without server-side encryption.\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_s3_bucket\"\n after := resource.change.after\n not after.server_side_encryption_configuration\n msg := sprintf(\n \"S3 bucket %q has no server-side encryption configured\",\n [resource.address],\n )\n}\n\n# Reject IAM policies with action \"*\" on resource \"*\".\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_iam_policy\"\n policy := json.unmarshal(resource.change.after.policy)\n statement := policy.Statement[_]\n statement.Effect == \"Allow\"\n \"*\" in statement.Action\n statement.Resource == \"*\"\n msg := sprintf(\"IAM policy %q grants Allow * on *\", [resource.address])\n}\n```\n\n**Pitfalls:**\n- Terraform plan JSON is verbose and version-specific. Pin the\n `terraform plan -json` schema you target.\n- `resource_changes[_].change.after` may be `null` for destroys \u2014\n guard against it.\n- For wide-radius changes, use `opa exec --decision` against a plan\n file in CI, not the live API.\n\n---\n\n## 5. API authorization (HTTP request gating)\n\n**When to use:** at the API gateway / reverse proxy layer, validate\neach request against the caller's identity and the requested\nendpoint.\n\n```rego\npackage api.authz\n\nimport rego.v1\n\ndefault allow := false\n\n# Public endpoints \u2014 no auth required.\npublic_endpoints := {\n {\"method\": \"GET\", \"path\": [\"health\"]},\n {\"method\": \"GET\", \"path\": [\"version\"]},\n}\n\nallow if some _ in public_endpoints; matches_endpoint(_)\n\n# Authenticated reads on resources the user has access to.\nallow if {\n input.method == \"GET\"\n input.user\n user_can_read(input.user, input.path)\n}\n\n# Authenticated writes only with specific scopes.\nallow if {\n input.method in {\"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n input.user\n \"write\" in input.user.scopes\n user_can_write(input.user, input.path)\n}\n\nmatches_endpoint(spec) if {\n spec.method == input.method\n spec.path == input.path\n}\n\nuser_can_read(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n\nuser_can_write(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n```\n\n**Pitfalls:**\n- `input.path` is typically an array (`[\"users\", \"alice\"]`), not a\n string. Build it consistently at the gateway.\n- Path-prefix matching is easy; full pattern matching is not. For\n parameterized routes, decode at the gateway and pass structured\n fields.\n\n---\n\n## 6. Rate limiting (with sliding window data)\n\n**When to use:** allow N requests per principal per window. Lightweight\nlimit enforcement; for high throughput, push to a dedicated rate\nlimiter.\n\n```rego\npackage rate\n\nimport rego.v1\n\n# Configuration: 100 requests per principal per 60-second window.\nlimit := 100\nwindow_seconds := 60\n\n# input.now is a unix timestamp in nanoseconds.\n# data.requests[principal] is an array of nanosecond timestamps.\n\ncurrent_window := requests if {\n requests := [t | some t in data.requests[input.principal]; t > input.now - window_seconds * 1000000000]\n}\n\ncount := count(current_window)\n\nallow if count < limit\n\ndeny_reason := sprintf(\n \"rate limit exceeded: %d requests in last %d seconds (limit %d)\",\n [count, window_seconds, limit],\n) if not allow\n```\n\n**Pitfalls:**\n- `data.requests` grows unbounded unless the writer prunes outside the\n window. Schedule prune on every write.\n- This pattern is *advisory* \u2014 under load, two concurrent decisions\n can both see `count == limit - 1` and both allow. For strict\n limits, use a Lua/Redis token bucket at the gateway and have OPA\n validate the token, not count requests.\n\n---\n\n## Where these patterns came from\n\nEach is distilled from production policy code. The full Rego files,\ntests, and policy data fixtures live in this server's GitHub\nrepository under `tests/fixtures/policies/`.\n\nFor more patterns, see:\n\n- OPA Playground: https://play.openpolicyagent.org/\n- Awesome OPA: https://github.com/anderseknert/awesome-opa\n- Styra DAS pattern library: https://docs.styra.com/das/policies\n";
8
+ export declare const PATTERNS = "# Rego pattern library\n\nCommon Rego patterns with working examples, tests, and pitfalls. Each\npattern is self-contained -- copy, adapt, and ship.\n\n---\n\n## 1. Role-based access control (RBAC)\n\n**When to use:** the simplest authorization model. Every user has one\nor more roles; each role grants a set of actions on a set of\nresources. Sufficient for ~80% of internal applications.\n\n```rego\npackage rbac\n\nimport rego.v1\n\ndefault allow := false\n\n# Permissions table -- extend as roles evolve.\npermissions := {\n \"admin\": {\"read\", \"write\", \"delete\", \"manage_users\"},\n \"editor\": {\"read\", \"write\"},\n \"viewer\": {\"read\"},\n}\n\nallow if {\n some role in input.user.roles\n input.action in permissions[role]\n}\n\n# Why was it denied? -- useful for audit logs.\ndeny_reasons contains reason if {\n not allow\n input.user\n reason := sprintf(\n \"user %q has roles %v, none grant %q\",\n [input.user.id, input.user.roles, input.action],\n )\n}\n\ndeny_reasons contains \"anonymous request\" if {\n not allow\n not input.user\n}\n```\n\n**Test:**\n\n```rego\npackage rbac_test\n\nimport rego.v1\nimport data.rbac\n\ntest_admin_can_delete if {\n rbac.allow with input as {\n \"user\": {\"id\": \"alice\", \"roles\": [\"admin\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_viewer_cannot_delete if {\n not rbac.allow with input as {\n \"user\": {\"id\": \"bob\", \"roles\": [\"viewer\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_anonymous_denied if {\n not rbac.allow with input as {\"action\": \"read\"}\n \"anonymous request\" in (rbac.deny_reasons with input as {\"action\": \"read\"})\n}\n```\n\n**Pitfalls:**\n- The `permissions` table grows unbounded. Move to data files\n (`data.permissions`) once you have more than ~20 roles.\n- Roles overlap with groups in real auth systems; map at the boundary\n rather than carrying both.\n\n---\n\n## 2. Attribute-based access control (ABAC)\n\n**When to use:** when \"who\" alone isn't enough -- decisions also depend\non resource attributes (ownership, tenant, sensitivity) and context\n(time of day, source IP).\n\n```rego\npackage abac\n\nimport rego.v1\n\ndefault allow := false\n\n# A user can read any resource they own.\nallow if {\n input.action == \"read\"\n input.resource.owner_id == input.user.id\n}\n\n# A user can read shared resources at their organization.\nallow if {\n input.action == \"read\"\n input.resource.shared\n input.resource.org_id == input.user.org_id\n}\n\n# Admins can do anything within their organization.\nallow if {\n \"admin\" in input.user.roles\n input.resource.org_id == input.user.org_id\n}\n\n# Don't show \"secret\" resources to anyone outside the owner's\n# organization, even admins.\nallow := false if {\n input.resource.classification == \"secret\"\n input.resource.org_id != input.user.org_id\n}\n```\n\n**Test:**\n\n```rego\npackage abac_test\n\nimport rego.v1\nimport data.abac\n\ntest_owner_reads_own if {\n abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\"},\n \"resource\": {\"owner_id\": \"u1\", \"org_id\": \"o1\"},\n }\n}\n\ntest_admin_blocked_from_secret_in_other_org if {\n not abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\", \"roles\": [\"admin\"]},\n \"resource\": {\n \"owner_id\": \"u2\",\n \"org_id\": \"o2\",\n \"classification\": \"secret\",\n },\n }\n}\n```\n\n**Pitfalls:**\n- Multiple `allow` rules combine with logical OR. Use explicit\n `allow := false if ...` to *override* an allow.\n- Don't compute attributes inside the policy; compute them at the\n boundary and pass via `input`.\n\n---\n\n## 3. Kubernetes admission control\n\n**When to use:** validate or mutate Kubernetes resources at admission\ntime. Run as a Gatekeeper, OPA-as-a-webhook, or Kyverno-equivalent\npolicy layer.\n\n```rego\npackage k8s.admission\n\nimport rego.v1\n\n# Reject pods without resource limits.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n container := input.request.object.spec.containers[_]\n not container.resources.limits.memory\n msg := sprintf(\n \"Pod %q container %q is missing resources.limits.memory\",\n [input.request.object.metadata.name, container.name],\n )\n}\n\n# Reject privileged containers in production.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n input.request.namespace != \"kube-system\"\n container := input.request.object.spec.containers[_]\n container.securityContext.privileged == true\n msg := sprintf(\n \"privileged containers are not allowed: %q in %q\",\n [container.name, input.request.object.metadata.name],\n )\n}\n```\n\n**Pitfalls:**\n- Use `input.request.object` for the resource being admitted; the\n envelope shape comes from Kubernetes, not your control.\n- For mutations, return a JSON Patch via the `patch` field. Test\n patches with the actual admission webhook in dry-run mode before\n enforcing.\n- Iteration order is undefined; never rely on `containers[0]` to mean\n anything specific.\n\n---\n\n## 4. Infrastructure-as-Code gates (Terraform)\n\n**When to use:** validate Terraform plans before apply. Catch overly\npermissive IAM, public S3 buckets, missing encryption.\n\n```rego\npackage terraform\n\nimport rego.v1\n\n# Reject S3 buckets without server-side encryption.\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_s3_bucket\"\n after := resource.change.after\n not after.server_side_encryption_configuration\n msg := sprintf(\n \"S3 bucket %q has no server-side encryption configured\",\n [resource.address],\n )\n}\n\n# Reject IAM policies with action \"*\" on resource \"*\".\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_iam_policy\"\n policy := json.unmarshal(resource.change.after.policy)\n statement := policy.Statement[_]\n statement.Effect == \"Allow\"\n \"*\" in statement.Action\n statement.Resource == \"*\"\n msg := sprintf(\"IAM policy %q grants Allow * on *\", [resource.address])\n}\n```\n\n**Pitfalls:**\n- Terraform plan JSON is verbose and version-specific. Pin the\n `terraform plan -json` schema you target.\n- `resource_changes[_].change.after` may be `null` for destroys --\n guard against it.\n- For wide-radius changes, use `opa exec --decision` against a plan\n file in CI, not the live API.\n\n---\n\n## 5. API authorization (HTTP request gating)\n\n**When to use:** at the API gateway / reverse proxy layer, validate\neach request against the caller's identity and the requested\nendpoint.\n\n```rego\npackage api.authz\n\nimport rego.v1\n\ndefault allow := false\n\n# Public endpoints -- no auth required.\npublic_endpoints := {\n {\"method\": \"GET\", \"path\": [\"health\"]},\n {\"method\": \"GET\", \"path\": [\"version\"]},\n}\n\nallow if some _ in public_endpoints; matches_endpoint(_)\n\n# Authenticated reads on resources the user has access to.\nallow if {\n input.method == \"GET\"\n input.user\n user_can_read(input.user, input.path)\n}\n\n# Authenticated writes only with specific scopes.\nallow if {\n input.method in {\"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n input.user\n \"write\" in input.user.scopes\n user_can_write(input.user, input.path)\n}\n\nmatches_endpoint(spec) if {\n spec.method == input.method\n spec.path == input.path\n}\n\nuser_can_read(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n\nuser_can_write(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n```\n\n**Pitfalls:**\n- `input.path` is typically an array (`[\"users\", \"alice\"]`), not a\n string. Build it consistently at the gateway.\n- Path-prefix matching is easy; full pattern matching is not. For\n parameterized routes, decode at the gateway and pass structured\n fields.\n\n---\n\n## 6. Rate limiting (with sliding window data)\n\n**When to use:** allow N requests per principal per window. Lightweight\nlimit enforcement; for high throughput, push to a dedicated rate\nlimiter.\n\n```rego\npackage rate\n\nimport rego.v1\n\n# Configuration: 100 requests per principal per 60-second window.\nlimit := 100\nwindow_seconds := 60\n\n# input.now is a unix timestamp in nanoseconds.\n# data.requests[principal] is an array of nanosecond timestamps.\n\ncurrent_window := requests if {\n requests := [t | some t in data.requests[input.principal]; t > input.now - window_seconds * 1000000000]\n}\n\ncount := count(current_window)\n\nallow if count < limit\n\ndeny_reason := sprintf(\n \"rate limit exceeded: %d requests in last %d seconds (limit %d)\",\n [count, window_seconds, limit],\n) if not allow\n```\n\n**Pitfalls:**\n- `data.requests` grows unbounded unless the writer prunes outside the\n window. Schedule prune on every write.\n- This pattern is *advisory* -- under load, two concurrent decisions\n can both see `count == limit - 1` and both allow. For strict\n limits, use a Lua/Redis token bucket at the gateway and have OPA\n validate the token, not count requests.\n\n---\n\n## Where these patterns came from\n\nEach is distilled from production policy code. The full Rego files,\ntests, and policy data fixtures live in this server's GitHub\nrepository under `tests/fixtures/policies/`.\n\nFor more patterns, see:\n\n- OPA Playground: https://play.openpolicyagent.org/\n- Awesome OPA: https://github.com/anderseknert/awesome-opa\n- Styra DAS pattern library: https://docs.styra.com/das/policies\n";
9
9
  //# sourceMappingURL=patterns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/resources/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,glTA8WpB,CAAC"}
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/resources/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,ojTA8WpB,CAAC"}