cfgaudit

Rules / CFG102

CFG102 two committed skills claim the same name, and one is silently dropped

Severity: warn OWASP: LLM04:2026 – Supply Chain OWASP Agentic: ASI04:2026 – Agentic Supply Chain Vulnerabilities: LLM04:2026 now covers the model and data supply chain and refers the agentic one, "including MCP servers and tool registries", to ASI04

What cfgaudit checks

Two SKILL.md files under one skills root declaring the same frontmatter name. Only one of them is loaded.

Measured against Copilot CLI 1.0.80 with copilot skill list, which resolves the real registry:

Setup Listed
aaa-real/ and zzz-shadow/, both name: deploy one entry, from aaa-real
aaa-shadow/ and zzz-real/, both name: deploy one entry, from aaa-shadow

The alphabetically first directory wins. The other file is not listed, not warned about, and not reachable under any other name. Verified in both directions so the ordering is the cause rather than a coincidence of naming.

Why this is worth a finding

A skill's name comes from its frontmatter, and the directory contributes nothing to it (#500). So nothing in the tree tells a reader which of two directories is dead. A repository can add a skill that replaces one contributors already rely on, and the only visible difference is a directory name that sorts earlier.

It is warn rather than error because a duplicate name is also an ordinary authoring mistake, and cfgaudit cannot tell the two apart from the files alone. Either way one skill is not running and nobody is told.

Why this rule exists and its sharper sibling does not

#501 recorded a stronger mechanism: a committed skill can replace a built-in Copilot skill by claiming its name, measured the same way. That one is not reported, because it needs a list of what the agent ships, the set is version-dependent, and asserting a list cfgaudit cannot verify is what the version-gate convention exists to prevent.

A collision between two committed files needs no such list. Both sides are in the scanned tree, so the finding is complete from the repository alone. That is what made a rule possible here at all.

What is not reported

Footprint

30 real repositories with two or more skills under .github/skills were scanned: 0 findings. The rule fires only on an exact duplicate name, which does not occur naturally in that sample, so this is preventive coverage of a measured mechanism rather than a common defect.

Trigger example

.github/skills/aaa-shadow/SKILL.md    name: deploy
.github/skills/zzz-real/SKILL.md      name: deploy

→ warn: only aaa-shadow loads; zzz-real is dropped.

References

Documentation for cfgaudit 1.18.1. Source of this page · Rule implementation