ADR-027 — Skill Content Trust Layer (Heuristic Scan, Warn-Don't-Block)
- Status: Reverted (2026-06-20) — see the revert note below
- Date: 2026-06-19
- Deciders: Yuxing Wu
- Spec: 005-skill-manager (FR-028, FR-029 — both removed)
⚠️ Reverted (2026-06-20, simplification 4.5). The skill content-trust scan layer is removed — Coffer no longer scans imported skills for risky content, no longer caches a verdict, and no longer gates enablement on an acknowledge. The heuristic was high-false-positive and, with the Git-fetch path already gone (4.3), the remaining local-import threat model did not justify the layer for a personal tool. The decision below is retained for history.
Amended (2026-06-20, simplification 4.3). Skill Git-fetch was removed; the scan-on-ingest layer now covers local import + adopt + in-place edits only.
Context
The competitive-landscape research (docs/research/agent-skills.md) identified the trust layer as the #1 highest-leverage gap: the whole skill ecosystem ships with no built-in sandboxing or signing, and the real threat is the bundled scripts a skill carries (OWASP "Agentic Skills Top 10", Snyk's ToxicSkills, a malicious file that passed every one of Anthropic's scanners). Coffer hardened fetching (SSRF guard, shallow clone, size cap, neutralized repo hooks — fetch since removed by simplification 4.3) but never looked at the content. For a tool that markets itself as a vault, content trust is on-mission.
A hard constraint shapes what is even possible: Coffer delivers skills but never runs them. The host agent (Claude Code, Codex, …) executes a skill's scripts and is the only thing that can honor a skill's allowed-tools. Coffer therefore cannot enforce runtime behavior. Its leverage is at ingest and enable time: make risk legible, and gate the act of granting a skill to an agent.
Decision
Add a level-L2 content trust layer: heuristic scanning plus a warn-don't-block acknowledgment gate.
- Heuristic scan. A pure domain scanner (
domain/skill/content_scan.py) walks a skill's text files and applies a small, versioned rule set (remote-exec pipes, network egress, secret/credential access, dangerous deletes, privilege escalation, obfuscated blobs), producing severity-tagged findings and an overall verdict. It is heuristic and non-authoritative: a finding is a prompt to review, and a clean report is not a guarantee. The ruleset carries a version so a stored verdict can be known stale. - Scan on every content entry. Import, adopt, and in-place file edits all re-scan; the verdict, findings count, ruleset version, and scan time are cached on the skill config (no migration —
config_jsonis opaque). Every scan is audited. - Warn, don't block ingest. A risky skill always enters the master store. Over-blocking is the wrong default — false positives are common and a clean scan is not proof of safety, so blocking ingest would train users to bypass the tool while providing false assurance.
- Gate enabling, not ingest. When the verdict is
high/critical, enabling the skill for an agent is refused (409) until the user explicitly acknowledges the risk (audited). The follow/auto-bind reconcilers skip such a skill rather than delivering it. Acknowledgment is content-scoped: it resets whenever the skill's content changes. Adoption is exempt — it consolidates a skill already present in the agent's workspace, so blocking it would remove a skill the agent had and refuse to restore it.
Alternatives considered
- L1 — inventory + provenance only. Surface scripts/hashes/declared
allowed-tools/source, no risk judgment. Rejected as the target: it leaves the user to eyeball every script. L1 is retained as the foundation L2 builds on. - L3 — policy enforcement that blocks ingest/enable on critical findings. Rejected for now: with documented scanner-evasion and false positives, hard blocking would reject legitimate skills and give false assurance. L3 remains a future opt-in policy on top of this layer.
- Enforce
allowed-toolsat runtime. Impossible by construction — Coffer does not execute skills. We parse and retainallowed-tools(ADR-aligned with FR-027) for legibility and future mismatch findings only.
Consequences
- Users get a per-skill risk signal and an auditable acknowledgment trail; a flagged skill is not silently delivered to every following agent.
- The scan is best-effort and heuristic; it will both miss (false negatives) and over-flag (false positives). The verdict is advisory, never a safety claim.
- Future work (roadmap items): content-scan findings surfaced in discovery,
allowed-tools-vs-behavior mismatch findings, and an optional L3 policy.