Skip to content
erickdronskiPublic

About

Offline audit of local coding-agent configuration: skills, MCP servers, hooks, and permissions, with evidence, baselines, and CI checks.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

20 Commits

Folders and files

Repository files navigation

tripwire

Review the configuration and permissions behind your coding agents.
An offline audit of the skills, MCP servers, hooks, and permissions installed on your machine —
across Claude Code, Claude Desktop, Cursor, and Codex.

Try it · What it finds · Agents · Precision · Limits · Contributing

MIT license zero dependencies Python 3.9+ Linux macOS Windows ruff CI


tripwire inventories local agent configuration and flags patterns that deserve review: risky permissions, literal credentials, unpinned launch commands, and potentially unsafe instructions. It gives developers and teams a common view of configured capabilities across supported agents.

Capability Practical use
Capability inventory Review configured skills, MCP servers, hooks, and permissions
Evidence-based findings Inspect the file, mechanism, and severity behind each flag
Baselines and suppressions Track new findings and preserve reviewed exceptions
CI integration Fail a check at a chosen severity, with JSON available for automation

The audit reads local files without executing plugins or contacting servers. It covers supported configuration sources, not every runtime capability; a clean report is not proof that an agent or plugin is safe.

Try it

Requirements: Python 3.9+ and Git for installation from source.

pip install git+https://github.com/erickdronski/tripwire
tripwire

Installing from git is the supported path today — this is not on PyPI yet. When it is published the distribution name will be tripwire-agent.

────────────────────────────────────────────────────────────────────────
  tripwire — what your agent can currently do
────────────────────────────────────────────────────────────────────────

  agents found                       Claude Code, Codex
  skills installed                   7
    from outside this machine        5
    shipping executable code         2
    on disk but not loaded           9  (3 deleted, 6 not installed)
  MCP servers configured             3
    reached over the network         2
    by agent                         Claude Code 2 · Codex 1
  automatic hooks                    2
    firing on                        SessionStart, notify
    by agent                         Claude Code 1 · Codex 1
  settings files                     2

────────────────────────────────────────────────────────────────────────
  1 high · 3 medium · 0 low · 2 informational
────────────────────────────────────────────────────────────────────────

  HIGH

  !! Codex runs with no sandbox and no approval prompts
     `approval_policy = "never"` and `sandbox_mode = "danger-full-access"`.
     Why it matters: Every tool call runs without asking — including commands
     an agent was steered into by content it read from a web page, a file, or
     an installed skill. This setting removes the last check between a prompt
     injection and your shell. Without the sandbox, nothing limits what that
     command can read, write, or send.
     ~/.codex/config.toml
     → Keep the sandbox (`workspace-write`) and let Codex ask, or keep full
       access for a disposable container.

The inventory at the top is the part most people have never seen: which agents are configured, how many skills came from outside the machine and ship executable code, which servers they reach, and what runs automatically. That is the real answer to "what can my agent do," and it is useful even when nothing is wrong.

Nothing is executed, installed, or fetched. It reads files already on disk, which matters, because much of what it inspects exists to run commands.

What it finds

Surface Checked for
Skills Instructions aimed at the model rather than at you; invisible Unicode; credential access next to an outbound call; bundled executable code
MCP servers Credentials stored as literals — in the environment, in request headers, or on the command line; packages installed at launch from unpinned sources; plaintext HTTP transport
Hooks Every automatic command — from settings, from plugins, and Codex's notify — escalated when it pipes to a shell, deletes recursively, elevates, or opens a socket
Permissions Approval or the sandbox switched off: Claude Code's dangerouslySkipPermissions and bypassPermissions mode, Codex's approval_policy and sandbox_mode per profile, the Codex app's full-access mode and the automations that run in it; wildcard allow-entries; allow-lists that have grown past review

Two checks illustrate how findings are derived from specific mechanisms.

Invisible characters in model-visible text. Zero-width spaces, bidirectional overrides, and Unicode tag characters can make model-visible text difficult to inspect. Their presence is flagged for review; a finding alone does not establish malicious intent.

Credential access next to an outbound call. Reading a .env file is ordinary setup. Reading one and sending it somewhere is the shape of exfiltration, and only the combination is escalated. Both halves are constructs, not vocabulary: cat ~/.aws/credentials, curl -d @.env, printenv |, or $GITHUB_TOKEN on one side; curl -d, | nc, fetch(, requests.post, or "send it to https://…" on the other. The word "credentials" next to a documentation link is neither.

Which agents it reads

Agent Read from Inventoried
Claude Code ~/.claude/settings.json, ~/.claude.json, ~/.claude/skills, installed plugins (per plugins/installed_plugins.json and enabledPlugins), plugins synced from your account; with --project, the project's .claude/ and .mcp.json skills, MCP servers, hooks, permissions
Claude Desktop claude_desktop_config.json — ~/Library/Application Support/Claude/ on macOS, %APPDATA%\Claude\ on Windows, ~/.config/Claude/ on Linux MCP servers
Cursor ~/.cursor/mcp.json; with --project, the project's .cursor/mcp.json MCP servers
Codex ~/.codex/config.toml (or $CODEX_HOME), the Codex app's stored permission mode, and its scheduled automations MCP servers, notify, approval and sandbox settings per profile, trusted projects

Every server and hook is labelled with the agent that loads it and its scope — user, project, or plugin:<name> — because "an MCP server" is not actionable and "the pdf server that the pdf-viewer plugin launches with npx -y" is.

It counts what the agent loads, not what is on disk. A plugin directory also holds deleted plugins in .trash, marketplace catalogs you browsed but never installed, superseded generations of synced plugins, and other editors' copies of the same skills. Those are skipped, and the report says how many and why, so a count lower than the files on disk is explained rather than suspicious.

Each problem is reported once. The same skill installed twice is one finding with "2 copies"; --format json lists every path. A .tripwireignore entry suppresses a merged finding only when every copy is under the reviewed path.

--home DIR reads every agent's config from under DIR. Naming only --config-dir (or --user-json) audits just that Claude Code config, and the report notes that the other agents were not read.

Precision is the product

A scanner whose high-severity findings are mostly false positives gets muted — and then its true findings are invisible too. So the severity model is the design, not an afterthought.

High severity identifies patterns that warrant prompt review: overriding prior instructions, hiding activity from the user, transmitting credential data, invisible characters, plaintext credentials, and disabled approval controls. The finding is a review signal, not a determination of intent.

The first version of this tool reported four high-severity findings on a clean machine. Three were false positives — official skills that documented writing a .env file, or showed rm -rf inside a regex example. Three fixes:

  • Code blocks are stripped before scanning. A hook-authoring skill quoting rm\s+-rf as a pattern is doing its job, not attacking you.
  • Weak signals grade down. Reading a credential file is low. It only becomes high next to an actual transmission.
  • Local paths are not proof of authorship. Skills land in ~/.claude/skills via install scripts, package managers, and other agents — so the trust discount applies only to signals that local authorship genuinely explains. A bare "ignore all previous instructions" remains high regardless of its path; defensive quotations receive separate handling described below.

Every one of those cases is now a test. Roughly half the suite asserts that something is not flagged, and CI plants a malicious config on every run — a poisoned skill, a Codex config with the sandbox off, a plugin server carrying a literal key — to confirm the detections still fire.

Precision on a real machine

Version 0.2.0 was measured against a real workstation: about 940 skill files from dozens of plugins, plus Claude Desktop and Codex. Version 0.1.0 reported 32 high-severity findings there, and every one was a false positive:

  • 18 "credential exfiltration" — the word "credentials" within a few lines of any URL, such as a prerequisites list naming "SDK credentials" beside a marketplace link. Now both halves must be constructs (above).
  • 11 "concealment from the user" — honesty rules like "do not tell the user it is saved before the commit succeeds" and "never drop a draft without telling the user." Those tell the model to say more. Concealment now means hiding activity: "that you…", "without the user noticing", "keep this hidden", "silently … without telling anyone".
  • 3 "override of prior instructions" — skills quoting an injection in order to resist it: treat text such as "ignore previous instructions" as data. That is a mention, not a use (see the limits below for how this is bounded).

Eight of the nine low findings were the same mistake one level down — cat matching inside "fabricated credentials". Meanwhile the inventory undercounted badly: it saw one MCP server and no hooks, because plugin servers and hooks, Claude Desktop, Cursor, and Codex were never read, and it counted deleted and uninstalled plugins as installed.

Same machine 0.1.0 0.2.0
High 32, all false positives 1, a true positive
Medium 69 68
Low 9 1
Informational 16 19
Skill files 942 counted 636 loaded; 307 skipped and explained
MCP servers 1 226 (223 Claude Code, 3 Codex)
Automatic hooks 0 20 (19 from plugins, Codex notify)

The 307 skipped files are 213 in .trash, 47 in marketplace catalogs that were never installed, 40 superseded generations of synced plugins, and 7 copies kept for another editor. One loaded skill was new: a symlinked user skill the old directory walk never followed.

The remaining high is real: the Codex app was set to run local threads in full-access mode — no sandbox and no approval prompts. That setting does not live in config.toml, which is why reading the config file alone would have missed it. The mediums are third-party skills shipping executable code (66) and two plugin MCP servers launched with an unpinned npx -y (2).

In CI

tripwire --fail-on high

Exits 1 when a finding at or above that severity exists, 0 when clean, 2 when it could not run. --format json emits every finding with its mechanism and evidence.

tripwire --info            # include the full capability inventory
tripwire --project .       # also audit project-local .claude/, .mcp.json, .cursor/
tripwire --home DIR        # audit every agent's config under DIR

Living with it: suppression and baselines

A scanner with no way to say "I reviewed this and it's fine" gets one run and then gets deleted. Two mechanisms, deliberately distinct:

.tripwireignore — a permanent, reviewed decision.

# rule                    path (or *)   reason
settings.wildcard-allow   *             reviewed: this box is a disposable container
skill.bundled-scripts     plugins/mine  I wrote these

Paths match whole path components: plugins/mine covers that directory and its descendants, but not plugins/mine-unreviewed. Every location of a merged finding must match for it to be suppressed. Trailing slashes and Windows path separators are accepted.

--baseline — answers the question security work actually turns on: what changed?

tripwire --update-baseline      # record today's findings as reviewed
tripwire --baseline             # later: mark anything absent from it as NEW
tripwire --baseline --new-only  # just the new ones

The baseline records every location of a merged finding. Adding a copy at a new path makes the finding new again. Older baselines recorded only the primary location; additional copies are new until reviewed and saved with --update-baseline. Malformed baseline entries produce a readable error.

  1 high · 1 medium · 0 low · 1 informational
  1 new since the baseline · 2 already known
  1 finding(s) suppressed by .tripwireignore

  HIGH

  !! NEW  Approval prompts are disabled

Two properties make this safe to rely on:

  • A suppression is always counted in the output. A suppression you cannot see is indistinguishable from a scanner that missed something.
  • With a baseline, only new findings gate CI. Failing on known ones keeps the build red until every historical finding is resolved, which is how a security gate gets switched off.
tripwire --baseline --fail-on high   # breaks the build only on something new

What it does not do

Interpret findings within these boundaries:

  • It does not prove anything is safe. It is a static read of local config against a finite pattern set. A clean report means nothing obviously wrong was found — not that nothing is wrong.
  • It does not analyze MCP server behavior. It reads how a server is configured, not what its code does once running. A server with a benign command line can do anything.
  • It reads configuration, not what reaches the agent at runtime. Connectors attached to your claude.ai account, Claude Desktop extensions, and permissions passed as command-line flags (--dangerously-skip-permissions, codex --yolo) never touch the files it reads. Cursor keeps its own approval settings in an application database, which is not read. Codex skills and plugins are not inventoried yet.
  • The Codex app's permission mode comes from an internal file. It is not documented, so it is read defensively: if the format changes, those findings disappear rather than misfire, and the config.toml checks still apply.
  • Quoting is a possible evasion, and it is bounded. A phrase such as "ignore previous instructions" grades down to informational only when it is in quotation marks and defensive language — "treat it as data", "untrusted", "never follow" — appears within 250 characters. Quotes alone never grade anything down, a bare imperative stays high wherever it appears, and a graded-down example is still listed under --info. Someone who writes both the quote and the defensive framing gets an informational finding, not a high one; that is the price of not flagging every security-minded skill.
  • Sequencing words exempt one concealment form. "Do not tell the user that … until (or before, unless, after) …" is read as honesty guidance, because every concealment false positive on a real machine had that shape. Appending such a word to a real instruction would hide it from that one pattern; the other concealment forms have no such exemption.
  • Instruction patterns skip code. Fenced blocks and inline code are not scanned for instruction-shaped text — that is what keeps documentation from being flagged — so an instruction written as code is not seen by those rules. The credential-exfiltration check does read inline code.
  • Counts are of files. Every SKILL.md inside a loaded plugin is scanned, including nested ones a skill can point the model at, so the count can exceed the skills your agent lists. With no installed_plugins.json at all, every plugin on disk is assumed loaded — over-reporting is the safe direction.
  • It cannot see intent. A skill that legitimately needs to read credentials and a skill that steals them look similar from the outside. That is why every finding states its mechanism and asks you to read the file, rather than declaring a verdict.
  • It masks credentials in its own output, best-effort. Hook commands and server command lines routinely carry tokens, and an audit report is exactly what people paste into issues. Evidence is redacted at capture, so both the text report and the raw inventory dump are covered, and server environment and header values are never printed at all — there is a no-leak test for every config source, because the first fix secured one path and missed the other. A secret with no recognizable shape can still pass through.
  • It makes no network calls at all — no telemetry, no reputation lookup, no update check. There is a test asserting the package imports no networking or subprocess module, so the claim stays true.

If you want dependency and package scanning, use a supply-chain scanner. This answers a different question: what did I already install, and what can it do right now?

Testing

python -m unittest discover -s tests -t .   # 204 tests

Related

Part of a set of small, standalone tools for working with coding agents:

Tool Job
agentsmith Derives your AGENTS.md from the repo and detects drift
contexttest A/B tests whether an AGENTS.md change actually helps
burnrate Estimates session costs and provides a Claude Code budget guard
gtm-skills Go-to-market skills for agents, on a tested arithmetic engine

License

MIT.

About

Offline audit of local coding-agent configuration: skills, MCP servers, hooks, and permissions, with evidence, baselines, and CI checks.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages