Security¶
HarnessWeaver's job is to keep AI applications inside the lines their owners draw. This page lists the threats it is designed against and the controls that answer each, the invariants an operator can rely on, and how to harden a deployment. To report a vulnerability, see the security policy.
Threats and controls¶
| Threat | Controls |
|---|---|
| Prompt injection through retrieved content or tool output | untrusted labels on context and tool results, injection.heuristic on input and context, the lethal-trifecta check, approvals for egress |
| A tool's definition swapped after review ("rug pull") | definition_digest pinning, checked at governor step 1 on every call. A Pydantic AI agent's tool is offered to the model with its manifest's definition, not the agent's |
| Data exfiltration | egress lists in manifests, the trifecta check, approval for risky tools, gateway allow-lists |
| PII leaking to logs or vendors | pii.regex redaction (or a stronger provider), capture_content: false locked by the baseline, the R9 conformance check |
| Privilege escalation through an LLM decision | Deterministic policy only (R3); judges and classifiers may only tighten |
| Cost runaway, denial of wallet | Per-run budgets and deadlines checked before every call; tenant budgets at the gateway; tool-call caps |
| Malicious generated code | transport: code with a sandbox such as monty; never eval or exec in the host (S4) |
| Supply-chain compromise | Locked dependencies, SHA-pinned CI actions, digest-pinned images, releases with SBOMs and signed attestations (S5); plugin allow-lists (S8) |
| Sidecar exposure | Binds 127.0.0.1 by default; a bearer token on every route except health, compared in constant time (S6) |
| Apps switching controls off | Locks and bounds enforced when a spec loads (R1, R10); require_level by risk tier |
Invariants you can rely on¶
These are the framework's promises, enforced in code and tests (RULES.md):
| Rule | Promise |
|---|---|
| R1 | A locked path or baseline item can't be overridden, removed or reordered by any later layer; trying is a load error |
| R2 | Baseline middleware fails closed; declaring it open is a load error |
| R3 | Probabilistic components never grant: they may route or tighten a decision, never turn a deny or an approval into an allow |
| R4 | No silent gaps: unknown keys, providers and hook names fail loading, and a runner or adapter that can't reach a hook the baseline needs fails loading |
| R9 | Prompts, outputs, tool arguments and tool results never reach logs, spans or traces before redaction; content capture is off by default |
| R10 | A value outside its bound fails loading; nothing is clamped. Per-run overrides may only lower overridable fields |
| R13 | Every decision (route, guard, tool allowed or denied, budget hit, approval, middleware failure) is a traced event with its reason |
| R14 | Everything runs offline; live providers refuse to call out in test mode |
| S1 | Keys come from the environment or the gateway, never from spec files |
| S2 | No tool runs without a manifest; a digest mismatch blocks the call |
| S3 | Tool output, retrieved context and network content are untrusted; the trifecta check runs on every tool call |
| S6 | harnessd binds locally and needs a token |
| S8 | Plugins are inert until a spec uses them; the org can lock plugins.allow |
Hardening a deployment¶
- Distribute the baseline yourself. Use the profile path or a plugin, not the app's repository: a team can edit its own files (Who owns a profile).
- Lock the baseline. At least
plugins.allow,observability.capture_content, the PII and secrets guards,tools.require_manifestandtools.approval_by_risk. Bound budgets and deadlines. - Require levels by risk.
require_levelwithcustomer_facing: L2andtakes_actions: L3(or stricter) keeps risky apps from shipping without tool governance and policy. - Gate egress. Give every tool with network access an
egresslist; keeptools.trifectaatrequire_approval, orblockwhere nobody should be asked. - Pin tools.
harnessweaver tools pin, and fail CI ondoctordigest errors. - Treat injection checks as one layer.
injection.heuristicis cheap and easy to evade; rely on labels, the trifecta check, approvals and least-privilege tools as well, and add a model-based detector where the stakes need it. - Keep keys at the gateway. Apps hold a gateway key, not provider keys.
- Run code in a sandbox. Only through
transport: codewith a sandbox provider. - Protect the sidecar. Local binding, a per-environment token from a secret store, rotation.
- Watch shadow guards. Promote guards on eval evidence (R8), and review
eval reportfor guards that would fire in production. - Keep recordings deliberate.
state.recordstores content; enable it only where you can protect the store, or lock it off. - Review spec changes. Post
harnessweaver spec diffon every pull request; a removed guard or a new plugin is visible before it merges.