Reference

17. 보안 취약점 판정과 규제·감사 증거

scanner 후보→귀속 가능한 취약점 판정, 정책 게이트, 통제-증거 SHA-256 인덱스와 준수 주장 경계

Source: docs/17-security-assurance.md

17. 보안 취약점 판정과 규제·감사 증거

현재 구현 상태 (v0.39.0): jam assurance verdictjam assurance evidence, 5개 버전 고정 JSON 계약, SHA-256 증거 인덱스, Git commit/source hash 상관 검증, 리뷰·예외 만료 게이트가 구현되어 있다. 이 기능은 품질 점수와 독립적이며 인증서나 법률상 준수 결론을 생성하지 않는다.

1. 보장 경계

JAM은 다음 세 개를 서로 다른 상태로 취급한다.

  1. scanner candidate: 정적 패턴, SAST, SCA가 발견한 검토 후보. 아직 취약점 확정이 아니다.
  2. security verdict: 특정 Git commit의 후보에 리뷰어가 근거를 붙여 confirmed, false_positive, accepted_risk로 판정한 기록.
  3. control evidence: 조직이 선택한 규제·표준 통제 항목과 해시 고정 산출물을 연결한 증거 준비도. 준수 인증이 아니다.

이 경계는 NIST SSDF SP 800-218의 안전한 개발 관행 및 취약점 대응 기록, OWASP ASVS 5.0의 검증 요구사항처럼 “어떤 활동을 수행하고 증거를 보존했는가”를 보여주기 위한 것이다. 특정 조직의 ISMS-P, ISO/IEC 27001, PCI DSS 적합 여부는 범위·통제 설계·운영 표본·독립 심사를 함께 평가해야 한다.

2. 취약점 판정 흐름

jam-run.json + jam-detail.csv + security-report.json
                         │
central security policy ├──> candidate ledger ──> attributed review
                         │                              │
                         └──────────────────────────────┴──> security-verdict.json
                                                               pass | fail | incomplete

jam-detail.csv의 SEC finding이 품질 점수에서 억제되었더라도 보안 후보에는 남는다. 품질 suppression은 부채 계산 예외이고, 취약점 위험 수용은 승인자·티켓·만료일이 있는 별도 accepted_risk 결정이다.

2.1 정책

포트폴리오 공통 정책은 예시 파일을 중앙 배포한다.

{
  "schema": "jam/security-policy@1",
  "id": "engineering/portfolio-security",
  "version": "1",
  "requiredSources": ["jam", "security-cli"],
  "blockedSeverities": ["critical", "high"],
  "requireReviewSeverities": ["critical", "high"],
  "blockKnownExploited": true,
  "allowAcceptedRisk": true,
  "allowIncompleteSources": false
}

blockKnownExploited는 scanner 또는 리뷰가 알려진 악용 상태를 기록한 confirmed 또는 accepted-risk 후보를 차단한다. allowAcceptedRisk=false이면 승인 형식이 완전해도 위험 수용은 게이트를 통과하지 못한다. 실제 악용 우선순위 데이터는 CISA Known Exploited Vulnerabilities Catalog 같은 관리된 소스에서 갱신해야 한다. 영향도 표기는 CVSS v4.0 벡터와 원문을 함께 보존한다. CVSS 점수만으로 도달 가능성이나 조직의 실제 영향을 확정하지 않는다.

모든 blockedSeveritiesrequireReviewSeverities에도 포함되어야 한다. 그렇지 않으면 정책 입력 자체를 거부한다. blockKnownExploited=true이면 severity와 무관하게 KEV 후보가 리뷰 대기가 되며, security-cli를 필수 source로 선언해야 한다.

2.2 최초 후보 생성과 리뷰

먼저 리뷰 없이 실행하면 현재 commit의 안정적인 candidate ID가 생성된다.

jam full . --component security --strict --out reports/jam
jam assurance verdict \
  --input reports/jam \
  --policy /etc/jam/security-policy.json \
  --out reports/jam/assurance \
  --gate

검토가 필요한 고위험 후보가 있으면 JSON을 작성한 뒤 다시 실행한다. 리뷰 예시targetCommit과 candidate ID를 현재 출력으로 교체해야 한다.

jam assurance verdict \
  --input reports/jam \
  --policy /etc/jam/security-policy.json \
  --review reviews/security-review.json \
  --out reports/jam/assurance \
  --gate

리뷰 상태 계약은 다음과 같다.

상태의미필수 추가 정보현재 스캔에 존재 가능
confirmed재현·도달 가능성·영향을 검토해 취약점으로 확인owner, ticket, reachability
false_positive코드 경로·구성·버전 증거로 해당 없음evidence
accepted_risk취약점/위험을 기한부 승인owner, ticket, approvedBy, expires, reachability
fixed과거 후보가 수정 commit 이후 사라짐fixedCommit아니오
verified수정 후 현재 런으로 재검증fixedCommit, verifiedRun(현재 jam-run SHA-256)아니오

현재 스캔에 남아 있는 후보를 fixed/verified로 적거나, 사라진 후보를 열린 상태로 유지하면 입력 오류다. 리뷰 commit이 스캔 commit과 다르거나 worktree가 dirty이면 게이트가 통과하지 않는다. 만료된 위험 수용은 fail이다.

심각도 재분류는 severityOverride와 함께 승인자와 CVSS vector를 필수로 요구한다. 출력은 scanner의 observedSeverity와 정책 평가에 사용한 severity를 모두 보존해 원래 신호를 덮어쓰지 않는다.

2.3 게이트와 종료 코드

결과조건--gate 종료 코드
pass필수 소스가 완전하고, 필수 리뷰가 끝났으며, 차단 확정 취약점/만료 예외가 없음0
fail차단 severity/KEV의 confirmed 취약점 또는 만료된 accepted risk1
incomplete필수 scanner 누락·실패, JAM 무결성 불완전, dirty/no-commit, 필수 리뷰 미완료3

--gate가 없으면 모든 상태에서 산출물을 기록하고 exit 0을 반환한다. 입력 스키마·정책·리뷰 자체가 잘못되면 exit 2다. 결과는 security-verdict.json과 사람이 읽는 security-verdict.md로 생성된다.

3. 규제·감사 증거 인덱스

통제 매핑은 JAM에 하드코딩하지 않는다. 같은 도구 결과도 조직의 적용 범위, 시스템 경계, 책임 분장에 따라 다른 통제를 지원하기 때문이다. 통제 소유자가 control plan 예시처럼 framework/version, scope, 평가자, 평가일, 근거, 필요한 증거 유형과 파일을 명시한다.

예시는 의도적으로 조직 내부 통제 ID를 사용한다. 실제 표준에 매핑할 때는 해당 버전의 정확한 requirement ID와 원문을 법적 범위 문서에서 고정해야 한다. 예를 들어 OWASP는 ASVS 5.0.0 항목을 v5.0.0-<chapter>.<section>.<requirement> 형식으로 참조할 것을 공식 저장소에서 권장한다. 단순히 ASVS-V1처럼 만든 ID를 표준 요구사항인 것처럼 사용하지 않는다.

jam assurance evidence \
  --plan assurance/control-plan.json \
  --out reports/audit \
  --gate

지원하는 증거 유형은 jam_run, security_verdict, sbom, sast, sca, secret_scan, iac_scan, image_scan, dast, penetration_test, ticket_export, policy, other다. jam_runsecurity_verdict는 내부 스키마를 검증하고 commit/source hash가 같은 대상을 가리키는지 비교한다. sbom은 CycloneDX JSON, SPDX 2.x JSON 또는 SPDX 3 JSON-LD의 식별 필드를 확인한다. SBOM 표준은 CISA SBOM 자료, SPDX 사양, SPDX 3.0.1 serialization, CycloneDX VEX를 기준으로 조직의 생성·배포·취약성 영향 상태 절차와 연결할 수 있다.

SBOM 검사는 형식 식별과 JSON 완전성 확인이지 전체 표준 conformance validation이 아니다. 감사 통제가 완전한 스키마 검증을 요구하면 공식 validator 결과를 별도 other 또는 policy 증거로 연결한다. 그 외 scanner·티켓·침투 테스트 유형도 JAM은 원본의 존재와 hash를 검증하고, 내용의 전문적 타당성은 assessor가 판정한다.

통제별 결과:

상태의미
supported선언한 구현 상태를 필요한 유효 증거가 지원
exception_open부분/미구현 또는 실패 결과가 유효한 기한부 예외로 관리됨
gap미구현·실패 또는 만료 예외가 있고 유효한 보상이 없음
evidence_incomplete파일 누락, 잘못된 스키마, 필수 유형 누락, 불완전 scan/verdict

전체 결과는 모두 supported이면 evidence_ready, gap/열린 예외가 있으면 action_required, 증거가 불완전하면 evidence_incomplete다. control-evidence.json은 모든 증거의 SHA-256, 크기, 대상 commit/source hash, 통제 연결과 만료 상태를 기록한다. complianceClaim은 항상 false다.

4. 감사에 제출할 때 추가로 필요한 것

JAM이 생성한 해시는 파일 변경을 탐지하지만 누가 언제 승인했는지에 대한 암호학적 신원 보증이나 원본의 장기 보존을 대신하지 않는다. 실제 감사 패키지는 다음을 함께 갖춰야 한다.

5. 300개 저장소 운영 권장안

  1. 중앙 security-policy.json과 framework별 control plan 템플릿을 버전 관리한다.
  2. 저장소마다 보호된 CI에서 jam full --component security --strict와 최초 verdict를 실행한다.
  3. 중앙 triage 큐가 incomplete 후보를 받아 commit 고정 리뷰 ledger를 PR로 남긴다.
  4. 최종 verdict와 SBOM·SAST/SCA·secret/IaC 결과를 evidence plan에 연결한다.
  5. --gate 결과를 포트폴리오 수집기에 전송하되 fail, incomplete, 실행 자체 실패를 다른 상태로 집계한다.
  6. accepted risk와 control exception 만료 알림을 최소 일 단위로 실행한다.
  7. 월별로 source coverage, 리뷰 대기시간, confirmed/fixed/verified 전환, 예외 만료, false-positive 비율을 검토한다. 품질 점수 평균으로 보안 위험을 상쇄하지 않는다.

6. 버전 고정 계약