Security
A security tool that overstates its boundary is worse than none, because people rely on the part that was never true.
Trust boundary
What Zence is, stated precisely
Zence reduces accidental and agent-mediated mistakes inside the supported Claude Code workflow. It is not a kernel sandbox, not an endpoint security product, and not a substitute for warehouse permissions.
Not intercepted
Not tamper-proof
Not your first line
When it cannot see
Ignorance never becomes permission
If a lookup fails, if a reference will not resolve, if an asset sits outside the boundary and no rule happened to cover it — the answer is ask, not allow.
| Situation | Result |
|---|---|
| Explicit high-risk violation | deny |
| DataHub unreachable during a data operation | ask, and it says the catalog was unreachable |
| DataHub unreachable, no assets referenced | allow, flagged as degraded |
| Asset named but unresolvable, during a write | ask |
| Resolved, outside the boundary, no rule matched | ask |
| The hook itself crashes or times out | ask on data-touching tools; quiet on a local read |
| Cross-client reference with a failed lookup | ask, never allow |
A crash is answered with ask only for tools that can touch data. Failing closed on everything would make a Zence bug indistinguishable from a policy violation, and train people to click through prompts — which is how a guardrail stops working.
Threat model
Specific threats
- Prompt injection via catalog metadata
- A dataset description is data, not instructions. Metadata is rendered as quoted, length-bounded text with newlines collapsed, and policy decisions read typed fields only — never free text.
- Untrusted MCP output
- Responses are schema-validated. An unexpected shape routes to ask rather than being parsed optimistically.
- Token theft
- The DataHub token lives in the system keychain via the plugin's userConfig, is never written to a workspace file, never logged, and never printed by the CLI. A test asserts it.
- Path traversal and symlinks
- Containment is checked after full resolution, so ../../etc/passwd and a file symlinked to .zence/policy.yaml are both caught. A path Zence cannot locate is treated as tampering.
- Shell injection
- Commands are parsed with shlex and never executed. Zence reads them the way a linter does.
- Disabling Zence from inside a session
- Edits to .zence/** and to Claude Code's hook configuration are denied, and that rule is triggered by a hardcoded flag rather than a policy condition — so it survives audit mode and cannot be waived by an exception.
- Duplicate or replayed write-back
- The DataHub document id is derived deterministically, so finalizing twice updates one record rather than creating two.
- Failing open on an outage
- The failure this project exists to prevent. A transport error is never reported as 'not in the catalog', and a rule that reads asset properties will not fire against evidence that failed to resolve.
Report a vulnerability through a private GitHub advisory, not a public issue.