diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2df162d89..ed4826406 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -38,4 +38,3 @@ updates: open-pull-requests-limit: 10 labels: - "dependencies" - - "github-actions" diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 8f5b1bc25..b68ffd2b3 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -83,6 +83,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@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 peeled commit; SHA pinning retained as supply-chain attack mitigation. with: sarif_file: normalized-scorecard-results.sarif diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index 07754a782..dd2547fe7 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -92,6 +92,6 @@ jobs: trivyignores: ./.trivyignore - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 with: sarif_file: trivy-results.sarif diff --git a/services/analysis-engine/tests/test_workflow_action_annotation_policy.py b/services/analysis-engine/tests/test_workflow_action_annotation_policy.py new file mode 100644 index 000000000..a3064cfaf --- /dev/null +++ b/services/analysis-engine/tests/test_workflow_action_annotation_policy.py @@ -0,0 +1,53 @@ +"""Regression tests for human-readable GitHub Action pin annotations.""" + +import re +from pathlib import Path + + +UPLOAD_SARIF_REFERENCE_RE = re.compile( + r"^\s*-\s+uses:\s+github/codeql-action/upload-sarif@" + r"(?P[0-9a-fA-F]{40})\s+#\s+v(?P\d+\.\d+\.\d+)(?:\s|$)" +) + + +def test_upload_sarif_sha_annotations_are_complete_and_consistent() -> None: + """Keep upload-sarif SHA pins and their review annotations in one identity.""" + repo_root = Path(__file__).resolve().parents[3] + workflow_dir = repo_root / ".github" / "workflows" + workflow_paths = sorted( + set(workflow_dir.glob("*.yml")) | set(workflow_dir.glob("*.yaml")) + ) + references: list[tuple[str, str, str]] = [] + incomplete: list[str] = [] + + for workflow_path in workflow_paths: + for line_number, line in enumerate( + workflow_path.read_text(encoding="utf-8").splitlines(), start=1 + ): + if ( + "github/codeql-action/upload-sarif@" not in line + or line.lstrip().startswith("#") + ): + continue + match = UPLOAD_SARIF_REFERENCE_RE.match(line) + location = f"{workflow_path.relative_to(repo_root)}:{line_number}" + if match is None: + incomplete.append(location) + continue + references.append( + (match.group("sha").lower(), match.group("version"), location) + ) + + assert not incomplete, ( + "upload-sarif pins must carry an adjacent semantic-version annotation: " + + ", ".join(incomplete) + ) + assert references, "repository workflows must contain a reviewed upload-sarif pin" + + identities = {(sha, version) for sha, version, _ in references} + assert len(identities) == 1, ( + "upload-sarif workflows disagree on the reviewed SHA/version identity: " + + ", ".join( + f"{location}={sha}@v{version}" for sha, version, location in references + ) + )