diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 27c5b540f..8440aad8d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,8 +32,8 @@ jobs: - python steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + - uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 with: languages: ${{ matrix.language }} - - uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 - - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + - uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 + - uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 \ No newline at end of file diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 2a4b6eaa9..9ec1220d1 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -79,6 +79,6 @@ jobs: python3 trusted-scorecard-scripts/scripts/checks/normalize_scorecard_sarif.py scorecard-sarif/results.sarif normalized-scorecard-results.sarif - - uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 peeled commit; SHA pinning retained as supply-chain attack mitigation. + - uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8; immutable SHA pinning retained as supply-chain attack mitigation. with: - sarif_file: normalized-scorecard-results.sarif + sarif_file: normalized-scorecard-results.sarif \ No newline at end of file diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index d79ec32e1..33044efb5 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -48,7 +48,7 @@ jobs: skip-dirs: 'services/analysis-engine/.venv' trivyignores: ./.trivyignore - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 peeled commit; SHA pinning retained as supply-chain attack mitigation. + uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8; immutable SHA pinning retained as supply-chain attack mitigation. if: always() with: - sarif_file: trivy-results.sarif + sarif_file: trivy-results.sarif \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index eea696893..a2121acea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace. - 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함. +### Changed + +- Updated every CodeQL Action phase and SARIF uploader together to the reviewed immutable `v4.37.8` revision, with a regression contract that prevents mixed `init`, `autobuild`, `analyze`, and `upload-sarif` versions. + ## [0.1.3] - 2026-04-29 ### Fixed @@ -65,4 +69,4 @@ - `ChordsFeature` (코드 분석) 화면에서 각 파트(Role)의 `transpositionPlan`(이조/조옮김 계획)을 표시하는 기능을 추가했습니다. - `RangesFeature` (음역대 분석) 화면에서 겹침 경고(Overlap warning) 외에 해당 파트의 채보(Transcription) 가능 노드 수를 요약하여 보여주는 기능을 추가했습니다. -- 신규 UI 요소에 대한 100% 테스트 커버리지를 보장하는 단위 테스트를 추가했습니다 (`apps/desktop/src/features/chords/index.test.tsx`, `apps/desktop/src/features/ranges/index.test.tsx`). +- 신규 UI 요소에 대한 100% 테스트 커버리지를 보장하는 단위 테스트를 추가했습니다 (`apps/desktop/src/features/chords/index.test.tsx`, `apps/desktop/src/features/ranges/index.test.tsx`). \ No newline at end of file diff --git a/docs/doctoring/codeql-action-atomic-revision.md b/docs/doctoring/codeql-action-atomic-revision.md new file mode 100644 index 000000000..9ce557a3c --- /dev/null +++ b/docs/doctoring/codeql-action-atomic-revision.md @@ -0,0 +1,65 @@ +# Atomic CodeQL Action revision policy + +## Decision + +BandScope treats the CodeQL Action lifecycle as one supply-chain dependency. Every checked-in reference to `github/codeql-action/init`, `autobuild`, `analyze`, and `upload-sarif` must use the same reviewed full-length commit SHA and matching release annotation. + +The current reviewed revision is CodeQL Action `v4.37.8` at commit `db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28`. The upstream annotated tag object `37f2634a92ba38a0926ef79a0748ac8ae7d95ab2` resolves to that commit. The release was published on August 21, 2026 and reports no user-facing changes; it retains the v4.37.7-era default CodeQL bundle `2.26.3`. + +GitHub documents `init` as the phase that initializes CodeQL, `autobuild` as the optional automatic build phase, and `analyze` as the phase that finalizes the database, runs queries, and uploads results. `upload-sarif` publishes SARIF generated by other tools. These phases exchange state and therefore move together in this repository rather than through independent dependency pull requests. + +## Threat and compatibility boundary + +A full commit SHA is the immutable execution identity. Tags remain useful release labels, but they are not accepted as the workflow execution reference. GitHub identifies a full-length commit SHA as the strongest immutable action reference and supports organization policy requiring that form. + +Independently updating one phase can leave the repository with mixed JavaScript bundles, CodeQL CLI expectations, feature flags, or SARIF transport behavior. Even when each individual release is valid, the mixed lifecycle has not been reviewed or tested as a unit. The atomic policy prevents both persistent drift and the transient mixed state that can occur when several Dependabot pull requests merge at different times. + +```mermaid +flowchart LR + A[CodeQL init] --> B[CodeQL autobuild] + B --> C[CodeQL analyze] + C --> D[GitHub code scanning] + E[Trivy and Scorecard SARIF] --> F[CodeQL upload-sarif] + R[One reviewed release SHA] --> A + R --> B + R --> C + R --> F +``` + +The change does not alter workflow triggers, language selection, build behavior, SARIF paths, permissions, or failure handling. It changes only the immutable CodeQL Action implementation identity and version comments. + +## Verification contract + +`services/analysis-engine/tests/test_codeql_action_revision_contract.py` scans every workflow and fails unless: + +1. all CodeQL Action phases use one exact reviewed SHA; +2. every reference carries the matching `v4.37.8` annotation; and +3. `codeql.yml` keeps `init`, `autobuild`, and `analyze` on that same revision. + +The scanner intentionally recognizes mutable and malformed revision tokens such as `@v4` before enforcing the exact-SHA invariant. A tag-style reference therefore becomes a failing value instead of disappearing from the evidence set because it did not already look like a 40-character SHA. + +Repository CI, CodeQL, SAST, dependency/security scans, SBOM generation, central coverage evidence, automated review, independent approval, and branch protection must all validate the final exact head. Results from split predecessor pull requests are not transferable. + +## Update procedure + +1. Identify the newest supported CodeQL Action v4 release from the upstream GitHub repository. +2. Verify the tag resolves to the intended upstream commit and inspect the release notes. +3. Add or update the contract expectation first and observe the RED failure against the old revision. +4. Update every `init`, `autobuild`, `analyze`, and `upload-sarif` reference in one branch. +5. Run the focused contract, workflow/static checks, and the complete repository gates. +6. Merge only after exact-current-head review and branch protection succeed without bypass. +7. Close split dependency pull requests as superseded; do not reuse their checks or approvals. + +## Rollback + +Rollback restores the previously accepted full-length SHA across every CodeQL Action phase in one reviewed commit. A partial rollback is prohibited. After rollback, rerun the same exact-head security, quality, SARIF publication, and review gates before accepting the branch. + +## References + +GitHub. (2026). *CodeQL Action v4.37.8* [Software release]. https://github.com/github/codeql-action/releases/tag/v4.37.8 + +GitHub. (2026). *CodeQL Bundle v2.26.3* [Software release]. https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.26.3 + +GitHub. (n.d.). *CodeQL code scanning for compiled languages*. GitHub Docs. Retrieved August 25, 2026, from https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/codeql-for-compiled-languages + +GitHub. (n.d.). *Secure use reference*. GitHub Docs. Retrieved August 25, 2026, from https://docs.github.com/en/actions/reference/security/secure-use \ No newline at end of file diff --git a/services/analysis-engine/tests/test_codeql_action_revision_contract.py b/services/analysis-engine/tests/test_codeql_action_revision_contract.py new file mode 100644 index 000000000..03d26b369 --- /dev/null +++ b/services/analysis-engine/tests/test_codeql_action_revision_contract.py @@ -0,0 +1,63 @@ +"""Supply-chain contracts for a coherent GitHub CodeQL Action revision.""" + +from __future__ import annotations + +import re +from pathlib import Path + +_REPOSITORY_ROOT = Path(__file__).resolve().parents[3] +_WORKFLOW_ROOT = _REPOSITORY_ROOT / ".github" / "workflows" +_EXPECTED_CODEQL_ACTION_SHA = "db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28" +_EXPECTED_CODEQL_ACTION_VERSION = "v4.37.8" +_CODEQL_ACTION_REFERENCE = re.compile( + r"github/codeql-action/(init|autobuild|analyze|upload-sarif)@([^\s#]+)([^\n]*)" +) + + +def _codeql_action_references() -> list[tuple[Path, str, str, str]]: + """Return every CodeQL Action reference from checked-in workflows.""" + references: list[tuple[Path, str, str, str]] = [] + for workflow_path in sorted(_WORKFLOW_ROOT.glob("*.y*ml")): + workflow_text = workflow_path.read_text(encoding="utf-8") + matches = _CODEQL_ACTION_REFERENCE.findall(workflow_text) + for action_name, revision_ref, suffix in matches: + reference = (workflow_path, action_name, revision_ref, suffix.strip()) + references.append(reference) + return references + + +def test_reference_scanner_detects_non_sha_codeql_references() -> None: + """Mutable CodeQL refs must be visible to the guard instead of being skipped.""" + workflow_line = "uses: github/codeql-action/init@v4 # mutable reference" + + matches = _CODEQL_ACTION_REFERENCE.findall(workflow_line) + + assert matches == [("init", "v4", " # mutable reference")] + + +def test_every_codeql_action_step_uses_the_same_reviewed_revision() -> None: + """Prevent independently updated phases from creating mixed CodeQL runtimes.""" + references = _codeql_action_references() + + assert references + assert { + revision_ref for _, _, revision_ref, _ in references + } == {_EXPECTED_CODEQL_ACTION_SHA} + expected_version = f"# {_EXPECTED_CODEQL_ACTION_VERSION}" + assert all(expected_version in suffix for _, _, _, suffix in references) + + +def test_analysis_workflow_keeps_init_autobuild_and_analyze_atomic() -> None: + """Require the analysis lifecycle to move as one immutable dependency unit.""" + workflow_path = _WORKFLOW_ROOT / "codeql.yml" + workflow_text = workflow_path.read_text(encoding="utf-8") + references = { + action_name: revision_ref + for action_name, revision_ref, _suffix in _CODEQL_ACTION_REFERENCE.findall(workflow_text) + } + + assert references == { + "init": _EXPECTED_CODEQL_ACTION_SHA, + "autobuild": _EXPECTED_CODEQL_ACTION_SHA, + "analyze": _EXPECTED_CODEQL_ACTION_SHA, + } \ No newline at end of file