Cisco AI Defense skill scanner: evaluation results

What changes when the LLM judge and the System One model are switched on.

Corpus shape decides how a skill can be packed

The benchmark corpora store skills as one or two text fields, not as directories, so they almost always fit a System One model's context whole. Real-world skills do not. Any packing or cost conclusion drawn from the small golden fixtures would be wrong about real input.

PopulationMedian filesMedian scanner-relevant bytesFits a 32k-token window whole
MaliciousSkillBench rows1 (single text field)2,936essentially all
ClawHub rows2 (skill_md + bundle)24,173most
Golden fixtures31,797100%
Real-world skills80576,74516%

The packing ladder

Three rungs, tried in order. A sends the whole record when it fits, which covers essentially every benchmark row. B fills the budget by priority when it does not: SKILL.md first because it fits 96% of the time and is the instruction surface where prompt injection lives, then scripts it references, then remaining code ordered by detected content type rather than file extension, then documentation truncated hardest. C falls back to one request per candidate finding for the long tail.

Measured, the ladder puts every benchmark row on rung A and 84% of real-world skills on rung B.

Rules that hold on every rung

Judge model, verified rather than assumed

PropertyWhat was measured
One model, one routegoogle.gemma-4-26b-a4b is reachable only on the Bedrock mantle OpenAI-compatible route. It is absent from list-foundation-models, there is no model-listing endpoint, and no other Gemma 4 size exists, so the model id is a pinned constant.
Strict schema works, after one removalSchema-constrained JSON succeeds once uniqueItems is stripped; the mantle validator rejects it outright and the request would otherwise degrade silently to loose JSON mode.
Context is ampleA 124,989-byte request was accepted at 40,855 prompt tokens, so the scanner's 100,000-character budget fits with headroom.
Prompt caching does not engagecached_tokens and cache_write_tokens stayed at zero across two byte-identical 4,859-token requests, so the largest assumed cost lever is unavailable on this route.
Code tokenizes worse than proseMeasured at 3.06 bytes per token on repetitive Python, not the 4.0 a naive estimator assumes, which is why budgets are enforced in bytes at a pessimistic 2 bytes per token.

Reproducibility discipline