diff --git a/docs/views/secret-detection-results-and-metrics.md b/docs/views/secret-detection-results-and-metrics.md index eec65f4..10dc761 100644 --- a/docs/views/secret-detection-results-and-metrics.md +++ b/docs/views/secret-detection-results-and-metrics.md @@ -32,6 +32,64 @@ Verifier는 detector가 아닙니다. Finding을 지우지 않고 triage 상태만 보조해야 합니다. Timeout, malformed response, low confidence는 모두 review-needed로 남깁니다. +## Finding lifecycle / triage / quality loop + +이 섹션은 scanner 후보가 triage state, verifier verdict, report/gate, GHAS parity까지 어떻게 이동하는지 설명합니다. 공개 문서이므로 실제 repository 이름, 실제 alert, 실제 secret, private path는 쓰지 않습니다. + +### Lifecycle flow + +Secret Detection 기본 경로는 `Secret Finding`입니다. + +1. Scanner가 후보를 찾으면 runtime은 raw scanner output을 canonical `Finding`으로 바꿉니다. 기본 상태는 `status=OPEN`, `triage.verdict=NEEDS_REVIEW`, `disposition=unreviewed`입니다. +2. 저장소는 finding identity와 scan-run observation을 분리합니다. 같은 finding이 다시 보이면 observation은 새로 남기되, 기존 `FINDING_STATE`의 manual/verifier triage는 덮어쓰지 않습니다. +3. Verifier는 redacted metadata만 보고 `TRUE_POSITIVE`, `FALSE_POSITIVE`, `NEEDS_REVIEW` 중 하나를 제안합니다. Invalid JSON, timeout, low confidence, unknown label은 `NEEDS_REVIEW`로 남깁니다. +4. Terminal verifier verdict만 durable disposition으로 기록합니다. `FALSE_POSITIVE`는 `status=FALSE_POSITIVE`로 내려가고 gate에서 빠집니다. `TRUE_POSITIVE`는 `triage.verdict=TRUE_POSITIVE`로 남으며 `status=OPEN`이면 계속 blocking signal입니다. `NEEDS_REVIEW`는 terminal verdict가 아니므로 disposition row를 쓰지 않습니다. +5. Manual triage도 같은 disposition channel을 사용합니다. 사람은 `true_positive` 또는 `false_positive`를 기록할 수 있고, 기록은 append-only state event와 global `FINDING_STATE`에 반영됩니다. +6. 재스캔 때는 direct `finding_id`와 line-move tolerant `match_key`를 확인합니다. 이미 `FALSE_POSITIVE`, `RESOLVED`, `IGNORED` 같은 non-blocking terminal state가 있으면 재검증을 줄입니다. Lookup 실패는 fail-safe로 처리해 finding을 숨기지 않습니다. +7. Report는 저장된 finding snapshot에 lifecycle state를 overlay한 뒤 triage verdict와 status count를 보여줍니다. Gate는 `status=OPEN`이고 verdict가 `NEEDS_REVIEW` 또는 `TRUE_POSITIVE`인 항목을 blocking으로 계산합니다. +8. GHAS parity는 별도 품질 측정 loop입니다. GHAS Secret Scanning alert을 read-only reference로 보고, local `Finding`과 canonical secret type, file, tolerated line window로 비교해 precision/recall과 gap bucket을 계산합니다. GHAS alert을 mutation하거나 scanner lifecycle state로 직접 승격하지 않습니다. + +`VulnerabilityFinding`은 별도 제품 경로입니다. SARIF-native SAST output은 `Finding`이 아니라 `VulnerabilityFinding`으로 normalize됩니다. Secret hash, Gitleaks payload, `Finding.disposition`을 쓰지 않고, SARIF rule/location/trace metadata와 `triage_state`, `verifier_verdict`를 사용합니다. Code vulnerability gate는 severity와 precision threshold를 보고, `triage_state=FALSE_POSITIVE`만 blocking에서 제외합니다. + +### State 용어 사전 + +| 용어 | 적용 대상 | 의미 | Gate 영향 | +| --- | --- | --- | --- | +| `NEEDS_REVIEW` | `Finding.triage.verdict`, `VulnerabilityFinding.triage_state` | 아직 사람 또는 verifier가 확정하지 못한 상태 | Secret은 `OPEN`이면 blocking, code-vuln은 severity/precision 조건을 만족하면 blocking | +| `TRUE_POSITIVE` | `Finding.triage.verdict`, verifier verdict | 실제 signal로 보는 terminal verdict | Secret은 `OPEN`이면 blocking | +| `FALSE_POSITIVE` | Secret/Vulnerability triage | 노이즈로 확정한 terminal verdict | Secret과 code-vuln 모두 blocking에서 제외 | +| `disposition` | Secret `Finding` read/dashboard view | `triage.verdict`에서 파생되는 표시값: `verified`, `false_positive`, `unreviewed` | 독립 저장값이 아니며 gate는 canonical status/verdict로 계산 | +| `verified` | Secret dashboard disposition | `TRUE_POSITIVE`의 표시 이름 | 안전하다는 뜻이 아니라 확인된 signal이라는 뜻 | +| `OPEN` | Secret `Finding.status` | 아직 release/gate 관점에서 닫히지 않은 상태 | `NEEDS_REVIEW` 또는 `TRUE_POSITIVE`와 만나면 blocking | +| `RESOLVED`, `IGNORED` | Secret `Finding.status` | non-blocking terminal state | 재검증 억제와 gate 제외에 사용 | +| `triage_state` | `VulnerabilityFinding` | code-vuln 전용 triage 상태 | `FALSE_POSITIVE`가 아니면 severity/precision gate 대상 | +| `GHAS parity` | Secret quality loop | GHAS alert 대비 local scanner 결과의 precision/recall 비교 | Lifecycle state를 직접 바꾸지 않는 read-only 품질 신호 | +| `drift` | Quality monitoring | GHAS-calibrated 품질 기준과 non-GHAS sample 분포가 멀어지는 현상 | SLO가 아니라 조기 경보로 취급 | + +### Quality loop copy + +품질 루프의 목표는 "찾은 후보를 줄이는 것"이 아니라 "recall을 낮추지 않으면서 사람이 봐야 할 후보를 더 잘 분류하는 것"입니다. + +- Scanner는 후보 생성 책임을 가집니다. Secret Detection은 Gitleaks-first이고, code vulnerability는 opt-in SARIF-native 경로입니다. +- Normalizer는 도구별 output을 제품 모델로 바꿉니다. Secret은 `Finding`, SAST는 `VulnerabilityFinding`으로 분리합니다. +- Verifier는 삭제 권한이 없습니다. Redacted metadata만 보고 triage verdict를 보조하며, 애매하면 `NEEDS_REVIEW`로 남깁니다. +- Disposition은 terminal decision을 재사용하기 위한 layer입니다. Secret false positive는 같은 finding 또는 line-moved same-secret 후보에서 재검증을 줄일 수 있습니다. +- Report와 gate는 저장된 결과를 다시 읽어 재현 가능해야 합니다. Secret gate는 unresolved signal을 막고, code-vuln gate는 severity/precision 기준의 blocking count를 봅니다. +- Synthetic evaluation은 precision, recall, false negative를 고정합니다. False positive reduction은 verifier나 policy가 triage 비용을 줄였는지 보는 보조 지표입니다. +- GHAS parity는 Secret Detection의 외부 기준선입니다. `secret_type`과 `rule_id`를 canonical type으로 맞추고, line tolerance와 state-aware truth filter로 local-only, GHAS-only, type-unmatched gap을 나눕니다. +- Drift는 GHAS가 없는 대상에 품질 머신을 적용할 때의 안전장치입니다. GHAS parity SLO를 대신하지 않고, 분포 변화나 verifier와 무관한 품질 저하를 조기에 보여주는 신호입니다. + +### Gap + +현재 문서화된 gap은 다음과 같습니다. + +- `Finding.disposition`은 Secret 전용 파생 view입니다. `VulnerabilityFinding`에는 동일한 disposition vocabulary가 아직 없습니다. +- Secret manual disposition은 `true_positive`와 `false_positive`만 노출합니다. `IGNORED` 같은 상태는 모델에는 있지만 사용자 CLI verdict로는 열려 있지 않습니다. +- `TRUE_POSITIVE` verifier verdict는 terminal triage이지만 `status=OPEN`으로 남아 gate blocking signal입니다. 실제 remediation 완료를 나타내는 별도 resolution flow는 분리되어 있습니다. +- `NEEDS_REVIEW`는 durable disposition으로 쓰지 않습니다. Verify queue는 deterministic job id로 flood를 줄이지만, 사람이 review해야 하는 애매한 후보 자체를 자동으로 닫지는 않습니다. +- GHAS parity는 Secret Scanning 기준의 품질 측정입니다. Code vulnerability GHAS parity, GHAS lifecycle automation, alert mutation은 현재 공개 기본 경로가 아닙니다. +- Non-GHAS drift는 조기 경보입니다. GHAS-enabled repo의 read-only parity SLO와 같은 acceptance gate로 취급하지 않습니다. + ## 비공개 결과 처리 제한된 evidence는 `private/`, internal system, restricted knowledge base에 둡니다. 공개 view에는 정확한 저장 위치, host, credential injection path를 쓰지 않습니다.