The static scanner gave one Windows agent configuration a D, 57/100. It reported 24 issues and marked 14 of them high severity.

A report like that can turn into a bad policy decision fast. We were evaluating a config scanner because our agent setup had one security gap we could not already explain with a spend audit. Prompts, hooks, permissions, and MCP configuration can all become an execution surface. The workstation had a public read-only knowledge connector and a browser broker. The risk category was real, even though we had not had an incident.

So we ran the cheapest possible version first:

Terminal window
npx ecc-agentshield scan --path C:/Users/<username>/.claude

It was an offline static scan. We left off --opus, --injection, --sandbox, --taint, and --deep, so it cost nothing in metered spend. It returned 0 critical findings, 14 highs, and a grade saying the configuration was in bad shape.

The grade was wrong about that. It was right that one small piece was missing.

The report described a different machine

The first high finding said CLAUDE.md was world-writable at mode 0o666. On a multi-user POSIX server that would matter, because another local account could edit an instruction file an agent will later load.

This was a single-login Windows desktop. The operative permission system was Windows ACLs, not Unix mode bits. The scanner took a compatibility view of the file, turned it into an attack path, and graded that path as real without establishing that another user could write the file.

A scanner finding is a conditional claim. If an attacker can reach this object through this permission model, then this configuration can produce this consequence. The scanner reported the file attribute and skipped the conditions.

The other highs followed the same pattern. We traced each one back to the source configuration instead of accepting the severity label.

Scanner signalWhat we checkedWhat it actually meant
CLAUDE.md marked world-writable at 0o666The Windows ACL context and who could log into the workstationA POSIX multi-user warning applied to a one-login Windows machine
A bootstrap hook and global instruction file flagged as attacker implantsThe hook text and the global configuration we wroteDeliberate bootstrap behavior: a PATH export, &>/dev/null, cargo install, and the <!-- rtk-instructions --> marker
Permission rules marked over-broadThe exact allow entriesA scoped python3 -c one-liner and find … -name .env*, which only lists names
A third-party check-config.sh script marked for possible leakageThe script body, its file reads, and its network behaviorIt trims a configuration key name and reads its own config. It does not exfiltrate data or make a network call.

The last row was the most useful diagnostic. The scanner had tripped on substrings in a clean third-party script from last30days. Stopping at the label would have meant treating a string match as evidence of data exfiltration. Reading the script took less time than deciding how to suppress the finding.

Coverage was the bigger problem. The scanner found three skills because it saw plugin .md shims. It missed roughly 40 real skills under .claude/skills. So the grade was not even a coherent score over the configuration that matters. It scored the subset the tool recognized, against a security model built around multi-user POSIX systems and proprietary skill conventions.

The project ships a false-positive-audit.md. That told us something about the tool’s limits, and about what running it weekly would look like. We would spend our time re-litigating the same mismatched assumptions.

The one finding that held up

The scan did find a cheap gap that was real on this machine. settings.json had an allow list and no deny list.

That does not become critical just because a scanner reported it. The existing allow rules still constrained what the agent could do. But a deny list is the right second boundary for a short set of operations we never want available through an agent session by default: force-push, rm -rf, and raw secret reads.

We kept the fix narrow. The goal was to make a bad operation harder to approve by accident, even if another permission rule changes later. Making the configuration look secure to a generic rubric was never the goal.

There was one piece of ordinary cleanup too. An old allow rule, Bash(find <retired-drive>/Projects/<legacy-project> …), still pointed at a retired project drive, even though active projects had moved to a different one. Stale configuration makes a future review harder, so we deleted it while the file was open.

This is why we do not dismiss a noisy scan outright. A report full of false positives can still force a useful look if you separate the scanner’s evidence from its conclusion. The evidence gave us an inventory of configuration edges to inspect. The conclusion did not carry over to this workstation.

Why it did not become a build gate

The obvious next step was to run the scanner on every configuration change and fail the build on any high finding. We rejected that. A gate is only useful when its failures are more trustworthy than the people who have to investigate them. Fourteen highs that all collapsed under a basic threat-model review would train us to ignore the next report.

We also skipped the scanner’s optional --opus tri-agent pass for a more elaborate verdict. What we needed to know was whether a plausible attacker could change the file on this Windows machine, and whether the flagged commands could do what the report implied. That took config inspection and reading the referenced script.

A recurring scanner may make sense later if the environment changes. A multi-user host, broader execution permissions, untrusted plugins, or more writable MCP definitions would change the calculation. On this setup, the recurring cost would be review fatigue on top of runtime.

The result is deliberately unglamorous. We kept the current configuration, added the missing deny-list boundary, removed the stale path, and installed no new permanent security ceremony.

Every high turned out to describe somebody else’s attacker, operating system, and configuration conventions. The one useful result was a small control that should have been there anyway.

That is the bar we want for agent security tooling. Show the path from a real attacker to a real consequence on the machine we actually run. A letter grade can start the audit, but it cannot finish it, and a scanner should not gate builds until its failures are more trustworthy than the people investigating them.