OpenSSF Scorecard로 오픈소스 저장소 보안 점수 매기기

npm에서 라이브러리를 하나 고를 때 무엇을 보시나요? 저는 스타 수만 보고 정하지는 않습니다. 저장소에 들어가서 이슈에 답이 잘 달리는지, 릴리스가 꾸준한지, 기여자가 몇 명이나 되는지를 훑어보죠.
그런데 그렇게 살펴봐도 손에 남는 건 “괜찮아 보인다”는 인상뿐입니다. 왜 이걸 골랐냐고 누가 물으면 딱 떨어지는 근거를 대기가 어렵고, node_modules에 딸려 들어온 수백 개의 간접 의존성을 하나하나 같은 눈으로 볼 수도 없고요. 눈으로 하는 판단은 저 혼자 쓰고 끝나는 데다, 개수가 늘어나면 금방 감당이 안 됩니다.
게다가 저장소를 열어봐도 보이지 않는 게 있습니다. 주간 다운로드가 수천만 건인 패키지라도 메인테이너 계정에 2단계 인증이 없을 수 있고, 아무나 main 브랜치에 직접 푸시할 수 있는 상태일 수도 있습니다. 실제로 그런 틈을 노린 사고가 꾸준히 있었죠. 2018년 event-stream은 지친 메인테이너가 낯선 사람에게 배포 권한을 넘겨준 뒤 암호화폐 지갑을 터는 코드가 심어졌고, 2021년 ua-parser-js는 메인테이너 계정이 탈취돼 악성 버전이 npm에 올라갔습니다. 2024년 xz-utils 백도어는 무려 2년에 걸쳐 신뢰를 쌓은 기여자가 심어놓은 것이었고요.
이런 사고의 공통점은 코드에 있는 버그가 아니라 프로젝트를 운영하는 방식의 허점이었다는 점입니다. 그렇다면 그 운영 방식을 숫자로 볼 수는 없을까요? 🤔 그걸 해보자는 게 바로 OpenSSF Scorecard입니다.
Scorecard가 측정하는 것
Scorecard는 OpenSSF(Open Source Security Foundation)에서 만든 자동 점검 도구입니다. 저장소를 훑어서 미리 정해둔 검사 항목마다 0점에서 10점을 매기고, 이를 종합해 총점을 냅니다.
여기서 가장 먼저 짚어야 할 게 있습니다. Scorecard는 취약점 스캐너가 아닙니다. 코드를 정적 분석해서 SQL 인젝션을 찾아내는 도구가 아니고, 의존성 목록을 뒤져 CVE(Common Vulnerabilities and Exposures)를 뽑아주는 도구도 아닙니다. Scorecard가 던지는 질문은 결이 다릅니다.
코드 리뷰 없이 머지된 커밋이 있는가? 워크플로우 토큰에 필요 이상의 권한이 열려 있는가? 릴리스 아티팩트에 서명을 하는가? 취약점 제보를 받을 창구를 공개해 두었는가? 의존성을 자동으로 갱신하는 장치가 있는가?
즉 결함 하나하나가 아니라 결함이 새어 들어올 만한 구조를 봅니다. 잠긴 문이 몇 개인지 세는 쪽에 가깝죠. 문이 잠겨 있다고 도둑이 안 드는 건 아니지만, 열려 있으면 확실히 위험합니다.
Scorecard는 매주 100만 개가 넘는 주요 오픈소스 프로젝트를 스캔해 결과를 공개하고 있어서, 내 저장소뿐 아니라 내가 쓰려는 의존성의 점수도 조회할 수 있습니다.
18가지 검사 항목과 가중치
기본으로 실행되는 검사는 18가지입니다. 각 항목에는 위험 등급이 붙어 있고, 이 등급이 총점을 계산할 때의 가중치가 됩니다. Critical은 10, High는 7.5, Medium은 5, Low는 2.5의 가중치를 갖습니다.
| 검사 항목 | 위험 등급 | 무엇을 보는가 |
|---|---|---|
| Dangerous-Workflow | Critical | 워크플로우에 스크립트 인젝션 같은 위험 패턴이 있는가 |
| Binary-Artifacts | High | 저장소에 실행 가능한 바이너리가 커밋돼 있는가 |
| Branch-Protection | High | 기본 브랜치에 보호 규칙이 걸려 있는가 |
| Code-Review | High | 변경 사항이 사람의 리뷰를 거쳐 머지되는가 |
| Dependency-Update-Tool | High | 의존성 자동 갱신 도구를 쓰고 있는가 |
| Maintained | High | 최근에 활발히 관리되고 있는가 |
| Signed-Releases | High | 릴리스 아티팩트에 서명이 붙는가 |
| Token-Permissions | High | 워크플로우 토큰이 최소 권한을 지키는가 |
| Vulnerabilities | High | 고쳐지지 않은 알려진 취약점이 있는가 |
| Fuzzing | Medium | 퍼즈 테스트를 돌리는가 |
| Packaging | Medium | 패키지 형태로 배포되는가 |
| Pinned-Dependencies | Medium | 의존성이 해시나 특정 버전으로 고정돼 있는가 |
| SAST | Medium | 정적 분석 도구를 CI에서 돌리는가 |
| Security-Policy | Medium | 취약점 신고 절차를 공개했는가 |
| CI-Tests | Low | PR이 머지되기 전에 테스트가 도는가 |
| CII-Best-Practices | Low | OpenSSF 모범 사례 배지를 받았는가 |
| Contributors | Low | 여러 조직에서 기여가 들어오는가 |
| License | Low | 라이선스 파일이 있는가 |
이 가중치 구조가 꽤 중요한데요. Dangerous-Workflow 한 항목이 License 네 항목과 맞먹는 무게를 갖습니다. 점수를 올리려고 손댈 곳을 고를 때, 라이선스 파일을 추가하는 것보다 워크플로우의 위험 패턴을 걷어내는 쪽이 총점에 네 배로 반영된다는 뜻이죠.
목록에는 없지만 Webhooks와 SBOM 검사도 실험 단계로 존재합니다. Webhooks는 저장소 관리자 권한 토큰이 있어야 돌아가기 때문에 기본 실행 대상에서 빠져 있습니다.
CLI로 직접 돌려보기
가장 빠르게 감을 잡는 방법은 내 저장소에 직접 돌려보는 겁니다. macOS와 Linux에서는 Homebrew로 설치합니다.
brew install scorecard
설치가 끝났으면 버전을 확인해 봅니다.
scorecard version
GitVersion: v5.5.0
GitCommit: c395761df6afe1a69e476bc60a013a94bcbc153f
GitTreeState: clean
실행하려면 GitHub 토큰이 필요합니다. Scorecard는 커밋 이력, PR 목록, 브랜치 보호 설정, 워크플로우 파일을 모두 GitHub API로 읽어오는데, 인증 없이는 시간당 60회 제한에 금방 막히기 때문입니다. 개인 액세스 토큰을 발급받아 GITHUB_AUTH_TOKEN 환경 변수에 넣어주면 됩니다. 공개 저장소만 검사한다면 별도 스코프 없이 발급한 토큰으로 충분합니다.
GitHub CLI로 이미 로그인해 두었다면 토큰을 따로 만들 필요 없이 이렇게 넘길 수 있습니다.
GITHUB_AUTH_TOKEN=$(gh auth token) scorecard --repo=github.com/ossf/scorecard
첫 대상으로는 Scorecard 저장소 자신을 골라봤습니다. 채점 기준을 만든 팀은 몇 점을 받는지 궁금하잖아요. 검사가 병렬로 돌면서 진행 상황이 표준 에러로 흘러나오고, 몇 분 뒤에 결과 표가 나옵니다. 표가 너무 넓어서 맨 오른쪽의 문서 링크 열은 잘라냈습니다.
Aggregate score: 8.9 / 10
Check scores:
|---------|------------------------|---------------------------------------------------------------------------------------|
| SCORE | NAME | REASON |
|---------|------------------------|---------------------------------------------------------------------------------------|
| 10 / 10 | Binary-Artifacts | no binaries found in the repo |
| 8 / 10 | Branch-Protection | branch protection is not maximal on development and all release branches |
| 10 / 10 | CI-Tests | 30 out of 30 merged PRs checked by a CI test -- score normalized to 10 |
| 5 / 10 | CII-Best-Practices | badge detected: Passing |
| 10 / 10 | Code-Review | all changesets reviewed |
| 10 / 10 | Contributors | project has 73 contributing companies or organizations |
| 10 / 10 | Dangerous-Workflow | no dangerous workflow patterns detected |
| 10 / 10 | Dependency-Update-Tool | update tool detected |
| 10 / 10 | Fuzzing | project is fuzzed |
| 10 / 10 | License | license file detected |
| 10 / 10 | Maintained | 13 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10 |
| 10 / 10 | Packaging | packaging workflow detected |
| 9 / 10 | Pinned-Dependencies | dependency not pinned by hash detected -- score normalized to 9 |
| 10 / 10 | SAST | SAST tool is run on all commits |
| 10 / 10 | Security-Policy | security policy file detected |
| 10 / 10 | Signed-Releases | 5 out of the last 5 releases have a total of 5 signed artifacts. |
| 9 / 10 | Token-Permissions | detected GitHub workflow tokens with excessive permissions |
| 0 / 10 | Vulnerabilities | 64 existing vulnerabilities detected |
|---------|------------------------|---------------------------------------------------------------------------------------|
점수를 읽는 법
8.9점입니다. 채점 기준을 직접 만든 팀도 만점은 아니라는 게 오히려 안심이 되는데요. 이 표 하나에 점수 읽는 법이 거의 다 들어 있습니다.
가장 눈에 띄는 건 Vulnerabilities 0점입니다. 알려진 취약점이 64건 잡혔거든요. 보안 도구를 만드는 팀조차 의존성에 딸려 오는 취약점을 전부 막지는 못한다는 뜻이고, 동시에 이 항목이 그만큼 0점을 벗어나기 어렵다는 뜻이기도 합니다.
그런데 High 등급에서 0점을 맞고도 총점은 8.9점이죠. 가중치가 실제로 어떻게 작동하는지 보여주는 대목입니다. 항목 하나가 바닥을 쳐도 나머지 열일곱 개가 받쳐주면 총점은 크게 무너지지 않습니다. 뒤집어 말하면 총점만 보고 안심할 때 0점짜리 구멍을 놓치게 된다는 얘기고요.
부분 점수도 눈여겨볼 만합니다. CII-Best-Practices가 5점인데 이유가 “badge detected: Passing”입니다. 배지를 받긴 받았는데 등급이 Passing에 머물러 절반만 인정된 거죠. Silver나 Gold까지 올라가야 만점입니다. Token-Permissions와 Pinned-Dependencies의 9점도 같은 맥락으로, 대부분 지켰지만 한두 군데가 걸린 상태입니다.
이 표에는 없지만 숫자 대신 ?가 찍히는 경우도 있습니다. 판정 불가라는 뜻인데요. 릴리스를 한 번도 만들지 않은 저장소에 릴리스 서명 여부를 물을 수는 없으니까요. 이렇게 채점 자체가 성립하지 않는 항목은 총점 계산에서 아예 빠지기 때문에 점수를 깎지도 올리지도 않습니다.
정리하면 총점 하나만 보고 좋다 나쁘다를 판단할 때 이 맥락이 통째로 사라집니다. 총점은 어디를 들여다볼지 알려주는 신호일 뿐이고, 정보는 항목별 REASON에 들어 있습니다.
어디가 문제인지 자세히 보기
REASON만으로는 어느 파일의 몇 번째 줄이 문제인지 알 수 없죠. --show-details 옵션을 붙이면 근거를 하나씩 보여줍니다. 관심 있는 항목만 --checks로 골라 돌리면 훨씬 빠르고요.
이번에는 Hono를 대상으로 워크플로우와 관련된 항목만 뽑아봤습니다.
GITHUB_AUTH_TOKEN=$(gh auth token) scorecard \
--repo=github.com/honojs/hono \
--checks=Token-Permissions,Pinned-Dependencies,Dangerous-Workflow \
--show-details
9.8점으로 거의 만점인데, DETAILS 열을 보면 왜 만점이 아닌지가 한 줄로 드러납니다.
| 9 / 10 | Pinned-Dependencies | Warn: npmCommand not pinned by hash:
| | | .github/workflows/release.yml:30
| | | Info: 29 out of 29 GitHub-owned GitHubAction dependencies pinned
| | | Info: 18 out of 18 third-party GitHubAction dependencies pinned
| | | Info: 0 out of 1 npmCommand dependencies pinned
액션 마흔일곱 개를 하나도 빠짐없이 SHA로 고정해 놓고도 9점입니다. 릴리스 워크플로우 30번째 줄의 패키지 설치 명령 하나가 걸렸거든요. Pinned-Dependencies는 액션만 세는 게 아니라 워크플로우에서 실행하는 설치 명령과 Dockerfile의 베이스 이미지까지 함께 봅니다. 액션만 고정해서는 만점이 나오지 않는 이유죠.
Token-Permissions는 반대로 만점인데, 잘 잠근 워크플로우가 어떤 모습인지 보여줍니다.
| 10 / 10 | Token-Permissions | Info: topLevel 'contents' permission set to 'read':
| | | .github/workflows/ci.yml:15
| | | Info: topLevel 'contents' permission set to 'read':
| | | .github/workflows/release.yml:9
| | | Info: topLevel 'contents' permission set to 'read':
| | | .github/workflows/autofix.yml:13
| | | Info: no jobLevel write permissions found
워크플로우마다 최상단에 읽기 권한만 선언해 두었고, 잡 수준에서 쓰기 권한을 연 곳은 한 군데도 없습니다. 마지막 줄의 no jobLevel write permissions found가 이 항목이 바라는 상태를 그대로 요약하고 있죠. 파일과 줄 번호가 다 찍히니 내 저장소에 돌렸을 때도 고칠 곳을 찾아 헤맬 일이 없습니다.
아직 공개하지 않은 저장소나 커밋 전 상태를 확인하고 싶다면 로컬 폴더를 직접 검사할 수도 있습니다.
scorecard --local=. --checks=Dangerous-Workflow,Token-Permissions
다만 로컬 검사는 파일로 확인할 수 있는 항목만 유효합니다. 브랜치 보호나 코드 리뷰처럼 GitHub API를 봐야 아는 항목은 제대로 채점되지 않습니다.
낮은 점수 올리기
앞에서 우선순위를 정했으니 실제 처방을 살펴보겠습니다.
Token-Permissions는 워크플로우가 GITHUB_TOKEN에 기본 권한을 통째로 받아 쓰고 있을 때 0점이 됩니다. 워크플로우 최상단에 permissions: read-all을 선언하고, 쓰기가 필요한 잡에만 개별적으로 권한을 열어주면 해결됩니다. 어떤 권한이 어떤 작업에 필요한지는 GitHub Actions의 권한 설정에서 자세히 다뤘습니다.
permissions: read-all
jobs:
deploy:
permissions:
contents: read
pages: write
Vulnerabilities는 OSV 데이터베이스를 조회해 아직 패치되지 않은 알려진 취약점을 셉니다. 앞서 본 64건처럼 대부분은 개발 의존성에 딸려 온 것인데, 실행 코드에 포함되지 않더라도 Scorecard는 구분하지 않고 셉니다. Dependabot의 보안 업데이트를 켜두면 패치가 나오는 대로 PR이 올라오니 이 숫자를 꾸준히 줄일 수 있습니다.
Pinned-Dependencies는 조금 성가신 항목입니다. 워크플로우에서 actions/checkout@v4처럼 태그로 액션을 참조하면 감점됩니다. 태그는 언제든 다른 커밋을 가리키도록 옮길 수 있어서, 액션 저장소가 손상되면 내 CI가 조용히 악성 코드를 실행하게 되거든요. 그래서 커밋 SHA로 고정하라고 요구합니다.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
읽기 어려워 보이지만 뒤에 버전을 주석으로 달아두면 Dependabot이 이를 알아보고 새 버전이 나올 때 SHA와 주석을 함께 갱신해 줍니다. 고정과 갱신을 둘 다 챙길 수 있는 셈이죠. 서드파티 액션을 SHA로 고정해야 하는 이유는 GitHub Actions를 안전하게 사용하는 방법에서 더 깊이 설명했습니다. 참고로 이 항목은 Dockerfile의 베이스 이미지 태그와 스크립트로 내려받는 설치 파일까지 함께 보기 때문에, 워크플로우만 고쳐서는 만점이 나오지 않는 경우가 많습니다.
Security-Policy는 저장소 루트나 .github/에 SECURITY.md 파일을 두면 채워집니다. 취약점을 어디로 어떻게 신고하면 되는지 적어두는 문서인데, 실제로 제보가 들어왔을 때의 대응 절차는 보안 권고문 대응 가이드에 정리해 두었습니다. License 항목도 마찬가지로 오픈소스 라이선스 파일 하나면 해결됩니다.
Signed-Releases는 릴리스 아티팩트에 서명을 요구하는 항목입니다. 디지털 서명으로 배포물이 중간에 바뀌지 않았음을 증명하는 것인데, Sigstore의 cosign을 릴리스 워크플로우에 붙이는 방식이 표준으로 자리 잡고 있습니다.
GitHub Actions로 자동화하기
한 번 점수를 올려놨어도 워크플로우를 새로 추가하다 보면 다시 내려가기 마련이죠. 공식 액션을 붙여두면 매주 자동으로 검사하고 결과를 저장소 Security 탭에 올려줍니다.
name: Scorecard analysis
on:
push:
branches: [main]
schedule:
- cron: "30 1 * * 6" # 매주 토요일
permissions: read-all
jobs:
analysis:
runs-on: ubuntu-latest
permissions:
security-events: write # SARIF 업로드용
id-token: write # publish_results의 OIDC 인증용
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
with:
sarif_file: results.sarif
몇 가지 짚어볼 만한 지점이 있습니다. persist-credentials: false는 체크아웃한 저장소에 토큰이 남지 않게 합니다. Scorecard가 검사하는 것과 같은 종류의 위험을 워크플로우 스스로 만들지 않기 위한 조치죠.
결과 형식으로 sarif를 쓰는 이유는 GitHub의 코드 스캐닝이 읽는 표준 형식이기 때문입니다. 마지막 단계에서 이 파일을 업로드하면 낮은 점수가 Security 탭에 경고로 뜨고, 어느 파일 어느 줄이 문제인지 코드 위에 바로 표시됩니다. 워크플로우 로그를 뒤질 필요가 없어지는 거죠.
publish_results: true는 결과를 공개 데이터셋에 올립니다. 배지를 달려면 반드시 켜야 하고, OpenSSF 팀이 매주 돌리는 스캔을 내 워크플로우 결과로 대체해 주기도 합니다. 물론 이건 점수가 전 세계에 공개된다는 뜻이니, 비공개하고 싶다면 이 옵션을 끄고 SARIF 업로드만 쓰면 됩니다.
트리거를 push와 schedule로만 잡은 것도 이유가 있습니다. pull_request에서 돌리는 것은 아직 실험 단계이고, 기본 브랜치를 기준으로 채점하는 도구라 포크에서 온 PR에서는 의미 있는 점수가 나오지 않습니다.
배지를 README에 달고 싶다면 이 마크다운을 넣으면 됩니다.
[](https://scorecard.dev/viewer/?uri=github.com/{owner}/{repo})
자동화하기 전에 걸리는 것들
워크플로우를 올려놓고 처음 돌리면 생각보다 자주 막힙니다. 미리 알아두면 좋을 세 가지가 있어요.
먼저 결과를 어디서 보는지입니다. SARIF를 업로드하면 저장소의 Security 탭 아래 Code scanning 메뉴에 항목별 경고가 쌓입니다. Actions 탭의 실행 로그가 아니라는 점을 헷갈리기 쉬운데요. 경고를 클릭하면 문제가 된 워크플로우 파일의 해당 줄로 바로 이동합니다.
그다음은 저장소 종류입니다. 공개 저장소라면 액션도 코드 스캐닝도 무료라 위 워크플로우를 그대로 붙이면 되지만, 비공개 저장소는 코드 스캐닝을 쓰려면 GitHub Advanced Security가 필요합니다. 이게 없다면 액션 대신 CLI로 돌리고 결과를 아티팩트로 남기는 편이 현실적입니다.
비공개 저장소에서 액션을 쓴다면 권한도 더 열어줘야 합니다. 기본 토큰으로는 커밋 조회나 SAST 도구 탐지가 막혀서 Resource not accessible by integration 오류를 만나거든요.
permissions:
security-events: write
id-token: write
contents: read
issues: read
pull-requests: read
checks: read
마지막으로 한계를 하나 짚고 갑니다. “점수가 7점 밑으로 떨어지면 빌드를 실패시키자”는 생각이 자연스럽게 들 텐데요. 액션이 받는 입력은 results_file, results_format, repo_token, publish_results, file_mode가 전부라 임계값을 걸 방법이 없습니다. 점수를 게이트로 쓰고 싶다면 결과를 JSON으로 받아 별도 단계에서 직접 판정해야 합니다.
- uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.json
results_format: json
- name: 총점 확인
run: |
echo "Scorecard 총점: $(jq '.score' results.json)"
jq -e '.score >= 7' results.json > /dev/null
jq -e는 결과가 거짓이면 종료 코드 1을 내보내기 때문에 별도의 비교문 없이 단계가 실패합니다.
다만 이렇게 게이트를 걸 때는 신중한 편이 낫습니다. 앞에서 봤듯 프로젝트 성격상 채울 수 없는 항목이 총점을 끌어내리는 경우가 많아서, 임계값을 잘못 잡으면 고칠 수도 없는 이유로 빌드가 계속 깨집니다. 총점 대신 Dangerous-Workflow 같은 특정 항목만 골라 감시하는 쪽이 대체로 더 쓸모 있습니다.
남의 프로젝트 점수도 볼 수 있습니다
사실 Scorecard가 가장 유용한 순간은 내 저장소를 채점할 때가 아니라 남의 코드를 가져다 쓸지 결정할 때입니다.
의존성 후보를 정했다면 CLI로 바로 조회해 볼 수 있습니다. GitHub 저장소 주소를 몰라도 패키지 이름만으로 됩니다.
GITHUB_AUTH_TOKEN=$(gh auth token) scorecard --npm=express
--pypi, --rubygems, --nuget 옵션도 같은 방식으로 동작합니다. Scorecard가 레지스트리 메타데이터에서 저장소 주소를 찾아 검사합니다.
직접 돌리기 번거롭다면 이미 공개된 결과를 조회하면 됩니다. https://scorecard.dev/viewer/?uri=github.com/{owner}/{repo} 주소로 들어가면 웹에서 점수와 항목별 근거를 볼 수 있고, https://api.scorecard.dev/projects/github.com/{owner}/{repo} 로 요청하면 JSON으로 받아올 수도 있습니다. 의존성 검토를 자동화하고 싶을 때 쓸 만하죠.
여기서 유의할 게 있습니다. 공개된 점수는 대부분 주간 스캔 결과라 최신이 아닐 수 있고, publish_results를 켜지 않은 프로젝트는 애초에 조회되지 않습니다. 점수가 없다고 해서 나쁜 프로젝트라는 뜻은 아닙니다.
점수에 매몰되지 않기
마지막으로 가장 중요한 이야기를 하겠습니다. 10점 만점이 안전을 보장하지 않습니다.
앞서 언급한 xz-utils 백도어를 떠올려 보죠. 그 프로젝트는 코드 리뷰도 거쳤고, CI도 돌았고, 릴리스도 정상적으로 나왔습니다. Scorecard의 검사 항목 대부분을 통과했을 겁니다. 공격자가 노린 것은 그 모든 절차를 통과하는 신뢰 자체였으니까요. Scorecard는 문이 잠겨 있는지 확인해 줄 뿐, 열쇠를 가진 사람이 나쁜 마음을 먹는 상황까지 막지는 못합니다.
반대로 점수가 낮다고 해서 위험한 것도 아닙니다. 개인이 만든 작은 유틸리티 라이브러리는 Contributors가 낮게 나올 수밖에 없고, 퍼즈 테스트가 필요 없는 프로젝트는 Fuzzing이 영원히 0점입니다. 맞지 않는 항목을 억지로 채우는 것은 점수를 위한 점수일 뿐이죠.
프로젝트 성격상 맞지 않는 항목이 있다면 저장소 루트에 scorecard.yml을 두고 이유를 남길 수 있습니다.
annotations:
- checks:
- binary-artifacts
reasons:
- reason: test-data # 테스트에만 쓰는 바이너리입니다
주의할 점은 이 주석이 점수를 바꾸지는 않는다는 것입니다. --show-annotations 옵션이나 뷰어에서 함께 표시될 뿐이죠. 점수를 조작하는 대신 맥락을 남기는 장치라고 이해하면 됩니다. 이게 더 건강한 방향이기도 하죠. 낮은 점수를 지우는 게 아니라 왜 낮은지 설명하는 쪽이니까요.
결국 Scorecard를 쓰는 가장 좋은 방법은 총점을 올리는 게임으로 접근하지 않는 겁니다. 표를 열어서 High 등급 중 0점인 항목이 있는지 보고, 그게 우리 프로젝트에 실제로 위험한지 판단하고, 위험하면 고치는 순서로 쓰는 거죠. 점수는 대화의 시작점이지 결론이 아닙니다.
마치며
Scorecard는 그동안 감으로만 판단하던 프로젝트의 보안 관행을 눈에 보이는 항목으로 바꿔줍니다. Scorecard 저장소가 8.9점, Hono가 9.8점이라는 숫자 자체보다, 왜 그 점수인지가 파일과 줄 번호로 딸려 나온다는 게 핵심이죠. 이미 알고 있던 문제가 아니라 몰랐던 문제를 짚어준다는 점에서 값어치가 있습니다.
시작은 간단합니다. brew install scorecard 한 줄로 설치하고 여러분의 저장소에 한 번 돌려보세요. 몇 분이면 어디가 열려 있는지 목록으로 나옵니다. High 등급에서 0점이 난 항목부터 하나씩 닫아나가고, 익숙해지면 워크플로우로 자동화해서 점수가 다시 내려가지 않게 지키면 됩니다.
여기서 다루지 못한 주제로는 소프트웨어 자재 명세서(SBOM)와 빌드 출처를 증명하는 SLSA 프레임워크가 있습니다. Scorecard가 측정하는 관행의 다음 단계에 해당하니, 공급망 보안을 더 파고들고 싶다면 이어서 살펴볼 만합니다.
각 검사가 어떤 기준으로 점수를 매기고 어떻게 고쳐야 하는지는 Scorecard 검사 문서에 항목별로 정리되어 있습니다.
This work is licensed under CC BY 4.0