From 784b23e62ff0df01ec4e8223c15fc62b3c8a46b4 Mon Sep 17 00:00:00 2001 From: "A. Ramos" Date: Fri, 22 May 2026 19:58:02 +0200 Subject: [PATCH 1/8] cvss4, finding names, recon read --- .opencode/skills/exploit-development/SKILL.md | 26 +++++++--------- .opencode/skills/finding-format/SKILL.md | 31 ++++++++++++++++--- AGENTS.md | 2 +- prompts/phase-1-recon.md | 3 ++ templates/finding.md | 4 +++ 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/.opencode/skills/exploit-development/SKILL.md b/.opencode/skills/exploit-development/SKILL.md index 466bbd6..08eadf6 100644 --- a/.opencode/skills/exploit-development/SKILL.md +++ b/.opencode/skills/exploit-development/SKILL.md @@ -218,24 +218,18 @@ Good PoC: if "DB_PASSWORD" in line: print(f"\n[!] IMPACT: Database password exposed: {line}") -## Severity adjustment rules +## Severity adjustment -### When to upgrade +After exploitation, recompute the CVSSv4 vector with the metrics now justified by demonstrated impact. The new score band determines the new label. -| From | To | Condition | -|---|---|---| -| INFO | LOW | Any concrete exploitation demonstrated | -| LOW | MEDIUM | Impact affects confidentiality, integrity, or availability meaningfully | -| MEDIUM | HIGH | Code execution, sensitive data exfiltration, or privilege escalation demonstrated | -| HIGH | CRITICAL | Unauthenticated RCE, full database access, or cross-tenant compromise demonstrated | +Typical metric refinements after a working PoC: -### When to downgrade +- `AC` / `AT` may drop once attack complexity and prerequisites are proven. +- `PR` / `UI` may drop if no authentication or interaction is actually required. +- `VC` / `VI` / `VA` (and `SC` / `SI` / `SA`) rise when concrete confidentiality, integrity, or availability impact is demonstrated on the vulnerable or subsequent system. +- `E` (threat) rises from `P`/`U` to `A` once a working PoC exists. -| From | To | Condition | -|---|---|---| -| CRITICAL | HIGH | Exploitation requires authentication or specific preconditions | -| HIGH | MEDIUM | Impact is limited to non-sensitive data or requires unlikely conditions | -| MEDIUM | LOW | Exploitation achieves only DoS or minimal information disclosure | +Score bands and labels are defined in `.opencode/skills/finding-format/SKILL.md`. Do not pick a label without recomputing the vector. ### Documentation requirement @@ -244,6 +238,10 @@ Always record: exploitation: severity_before: "MEDIUM" severity_after: "HIGH" + cvss_v4: + vector: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:A" + score: 9.3 + justification: "Unauthenticated RCE demonstrated against a default install (see PoC and recording)." And include justification in `# Exploitation Result`. diff --git a/.opencode/skills/finding-format/SKILL.md b/.opencode/skills/finding-format/SKILL.md index 4cc192d..b3f1d49 100644 --- a/.opencode/skills/finding-format/SKILL.md +++ b/.opencode/skills/finding-format/SKILL.md @@ -55,17 +55,22 @@ Rules: - Use four digits for the numeric id. - Use lowercase slugs. - Use hyphens instead of spaces. -- Keep the slug short but meaningful. +- Generate the slug from a short vulnerability-style title, not from the raw finding description; max 8 words, avoid code terms, function names, endpoint internals, causal chains. - Do not reuse ids. - Do not change ids after creation. -Examples: +Good Examples: CC-0001-missing-owner-check.md - CC-0002-stack-buffer-overflow-in-parser.md - CC-0003-unsafe-yaml-deserialization.md CC-0004-command-injection-via-backup-name.md +Bad examples: + +- `CC-0020-username-comparison-in-uh-auth-check-uses-non-constant-time-strcmp-leaking-realm-usernames-via-timing.md` +- `CC-0021-unbounded-crypt-3-invocations-on-every-basic-auth-attempt-enable-single-threaded-daemon-dos.md` + + + ## Required frontmatter Every finding must start with YAML frontmatter. @@ -77,6 +82,10 @@ Minimum fields: title: "Short vulnerability title" status: "PENDING" severity: "MEDIUM" + cvss_v4: + vector: "" + score: 0.0 + justification: "" confidence: "LOW" category: "Unclassified" cwe: [] @@ -148,7 +157,19 @@ Use only: - `LOW` - `INFO` -Severity should reflect realistic impact, not just theoretical bug class. +Derive `severity` from a CVSSv4 vector that reflects realistic impact, not just theoretical bug class. Record the full vector string, the computed score, and a one-line justification in the `cvss_v4` frontmatter block. The label MUST match the score band below. + +Score-to-label bands: + +| CVSSv4 base+threat score | Label | +|--------------------------|----------| +| 9.0 – 10.0 | CRITICAL | +| 7.0 – 8.9 | HIGH | +| 4.0 – 6.9 | MEDIUM | +| 0.1 – 3.9 | LOW | +| 0.0 | INFO | + +`cvss_v4.vector` is **required** when status is `CONFIRMED` or `EXPLOITED`. For `PENDING` findings, populate it with the best base-metric estimate available; threat/environmental metrics may be refined during validation and exploitation. ## Confidence values diff --git a/AGENTS.md b/AGENTS.md index a8957af..9a7ad8d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -250,7 +250,7 @@ Use these severity levels: - `LOW` - `INFO` -Do not over-focus on CVSS in the PoC. Prefer clear technical impact. +Derive severity from a CVSSv4 vector. Record the vector and score in the finding's `cvss_v4` frontmatter block; map the score band to the label above. See `.opencode/skills/finding-format/SKILL.md` for the band table. ## Status values diff --git a/prompts/phase-1-recon.md b/prompts/phase-1-recon.md index 6050346..33a6215 100644 --- a/prompts/phase-1-recon.md +++ b/prompts/phase-1-recon.md @@ -61,6 +61,9 @@ Document: - interesting files for Phase 2, - validation strategy. +Scan for documentation like `src/README*`, `src/CHANGELOG*`, `src/HISTORY*`, `src/NEWS*`, `src/SECURITY*`, `src/THREAT_MODEL*`, `src/CONTRIBUTING*`, `src/docs/` and similar. +Distill declared threat model, past CVEs, trust boundaries, and third-party components into the relevant notes; treat author claims as input to verify, not facts. + ### File risk index Create `itemdb/notes/file-risk-index.yml` using the schema in `templates/file-risk-index.yml`. diff --git a/templates/finding.md b/templates/finding.md index 8ad50fe..81c5e8d 100644 --- a/templates/finding.md +++ b/templates/finding.md @@ -3,6 +3,10 @@ id: "CC-0000" title: "Short vulnerability title" status: "PENDING" severity: "MEDIUM" +cvss_v4: + vector: "" + score: 0.0 + justification: "" confidence: "LOW" category: "Unclassified" cwe: [] From 7fe7bc05f613a194b2b774d55b31d6b174de47df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= Date: Fri, 22 May 2026 23:31:31 +0200 Subject: [PATCH 2/8] Validate CVSSv4 frontmatter --- tools/check-frontmatter.py | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tools/check-frontmatter.py b/tools/check-frontmatter.py index 2a44755..b6a7df3 100755 --- a/tools/check-frontmatter.py +++ b/tools/check-frontmatter.py @@ -58,6 +58,7 @@ "title", "status", "severity", + "cvss_v4", "confidence", "category", "cwe", @@ -76,6 +77,12 @@ "updated_at", ] +REQUIRED_CVSS_V4_FIELDS = [ + "vector", + "score", + "justification", +] + REQUIRED_VALIDATION_FIELDS = [ "status", "methods", @@ -97,6 +104,7 @@ FINDING_ID_RE = re.compile(r"^CC-\d{4,}$") FINDING_FILENAME_RE = re.compile(r"^CC-\d{4}-[a-z0-9]+[-_a-z0-9]*\.md$", re.IGNORECASE) SECTION_RE = re.compile(r"^# (?P.+?)\n(?P<body>.*?)(?=^# |\Z)", re.MULTILINE | re.DOTALL) +CVSS_V4_VECTOR_RE = re.compile(r"^CVSS:4\.0/") REQUIRED_EXPLOITED_SECTIONS = [ "Root cause analysis", @@ -151,6 +159,64 @@ def has_remediation_code(value: str) -> bool: return "```diff" in value or "```patch" in value or "```c" in value or "```" in value +def severity_from_cvss_v4_score(score: float) -> str: + if score == 0.0: + return "INFO" + if 0.1 <= score <= 3.9: + return "LOW" + if 4.0 <= score <= 6.9: + return "MEDIUM" + if 7.0 <= score <= 8.9: + return "HIGH" + if 9.0 <= score <= 10.0: + return "CRITICAL" + return "" + + +def validate_cvss_v4(data: Dict[str, object], status: object, severity: object) -> List[str]: + errors: List[str] = [] + cvss_v4 = data.get("cvss_v4") + + if not isinstance(cvss_v4, dict): + return ["cvss_v4 must be an object"] + + for field in REQUIRED_CVSS_V4_FIELDS: + if field not in cvss_v4: + errors.append(f"missing cvss_v4 field: cvss_v4.{field}") + + vector = cvss_v4.get("vector") + score = cvss_v4.get("score") + justification = cvss_v4.get("justification") + + if vector is not None and not isinstance(vector, str): + errors.append("cvss_v4.vector must be a string") + if justification is not None and not isinstance(justification, str): + errors.append("cvss_v4.justification must be a string") + if score is not None and not isinstance(score, (int, float)): + errors.append("cvss_v4.score must be a number") + + if status in ("CONFIRMED", "EXPLOITED"): + if not isinstance(vector, str) or is_placeholder(vector): + errors.append(f"{status} status requires populated cvss_v4.vector") + elif not CVSS_V4_VECTOR_RE.match(vector): + errors.append("cvss_v4.vector must start with 'CVSS:4.0/'") + + if not isinstance(justification, str) or is_placeholder(justification): + errors.append(f"{status} status requires populated cvss_v4.justification") + + if isinstance(score, (int, float)): + expected_severity = severity_from_cvss_v4_score(float(score)) + if not expected_severity: + errors.append("cvss_v4.score must be between 0.0 and 10.0") + elif severity in SEVERITIES and severity != expected_severity: + errors.append( + f"severity {severity!r} does not match cvss_v4.score {score!r} " + f"(expected {expected_severity!r})" + ) + + return errors + + def validate_finding(path: Path) -> List[str]: errors: List[str] = [] @@ -185,6 +251,8 @@ def validate_finding(path: Path) -> List[str]: if severity not in SEVERITIES: errors.append(f"invalid severity: {severity!r}") + errors.extend(validate_cvss_v4(data, status, severity)) + confidence = data.get("confidence") if confidence not in CONFIDENCES: errors.append(f"invalid confidence: {confidence!r}") From f6c292c2a1c398e638987cd074e4f198ed771b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= <pruiz@users.noreply.github.com> Date: Fri, 22 May 2026 23:37:49 +0200 Subject: [PATCH 3/8] Refine finding slug and CVSS guidance --- .opencode/skills/finding-format/SKILL.md | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.opencode/skills/finding-format/SKILL.md b/.opencode/skills/finding-format/SKILL.md index b3f1d49..b0dba8b 100644 --- a/.opencode/skills/finding-format/SKILL.md +++ b/.opencode/skills/finding-format/SKILL.md @@ -55,22 +55,27 @@ Rules: - Use four digits for the numeric id. - Use lowercase slugs. - Use hyphens instead of spaces. -- Generate the slug from a short vulnerability-style title, not from the raw finding description; max 8 words, avoid code terms, function names, endpoint internals, causal chains. +- Derive the slug from a short vulnerability-style title. +- Do not necessarily derive it directly from the raw finding description. +- Keep the slug concise: favor 8 words or fewer. +- Include the product surface when it helps locate the finding mentally. +- Avoid code terms, function names, endpoint internals, and long causal chains. - Do not reuse ids. - Do not change ids after creation. -Good Examples: +Good examples: CC-0001-missing-owner-check.md + CC-0002-stack-buffer-overflow-in-parser.md + CC-0003-unsafe-yaml-deserialization.md CC-0004-command-injection-via-backup-name.md + CC-0005-path-traversal-in-admin-export.md Bad examples: - `CC-0020-username-comparison-in-uh-auth-check-uses-non-constant-time-strcmp-leaking-realm-usernames-via-timing.md` - `CC-0021-unbounded-crypt-3-invocations-on-every-basic-auth-attempt-enable-single-threaded-daemon-dos.md` - - ## Required frontmatter Every finding must start with YAML frontmatter. @@ -157,19 +162,14 @@ Use only: - `LOW` - `INFO` -Derive `severity` from a CVSSv4 vector that reflects realistic impact, not just theoretical bug class. Record the full vector string, the computed score, and a one-line justification in the `cvss_v4` frontmatter block. The label MUST match the score band below. - -Score-to-label bands: - -| CVSSv4 base+threat score | Label | -|--------------------------|----------| -| 9.0 – 10.0 | CRITICAL | -| 7.0 – 8.9 | HIGH | -| 4.0 – 6.9 | MEDIUM | -| 0.1 – 3.9 | LOW | -| 0.0 | INFO | +Severity must reflect realistic impact, not just theoretical bug class. +When `cvss_v4.score` is populated, `severity` must match the score band +defined by the exploit-development skill. -`cvss_v4.vector` is **required** when status is `CONFIRMED` or `EXPLOITED`. For `PENDING` findings, populate it with the best base-metric estimate available; threat/environmental metrics may be refined during validation and exploitation. +`cvss_v4.vector`, `cvss_v4.score`, and `cvss_v4.justification` are +required frontmatter fields. They may stay empty or zero for early +`PENDING` findings, but must be populated when status is `CONFIRMED` or +`EXPLOITED`. ## Confidence values From 711a185cbbadfbd65d569ec8df813d47e52ebe0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= <pruiz@users.noreply.github.com> Date: Fri, 22 May 2026 23:38:39 +0200 Subject: [PATCH 4/8] Clarify exploit CVSS scoring --- .opencode/skills/exploit-development/SKILL.md | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.opencode/skills/exploit-development/SKILL.md b/.opencode/skills/exploit-development/SKILL.md index 08eadf6..58db462 100644 --- a/.opencode/skills/exploit-development/SKILL.md +++ b/.opencode/skills/exploit-development/SKILL.md @@ -218,18 +218,32 @@ Good PoC: if "DB_PASSWORD" in line: print(f"\n[!] IMPACT: Database password exposed: {line}") -## Severity adjustment +## CVSSv4 scoring -After exploitation, recompute the CVSSv4 vector with the metrics now justified by demonstrated impact. The new score band determines the new label. +After exploitation, recompute the CVSSv4 vector with the metrics now justified by demonstrated impact. Typical metric refinements after a working PoC: - `AC` / `AT` may drop once attack complexity and prerequisites are proven. - `PR` / `UI` may drop if no authentication or interaction is actually required. - `VC` / `VI` / `VA` (and `SC` / `SI` / `SA`) rise when concrete confidentiality, integrity, or availability impact is demonstrated on the vulnerable or subsequent system. -- `E` (threat) rises from `P`/`U` to `A` once a working PoC exists. +- `E` (threat) rises from `P`/`U` to `A` only when a working PoC exists. + +If exploitation is marked `NOT_FEASIBLE`, do not invent demonstrated-impact metrics. Preserve or refine the vector only according to evidence already proven by validation and the failed exploitation attempt. + +## Severity adjustment + +Once the CVSSv4 vector and score are decided, update the finding severity to match the score band. + +| CVSSv4 base+threat score | Severity | +|--------------------------|----------| +| 9.0 - 10.0 | CRITICAL | +| 7.0 - 8.9 | HIGH | +| 4.0 - 6.9 | MEDIUM | +| 0.1 - 3.9 | LOW | +| 0.0 | INFO | -Score bands and labels are defined in `.opencode/skills/finding-format/SKILL.md`. Do not pick a label without recomputing the vector. +Do not pick a severity label without recomputing or preserving a justified vector. ### Documentation requirement @@ -350,6 +364,7 @@ Before finishing: `# Recording` section, - CWE id(s) are assigned in finding frontmatter, - impact narrative is clear and concrete, +- CVSSv4 vector, score, and justification are recorded in finding frontmatter, - severity was assessed and adjusted if warranted, - finding frontmatter `exploitation:` block is updated, - finding `# Exploitation Result` section is updated, From 995aeb27f9ec4209bc17b0bca8f00d23ef1f589e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= <pruiz@users.noreply.github.com> Date: Fri, 22 May 2026 23:39:18 +0200 Subject: [PATCH 5/8] Bound documentation scan during recon --- prompts/phase-1-recon.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prompts/phase-1-recon.md b/prompts/phase-1-recon.md index 33a6215..d9b2840 100644 --- a/prompts/phase-1-recon.md +++ b/prompts/phase-1-recon.md @@ -61,7 +61,10 @@ Document: - interesting files for Phase 2, - validation strategy. -Scan for documentation like `src/README*`, `src/CHANGELOG*`, `src/HISTORY*`, `src/NEWS*`, `src/SECURITY*`, `src/THREAT_MODEL*`, `src/CONTRIBUTING*`, `src/docs/` and similar. +Recursively scan `src/` for high-signal documentation such as `README*`, `SECURITY*`, `THREAT_MODEL*`, `CONTRIBUTING*`, `docs/`, and similar. Also inspect `CHANGELOG*`, `HISTORY*`, and `NEWS*`, but prefer top-level or component-relevant files. + +If the repository has dozens of changelog/history/news files, do not process them exhaustively. Summarize the pattern, prioritize files near the primary target or security-relevant components, and record that scope decision. + Distill declared threat model, past CVEs, trust boundaries, and third-party components into the relevant notes; treat author claims as input to verify, not facts. ### File risk index From 14da7ff62f09ab03cecbdeef280adb3bca7fd9ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= <pruiz@users.noreply.github.com> Date: Fri, 22 May 2026 23:41:03 +0200 Subject: [PATCH 6/8] Keep AGENTS severity guidance minimal --- AGENTS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9a7ad8d..e50d0e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -250,8 +250,6 @@ Use these severity levels: - `LOW` - `INFO` -Derive severity from a CVSSv4 vector. Record the vector and score in the finding's `cvss_v4` frontmatter block; map the score band to the label above. See `.opencode/skills/finding-format/SKILL.md` for the band table. - ## Status values Use only these status values: From 2bb96e43d6c7b82270dc90847bd091cad7cbf87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= <pruiz@users.noreply.github.com> Date: Fri, 22 May 2026 23:48:04 +0200 Subject: [PATCH 7/8] Add external security context to recon --- prompts/phase-1-recon.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prompts/phase-1-recon.md b/prompts/phase-1-recon.md index d9b2840..47daabf 100644 --- a/prompts/phase-1-recon.md +++ b/prompts/phase-1-recon.md @@ -65,6 +65,10 @@ Recursively scan `src/` for high-signal documentation such as `README*`, `SECURI If the repository has dozens of changelog/history/news files, do not process them exhaustively. Summarize the pattern, prioritize files near the primary target or security-relevant components, and record that scope decision. +Review external public context for prior security advisories, CVE references, historical security fixes, release notes, and recurring bug classes affecting this project or closely related upstream components. Prefer project advisories, GitHub Security Advisories, NVD/CVE entries, issue trackers, release notes, and distribution advisories. + +Use external context only as reconnaissance input: distill affected components, historical bug patterns, trust boundaries, and fixed attack surfaces into the notes. Do not treat external claims as proof that the current source tree is affected; verify everything against `src/` before creating findings. + Distill declared threat model, past CVEs, trust boundaries, and third-party components into the relevant notes; treat author claims as input to verify, not facts. ### File risk index From 0a45b3f8adc13ff7fd99fe15666301607f8518a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Ruiz=20Garc=C3=ADa?= <pruiz@users.noreply.github.com> Date: Fri, 22 May 2026 23:53:23 +0200 Subject: [PATCH 8/8] Update frontmatter tests for CVSSv4 --- tests/test_check_frontmatter.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_check_frontmatter.py b/tests/test_check_frontmatter.py index 7f842f0..3579687 100644 --- a/tests/test_check_frontmatter.py +++ b/tests/test_check_frontmatter.py @@ -8,6 +8,10 @@ title: "Valid finding" status: "PENDING" severity: "MEDIUM" +cvss_v4: + vector: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N" + score: 5.0 + justification: "Unit-test fixture with medium impact." confidence: "LOW" category: "Test" cwe: [] @@ -68,6 +72,10 @@ def test_validate_frontmatter_status_directory_mismatch(tmp_path): title: "Exploited finding" status: "EXPLOITED" severity: "HIGH" +cvss_v4: + vector: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N/E:A" + score: 8.0 + justification: "Unit-test fixture with demonstrated high impact." confidence: "CONFIRMED" category: "Test" cwe: {cwe}