feat: classify finding severity with custom rubrics - #791
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
finding_severity_assessments is keyed only by stable finding_id, but findings are intentionally global across scans and finding_occurrences carries the scan-specific occurrence. If the same finding appears in scans A and B, classifying B overwrites A's row (including occurrence_id/input_sha256). Publishing A then sees B's assessment and fails validation; reclassifying A in turn breaks B. Could the checkpoint key be scan/occurrence-scoped (for example (scan_id, finding_id) or occurrence_id) and add a two-scan same-finding regression?
Summary
Let CLI and SDK users classify existing findings under a custom severity rubric and resume interrupted classification without repeating successful model calls. Each successful finding is checkpointed in SQLite before the next finding starts. Omitting a rubric inherits the original severity without a model call.
Changes
codex-security classify-severitywith exactly one of--scan ID(including unique prefixes andlatest) or--scan-dir PATH, optional--rubric PATH, repeatable--knowledge-base PATHand--finding-id ID, and existing model/effort controls.--reprocess(default false) and SDKreprocess: trueto bypass matching checkpoints for the selected findings. Normal runs reuse assessments only when evidence, rubric, and context hashes match. Exclusions are reusable assessments with null severity.classifySeverityprimitive and the database-backedclassifyScanSeverityandclassifyScanDirectorySeveritywrappers. Each rubric evaluation uses the supplied report/context in a separate read-only Codex turn without tools or source inspection.finding_severity_assessmentsstores the latest assessment per finding ID, andscan_severity_classificationsstores each scan's requested selection and policy/context hashes. Save each successful row independently; a failed reassessment retains that finding's previous row. Preserve original findings and sealed artifacts.severity-classification.jsonafter a successful run. Publication rejects incomplete or stale selections, omits exclusions, and uses assessed severity for ticket priority/title. SDK callers can still supply an assessment explicitly.publish scan --to linear --finding-id IDfor direct selection while retaining full sealed-scan membership checks. Update help, SDK types, documentation, package fixtures, and bundled-file contracts.Testing
pnpm run typesandpnpm run format: passed.pnpm packandpnpm run check:package -- <tarball>: passed. Validated 411 archive entries, installed exports and strict NodeNext consumer types, CLI/SDK lifecycle, credential locking, plugin/MCP startup, and nested worker startup.classify-severity --helpandgit diff --check: passed.Risk and rollout
This adds public CLI/SDK surface and an additive local database migration. Classification of external scan directories also uses the configured local state database. Existing JSON-only assessments require one classification run to populate checkpoints; the JSON export is no longer read as an authoritative assessment. Classification and publication must use the same state directory.
Reprocessing replaces successful rows individually and retains rows outside the selected set. There is no assessment history or whole-scan rollback. Changing only the model or effort requires
--reprocess. StandaloneclassifySeverityremains an in-memory operation. Original scan severity and existing Linear tickets are unchanged;--skip-existingretains its current behavior.Public disclosure review