SARIF 통합
모든 코드 스캐닝 도구(CodeQL, Semgrep, Snyk, Checkmarx, Trivy, Bandit, GitLab SAST, OWASP ZAP)의 SARIF 출력을 SecureCodingHub로 푸시하세요. run의 고유한 CWE 각각은 커밋 작성자 계정의 과제가 되며 — 일반적으로 머지 후 몇 초 안에 생성됩니다.
왜 CI가 올바른 트리거인가
"모든 개발자가 동일한 OWASP Top 10 과정을 듣는" 일반적인 롤아웃은 완료율이 낮고 유지율은 더 낮습니다. CI 통합은 이 모델을 뒤집습니다: 교육은 문제를 도입한 개발자에게 구체적으로 할당되며, 그들이 필요로 하는 취약성 클래스에 대해 구체적으로 할당되고, 이를 노출한 머지와 연결된 14일 마감일이 있습니다. 그것이 감사관이 말하는 적시 보안 코딩 교육입니다.
엔드 투 엔드 작동 방식
SAST 도구가 CI 작업의 일부로 SARIF 2.1.0 파일을 생성합니다.
CI 단계가 커밋 메타데이터를 요청 헤더로 사용하여 파일을 /api/public/v1/sarif에 POST합니다.
SecureCodingHub가 모든 result를 파싱하고, CWE 태그를 추출한 다음, 각각을 내부 CWE-주제 맵에서 조회합니다.
각 고유한 매핑된 주제에 대해, 이메일이 X-Commit-Author-Email와 일치하는 사용자에게 14일 마감일로 과제가 생성됩니다.
sarif.ingested 웹훅이 동기 응답이 반환하는 것과 동일한 페이로드와 함께 발사됩니다.
엔드포인트
POST /api/public/v1/sarif
Authorization: Bearer scs_live_…
Content-Type: application/json
X-Source: github-codeql
X-Repo: acme/payments-api
X-Branch: main
X-Commit-Sha: 9c2f4b8e0c1d…
X-Commit-Author-Email: jane.smith@acme.com
<SARIF 2.1.0 JSON body, up to 10 MB>필수 범위: sarif:ingest.
요청 헤더
| 헤더 | 필수 | 의미 |
|---|---|---|
X-Source | — | 자유 형식 도구 식별자 — 예: github-codeql, snyk, semgrep-ci. 감사를 위해 기록됩니다. |
X-Repo | — | 리포지토리 슬러그 (owner/name). 감사를 위해 기록됩니다. |
X-Branch | — | 브랜치 이름. 감사를 위해 기록됩니다. |
X-Commit-Sha | 권장 | 커밋 해시. 24시간 멱등성을 위해 X-Repo와 함께 사용됩니다. |
X-Commit-Author-Email | 권장 | 커밋 작성자의 이메일. SecureCodingHub 사용자로 해결되며, 과제가 그들에게 첨부됩니다. 이메일이 사용자와 일치하지 않으면 run은 여전히 수집되지만 과제는 생성되지 않습니다. |
응답
{
"ingestionId": "7e4c9d09-86ca-4125-bbd8-7fae54c8f654",
"findingsCount": 2,
"uniqueCwes": ["CWE-79", "CWE-89"],
"unmappedCwes": [],
"assignmentsCreated": 12,
"notes": []
}findingsCount— 파싱된result항목의 총 수.uniqueCwes— 모든 결과에서 언급된 CWE 집합.unmappedCwes— 주제로 매핑할 수 없었던 CWE. 적용 범위 추가를 원하시면 지원으로 보내주세요.assignmentsCreated— 새 과제 레코드 수(기존 중복은 건너뜁니다).notes— 치명적이지 않은 경고(예: 잘못된 커밋 작성자 이메일 형식).
멱등성
동일한 SARIF 파일을 다시 수집하는 것은 안전합니다. 튜플 (organization, repo, commit_sha)이 멱등성 키입니다 — 24시간 윈도우 내의 중복은 원래 ingestionId와 assignmentsCreated: 0와 함께 200를 반환합니다. 이는 동일한 커밋에서 다시 실행되는 CI 워크플로(리베이스, 수동 재실행, PR에 푸시)가 중복 과제를 쌓지 않음을 의미합니다.
GitHub Actions 예시
CodeQL 또는 Semgrep 작업이 results.sarif를 생성한 후 워크플로에 이 단계를 추가하세요:
- name: Send SARIF to SecureCodingHub
if: always()
run: |
curl -sS -f -X POST \
-H "Authorization: Bearer $SCH_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Source: github-codeql" \
-H "X-Repo: ${{ github.repository }}" \
-H "X-Branch: ${{ github.ref_name }}" \
-H "X-Commit-Sha: ${{ github.sha }}" \
-H "X-Commit-Author-Email: ${{ github.event.head_commit.author.email }}" \
--data-binary @results.sarif \
https://api.limeplate.com/api/public/v1/sarif
env:
SCH_API_KEY: ${{ secrets.SCH_API_KEY }}키를 리포지토리 또는 조직 시크릿에 저장하세요. if: always() 가드는 보안 작업이 빌드를 실패로 표시했더라도 결과가 전송되도록 보장합니다 — 이때가 교육 과제가 가장 발사되기를 원하는 시점입니다.
GitLab CI 예시
send_sarif_to_sch:
stage: post-security
image: curlimages/curl:latest
needs: ["sast"]
script:
- >
curl -sS -f -X POST
-H "Authorization: Bearer $SCH_API_KEY"
-H "Content-Type: application/json"
-H "X-Source: gitlab-sast"
-H "X-Repo: $CI_PROJECT_PATH"
-H "X-Branch: $CI_COMMIT_BRANCH"
-H "X-Commit-Sha: $CI_COMMIT_SHA"
-H "X-Commit-Author-Email: $GITLAB_USER_EMAIL"
--data-binary @gl-sast-report.json
https://api.limeplate.com/api/public/v1/sarif
rules:
- if: $CI_COMMIT_BRANCHGitLab SAST 보고서는 SARIF와 호환됩니다 — 변환이 필요하지 않습니다.
Semgrep 예시
semgrep ci --sarif --output results.sarif
curl -sS -f -X POST \
-H "Authorization: Bearer $SCH_API_KEY" \
-H "X-Source: semgrep" \
-H "X-Repo: $REPO" \
-H "X-Commit-Sha: $COMMIT" \
-H "X-Commit-Author-Email: $AUTHOR_EMAIL" \
--data-binary @results.sarif \
https://api.limeplate.com/api/public/v1/sarifCWE 적용 범위
SecureCodingHub는 현재 OWASP Top 10, OWASP API Security Top 10, OWASP Mobile Top 10 및 CWE Top 25를 교육 주제에 매핑합니다. 해당 집합 외부의 CWE ID로 태그된 결과는 응답의 unmappedCwes 목록에 나타납니다. SARIF 태그 측에서 허용하는 일반적인 형식:
external/cwe/cwe-89(CodeQL 표준)cwe-89CWE-89cwe:89
태그는 result.properties.tags와 해당하는 tool.driver.rules[].properties.tags 모두에서 읽힙니다.
본문 크기 상한
SARIF 엔드포인트는 압축되지 않은 최대 10 MB까지의 페이로드를 허용합니다. 단일 리포지토리에 대한 대부분의 실제 SAST 보고서는 이 한도보다 훨씬 적습니다. 한도를 초과하는 엔터프라이즈 규모의 monorepo 보고서가 있다면, 도구 드라이버별로 분할(드라이버당 하나의 SARIF run)하고 각 부분을 POST하세요 — 동일한 커밋 SHA 아래에서 멱등적입니다.
실패 모드
| 증상 | 가능한 원인 |
|---|---|
assignmentsCreated: 0와 함께 200 | 커밋 작성자 이메일이 조직의 사용자가 아니거나, 모든 CWE가 이미 할당되었습니다. |
비어 있지 않은 unmappedCwes와 함께 200 | 결과가 아직 교육 주제가 없는 CWE를 참조합니다. 목록을 지원팀으로 전달하세요. |
400 empty_body | Curl이 --data-binary @file.sarif를 누락했습니다. |
413 body_too_large | SARIF가 10 MB를 초과합니다. 드라이버별로 분할하세요. |
idempotency:로 시작하는 notes 항목과 함께 200 | 동일한 (repo, commit)이 24시간 이내에 이미 수집되었습니다. 응답은 그 외에는 assignmentsCreated: 0와 함께 새 수집과 동일합니다 — 무시해도 안전합니다. |