SASY — Seamless Agent Security
Sasy Labs · Survey

A survey of coding-agent security guards

A survey of Claude Code guardrails organized by what each tool can observe: a command, an audit trail, a sandbox boundary, or the provenance of data across a session.


Most guards judge a command by how it looks; sasy-guard is the only one that observes the provenance of the data that command depends on.

1. Introduction and the question

When a developer lets Claude Code work on a real repository, the agent enters a setting in which ordinary development actions already have security consequences. It can read files, run commands, edit code, and make network calls while the shell may hold credentials, git remotes, package tokens, and a working tree it can change. The agent may be fixing a build, rotating a key, or testing a migration, but the same capabilities that make those tasks useful also create paths for disclosure, mutation, and execution.

The practical question is what should sit between the agent and the machine. The candidate mechanisms differ in how much of the agent’s activity they can observe, and therefore in how discerning a decision they can make. The narrowest observes only the single command about to run and judges it by how it looks. An audit layer observes the whole sequence of actions and preserves it as evidence, though only after each action has already run. The most comprehensive observes the provenance of data across the entire session, tracing where the values in the current action came from and which earlier, possibly sensitive, steps they depended on. A single guard may combine several of these vantage points, but the widest one it can observe sets a ceiling on the decisions it can make, since no mechanism can act on information it never sees.

Containment sits on a different axis altogether. A sandbox reasons about none of this, yet by confining the process it limits the damage any command can do, whatever a policy does or does not understand. It is complementary to the observation-based mechanisms rather than a point on the same scale, and the two can be combined.

In what follows, the central question is what each tool can actually see before it says allow, ask, deny, or record.

2. Vocabulary

A PreToolUse hook is Claude Code’s interception point before a tool call runs. It receives the pending action and can allow it, ask the user, or deny it before the shell command, file edit, or other tool call reaches the machine. This placement distinguishes prevention from evidence, because a decision made after execution can explain what happened but cannot stop the immediate effect.

We use write-then-execute for a sequence in which an agent writes malicious code to an allowed file and later invokes it through an allowed runner, such as npm test or python script.py. Because the danger need not be visible in the runner command, this sequence remains a limitation of command-layer guards, including sasy-guard. Provenance can catch exfiltration when outbound bytes depend on a sensitive source, but it does not prove that a newly written script is harmless.

Provenance means origin and lineage: where data came from, and which later actions depended on it. Taint is the marker that says a value depends on a sensitive source. If a command reads .env, transforms the contents, and sends the result outward later, taint is the reason a policy can still remember the origin. Egress is data leaving the machine or sandbox; fail-closed means calls are denied when the enforcement component is unavailable; and an allowlist is a list of permitted targets or actions.

Datalog is a rule language for deriving facts from other facts, and it matters here because policy decisions often depend on relationships among events rather than on a single string. A reference monitor is the enforcement component that mediates access, so the agent does not decide whether its own action is allowed. With those terms in place, the tools separate into permission guards that decide in the hook path, audit receipts that record after execution, and OS sandboxes that accept coarser control in exchange for a containment boundary.

3. The survey

We examine the tools in an order that exposes the design space: nah, rampart, cupcake, railguard, claude-hook-advisor, prismor, punkgo-jack, and the sandbox pair of sandclaude and claude-container. For each tool, we ask what it is, what mechanism it uses, what it does well, and where that mechanism stops. This is not a single ranking, because a pre-execution policy engine, a post-execution audit log, and a Docker boundary answer different questions.

The axes are therefore observational as much as functional. A tool may know a command deeply while knowing nothing about prior values, or it may preserve evidence without preventing the action, or it may reduce blast radius without understanding semantic data flow. The survey keeps those distinctions visible so that each tool is evaluated against the kind of thing it can actually see.

nah

nah is a lightweight Python guard distributed through PyPI. The project recommends sandboxing agents and provides a guard for cases in which they are not sandboxed. A Bash parser maps a single command into 43 action types, then checks a fixed JSON policy table, with local context such as paths, sensitive locations, and curl hosts refining the result.

The mechanism gives nah more command-local shell depth than most tools here, but it also defines the boundary: nah is stateless and per-command, reading the transcript for logging rather than for policy decisions, so it can catch cat .env piped to curl inside one pipeline but not a read and egress split across tool calls. nah has removed its taint.py and provenance.py, so its current design moves away from persistent data-origin tracking.

Compared with sasy-guard, nah has a smaller installation footprint, PyPI packaging, multi-runtime reach across Claude Code, Codex, and terminal workflows, and deeper single-command parsing. It is a low-friction guard when the risk is visible in one command.

rampart

rampart is a Go policy engine distributed as a single binary. It carries more operations machinery than any other tool surveyed, covering Claude Code and OpenClaw hooks, then extending to Cline, Codex, wrapped shell commands, an MCP proxy for Model Context Protocol traffic, and LD_PRELOAD syscall interception, a Unix technique for watching low-level calls.

The operations surface includes terminal and web dashboards, security-tool exports through syslog and Common Event Format logs, webhook alerts, a policy REPL, a preflight API for checking before execution, a mapping to the OWASP Agentic-2026 security checklist, inline policy tests, and starter templates. Its policy model is a deterministic PreToolUse guard with YAML match -> when -> action rules; deny wins through priorities, and conditions include command_matches, path_matches, domain_matches, response_matches, and call_count.

rampart stops short of taint and data flow. Its cross-call state is limited to sliding-window call counters, per-session approvals, and one-response secret-regex scanning. The author named bypassing .env files through side channels as a threat, but rampart answers with per-command obfuscation detection rather than provenance. It is therefore strong where a team needs an operational policy system for local events, not a general source-to-sink graph.

cupcake

cupcake is a Rust policy-enforcement layer distributed through npm, Nix, and binaries. It is the only rival in this set with a compiled policy language. More precisely, it compiles Rego, a policy language, to Wasm, portable WebAssembly bytecode, and evaluates it through wasmtime, a WebAssembly runtime.

That mechanism matters because teams author policies in a real language and run them in a deterministic core instead of just toggling regexes or editing YAML tables. cupcake also provides SLSA-3 supply-chain build integrity, support for Claude Code, Cursor, Factory, and OpenCode, plus TypeScript and Python bindings.

cupcake evaluates one hook event at a time, which leaves memory as the main gap. It does not read the transcript, reconstruct a session graph, or track taint or lineage, so a later egress decision cannot be tied back to an earlier .env read. Its product surface includes five decision types, including Modify, context injection, auto-linting after edits, and an optional LLM Watchdog that is disabled by default. The Watchdog is separate from the deterministic core.

railguard

railguard is a compact Rust guard distributed through crates.io, with roughly 10.5k lines of code. Its main design choice is pairing ordered regex rules with OS-kernel sandboxing. The decision path checks allowlist regexes, blocklist regexes, approve regexes, and then falls back to default allow, while a 29-rule heuristic threat classifier sits beside that path.

The sandbox layer uses the kernel’s own confinement tools: macOS sandbox profiles (sandbox-exec, SBPL) and Linux sandboxing (bwrap, Landlock). The provenance terminology requires care, however, because railguard’s provenance.rs signs memory .md files for integrity and tamper detection rather than semantic data lineage; it does not tell whether an outbound action depends on a prior sensitive read.

railguard has no policy language and no cross-call data-flow tracking. Its state is a shallow keyword-overlap retry detector in a three-call window plus per-session block history. Its operational extras include file snapshots, rollback, a ratatui terminal dashboard and replay view, and multi-agent file-locking.

claude-hook-advisor

claude-hook-advisor is a Claude Code productivity advisor, not a security-enforcement product. It is a Rust tool distributed through crates.io. Its relevant behavior is deny-and-rewrite guidance: if Claude reaches for a disfavored CLI tool, the hook can deny the action and steer it to the preferred tool, such as npm to bun.

claude-hook-advisor checks file edits with about 27 hardcoded literal substring patterns that can only be toggled. Bash commands are checked only against tool-preference mappings, so cat .env piped to curl passes untouched. There is no parser, AST, policy language, provenance, or tamper resistance. SQLite history and the already-warned file are analytics and deduplication state, not security decision state.

prismor

prismor is a broad Python policy engine for coding agents. It ships hooks for Claude Code, Cursor, Windsurf, OpenClaw, Hermes, Codex, and GitHub Copilot CLI, plus adapters for OpenAI Agents SDK, CrewAI, LangChain/LangGraph, browser-use, Vercel AI SDK, and an HTTP eval-server.

prismor has 64 YAML detection rules with observe or enforce modes, non-overridable core categories, allowlists, project overlays, and optional signed remote policy overlays, meaning centrally signed rules layered on top of local policy. Its secret cloaking substitutes @@SECRET:name@@ placeholders at Bash execution time, scrubs Bash output, can deny raw-secret reads, and can scrub MCP responses. It also scores packages with registry metadata, install scripts, OSV lookup, typosquat checks, and hardcoded indicators for known malicious packages.

prismor complicates the simple two-axis story because it has some session memory. It stores JSONL events, meaning one JSON event per line, plus SQLite sessions and findings. It can persist a prompt-injection taint flag and seen domains, then escalate later network events. That is real cross-call state, but it is not value-level source-to-sink lineage from an earlier read or tool output into a later outbound argument. Relative to sasy-guard, the hard stops are policy and provenance: prismor compiles regex alternations and evaluates YAML rules at runtime, but it does not have an authored policy language compiled ahead of enforcement; its default posture is observe-heavy, semantic prompt-injection detection is opt-in, and local hooks and YAML remain editable unless an enterprise signed remote policy layer is in use.

punkgo-jack

punkgo-jack is an orthogonal audit tool written in Rust, not a prevention layer. It borrows the machinery of certificate transparency: an append-only Merkle log (RFC 6962), signed checkpoints (Ed25519), trusted timestamp anchoring (RFC 3161), inclusion and consistency proofs, and Go sumdb/tlog cross-language verification.

Those mechanisms can prove what happened after the fact, but punkgo-jack cannot stop execution. It registers PostToolUse, PostToolUseFailure, SessionStart, and SessionEnd, but no PreToolUse hook, so it cannot allow, ask, deny, or stop a leak before the tool call runs. Signed decision receipts could extend sasy-guard by making every allow, ask, and deny externally verifiable.

the sandbox pair

sandclaude and claude-container are OS containment tools rather than semantic policy engines. They answer a real criticism of hook-only systems, because a kernel boundary can reduce blast radius when an agent runs arbitrary processes.

sandclaude is a Bash script and Dockerfile that runs Claude with --dangerously-skip-permissions inside an Ubuntu Docker container. That gives a real host boundary outside the mounted workspace. The leaks are also explicit: the workspace is mounted read-write, network egress is open, live Claude OAuth is present, the host gh token and a Jira token are injected, passwordless sudo is available, and the image build is heavyweight.

claude-container adds a more distinctive mechanism: a Docker-socket proxy. It rejects privileged containers, dangerous capabilities, host PID, host networking, and bind mounts outside the current working directory, while supporting Claude, Codex, and Gemini. Inside claude-container, full egress remains available; mounted AWS credentials, GitHub config, and git credentials are readable; and there is no per-action allow, ask, deny, compiled policy language, data-flow tracking, or configured exfiltration defense.

Rather than replacing a policy guard, these containment layers compose with one: running the policy brain inside the sandbox lets the OS boundary limit blast radius while the policy layer reasons about what data is leaving.

4. The pattern

Most tools intercept the same hook-shaped moment and judge a local event. punkgo-jack records after execution, while sandboxes move the boundary to the operating system. These are all useful layers, but they observe different things. Only cupcake and sasy-guard have compiled policy languages: cupcake uses Rego to Wasm, while sasy-guard uses Souffle Datalog and evaluates policies over a reconstructed session graph. prismor has regex compilation inside its runtime, but the authored policy surface is YAML evaluated by the engine. We introduced sasy-guard in an earlier post, focused on the hook and graph mechanism.

Only sasy-guard has value-level whole-session provenance among these tools: nah deleted its taint and provenance files; rampart keeps counters, approvals, and one-response secret scans; railguard uses its provenance file for integrity signing rather than data lineage; cupcake evaluates each hook event independently, so no earlier read can inform a later decision; and prismor persists narrow session taint for prompt-injection findings and later network escalation, but not a general data-flow graph. Our analysis therefore treats sandbox composition, allowlists, and data-flow reasoning as distinct controls rather than evidence that a hook forms a complete boundary. The limitation stays real: write-then-execute remains a gap for command-layer guards, and sasy-guard’s taint slice is turn-scoped.

Two-axis positioning chart for Claude Code guardrails

The chart compares what each tool can decide before execution, while sandboxes and audit tools address adjacent layers rather than the same pre-execution decision problem.

5. sasy-guard

sasy-guard reconstructs the session as a message-dependency graph and evaluates compiled Souffle Datalog policies over that graph. In plain language, the guard asks where the data in the current action came from across the session before it lets the action run.

The core example is an outbound curl whose bytes trace back to a .env read. The data may move through a subshell, base64, or a subagent. If the dependency graph still reaches the sensitive source, the policy can deny the egress. sasy-guard’s baseline strengths are deterministic enforcement with no LLM in the path, roughly 13 guard groups, policy pinned per session, a locked engine baked and bound by hash, and fail-closed behavior when enforcement is unavailable.

Value-level whole-session provenance supplies the useful composition: a denylist can catch a known-bad command, and a policy language can express richer rules, while provenance lets those rules ask whether this outbound action depends on data it should not carry. That is the specific niche sasy-guard occupies; it does not replace sandboxes, audit receipts, or command-level heuristics.

6. What the rivals do better

nah has the advantage of a small installation footprint, PyPI packaging, multi-runtime reach, and a deep single-command shell parser when a team needs low-friction command checks. rampart has single-binary packaging, Homebrew installation, dashboards, security-event (SIEM) exports, webhook alerts, OWASP mapping, syscall interception, and starter templates, which give it the most extensive operations layer among the tools in this survey.

cupcake has Rego-to-Wasm policy compilation, SLSA-3 builds, npm, Nix, binary distribution, multiple harnesses, TypeScript and Python bindings, Modify decisions, and workflow automation. prismor has broader coding-agent and framework coverage, secret cloaking, supply-chain scoring, SIEM-shaped exports, signed remote policy overlays, and narrow prompt-injection taint across a session. railguard and the sandbox pair provide kernel-backed containment, which a hook cannot replace. punkgo-jack provides externally verifiable audit evidence through Merkle logs, signed checkpoints, and timestamp anchoring.

7. The comparison table

The table summarizes the field. The two middle columns capture the axes that separate the tools most sharply, while tamper resistance describes how hard it is for the agent to edit or switch off the guard’s own rules. prismor’s “Narrow” means a persisted prompt-injection taint flag, not value-level lineage.

ToolCategoryPolicy modelCompiled policySession provenanceTamper resistanceStrongest fit
sasy-guardPermission guardSouffle DatalogYesYeslockedProvenance-aware policy guard
nahPermission guardJSON table plus YAML knobsNoNorule-basedLow-friction single-command checks
rampartPolicy engine / firewallYAML glob, substring, regexNoNoplaintextPackaged operations story
cupcakePolicy-enforcement layerRego to WasmYesNoplaintextClosest policy-language rival
railguardGuard with OS sandboxOrdered regex rulesNoNorule-basedRegex plus kernel containment
claude-hook-advisorAdvisor / lintTool mappings and substringsNoNoplaintextTeam workflow nudges
prismorPolicy engineYAML plus regex rulesNoNarrowrule-basedBroad hook and secret guardrails
punkgo-jackAudit receiptsNoneNoNon/aProof after execution
sandclaudeOS sandboxNoneNoNon/aSimple container boundary
claude-containerOS sandboxNoneNoNon/aNested-container guardrails

8. Choosing a tool

The practical takeaway is that the right tool depends on the object the team needs to control. nah suits teams that want low-friction single-command checks, while rampart fits organizations that require packaging and enterprise operations. cupcake serves teams for which a compiled Rego-to-Wasm policy engine across harnesses matters most, and prismor addresses the case where broad agent and framework coverage, secret cloaking, and supply-chain guardrails are the immediate need. punkgo-jack answers the requirement of proof after the fact, and the sandboxes come first when the job is to limit blast radius.

sasy-guard applies when the risk is data moving across normal-looking steps and the policy needs to ask where the outbound action’s data came from. A layered posture combines a sandbox boundary for containment, a provenance-aware policy guard for semantic decisions, and audit receipts as the evidence layer.

References