OpenSSF Scorecard 태세
Apothem의 OpenSSF Scorecard 목표와 점수를 계속 개선하는 데 쓰는 증거 표면입니다.
Apothem은 OpenSSF Scorecard 최고 점수를 목표로 하면서, Scorecard가 새로운 단독 메인테이너 저장소에서 무엇을 추론할 수 있고 무엇을 추론할 수 없는지에 대해 공개 외관을 정직하게 유지합니다. 이 문서는 각 검사, 그 검사를 뒷받침하는 증거 표면, 그리고 태세를 최신으로 유지하는 통제를 열거합니다.
현재 점수는 Scorecard 뷰어에 있으며, 배지는 README 헤더에 있습니다.
검사별 증거 매트릭스
| 검사 | 증거 표면 | 비고 |
|---|---|---|
| Binary-Artifacts | binary-artifacts-sweep.md | 소스에서 추적되는 실행 가능 바이너리는 0개입니다. 생성된 배포 아카이브는 dist/에 머뭅니다. |
| Branch-Protection | branch-protection-ruleset.md | 공개 릴리스 승격 전에 main이 보호됩니다. 새 릴리스 커밋이 반영된 후에는 강제 푸시와 삭제가 차단됩니다. |
| CI-Tests | .github/workflows/ci.yml | Pytest 매트릭스, ruff, mypy, Trivy, 문서 빌드, 패키징 스모크 검사. |
| CII-Best-Practices | OpenSSF Best Practices 등록 | OpenSSF Best Practices 사이트에서 메인테이너 측 등록이 필요합니다. 이는 저장소 파일만으로는 완료할 수 없습니다. |
| Code-Review | 브랜치 보호 + CODEOWNERS | 저장소 관리자를 제외한 모든 사람에게 main에 대한 풀 리퀘스트가 필수이며, 프로젝트에 메인테이너가 한 명인 동안 브랜치 보호는 승인 검토 0건을 요구합니다(branch-protection-ruleset.md 참조). 검토된 PR이 생기기 전까지 Scorecard 이력은 제한적으로 남습니다. |
| Contributors | AUTHORS | 새로운 단독 메인테이너 프로젝트는 여러 조직에 걸친 기여 이력을 만들어 낼 수 없습니다. 이 항목은 기여자가 합류하면서 자연스럽게 개선됩니다. |
| Dangerous-Workflow | 워크플로 감사 | pull_request_target이 없습니다. Actions는 SHA로 고정되고 명시적 권한으로 범위가 한정됩니다. |
| Dependency-Update-Tool | .github/dependabot.yml | Dependabot은 GitHub Actions 고정, 웹사이트, vsce 패키저 잠금 파일, 해시 잠금된 CI pip, pre-commit 훅에 대해 그룹화된 주간 업데이트를, channel-smoke 잠금 파일에 대해 월간 업데이트를 제안합니다. Dependabot 보안 업데이트는 감지된 모든 매니페스트를 다루지만, scripts/ci/에 있는 CI 의존성 및 도구의 전체 잠금 세트는 제외됩니다. 경고가 잠긴 패키지를 지목하면 scripts/dev/refresh_ci_locks.py가 이 세트를 다시 생성합니다. .gitattributes는 이 파일 하나를 GitHub 소스 아카이브에 유지하며, 액션의 기본 아카이브 모드로 실행하는 스캔이 이 아카이브를 읽습니다. Scorecard 워크플로는 git 모드로 실행되어 클론에서 이 파일을 읽습니다. |
| Fuzzing | tests/property/ | Hypothesis 속성 테스트가 파서와 frontmatter 불변식을 검증합니다. 업스트림 Scorecard는 Hypothesis를 퍼저 통합으로 인정하지 않을 수 있습니다. |
| License | LICENSE | 저장소 루트의 MIT 라이선스. |
| Maintained | Git 히스토리 | 현재 릴리스 커밋과 활발한 워크플로 주기가 유지 관리를 입증합니다. 매우 새로운 저장소는 연령 경고를 받을 수 있습니다. |
| Packaging | .github/workflows/ | sdist, wheel, SBOM, SLSA 출처, Sigstore 서명을 생성하는 릴리스, Pages, npmjs.com 워크플로. |
| Pinned-Dependencies | 워크플로 감사 + 해시 잠금된 요구 사항 | Actions는 SHA로 고정됩니다. 릴리스 도구, CI pip 업그레이드, CI 프로젝트 및 테스트 의존성, CI 도구(ruff, bandit, pip-audit, pip-licenses, pre-commit)는 --require-hashes를 사용해 설치됩니다. 프로젝트, 테스트, 도구 설치는 scripts/ci/의 잠금 파일 9개를 읽습니다. pip 업그레이드와 릴리스 도구는 각자 자체 잠금 파일을 읽고, 패키지 자체는 --no-deps -e . 옵션으로 설치됩니다. |
| SAST | .github/workflows/codeql.yml | Python과 Actions 전반에 걸친 CodeQL security-extended 쿼리. |
| Security-Policy | SECURITY.md | 조율된 공개 정책, 지원 버전 매트릭스, 대응 타임라인. |
| Signed-Releases | .github/workflows/release.yml | Sigstore 키리스 서명, SLSA 출처, SBOM, 체크섬, 릴리스 아티팩트. |
| Token-Permissions | 워크플로 감사 | 워크플로는 기본적으로 읽기 전용 권한을 사용합니다. 쓰기 범위는 작업 로컬이며 배포 및 패키지 작업에 묶여 있습니다. |
| Vulnerabilities | .github/workflows/pip-audit.yml + .github/workflows/dependency-review.yml | 주간 취약점 스캔과 SARIF 업로드를 포함한 CI 스캔. dependency-review 게이트는 취약하거나 허용되지 않은 라이선스의 의존성을 도입하는 풀 리퀘스트를 차단합니다. 이 검사 자체가 저장소 매니페스트에서 찾은 의존성을 OSV에서 조회하므로 pip-audit.yml도 OSV에 질의합니다(pip-audit -s osv). 따라서 그 발견 사항은 검사의 발견 사항과 일치합니다. |
점수 해석
Scorecard는 각 검사를 0-10 척도로 채점하며, 전체 점수는 가중 평균입니다. 저장소 이력, 서드파티 등록, 기여자 다양성에 의존하는 통제는 과장해 주장하지 않고 명시적으로 추적합니다. 파일로 통제되는 검사에서 드리프트가 발생하면 즉시 시정 조치가 시작됩니다.
Scorecard 워크플로는 file_mode: git으로 액션을 실행하므로 Scorecard는 저장소의 클론을 읽습니다. 소스 아카이브에는 .github/가 빠져 있습니다(export-ignore, .gitattributes에서 지정). 따라서 액션의 기본 아카이브 모드에서는 Dangerous-Workflow, Token-Permissions, Pinned-Dependencies, Packaging에 워크플로가 보이지 않게 됩니다.
Pinned-Dependencies는 두 가지 이유로 의도적으로 10 미만에 머뭅니다. SLSA 생성기는 릴리스 태그로 참조되며, 이는 SHA 고정의 유일한 예외입니다. 그 출처가 해당 태그에 묶여 있기 때문입니다. evals 워크플로는 실행마다 선택되는 버전의 Claude Code CLI를 npm install --global로 설치합니다. Scorecard가 워크플로에서 스캔하는 Python 설치는 해시 잠금된 설치, 패키지 자체의 로컬 설치, 같은 실행에서 빌드한 wheel 설치 중 하나입니다.
갱신 주기
- 푸시마다: Scorecard 워크플로는
main으로의 모든 푸시에서 다시 실행됩니다. - 매주: 월요일 02:30 UTC — 업스트림 채점 방법론의 변경 + 기존에 고정된 의존성을 대상으로 새로 공개된 취약점을 포착합니다.
- 릴리스마다: 공개 승격 전에 메인테이너가 현재 Scorecard 뷰어 표면을 검증하고 플랫폼 상태 제약을 기록합니다.
참고 자료
- OpenSSF Scorecard — 업스트림 프로젝트
- Scorecard 검사 참조 — 검사별 의미
ossf/scorecard-action— GitHub Actions 래퍼- SLSA 프레임워크 — 공급망 등급(apothem은 SLSA-3으로 배포됨)