Skip to content
T threat.cafe

· Priya Venkatraman (editorial persona) · Enforcement architecture · on threat.cafe

Known bad, no known bad, unknown: a three-state policy model for agent artifacts

Your agent artifact policy needs three answers, not two. Known bad means a trusted source has a record that matches this exact package, skill or server version. No known bad means someone you trust checked this exact build and found nothing. Unknown means nobody you trust has checked it. Most of what agents install today is unknown: in one August 2026 census of the skills.sh catalogue, 87.8% of listings had never been audited. A two-state "safe or unsafe" verdict forces you either to block most new artifacts or to wave through code nobody has read. Three states let you block, log or ask a person, each where it fits.

The question you are actually asking

You want a rule your agents can follow without a person reading every install. You have some sources of bad news: advisories, scanners, a list someone curates. When those sources say nothing about an artifact, you have to decide what "nothing" means.

That is the whole problem. "Nothing" can mean two very different things.

Why "safe" is the wrong word

Think of a food-safety inspector. A restaurant can be in three states. It failed an inspection. It passed an inspection last month. Or it opened yesterday and nobody has inspected it yet. The third restaurant is not unsafe. It is also not "passed." A sign on the door that says "no violations on record" would be true and misleading at the same time.

Agent artifacts work the same way. A scanner that returns no findings has told you something only if it actually scanned that exact version. A lookup that finds no record in an advisory database has told you nothing about an artifact the database has never seen.

The tools you already use hint at this. ClawHub sorts VirusTotal results into benign, suspicious and malicious, and still warns users that "a clean scan doesn't mean a skill is safe" (OpenClaw, 7 February 2026). The OSV format, which most open advisory data uses, has no field for "checked and clean" at all. A version that matches no record is simply a non-match (OSV schema). If your policy needs to know that someone checked, you have to store that fact yourself.

How big the unknown pile is

The Skillselion census took a dump of the skills.sh catalogue on 12 August 2026 (Skillselion, Agent Skill Security Census):

  • 79,848 listings, with 41,791 audit verdicts from five providers: Socket, Snyk, Gen Agent Trust Hub, ZeroLeaks and Runlayer.
  • 87.8% of listings had never been audited.
  • The audited 12.2% held 83.3% of installs, so popular skills are more likely to have been checked.
  • Among the 1,233 listings rated by more than one auditor, 12.7% had one auditor rate them HIGH or CRITICAL while another rated them LOW or SAFE.

The census counts registry install events, not active users, and it inherits each auditor's standards. Even so, the shape is clear. Most listings sit in the unknown pile. And even "checked" is not one answer when checkers disagree one time in eight.

The three states, defined

State          What must be true                                  What it is NOT
-------------  -------------------------------------------------  -----------------------------
known_bad      A trusted, non-withdrawn record matches this        A guess, a heuristic score,
               exact name and version (or content hash)            or a similar-looking name
no_known_bad   A named checker examined this exact build, on a     A promise. It expires and it
               recorded date, and reported nothing malicious       resets on every new version
unknown        Neither of the above                                 Unsafe. Just unexamined

Two rules keep the states honest.

A new version starts at unknown. A check of version 1.0.15 says nothing about 1.0.16. The postmark-mcp case shows why: the hidden BCC line that copied every outgoing email arrived in version 1.0.16, not in the package's first release (The Hacker News, 29 September 2025).

Conflicting verdicts are unknown, not no known bad. If one auditor says CRITICAL and another says SAFE, nobody has given you a clean answer. Treat the artifact as unexamined until a person resolves the conflict.

Default actions by state and place

The same state can deserve a different action on a laptop, in CI and at a gateway. Here is a starting table:

State          Developer laptop                 CI pipeline                      Remote MCP gateway
-------------  -------------------------------  -------------------------------  ------------------------------
known_bad      Block                            Block, fail the build            Block the connection
no_known_bad   Proceed, log                     Proceed, log                     Proceed, log
unknown        Low privilege: warn and log      Block anything not already in    Ask a person before first
               Shell/network/secrets: ask       the lockfile or approved list    connection

Why the split for unknown on a laptop? Because privilege matters more than novelty. An unknown skill that only formats Markdown is a small bet. An unknown skill that runs shell commands, or an MCP server that holds your tokens, is a large one. The same census found that shell-capable skills account for at least 43.5% of installs, so "ask on high privilege" is not a rare branch.

Why block unknown in CI? A pipeline should install what a person already approved and pinned. New artifacts belong in a pull request, not a build.

Warn budgets: how to avoid drowning people

Warnings only work if people read them. Splunk's 2025 survey of 2,058 security leaders found that 59% have too many alerts and 55% deal with too many false positives (Cisco newsroom, 20 May 2025). An agent that warns on every unknown install will add to that pile within a week.

A warn budget is a cap on how many unknown-state warnings one person sees in a day. When the cap is reached, the next unknown does not produce another warning. It goes to a review queue instead, and the install waits or proceeds according to its privilege class. The effect is that warnings stay rare enough to read, and the overflow becomes work for a reviewer rather than noise for a developer.

A worked example

Illustrative example: a team's agents install 100 new artifacts in a week. Suppose 90 are unknown, 9 are no known bad, and 1 is known bad. The numbers are round on purpose.

  • Two states, block anything not proven clean. 91 blocks. Developers turn the control off by Wednesday.
  • Two states, allow anything not proven bad. 1 block, 90 unexamined installs, and no record that they were unexamined.
  • Three states with a privilege split. 1 block. Of the 90 unknowns, say 60 are low privilege: logged, with a warning up to each person's budget. The other 30 ask a person. The 9 no-known-bad installs proceed and are logged.

The third row still asks for 30 decisions. That is the honest cost of a large unknown pile. But every one of those 30 is a high-privilege artifact nobody has checked, which is exactly where a person's attention belongs.

A sample policy file

The format below is illustrative and not tied to any tool. It shows the decisions a real policy has to encode:

# agent-artifact-policy.yaml (illustrative format)
states:
  known_bad:
    laptop: block
    ci: block
    gateway: block
  no_known_bad:
    laptop: allow_and_log
    ci: allow_and_log
    gateway: allow_and_log
    expires_after_days: 30      # re-check; new versions reset to unknown
  unknown:
    laptop:
      low_privilege: warn_and_log
      high_privilege: ask        # shell, network, secrets, MCP servers
    ci: block_unless_pinned
    gateway: ask
conflicting_verdicts: unknown
warn_budget_per_person_per_day: 3
overflow: review_queue

Mapping the states to OSV data

If your bad news comes from OSV-format records, the mapping is short:

  • known_bad: an OSV record, often with a MAL- identifier, whose affected versions include this version and which has no withdrawn timestamp.
  • A withdrawn record: the artifact drops back to whatever your own check history says. Withdrawal means the report was wrong, not that someone checked the artifact.
  • no_known_bad: not expressible in OSV. Store it beside your data: checker, date, exact version or hash.
  • unknown: the default when neither exists.

Where this simple model stops working

Known-bad lists lag. In the March 2026 axios compromise, the malware advisory was published at 03:15:49 UTC on 31 March (OSV, GHSA-fw8c-xr5c-95f9), close to three hours after the first bad version reached the registry at 00:21 UTC (npm registry). During those hours, the artifact was unknown, not known bad. The three states do not shrink that gap. Pair them with controls that act on unknown, such as a release-age delay or the privilege split above.

No-known-bad also ages. A check is a snapshot of one build on one day.

Decision checklist

  • Can your tools tell "checked, nothing found" apart from "never checked"? If not, start recording check history.
  • Does a new version reset to unknown?
  • Are conflicting verdicts treated as unknown?
  • Do unknown artifacts with shell, network or secret access ask a person?
  • Does CI block anything not pinned or approved?
  • Is there a warn budget, and where does the overflow go?
  • What share of installs land in each state each week? Track it; it is the number that tells you whether the policy is livable.

The threat.cafe counter uses the same three answers, known bad, no known bad and unknown, if you want to see the vocabulary applied to real 2025 and 2026 artifacts.

Sources

#policy #agent-skills #mcp #alert-fatigue #osv

All posts