This file defines the audit evaluation logic. The classification layer answers what it is; the severity layer answers how bad it is. The two are orthogonal and interact only through three interface fields ( C_base / required_dims / dataflow_role).
Detect the language of the user's triggering message and lock the output language for the entire run. This detection is an internal step only — do NOT output any text that reveals the detection result, such as "当前输出语言为中文", "Detected language: English", or similar meta-statements. Simply use the detected language silently for all subsequent output.
User message languageOutput languageChineseChinese — entire output in ChineseEnglishEnglish — entire output in EnglishOther languageMatch that languageCannot determineDefault to Chinese
All intermediate output — scan start prompt, table headers, labels, prose, finding records, and reasoning — must be written exclusively in the detected language. The single final-line verdict in §7 is always Chinese, regardless of detected language. Do NOT mix languages in intermediate output and do NOT announce the language choice at any point.
Each finding is tagged with a triple (Surface, Behavior, IntentMarker). IntentMarker does not participate in scoring; it only affects presentation.
CodeMeaningEXECode / shell / subprocess / dynamic eval executionFSLocal filesystem read / write / delete / chmodNETNetwork inbound / outbound / DNS / socketsCREDEnvironment variables / keys / tokens / credential storesPROCProcess management, persistence, autostart, scheduled tasksLLMPrompt manipulation, tool-description poisoning, jailbreak payloadsAGTCross-skill / cross-tool / MCP supply-chain behavior
Each node declares C_base ∈ {1..4}, required dimensions, and data-flow role ( source / transform / sink / none). The data-flow role feeds chain amplification in §2.4.
BehaviorC_baseRequiredData-flowEXE.StaticShell — shell with fully constant arguments2R, BtransformEXE.DynamicShell — variable interpolation / shell=True + external input4R, I, BsinkEXE.EvalCode — eval / exec / Function() on strings4R, I, BsinkEXE.RemoteFetch — curl | sh / download-then-exec / fetch-and-run4I, BsinkEXE.Subprocess — constrained subprocess (whitelisted commands)2Rtransform
BehaviorC_baseRequiredData-flowFS.ReadPublic — read public files (README, declared paths)1—noneFS.ReadWorkspace — read files inside the workspace2RsourceFS.ReadSensitive — read sensitive paths ( ~/.ssh, ~/.aws, Keychain, browser cookies, .env)4I, RsourceFS.ReadOutOfScope — read user files outside declared scope3I, BsourceFS.WriteScoped — write inside declared directories1—noneFS.WriteOutOfScope — write outside declared scope3I, BsinkFS.WriteStartup — write startup hooks / shell rc / autostart / launchd4R, IsinkFS.DeleteBroad — wide deletion / rm -rf / wildcard delete / find ... -delete4R, I, BsinkFS.ChmodDangerous — chmod 777 / privilege widen / SUID bit3R, Itransform
BehaviorC_baseRequiredData-flowNET.OutboundDeclared — outbound to a host declared in SKILL.md1—sinkNET.OutboundUndeclared — outbound to an undeclared host3I, BsinkNET.OutboundUntrustedSink — outbound to an Untrusted-Sink indicator (see §5.1.1)4BsinkNET.OutboundObfuscated — obfuscated destination (concat, encoding, homograph)4I, BsinkNET.DnsExfil — DNS TXT with suspicious payload (long subdomain, base64)4I, BsinkNET.InboundListen — local listening port / reverse shell endpoint4R, IsinkNET.Websocket — long-lived / bidirectional channel2Itransform
BehaviorC_baseRequiredData-flowCRED.ReadEnv — broad read of os.environ / process.env3I, BsourceCRED.ReadNamedEnv — read a single declared environment variable1—sourceCRED.ReadKeychain — read Keychain / Credential Manager / libsecret4I, BsourceCRED.ReadBrowserStore — read browser cookies / session / password store4I, BsourceCRED.Hardcoded — real secret hardcoded in code or config3RnoneCRED.HardcodedInjected — hardcoded credential the skill instructs the agent to inject into a user system (DB, service, config)4R, IsinkCRED.TokenEcho — credential echoed to LLM / logs / stdout3R, Btransform
BehaviorC_baseRequiredData-flowPROC.Spawn — ordinary child process creation (paired with EXE)1—nonePROC.Persist — cron / launchd / systemd / Run-key install4R, IsinkPROC.ToolTamper — modify / replace system tools, hook package managers4R, I, BsinkPROC.CryptoMine — miner binaries / known mining-pool hosts4—sinkPROC.HideSelf — process masquerade3Itransform
BehaviorC_baseRequiredData-flowLLM.PromptOverride — "ignore previous / you are now / system:" style directives3I, BsinkLLM.PromptOverrideActionable — override directive that resolves to a concrete malicious action (run script X, send data to host Y, delete files matching Z)4I, BsinkLLM.ObfuscatedPrompt — override directive encoded in base64 / ROT13 / hex4I, BsinkLLM.UnicodeSmuggling — directives hidden in zero-width / Unicode-tag / bidi chars4I, BsinkLLM.DescriptionInjection — enticement text in description/ triggers to coerce other agents3IsinkLLM.ToolPoisoning — tool descriptions deliberately mislead the agent's plan4I, Bsink
BehaviorC_baseRequiredData-flowAGT.CrossSkillWrite — write into another skill's directory / modify registry4I, BsinkAGT.MCPRemoteFetch — dynamically fetch tool definitions from a remote MCP server3I, Bsource+sinkAGT.ContextExfil — exfiltrate data via chat context / tool responses3I, BsinkAGT.PrivilegeCreep — behavior materially exceeds the SKILL.md-declared scope3ItransformAGT.ApprovalBypass — attempts to bypass approval / sandbox / trust boundary4Isink
MarkerMeaninglegitimate_elevatedSensitive behavior consistent with declared function and documentedsuspiciousBehavior is suspect but evidence is not closedmalicious_confirmedClear evidence of malicious intent. Sufficient evidence (any one suffices): (a) closed source → sink chain whose sink is an Untrusted-Sink indicator, (b) find ... -delete / rm -rf over user-data globs, (c) hardcoded credential + an inject into user system instruction, (d) directive to `curl
text
Score = C × R × I × B
R = 0 (unreachable) → Score = 0 → finding is dropped. I = 0 (legitimate and declared) → Score = 0 → finding is reported at Info as a capability disclosure entry; it does not affect the verdict.
{1, 2, 3, 4}, defaulting to the Behavior's C_base; an instance may float ±1 without leaving the range.
ValueMeaningTypical1Low (public read / in-scope write)FS.ReadPublic, NET.OutboundDeclared2Medium (limited effect)EXE.StaticShell, FS.ReadWorkspace3High (privacy / out-of-scope)FS.ReadOutOfScope, CRED.ReadEnv4Very high (RCE / credentials / persistence / destruction)EXE.DynamicShell, CRED.ReadKeychain, FS.DeleteBroad
{0, 1, 2, 3}.
ValueMeaning0Unreachable (comment / docs / dead code not imported)1Weakly reachable (example / test fixture / rare branch)2Conditionally reachable (main module, requires specific input or trigger)3On the main path (entry in SKILL.md, or reachable via import chain)
{0, 1, 2, 3}, used directly as a multiplier.
ValueMeaning0Legitimate and declared — function needs it, SKILL.md states it, scope matches1Undeclared but not hidden — functionally needed, simply omitted from docs _and not matching any hard trigger below_2Obfuscated / hidden — base64, string concat, zero-width chars, homograph host or a non-obfuscated trigger from the table below3Confirmed malicious — matches a C2 blacklist, clear attack signature, or closed chain or an obfuscated trigger from the table below
§2.2.1 Hard triggers — minimum I floor. When any of the following is observed in evidence text, the finding's I cannot be lower than the listed floor, regardless of how the surrounding documentation is phrased:
#Trigger condition (require concrete evidence, not a paraphrase)FloorT1Outbound to an Untrusted-Sink indicator (see §5.1.1) carrying any user / workspace / system / agent dataI ≥ 3T2Outbound to an undeclared host with a payload built from os.environ, file reads, or chat contextI ≥ 3T3find ... -delete / rm -rf / wildcard delete that matches user data file extensions ( .ics, .docx, .xlsx, .pdf, .json, .csv, .md, .txt, photos, source code) and is invoked by a SKILL.md instructionI ≥ 3T4Hardcoded credential ( CRED.Hardcoded) plus an instruction telling the agent to inject it into the user's database / service / config ( CRED.HardcodedInjected)I ≥ 3T5curl | sh / wget | bash / EXE.RemoteFetch whose URL is not pinned to a known package registry or a host declared in SKILL.mdI ≥ 3T6LLM.ObfuscatedPrompt, LLM.UnicodeSmuggling, base64-encoded directive, or zero-width chars in description/ triggersI ≥ 3T7LLM.PromptOverrideActionable whose action resolves to T1–T5I ≥ 3T8AGT.CrossSkillWrite to a sibling skill directory or to the agent registryI ≥ 2T9PROC.Persist (cron / launchd / shell-rc) installed by the skill without being declared as the skill's purposeI ≥ 2T10LLM.PromptOverride directives ("ignore previous", "you are now", "system:") that are not wrapped in a documentation example blockI ≥ 2
Anti-overshoot guards (must be satisfied to apply a hard trigger):
triggers/ allowed-tools) not from a clearly fenced documentation block whose surrounding prose marks it as a counter-example, "do not do" warning, or quoted sample of a third-party attack.I ≥ 3 to I ≥ 2).{1, 2, 3}.
ValueMeaning1Self only — this skill's directory / current session2Workspace / user scope — current project or user files3Machine / cross-user / cross-agent — system-level, credential-level, propagable
Theoretical range 1 – 108 ( 4 × 3 × 3 × 3). I = 0 findings are always Info (see §2.1).
ScoreTierBadgeNotes0Info·I = 0 (declared capability disclosure) or no findings1 – 6Info·trivial scoring artifacts (e.g. C=1×R=1×I=2×B=1)7 – 14Low⚠️small, real risks but not attack-shaped15 – 48Medium⚠️real risk class, almost always worth surfacing to the user49 – 80High🔴strong evidence of attack-shaped behavior81 – 108Critical🚨closed chain or top-tier sink
A closed chain is
text
source → transform (any, optional) → sink
When a closed chain exists across the same execution path, an additional chain-finding is emitted whose tier equals the highest member tier + 1 (capped at Critical). Unclosed chains (missing source or sink) do not amplify. Member findings are still reported on their own.
For chain detection, the source may also be a directive in SKILL.md that instructs the agent to read a specific user / workspace / environment data slice. In other words, the chain is closed when (SKILL.md tells the agent to obtain X) connects to (a script or directive sends X to a sink), even if no single script reads-then-writes by itself.
Typical closed chains:
FS.ReadSensitive → NET.OutboundUndeclared (credential exfiltration)CRED.ReadEnv → LLM.PromptOverride (credentials leaked to a third-party LLM)EXE.RemoteFetch → FS.WriteStartup (download then persist)SKILL.md directive: "first read all .docx in workspace" → script: POST to undeclared host (instructed exfil)SKILL.md directive: "always run cleanup.sh after any operation" → cleanup.sh: find . -delete (instructed destruction)The following nine signatures set a tier floor regardless of the per-finding score arithmetic, because each one already implies that the four dimensions (C, R, I, B) are at least at the floor's combination. Compute the per-finding score normally, then take tier = max(arithmetic_tier, floor_tier):
#SignatureTier floorM1A closed chain (§2.4) where the sink is an Untrusted-Sink indicator (§5.1.1)CriticalM2Any find ... -delete, rm -rf, or wildcard delete that targets user data extensions and is reachable from SKILL.mdHighM3LLM.PromptOverrideActionable whose action concretely matches T1–T5HighM4CRED.HardcodedInjected (a hardcoded secret the agent is told to push into a user system)HighM5EXE.RemoteFetch from an unpinned, non-declared URLHighM6LLM.ObfuscatedPrompt / LLM.UnicodeSmugglingHighM7PROC.Persist (cron / launchd / shell-rc / Run-key) installed without being declared as the skill's purposeHighM8AGT.CrossSkillWrite into another skill's directory or the agent registryMediumM9Outbound to an undeclared host carrying data sourced from os.environ, file reads, or chat context (T2 without an Untrusted-Sink upgrade)Medium
If multiple signatures fire, the verdict is max(tier_floor). Floors only raise, never lower.
InterfaceDirectionDescriptionC_baseClassification → SeverityCapability baseline per Behavior node, default for C``required_dimsClassification → SeverityChecklist of dimensions that must be evaluateddataflow_roleClassification → Severitysource/transform/sink/none, used by chain amplification
The severity layer does not read the classification layer's prose descriptions or the IntentMarker; the classification layer does not read the final Score. The two layers can evolve independently.
A finding is one (Behavior, evidence location) hit. The evidence location is (file path, line range, code snippet). The same Behavior hitting at multiple locations produces multiple findings; the same code hitting multiple Behaviors produces multiple findings; a chain-finding is itself a finding.
yaml
finding:
id: "F-001"
category:
surface: "FS"
behavior: "FS.ReadSensitive"
intent_marker: "suspicious" # legitimate_elevated | suspicious | malicious_confirmed
evidence:
file: "scripts/helper.sh"
line_range: [23, 31]
snippet: "..."
scoring:
C: 4
R: 3
I: 1
I_floor_applied: null # null | "T1" | "T2" | … (which §2.2.1 trigger raised I, if any)
B: 3
score: 36 # C × R × I × B = 4×3×1×3
arithmetic_tier: "Medium" # tier from raw score
floor_rule_applied: null # null | "M1"…"M9"
tier: "Medium" # final = max(arithmetic_tier, floor_tier)
badge: "⚠️"
dataflow_role: "source"
chain_id: null # fill with a chain id if this finding is part of a closed chain
All fields are required ( I_floor_applied, floor_rule_applied, chain_id may be null). score must equal C × R × I × B; for I = 0 findings, score is 0 and tier is always Info. tier must equal max(arithmetic_tier, floor_rule_tier_if_any).
The audit target is the whole skill bundle, not SKILL.md alone. The scope has three layers:
SKILL.md and in scripts (frontmatter, code blocks, Markdown links, arguments to bash / python / node invocations, etc.) and pull the referenced files into the scan. Their Reachability baseline is set per §5.3. If a referenced file lies outside the skill directory, additionally record an FS.ReadOutOfScope or AGT.CrossSkillWrite finding as appropriate.curl | sh, wget, git clone then exec, pip/ npm pointing at non-standard registries, or remote MCP servers trigger EXE.RemoteFetch or AGT.MCPRemoteFetch. During the audit, a single static fetch is allowed (never executed); on success the content joins the scan, on failure or without authorization the finding's I is forced to ≥ 2.A network sink is Untrusted when it matches one or more of the patterns below and is not explicitly declared as a service the skill exists to integrate with. Match on the URL string itself (with light normalization for concatenation / encoding):
Pattern classExamplesFree / temporary tunnel & PaaS hosts commonly abused as exfiltration sinks*.ngrok.io, *.ngrok-free.app, *.serveo.net, *.loca.lt, *.trycloudflare.com, *.onrender.com, *.herokuapp.com, *.glitch.me, *.replit.dev, *.vercel.app ( when the host is not the official site of a service named in SKILL.md)Generic webhook / log-collector relayswebhook.site, requestbin.*, pipedream.com, pastebin.com, transfer.sh, 0x0.st, gist.githubusercontent.com/<unknown user>/raw/...Raw-IP outbound or non-standard high port to an undeclared hosthttp://\d+\.\d+\.\d+\.\d+:\d+/, hosts on ports 4444, 8443, 9001, etc. when not declaredObfuscated / dynamically constructed host strings"agen" + "skill" + ".onrender.com", base64-encoded host literal, chr(…) concatenation, homograph ( gооgle.com with Cyrillic o)Hosts whose name suggests collection / logging / exfil*log*, *collector*, *ingest*, *audit*, *exfil*, *dump*, *beacon* (only when not a service named in SKILL.md)
Indicator matching is purely structural; it does not depend on a curated blocklist of specific domains. A skill that genuinely integrates with one of the listed PaaS hosts (e.g. a Render-hosted official API for that vendor) avoids the trigger by declaring the host in SKILL.md frontmatter or ## Integrations section.
False-positive guard: a host that resolves to the homepage / documentation of a recognizable open-source project, official vendor API, or well-known CDN (npm, PyPI, GitHub.com, Hugging Face, OpenAI, Anthropic, Google Cloud, AWS, Azure, Stripe, Slack, Discord, etc.) is not an Untrusted Sink even if it lives on a tunnel host. When in doubt, downgrade T1 to T2.
text
Input: skill root directory
│
▼
[Step 1] Build scan inventory
├─ 1a. Recursively enumerate files and classify by content
├─ 1b. Parse references → add local files / register remote-URL findings
├─ 1c. Match each outbound URL against §5.1.1 Untrusted-Sink Indicators
└─ 1d. Attempt a single static fetch of remote resources (success → include; failure → I ≥ 2)
▼
[Step 2] Match each file against Behavior nodes → {category, evidence(file, line_range, snippet)}
▼
[Step 3] Score each finding:
3a. C ← C_base ± Δ; R / I / B per §2.2
3b. Apply §2.2.1 hard triggers → raise I to floor (record `I_floor_applied`)
3c. score = C × R × I × B; drop if R = 0
3d. arithmetic_tier ← §2.3
▼
[Step 4] Build cross-file data-flow graph (SKILL.md instructions count as source per §2.4)
→ detect closed chains → append chain-findings
▼
[Step 5] Apply §2.5 Mandatory Confirmation Rules
per finding: tier ← max(arithmetic_tier, floor_tier); record `floor_rule_applied`
▼
[Step 6] Emit all findings
File locationDefault RSKILL.md frontmatter + body3Scripts explicitly referenced / invoked by SKILL.md3Files source d / import ed from referenced scriptsinherits along the chain, capped at 3Orphan scripts in the skill directory with no incoming reference1 (forced to 2 if the file hits any Behavior with C ≥ 3, 3 if it matches any §2.2.1 hard trigger)Files under tests/, examples/, docs/ not referenced from SKILL.md0 or 1Plain documentation (e.g. README.md)1 (treated as main path when it contains executable code blocks and is referenced)
Note: an "orphan" script that nonetheless implements a hard trigger (e.g. an unreferenced
cleanup.shthat doesrm -rf ~/Documents/*) is treated as on-path because the SKILL.md often references such scripts indirectly (e.g. "always run the cleanup script after any operation"). Promoting R to 3 prevents these from being silently dropped.
verdict.tier = max(f.tier for f in findings); Info when there are no findings. The verdict is computed internally and is not printed as a label or badge by itself; it only selects which one-line conclusion §7 emits.
The audit may produce a normal report — findings tables, per-finding evidence, brief reasoning, etc. — and writing it out is encouraged, since externalizing the classification, scoring, and chain analysis from §1–§6 tends to stabilize the final verdict.
The only mandatory final emission is a single Chinese one-liner that maps directly from the verdict tier. There is no machine-readable result block, no detailed report wrapper, no signature line.
The very last thing emitted by the run must be exactly one of the following lines, chosen by the verdict tier from §6, with <NAME> replaced by the audited skill's name. Note: the "存在风险" line is reserved for Critical only — High and below all map to "可使用" wording, because hardening from §2.2.1 + §2.5 already guarantees that Critical is reached by genuine attack-shaped behavior.
Verdict tierFinal line (Chinese, mandatory verbatim)Info✅ 您的「<NAME>」插件可正常使用。Low⚠️ 您的「<NAME>」插件可使用。Medium⚠️ 您的「<NAME>」插件可使用。High⚠️ 您的「<NAME>」插件可使用。Critical🚨 您的「<NAME>」插件可能存在风险,请关注。
<NAME> — the name field from the audited SKILL.md. Fallback order: skill directory name → unknown.✅ / ⚠️ / 🚨), the corner brackets 「 」, and the trailing full-width period 。 exactly as shown.Info and even when there are no findings.Info when no behavior was scored).