@shomra/agent 0.3.17 → 0.3.18
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/NOTICE +1 -1
- package/README.md +57 -57
- package/package.json +3 -9
- package/shomra.mjs +9 -7168
- package/src/agents/hook-command.mjs +19 -0
- package/src/agents/hook-files.mjs +41 -0
- package/src/agents/installers.mjs +203 -0
- package/src/artifacts/matchers.mjs +59 -0
- package/src/artifacts/report.mjs +50 -0
- package/src/cli/flags.mjs +68 -0
- package/src/cli/help-sections.mjs +309 -0
- package/src/cli/help.mjs +27 -0
- package/src/cli/main.mjs +55 -0
- package/src/cli/registry.mjs +80 -0
- package/src/cli/suggestions.mjs +33 -0
- package/src/commands/add.mjs +149 -0
- package/src/commands/agent-identity.mjs +46 -0
- package/src/commands/check.mjs +194 -0
- package/src/commands/corpus.mjs +126 -0
- package/src/commands/design.mjs +168 -0
- package/src/commands/doctor.mjs +209 -0
- package/src/commands/fix.mjs +115 -0
- package/src/commands/gate.mjs +154 -0
- package/src/commands/git-hooks.mjs +163 -0
- package/src/commands/init.mjs +36 -0
- package/src/commands/install-hook.mjs +51 -0
- package/src/commands/llm-proxy.mjs +153 -0
- package/src/commands/mcp-add.mjs +185 -0
- package/src/commands/mcp.mjs +143 -0
- package/src/commands/memory-scan.mjs +181 -0
- package/src/commands/model-scan.mjs +99 -0
- package/src/commands/models.mjs +145 -0
- package/src/commands/new.mjs +64 -0
- package/src/commands/plan.mjs +87 -0
- package/src/commands/pr.mjs +249 -0
- package/src/commands/protect.mjs +38 -0
- package/src/commands/provenance.mjs +91 -0
- package/src/commands/redteam.mjs +166 -0
- package/src/commands/rules.mjs +220 -0
- package/src/commands/run.mjs +128 -0
- package/src/commands/scan-zip.mjs +118 -0
- package/src/commands/scan.mjs +102 -0
- package/src/commands/secrets.mjs +99 -0
- package/src/commands/status.mjs +50 -0
- package/src/commands/why.mjs +88 -0
- package/src/core/api-client.mjs +66 -0
- package/src/core/api-key.mjs +6 -0
- package/src/core/circuit-breaker.mjs +42 -0
- package/src/core/config.mjs +37 -0
- package/src/core/exit-codes.mjs +9 -0
- package/src/core/json-file.mjs +13 -0
- package/src/core/numbers.mjs +4 -0
- package/src/core/package-root.mjs +10 -0
- package/src/core/terminal.mjs +16 -0
- package/src/core/version.mjs +14 -0
- package/src/core/wire-limits.mjs +53 -0
- package/src/corpus/screening.mjs +127 -0
- package/{ai-usage.mjs → src/detect/ai-usage.mjs} +0 -27
- package/src/detect/code-sast.mjs +2 -0
- package/{design.mjs → src/detect/design.mjs} +17 -106
- package/src/detect/guard-signals.mjs +18 -0
- package/{model-refs.mjs → src/detect/model-refs.mjs} +18 -77
- package/src/detect/sast/chains.mjs +30 -0
- package/src/detect/sast/path-expressions.mjs +76 -0
- package/src/detect/sast/rules-chains.mjs +33 -0
- package/src/detect/sast/rules-config.mjs +51 -0
- package/src/detect/sast/rules-javascript.mjs +109 -0
- package/src/detect/sast/rules-python.mjs +292 -0
- package/src/detect/sast/scanner.mjs +104 -0
- package/src/detect/sast/source-lines.mjs +115 -0
- package/src/detect/sast/taint.mjs +71 -0
- package/src/detect/signals/artifacts.mjs +113 -0
- package/src/detect/signals/autonomy.mjs +55 -0
- package/src/detect/signals/config-markers.mjs +28 -0
- package/src/detect/signals/credential-harvest.mjs +64 -0
- package/src/detect/signals/durable-claims.mjs +73 -0
- package/src/detect/signals/egress.mjs +56 -0
- package/src/detect/signals/execution-hijack.mjs +128 -0
- package/src/detect/signals/gate.mjs +91 -0
- package/src/detect/signals/injection.mjs +55 -0
- package/src/detect/signals/lines.mjs +42 -0
- package/src/detect/signals/masking.mjs +99 -0
- package/src/detect/signals/memory.mjs +357 -0
- package/src/detect/signals/packages.mjs +45 -0
- package/src/detect/signals/propagation.mjs +86 -0
- package/src/detect/signals/prose-context.mjs +82 -0
- package/src/detect/signals/scan.mjs +91 -0
- package/src/detect/signals/secrets.mjs +85 -0
- package/src/detect/signals/sensitive.mjs +9 -0
- package/src/detect/signals/severity.mjs +10 -0
- package/src/detect/signals/shell.mjs +96 -0
- package/src/detect/signals/staged-fetch.mjs +66 -0
- package/src/detect/signals/text-match.mjs +35 -0
- package/src/gate/batch.mjs +157 -0
- package/src/gate/environment.mjs +122 -0
- package/src/gate/repo-policy.mjs +65 -0
- package/src/gate/result.mjs +53 -0
- package/src/gate/sarif.mjs +33 -0
- package/src/gate/sast.mjs +64 -0
- package/src/gate/suppressions.mjs +0 -0
- package/src/guard/classify.mjs +50 -0
- package/src/guard/emit.mjs +51 -0
- package/src/guard/ignore.mjs +24 -0
- package/src/guard/ledger.mjs +112 -0
- package/src/guard/model-load.mjs +50 -0
- package/src/guard/normalize.mjs +77 -0
- package/src/guard/options.mjs +10 -0
- package/src/guard/prompt-guard.mjs +184 -0
- package/src/guard/report.mjs +35 -0
- package/src/guard/result-guard.mjs +140 -0
- package/src/guard/tool-guard.mjs +166 -0
- package/src/inventory/agent-artifacts.mjs +5 -0
- package/src/inventory/agent-posture.mjs +249 -0
- package/src/inventory/artifacts/classify.mjs +27 -0
- package/src/inventory/artifacts/discover.mjs +187 -0
- package/src/inventory/artifacts/file-read.mjs +42 -0
- package/src/inventory/artifacts/hooks.mjs +14 -0
- package/src/inventory/artifacts/limits.mjs +37 -0
- package/src/inventory/artifacts/marketplaces.mjs +45 -0
- package/src/inventory/artifacts/roots.mjs +20 -0
- package/src/inventory/artifacts/walk.mjs +36 -0
- package/src/inventory/discovery/ai-dependencies.mjs +161 -0
- package/src/inventory/discovery/ai-tools.mjs +23 -0
- package/src/inventory/discovery/all.mjs +40 -0
- package/src/inventory/discovery/coding-agents.mjs +77 -0
- package/src/inventory/discovery/fs-read.mjs +36 -0
- package/src/inventory/discovery/local-runtimes.mjs +53 -0
- package/src/inventory/discovery/mcp-clients.mjs +67 -0
- package/src/inventory/discovery/mcp-servers.mjs +78 -0
- package/src/inventory/discovery/model-keys.mjs +97 -0
- package/src/inventory/discovery/platform.mjs +16 -0
- package/src/inventory/discovery/rules-files.mjs +25 -0
- package/src/inventory/discovery/vector-stores.mjs +176 -0
- package/src/inventory/discovery/workspace.mjs +124 -0
- package/src/inventory/discovery.mjs +10 -0
- package/src/mcp/child-process.mjs +50 -0
- package/src/mcp/config-wrapping.mjs +75 -0
- package/src/mcp/connect-gate.mjs +45 -0
- package/src/mcp/hosts.mjs +16 -0
- package/src/mcp/jsonrpc.mjs +48 -0
- package/src/mcp/lookup.mjs +50 -0
- package/src/mcp/screening.mjs +103 -0
- package/src/mcp/server-tools.mjs +97 -0
- package/src/mcp/server.mjs +102 -0
- package/src/mcp/shim.mjs +205 -0
- package/src/models/lookup.mjs +79 -0
- package/src/models/references.mjs +103 -0
- package/src/rules/context.mjs +98 -0
- package/src/rules/generate.mjs +103 -0
- package/src/rules/sections.mjs +145 -0
- package/src/scaffold/agent-project.mjs +185 -0
- package/src/scaffold/artifact-templates.mjs +35 -0
- package/code-sast.mjs +0 -1063
- package/discovery.mjs +0 -977
- package/guard-ledger.mjs +0 -239
- package/guard-signals.mjs +0 -2055
package/NOTICE
CHANGED
|
@@ -7,4 +7,4 @@ Licensed under the Apache License, Version 2.0. See the LICENSE file for the
|
|
|
7
7
|
full terms.
|
|
8
8
|
|
|
9
9
|
The detection rules, signatures, and analyzers in this package are intended for
|
|
10
|
-
authorized security use
|
|
10
|
+
authorized security use - scanning your own AI artifacts, code, and machines.
|
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
**Adversarial assurance for AI agents**, as a local-first CLI. It sits inside
|
|
4
4
|
your coding agent and CI and blocks dangerous tool-calls, shell commands and
|
|
5
|
-
data exfiltration *before they run*
|
|
5
|
+
data exfiltration *before they run* - on your machine, even offline. Enrolled,
|
|
6
6
|
it also attacks your org’s own guardrails (`shomra admin redteam`) and turns
|
|
7
7
|
each breach into a false-positive-gated control (`shomra harden`). It also vets AI
|
|
8
8
|
artifacts (MCP configs, Skills, slash commands, hooks, rules files) before they
|
|
9
|
-
install. Start with a free on-machine scan
|
|
9
|
+
install. Start with a free on-machine scan - no signup.
|
|
10
10
|
|
|
11
|
-
Zero dependencies
|
|
11
|
+
Zero dependencies - Node ≥ 18 built-ins only.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -29,22 +29,22 @@ npx @shomra/agent gate .mcp.json
|
|
|
29
29
|
|
|
30
30
|
## Auth (optional)
|
|
31
31
|
|
|
32
|
-
Shomra is **local-first**
|
|
32
|
+
Shomra is **local-first** - there is no built-in backend and no telemetry. `gate`,
|
|
33
33
|
`check`, `models`, `secrets`, and the runtime firewall all run fully on your
|
|
34
34
|
machine with no key and no network. You only connect to a Shomra org to layer on
|
|
35
35
|
your **org policy**, cloud/deep scans, AI fixes, and the Model Security Index.
|
|
36
36
|
|
|
37
37
|
- **Dev machine:** `shomra init --key shm_live_… --url https://shomra.your-co.com` (writes `~/.shomra/config.json`).
|
|
38
|
-
- **CI / headless:** set env vars instead
|
|
39
|
-
- `SHOMRA_API_KEY`
|
|
40
|
-
- `SHOMRA_URL`
|
|
38
|
+
- **CI / headless:** set env vars instead - no `init` needed:
|
|
39
|
+
- `SHOMRA_API_KEY` - your org key
|
|
40
|
+
- `SHOMRA_URL` - your backend URL
|
|
41
41
|
|
|
42
42
|
Without either, every backend-only feature degrades cleanly to the on-machine result.
|
|
43
43
|
|
|
44
44
|
## Quickstart
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
-
shomra check # "is my repo safe?"
|
|
47
|
+
shomra check # "is my repo safe?" - gate every AI artifact at once
|
|
48
48
|
shomra check --staged # only what's git-staged (pre-commit / editor-on-save)
|
|
49
49
|
shomra check --fix # gate, then remediate what isn't clean, in place
|
|
50
50
|
shomra fix .mcp.json --apply # AI-fix one artifact and write it back
|
|
@@ -58,17 +58,17 @@ shomra status # config + firewall health
|
|
|
58
58
|
shomra help # full command list
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
`check`, `fix` and `why` are the verbs a developer lives in
|
|
61
|
+
`check`, `fix` and `why` are the verbs a developer lives in - everything below is
|
|
62
62
|
CI, governance, or one-time setup. Findings carry a **file:line**, so `check --json`
|
|
63
63
|
drives precise editor squiggles and `why`/`fix` point at the exact offending line.
|
|
64
64
|
|
|
65
65
|
- **`check`** is the developer front door: it finds every AI artifact in the tree
|
|
66
66
|
(MCP configs, Skills, slash commands, hooks, rules files) and gates them in one
|
|
67
|
-
shot, **local-first**
|
|
67
|
+
shot, **local-first** - a real on-machine verdict with no backend or key, org
|
|
68
68
|
policy layered on when enrolled. It's `gate --all` with dev ergonomics
|
|
69
69
|
(`--staged` / `--changed` scoping, `--fix`, clean `--json` for an IDE extension).
|
|
70
70
|
- **`fix`** generates a minimal fix for what the gate flags and (with `--apply`)
|
|
71
|
-
writes it back to your working tree
|
|
71
|
+
writes it back to your working tree - the fix is produced on the platform with
|
|
72
72
|
your org's AI key, so no provider key sits on the dev machine. Nothing is
|
|
73
73
|
committed or pushed. Without AI on the server it prints deterministic guidance.
|
|
74
74
|
|
|
@@ -87,7 +87,7 @@ shomra new agent triage-bot # a project that starts compliant
|
|
|
87
87
|
|
|
88
88
|
Guard enforcing on every model call, an egress allowlist in code rather than in
|
|
89
89
|
the prompt, untrusted input kept out of the system prompt, secrets referenced
|
|
90
|
-
from the environment, and the gate wired into CI
|
|
90
|
+
from the environment, and the gate wired into CI - from commit zero. Remediating
|
|
91
91
|
a project into this shape later means changing decisions that have already been
|
|
92
92
|
built on.
|
|
93
93
|
|
|
@@ -104,23 +104,23 @@ on top.
|
|
|
104
104
|
| Code | Meaning |
|
|
105
105
|
|------|---------|
|
|
106
106
|
| `0` | clean / pass |
|
|
107
|
-
| `1` | hard fail
|
|
108
|
-
| `2` | soft fail
|
|
109
|
-
| `3` | usage / config error
|
|
107
|
+
| `1` | hard fail - BLOCK, vulnerable model, secret found, FAIL verdict, below `--min`, regression (also `--strict` + backend outage) |
|
|
108
|
+
| `2` | soft fail - FLAG under `--strict` (REVIEW when strict) |
|
|
109
|
+
| `3` | usage / config error - not configured, bad flags, unknown command |
|
|
110
110
|
|
|
111
111
|
**Backend outage:** by default it falls back to the on-machine verdict (org
|
|
112
112
|
policy not applied). `--strict` fails closed (exit 1) because org policy can't be
|
|
113
113
|
verified. Every backend call is bounded by `SHOMRA_API_TIMEOUT_MS` (default 30s),
|
|
114
114
|
so a job never hangs.
|
|
115
115
|
|
|
116
|
-
### GitHub Actions
|
|
116
|
+
### GitHub Actions - the reusable action
|
|
117
117
|
|
|
118
118
|
```yaml
|
|
119
119
|
- uses: actions/checkout@v4
|
|
120
120
|
- uses: shomra-org/agent@v0
|
|
121
121
|
with:
|
|
122
122
|
args: check # --strict is appended unless fail-on-flag: 'false'
|
|
123
|
-
api-key: ${{ secrets.SHOMRA_API_KEY }} # optional
|
|
123
|
+
api-key: ${{ secrets.SHOMRA_API_KEY }} # optional - the gate is local-first
|
|
124
124
|
url: ${{ secrets.SHOMRA_URL }}
|
|
125
125
|
```
|
|
126
126
|
|
|
@@ -131,7 +131,7 @@ self-hosted Git and GitHub Enterprise, `shomra install-precommit --pre-receive`
|
|
|
131
131
|
|
|
132
132
|
The hand-written workflow below still works and shows what the action does.
|
|
133
133
|
|
|
134
|
-
### GitHub Actions
|
|
134
|
+
### GitHub Actions - by hand
|
|
135
135
|
|
|
136
136
|
```yaml
|
|
137
137
|
name: Shomra AI-artifact gate
|
|
@@ -153,11 +153,11 @@ jobs:
|
|
|
153
153
|
CI provider, repo, branch and commit are auto-detected and recorded, so security
|
|
154
154
|
sees local-vs-CI gate activity in the dashboard.
|
|
155
155
|
|
|
156
|
-
### GitHub PR reviewer
|
|
156
|
+
### GitHub PR reviewer - inline annotations (`shomra pr`)
|
|
157
157
|
|
|
158
158
|
`shomra pr` is a richer PR-native path: on a `pull_request` it gates only the AI
|
|
159
159
|
artifacts **changed vs the base branch** and posts a **GitHub Check Run with
|
|
160
|
-
inline annotations** on the offending lines
|
|
160
|
+
inline annotations** on the offending lines - no GitHub App or extra backend
|
|
161
161
|
required, just the workflow's `GITHUB_TOKEN`. Scaffold it in one shot:
|
|
162
162
|
|
|
163
163
|
```bash
|
|
@@ -183,8 +183,8 @@ jobs:
|
|
|
183
183
|
- run: npx @shomra/agent pr
|
|
184
184
|
env:
|
|
185
185
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
186
|
-
SHOMRA_API_KEY: ${{ secrets.SHOMRA_API_KEY }} # optional
|
|
187
|
-
SHOMRA_URL: ${{ secrets.SHOMRA_URL }} # optional
|
|
186
|
+
SHOMRA_API_KEY: ${{ secrets.SHOMRA_API_KEY }} # optional - applies org policy
|
|
187
|
+
SHOMRA_URL: ${{ secrets.SHOMRA_URL }} # optional - your backend
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
The check-run **conclusion** mirrors the gate: `failure` on any BLOCK, `neutral`
|
|
@@ -192,7 +192,7 @@ on FLAG (or `failure` with `--strict`), `success` when clean. `--dry-run`/`--jso
|
|
|
192
192
|
print the computed check-run without posting. Without `SHOMRA_API_KEY` it still
|
|
193
193
|
runs local-first; with a key, your **org policy** (below) drives the verdict.
|
|
194
194
|
|
|
195
|
-
### SARIF
|
|
195
|
+
### SARIF - native code-scanning annotations
|
|
196
196
|
|
|
197
197
|
`gate`/`gate --all`/`check` accept `--sarif` to emit SARIF 2.1.0, which GitHub
|
|
198
198
|
(and GitLab) render as inline code-scanning annotations without a custom parser:
|
|
@@ -210,14 +210,14 @@ runs local-first; with a key, your **org policy** (below) drives the verdict.
|
|
|
210
210
|
|
|
211
211
|
When enrolled, the same **org policy** that governs the dashboard decides the CI
|
|
212
212
|
verdict (worst-wins across your org + project rules), so a build blocks on the
|
|
213
|
-
policies you defined
|
|
213
|
+
policies you defined - not just a fixed severity threshold. Two things follow:
|
|
214
214
|
|
|
215
215
|
- **Findings you've triaged away don't re-block the build.** If a security owner
|
|
216
216
|
has **accepted the risk** or **ignored** a finding in the platform, the gate
|
|
217
217
|
records the policy hit for transparency but no longer counts it toward
|
|
218
218
|
BLOCK/FLAG. Matching is per file+line and survives across scan sources, and an
|
|
219
219
|
accepted-risk that has **expired** re-blocks automatically.
|
|
220
|
-
- **Mandatory guardrails are exempt**
|
|
220
|
+
- **Mandatory guardrails are exempt** - a finding that trips a policy marked
|
|
221
221
|
*mandatory* still blocks even if someone accepted the risk. Use a policy
|
|
222
222
|
exception for those.
|
|
223
223
|
|
|
@@ -244,12 +244,12 @@ A pre-commit hook is a courtesy: it lives on the developer's machine, it is one
|
|
|
244
244
|
at all. A pre-receive hook runs on the **server**, on every push, for every
|
|
245
245
|
developer. Same check; the difference between a reminder and a control.
|
|
246
246
|
|
|
247
|
-
It **fails closed**
|
|
247
|
+
It **fails closed** - the opposite of the client hook. Blocking a local commit
|
|
248
248
|
because a binary is missing is hostile; waving a push through for the same reason
|
|
249
249
|
makes deleting the binary the bypass.
|
|
250
250
|
|
|
251
251
|
Available on self-hosted Git (GitLab, Gitea, Bitbucket DC, plain bare repos) and
|
|
252
|
-
GitHub Enterprise. GitHub.com does not run server-side hooks
|
|
252
|
+
GitHub Enterprise. GitHub.com does not run server-side hooks - use the action as
|
|
253
253
|
a required status check instead.
|
|
254
254
|
|
|
255
255
|
### pre-commit (local, blocks risky artifacts before they land)
|
|
@@ -274,7 +274,7 @@ Add `--json` anywhere to get machine-readable output for custom reporting.
|
|
|
274
274
|
wires Shomra into a coding agent's own hooks. It is **tiered**: catastrophic tool
|
|
275
275
|
calls (`curl|sh`, reverse shells, base64 RCE, live secrets, injection) are
|
|
276
276
|
blocked on-machine with zero network; only policy-relevant calls escalate to the
|
|
277
|
-
backend, behind a short timeout + circuit breaker
|
|
277
|
+
backend, behind a short timeout + circuit breaker - so a slow or down backend
|
|
278
278
|
never freezes the agent. Fail-open by default; `SHOMRA_GUARD_STRICT=1` fails
|
|
279
279
|
closed on the server tier.
|
|
280
280
|
|
|
@@ -285,12 +285,12 @@ Three channels are screened:
|
|
|
285
285
|
| **Tool call** | PreToolUse / `beforeShellExecution` | the shell command, artifact write or MCP call, before it runs |
|
|
286
286
|
| **Tool result** | PostToolUse / `afterMCPExecution` | injection, exfil sinks and hidden payloads in what a fetch/read brings *back* |
|
|
287
287
|
| **Prompt** | `UserPromptSubmit` (Claude Code) / `beforeSubmitPrompt` (Cursor) | what **you** paste, before it leaves the machine |
|
|
288
|
-
| **Plan** | `PreToolUse` on `ExitPlanMode` (Claude Code) | nothing
|
|
288
|
+
| **Plan** | `PreToolUse` on `ExitPlanMode` (Claude Code) | nothing - it *informs*. See [`shomra plan`](#shomra-plan--threat-model-what-the-agent-is-about-to-build) |
|
|
289
289
|
|
|
290
290
|
The prompt channel is the one a person controls, and the only one where the leak
|
|
291
291
|
is a paste rather than a tool call. A live credential in a prompt is refused;
|
|
292
292
|
pasted text that reads as an instruction to an agent is passed through but
|
|
293
|
-
flagged **to the model** as untrusted data rather than blocked
|
|
293
|
+
flagged **to the model** as untrusted data rather than blocked - you meant to
|
|
294
294
|
send it, the risk is that you did not read it. Backtick-quoted payloads are
|
|
295
295
|
down-ranked, so asking *why does `<pattern>` get flagged* is never blocked.
|
|
296
296
|
`SHOMRA_PROMPT_GUARD_OFF=1` disables just this channel. Only the two vendors with
|
|
@@ -310,10 +310,10 @@ shomra rules --check # CI: fail when the block goes stale
|
|
|
310
310
|
shomra mcp install # let the agent gate its own content before writing it
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
-
### `shomra design`
|
|
313
|
+
### `shomra design` - threat-model the ticket, not the repo
|
|
314
314
|
|
|
315
|
-
Every other command needs an artifact. This one reads a **description**
|
|
316
|
-
a design doc, a Jira/Linear ticket, a PR body
|
|
315
|
+
Every other command needs an artifact. This one reads a **description** - an RFC,
|
|
316
|
+
a design doc, a Jira/Linear ticket, a PR body - and answers the only question
|
|
317
317
|
worth asking before anyone writes code: does the thing being described hand an
|
|
318
318
|
attacker a path from untrusted input to a consequence?
|
|
319
319
|
|
|
@@ -327,7 +327,7 @@ shomra design docs/rfc-042.md --checklist | gh issue comment 42 -F -
|
|
|
327
327
|
It uses the platform's own model: capabilities split into **sources** (untrusted
|
|
328
328
|
input, sensitive data, filesystem) and **sinks** (network egress, execution,
|
|
329
329
|
destructive action). A closed source→sink pair is an attack path. That model does
|
|
330
|
-
not care whether the capabilities came from a scan or from a sentence
|
|
330
|
+
not care whether the capabilities came from a scan or from a sentence - here they
|
|
331
331
|
come from a sentence, and each one cites the line that evidenced it so you can
|
|
332
332
|
disagree with the machine's reading.
|
|
333
333
|
|
|
@@ -336,7 +336,7 @@ anyone actually acts on: paste it into the ticket as acceptance criteria.
|
|
|
336
336
|
|
|
337
337
|
> **It reads prose, so it sees only what was written down.** There is deliberately
|
|
338
338
|
> no clean verdict. `NOT_DESCRIBED` means the document did not describe
|
|
339
|
-
> capabilities in a way this matched
|
|
339
|
+
> capabilities in a way this matched - it is **not** a statement that the system
|
|
340
340
|
> has none. A threat model that reads as a clean bill of health is worse than
|
|
341
341
|
> none, because it is consumed exactly when the design is still cheap to change.
|
|
342
342
|
|
|
@@ -344,7 +344,7 @@ Exit codes: `1` when untrusted input reaches execution or a destructive action
|
|
|
344
344
|
(the shape where the attacker picks the action), `2` for any other closed path
|
|
345
345
|
under `--strict`.
|
|
346
346
|
|
|
347
|
-
### `shomra plan`
|
|
347
|
+
### `shomra plan` - threat-model what the agent is about to build
|
|
348
348
|
|
|
349
349
|
`design` reads a document a human remembered to write. Coding agents produce a
|
|
350
350
|
**plan** before every non-trivial task, constantly and automatically, and nothing
|
|
@@ -357,11 +357,11 @@ refuse it three tool calls later.
|
|
|
357
357
|
|
|
358
358
|
Three ways in, deliberately redundant, strongest first:
|
|
359
359
|
|
|
360
|
-
1. **`shomra_review_plan`**
|
|
360
|
+
1. **`shomra_review_plan`** - an MCP tool, so any MCP-capable agent can call it
|
|
361
361
|
mid-task with no vendor hook. Register it with `shomra mcp install`.
|
|
362
362
|
2. **The rules block asks the agent to call it.** Once the MCP server is
|
|
363
363
|
registered, `shomra rules --write` adds a *Before you implement a plan*
|
|
364
|
-
section
|
|
364
|
+
section - so `mcp install` and `rules --write` compose into a closed loop.
|
|
365
365
|
3. **A Claude Code `PreToolUse` hook on `ExitPlanMode`**, wired by
|
|
366
366
|
`install-hook`. Zero-effort, but that tool name is not in the published hook
|
|
367
367
|
docs, so it is the optional path and never the only one.
|
|
@@ -371,22 +371,22 @@ shomra plan plan.md # or: … | shomra plan -
|
|
|
371
371
|
```
|
|
372
372
|
|
|
373
373
|
**A plan is a proposal, so the default is to inform, never refuse.** Denying a
|
|
374
|
-
plan spends a turn and tells the model only that it was wrong, not how
|
|
374
|
+
plan spends a turn and tells the model only that it was wrong, not how - the
|
|
375
375
|
controls are the useful payload. Only untrusted-input-reaches-a-hard-sink
|
|
376
376
|
escalates to *ask*, and only under `SHOMRA_GUARD_STRICT=1`.
|
|
377
377
|
`SHOMRA_PLAN_GUARD_OFF=1` disables just this channel.
|
|
378
378
|
|
|
379
|
-
### `shomra corpus`
|
|
379
|
+
### `shomra corpus` - screen the index, not the retrieval
|
|
380
380
|
|
|
381
381
|
The result firewall screens what a retrieval brings *back*. Nothing screened what
|
|
382
|
-
went **in**
|
|
382
|
+
went **in** - so a poisoned document sits in the vector store indefinitely,
|
|
383
383
|
clean-until-retrieved, and is judged for the first time at the worst possible
|
|
384
384
|
moment: as one chunk, stripped of its document, inside a request a user is
|
|
385
385
|
waiting on.
|
|
386
386
|
|
|
387
387
|
Index time wins on all three counts. The whole document is present, so a payload
|
|
388
388
|
split across paragraphs is visible. The cost is paid once per document instead of
|
|
389
|
-
once per retrieval. And a document that fails is simply never embedded
|
|
389
|
+
once per retrieval. And a document that fails is simply never embedded - a
|
|
390
390
|
control rather than a detection.
|
|
391
391
|
|
|
392
392
|
```bash
|
|
@@ -398,8 +398,8 @@ shomra corpus ./kb --manifest .shomra/corpus.json
|
|
|
398
398
|
HIGH Injected instruction: "ignore all previous" (line 243 · chunk 19)
|
|
399
399
|
✗ QUARANTINE hidden.md
|
|
400
400
|
CRITICAL Invisible / bidirectional characters
|
|
401
|
-
⚠ 2 files could not be read
|
|
402
|
-
2 × binary format
|
|
401
|
+
⚠ 2 files could not be read - they are NOT covered by the result above:
|
|
402
|
+
2 × binary format - no text extractor
|
|
403
403
|
```
|
|
404
404
|
|
|
405
405
|
Findings carry the **chunk index**, not just the line, because retrieval returns
|
|
@@ -408,16 +408,16 @@ point of the command: feed it to your ingestion job so a quarantined document is
|
|
|
408
408
|
never embedded.
|
|
409
409
|
|
|
410
410
|
> **Absence accounting is load-bearing.** Real corpora are mostly PDF, DOCX and
|
|
411
|
-
> PPTX
|
|
411
|
+
> PPTX - formats this cannot read. A screen that silently skips them and prints
|
|
412
412
|
> "clean" is a lie about the majority of the corpus, so every unreadable file is
|
|
413
413
|
> counted and reported next to the verdict, and `--strict` fails on them:
|
|
414
414
|
> *we could not check it* is not *it is fine*.
|
|
415
415
|
|
|
416
|
-
Fenced code blocks are down-ranked
|
|
416
|
+
Fenced code blocks are down-ranked - a docs corpus is full of examples, and an
|
|
417
417
|
example is not a live instruction. A directive in prose is the real threat and
|
|
418
418
|
survives the down-rank.
|
|
419
419
|
|
|
420
|
-
### `shomra add`
|
|
420
|
+
### `shomra add` - vet at acquisition, not after
|
|
421
421
|
|
|
422
422
|
`mcp add` gated one channel. An agent acquires from four, and the other three had
|
|
423
423
|
no gate at all: a skill copied out of a gist, a model pulled from the Hub, a
|
|
@@ -436,11 +436,11 @@ the question changes from *should we take this?* to *is it safe to remove?*,
|
|
|
436
436
|
which is a much worse question to be asked.
|
|
437
437
|
|
|
438
438
|
**Unknown is never clean.** An unreachable Model Index, an unscanned model, and a
|
|
439
|
-
package the AI catalog does not recognise all return **FLAG**, not ALLOW
|
|
439
|
+
package the AI catalog does not recognise all return **FLAG**, not ALLOW -
|
|
440
440
|
"we could not check" and "it is fine" are different answers.
|
|
441
441
|
|
|
442
|
-
**`shomra rules`** compiles what Shomra actually enforces
|
|
443
|
-
already trips, plus your org's policy when enrolled
|
|
442
|
+
**`shomra rules`** compiles what Shomra actually enforces - plus what *this repo*
|
|
443
|
+
already trips, plus your org's policy when enrolled - into the agent's own
|
|
444
444
|
context files:
|
|
445
445
|
|
|
446
446
|
| Agent | File |
|
|
@@ -465,7 +465,7 @@ The generated block is itself an AI rules file, so `shomra rules` gates its own
|
|
|
465
465
|
output and refuses to write anything its own checker would block.
|
|
466
466
|
|
|
467
467
|
**`shomra mcp install`** registers Shomra *as* an MCP server with your agents, so
|
|
468
|
-
the model can call it in its own loop
|
|
468
|
+
the model can call it in its own loop - most usefully `shomra_review_change`,
|
|
469
469
|
which takes proposed file content plus its intended path and returns a verdict
|
|
470
470
|
**without writing anything to disk**. A BLOCK there costs nothing; the same
|
|
471
471
|
content on disk costs a blocked tool call and a wasted turn. `shomra_rules`,
|
|
@@ -476,21 +476,21 @@ prefer to wire it by hand.
|
|
|
476
476
|
## Adopting Shomra on an existing repo
|
|
477
477
|
|
|
478
478
|
A brand-new gate on a repo with history will flag things. Three layers make
|
|
479
|
-
adoption friction-free
|
|
479
|
+
adoption friction-free - all of them re-grade the artifact, so a fully
|
|
480
480
|
suppressed file drops to ALLOW and never fails the build:
|
|
481
481
|
|
|
482
482
|
- **`shomra baseline`** records every current finding (line-independent
|
|
483
|
-
fingerprints) in `.shomra/baseline.json`
|
|
483
|
+
fingerprints) in `.shomra/baseline.json` - commit it so the whole team shares
|
|
484
484
|
it. From then on only findings introduced *after* the baseline fail; re-run it
|
|
485
485
|
to refresh after cleanups. Skip it per-run with `--no-baseline`.
|
|
486
|
-
- **`.shomraignore`**
|
|
486
|
+
- **`.shomraignore`** - a repo file of `path/glob` lines (skip the file) or
|
|
487
487
|
`path/glob :: title-substring` lines (skip one finding class in those files).
|
|
488
488
|
The runtime firewall honors it too, so test fixtures and detection source
|
|
489
489
|
aren't withheld. Silence a single finding inline with `// shomra-ignore` (or
|
|
490
490
|
`# shomra-ignore`) on the finding's line or the line above, or opt a whole
|
|
491
491
|
file out with `shomra-ignore-file` in its first lines (works in JSON as a
|
|
492
492
|
`"_shomra": "shomra-ignore-file"` key). `--no-suppress` ignores all of this.
|
|
493
|
-
- **`.shomra/policy.yml`**
|
|
493
|
+
- **`.shomra/policy.yml`** - policy-as-code, reviewed in PRs like any code:
|
|
494
494
|
|
|
495
495
|
```yaml
|
|
496
496
|
block: high # min severity that BLOCKS (critical|high|medium|low|none)
|
|
@@ -500,7 +500,7 @@ suppressed file drops to ALLOW and never fails the build:
|
|
|
500
500
|
```
|
|
501
501
|
|
|
502
502
|
For a local verdict the repo policy fully re-grades; when the backend
|
|
503
|
-
returned an org decision it can only make it *stricter* (worst-wins)
|
|
503
|
+
returned an org decision it can only make it *stricter* (worst-wins) - repo
|
|
504
504
|
config never loosens org enforcement. `--no-policy` skips it.
|
|
505
505
|
|
|
506
506
|
## Environment variables
|
|
@@ -511,7 +511,7 @@ suppressed file drops to ALLOW and never fails the build:
|
|
|
511
511
|
| `SHOMRA_URL` | Backend URL (overrides config) |
|
|
512
512
|
| `SHOMRA_API_TIMEOUT_MS` | Per-request backend timeout (default 30000) |
|
|
513
513
|
| `SHOMRA_AGENT` | Agent-identity handle presented to `llm-proxy` + firewall |
|
|
514
|
-
| `SHOMRA_GATE_CONCURRENCY` | Parallel backend calls in batch gate / model lookups (default 8, 1
|
|
514
|
+
| `SHOMRA_GATE_CONCURRENCY` | Parallel backend calls in batch gate / model lookups (default 8, 1-32) |
|
|
515
515
|
| `SHOMRA_GH_TOKEN` | GitHub token for `shomra pr` (falls back to `GITHUB_TOKEN`) |
|
|
516
516
|
| `SHOMRA_GUARD_STRICT` | `1` = firewall fails closed on the server tier |
|
|
517
517
|
| `SHOMRA_GUARD_LOCAL` | `0` = disable the on-machine Tier-0 guard |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shomra/agent",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Shomra
|
|
3
|
+
"version": "0.3.18",
|
|
4
|
+
"description": "Shomra - adversarial assurance for AI agents, as a local-first CLI. Blocks dangerous tool-calls before they run, attacks your own guardrails to prove they hold, and gates AI artifacts in your editor and CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"shomra": "./shomra.mjs"
|
|
@@ -14,13 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"shomra.mjs",
|
|
17
|
-
"
|
|
18
|
-
"guard-signals.mjs",
|
|
19
|
-
"guard-ledger.mjs",
|
|
20
|
-
"code-sast.mjs",
|
|
21
|
-
"model-refs.mjs",
|
|
22
|
-
"ai-usage.mjs",
|
|
23
|
-
"design.mjs",
|
|
17
|
+
"src",
|
|
24
18
|
"README.md",
|
|
25
19
|
"LICENSE",
|
|
26
20
|
"NOTICE"
|