From 4a3ff1323c956c294dc01955d6603d526255f4b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:07:41 +0000 Subject: [PATCH 01/23] build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.8 Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.0 to 4.37.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 27c5b540f..e3d704b52 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,7 +32,7 @@ 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 From 661afd39839c947569de5b1227f17a4c36e7c9e1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 18:16:05 -0700 Subject: [PATCH 02/23] test(codeql): require coordinated action versions --- .../test_codeql_action_version_contract.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 services/analysis-engine/tests/test_codeql_action_version_contract.py diff --git a/services/analysis-engine/tests/test_codeql_action_version_contract.py b/services/analysis-engine/tests/test_codeql_action_version_contract.py new file mode 100644 index 000000000..2f4c69b0e --- /dev/null +++ b/services/analysis-engine/tests/test_codeql_action_version_contract.py @@ -0,0 +1,23 @@ +"""Regression contract for coordinated CodeQL Action component upgrades.""" + +from __future__ import annotations + +import re +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[3] +CODEQL_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "codeql.yml" +CODEQL_ACTION_PATTERN = re.compile( + r"github/codeql-action/(?:init|autobuild|analyze)@([0-9a-f]{40})\s+#\s+(v[0-9.]+)" +) + + +def test_codeql_job_uses_one_action_release() -> None: + """Require init, autobuild, and analyze to use one reviewed CodeQL release.""" + workflow = CODEQL_WORKFLOW.read_text(encoding="utf-8") + action_refs = CODEQL_ACTION_PATTERN.findall(workflow) + + assert len(action_refs) == 3 + assert len({sha for sha, _version in action_refs}) == 1 + assert len({version for _sha, version in action_refs}) == 1 From 9532f27b4fc711e198f944e982669eba184519c2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 18:16:20 -0700 Subject: [PATCH 03/23] fix(codeql): keep action components aligned --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e3d704b52..54616541d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,5 +35,5 @@ jobs: - 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 From ffcc758f5868d23045619ef498853c69a0c8b9e5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:13:27 -0700 Subject: [PATCH 04/23] test(codeql): require coordinated SARIF uploader release --- ...st_codeql_upload_sarif_version_contract.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py diff --git a/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py b/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py new file mode 100644 index 000000000..17419ea6b --- /dev/null +++ b/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py @@ -0,0 +1,24 @@ +"""Regression contracts for CodeQL SARIF uploader provenance comments.""" + +from __future__ import annotations + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[3] +EXPECTED_SHA = "db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28" +EXPECTED_VERSION = "v4.37.8" +UPLOAD_SARIF_REF = ( + f"github/codeql-action/upload-sarif@{EXPECTED_SHA} # {EXPECTED_VERSION} peeled commit;" +) +WORKFLOWS = ( + REPO_ROOT / ".github" / "workflows" / "ossf-scorecard.yml", + REPO_ROOT / ".github" / "workflows" / "trivy.yml", +) + + +def test_upload_sarif_sha_and_version_comment_move_together() -> None: + """Keep each immutable SARIF uploader pin paired with its reviewed release label.""" + for workflow in WORKFLOWS: + contents = workflow.read_text(encoding="utf-8") + assert contents.count(UPLOAD_SARIF_REF) == 1 From b8c1d1d2ba0fc52c8af91951b9915c21078a82d9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:13:53 -0700 Subject: [PATCH 05/23] fix(codeql): align Scorecard SARIF uploader release --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 2a4b6eaa9..a66720ee9 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 peeled commit; SHA pinning retained as supply-chain attack mitigation. with: sarif_file: normalized-scorecard-results.sarif From 871ac355c059c78a2456d4d26e384225f7461ba2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:14:18 -0700 Subject: [PATCH 06/23] fix(codeql): align Trivy SARIF uploader release --- .github/workflows/trivy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index d79ec32e1..db6c3db87 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 peeled commit; SHA pinning retained as supply-chain attack mitigation. if: always() with: sarif_file: trivy-results.sarif From d2b70979dc9dba3e2fe486a09e5151b1a91d6610 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:16:37 -0700 Subject: [PATCH 07/23] test(dependabot): require existing CI/CD label --- .../tests/test_dependabot_label_contract.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 services/analysis-engine/tests/test_dependabot_label_contract.py diff --git a/services/analysis-engine/tests/test_dependabot_label_contract.py b/services/analysis-engine/tests/test_dependabot_label_contract.py new file mode 100644 index 000000000..4bec93429 --- /dev/null +++ b/services/analysis-engine/tests/test_dependabot_label_contract.py @@ -0,0 +1,18 @@ +"""Regression contract for Dependabot's configured repository labels.""" + +from __future__ import annotations + +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[3] +DEPENDABOT_CONFIG = REPO_ROOT / ".github" / "dependabot.yml" + + +def test_github_actions_updates_use_repository_ci_cd_taxonomy() -> None: + """Keep GitHub Actions updates on an existing CI/CD taxonomy label.""" + config = DEPENDABOT_CONFIG.read_text(encoding="utf-8") + github_actions = config.split('package-ecosystem: "github-actions"', maxsplit=1)[1] + + assert '- "area: ci-cd"' in github_actions + assert '- "github-actions"' not in github_actions From 9fc32ab18ce12a78a558de15af004070a7d01fd6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:16:51 -0700 Subject: [PATCH 08/23] fix(dependabot): use existing CI/CD label --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2df162d89..8221219aa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -38,4 +38,4 @@ updates: open-pull-requests-limit: 10 labels: - "dependencies" - - "github-actions" + - "area: ci-cd" From 481766c51f70fc0583bf68462d6ad3ca49e4451c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:39:17 -0700 Subject: [PATCH 09/23] fix(codeql): normalize contract import spacing --- .../analysis-engine/tests/test_codeql_action_version_contract.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/analysis-engine/tests/test_codeql_action_version_contract.py b/services/analysis-engine/tests/test_codeql_action_version_contract.py index 2f4c69b0e..6e225686d 100644 --- a/services/analysis-engine/tests/test_codeql_action_version_contract.py +++ b/services/analysis-engine/tests/test_codeql_action_version_contract.py @@ -5,7 +5,6 @@ import re from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[3] CODEQL_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "codeql.yml" CODEQL_ACTION_PATTERN = re.compile( From 19484ed5f1d444f5aa9d001523c9762976012316 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 25 Aug 2026 19:39:38 -0700 Subject: [PATCH 10/23] fix(codeql): normalize SARIF contract spacing --- .../tests/test_codeql_upload_sarif_version_contract.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py b/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py index 17419ea6b..523c548b1 100644 --- a/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py +++ b/services/analysis-engine/tests/test_codeql_upload_sarif_version_contract.py @@ -4,7 +4,6 @@ from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[3] EXPECTED_SHA = "db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28" EXPECTED_VERSION = "v4.37.8" From 2c8a453127e75ea2e919fff9aff3a652d32cea17 Mon Sep 17 00:00:00 2001 From: seonghobae Date: Fri, 28 Aug 2026 12:28:05 +0900 Subject: [PATCH 11/23] style: normalize dependabot contract imports --- services/analysis-engine/tests/test_dependabot_label_contract.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/analysis-engine/tests/test_dependabot_label_contract.py b/services/analysis-engine/tests/test_dependabot_label_contract.py index 4bec93429..4fad611ee 100644 --- a/services/analysis-engine/tests/test_dependabot_label_contract.py +++ b/services/analysis-engine/tests/test_dependabot_label_contract.py @@ -4,7 +4,6 @@ from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[3] DEPENDABOT_CONFIG = REPO_ROOT / ".github" / "dependabot.yml" From be5fd3702e822d0955751275a931a2db9c96a634 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 10:07:26 +0900 Subject: [PATCH 12/23] test(scorecard): reproduce missing PR code-scanning evidence --- ...est_scorecard_pr_code_scanning_contract.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py diff --git a/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py b/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py new file mode 100644 index 000000000..61fb7afde --- /dev/null +++ b/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py @@ -0,0 +1,31 @@ +"""Regression contract for OpenSSF Scorecard evidence on pull-request heads.""" + +from __future__ import annotations + +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[3] +WORKFLOW = REPO_ROOT / ".github" / "workflows" / "ossf-scorecard.yml" +PR_OR_DEFAULT_BRANCH = ( + "github.event_name == 'pull_request' || github.ref == " + "format('refs/heads/{0}', github.event.repository.default_branch)" +) +PR_SAFE_TRUSTED_REF = ( + "ref: ${{ github.event_name == 'pull_request' && " + "github.event.pull_request.base.sha || github.ref_name }}" +) + + +def test_scorecard_produces_pr_code_scanning_evidence_without_pr_publishing() -> None: + """Keep PR SARIF coverage while publishing only trusted default-branch runs.""" + contents = WORKFLOW.read_text(encoding="utf-8") + + assert "pull_request_target:" not in contents + assert " pull_request:\n branches:\n - develop\n - main\n" in contents + assert contents.count(f"if: {PR_OR_DEFAULT_BRANCH}") == 3 + assert ( + "publish_results: ${{ github.event_name != 'pull_request' && " + "github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}" + in contents + ) + assert PR_SAFE_TRUSTED_REF in contents From 4b9050d41e3bdfe742c6e4af1b6446d61afb611b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 10:08:05 +0900 Subject: [PATCH 13/23] fix(scorecard): emit SARIF for pull-request heads --- .github/workflows/ossf-scorecard.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index a66720ee9..2b9c31c96 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -8,6 +8,10 @@ on: branches: - develop - main + pull_request: + branches: + - develop + - main permissions: read-all @@ -27,13 +31,13 @@ jobs: with: persist-credentials: false - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 - if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + if: github.event_name == 'pull_request' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) with: results_file: results.sarif results_format: sarif - publish_results: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + publish_results: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + if: github.event_name == 'pull_request' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) with: name: ossf-scorecard-results path: results.sarif @@ -41,7 +45,7 @@ jobs: scorecard-sarif-upload: name: scorecard-sarif-upload needs: analysis - if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + if: github.event_name == 'pull_request' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) runs-on: ubuntu-latest permissions: actions: read @@ -63,7 +67,7 @@ jobs: with: persist-credentials: false path: trusted-scorecard-scripts - ref: ${{ github.ref_name }} + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.ref_name }} - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ossf-scorecard-results From d50df535af50f1163e6c1767a3a49a493815b56a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 18:58:00 +0900 Subject: [PATCH 14/23] fix(scorecard): restore default-branch publish guard --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 2b9c31c96..dd7c2270c 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -35,7 +35,7 @@ jobs: with: results_file: results.sarif results_format: sarif - publish_results: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + publish_results: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: github.event_name == 'pull_request' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) with: From e6ea024edccf5ec33058a316d15510fb4760f446 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:00:46 +0900 Subject: [PATCH 15/23] test(scorecard): align PR publish guard contract --- .../test_scorecard_pr_code_scanning_contract.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py b/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py index 61fb7afde..3d03fd0df 100644 --- a/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py +++ b/services/analysis-engine/tests/test_scorecard_pr_code_scanning_contract.py @@ -6,10 +6,8 @@ REPO_ROOT = Path(__file__).resolve().parents[3] WORKFLOW = REPO_ROOT / ".github" / "workflows" / "ossf-scorecard.yml" -PR_OR_DEFAULT_BRANCH = ( - "github.event_name == 'pull_request' || github.ref == " - "format('refs/heads/{0}', github.event.repository.default_branch)" -) +DEFAULT_BRANCH_GUARD = "github.ref == format('refs/heads/{0}', github.event.repository.default_branch)" +PR_OR_DEFAULT_BRANCH = f"github.event_name == 'pull_request' || {DEFAULT_BRANCH_GUARD}" PR_SAFE_TRUSTED_REF = ( "ref: ${{ github.event_name == 'pull_request' && " "github.event.pull_request.base.sha || github.ref_name }}" @@ -23,9 +21,10 @@ def test_scorecard_produces_pr_code_scanning_evidence_without_pr_publishing() -> assert "pull_request_target:" not in contents assert " pull_request:\n branches:\n - develop\n - main\n" in contents assert contents.count(f"if: {PR_OR_DEFAULT_BRANCH}") == 3 - assert ( - "publish_results: ${{ github.event_name != 'pull_request' && " - "github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}" - in contents - ) + publish_lines = [ + line.strip() + for line in contents.splitlines() + if line.strip().startswith("publish_results:") + ] + assert publish_lines == [f"publish_results: ${{{{ {DEFAULT_BRANCH_GUARD} }}}}"] assert PR_SAFE_TRUSTED_REF in contents From 046e221c1ba5d2c1499f90ff340dde021bed08f6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:01:39 +0900 Subject: [PATCH 16/23] test(trivy): add PR scan contract verifier --- scripts/checks/verify_trivy_pr_scan.py | 179 +++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 scripts/checks/verify_trivy_pr_scan.py diff --git a/scripts/checks/verify_trivy_pr_scan.py b/scripts/checks/verify_trivy_pr_scan.py new file mode 100644 index 000000000..4aa55b4b6 --- /dev/null +++ b/scripts/checks/verify_trivy_pr_scan.py @@ -0,0 +1,179 @@ +"""Fail closed when Trivy code scanning cannot run on pull-request heads.""" + +from pathlib import Path + +TRIVY_WORKFLOW = Path(".github/workflows/trivy.yml") + + +def _indented_block(lines: list[str], header: str, indent: int) -> list[str]: + """Return the YAML-like block nested under an exact-indentation mapping key.""" + prefix = " " * indent + target = f"{prefix}{header}:" + for index, line in enumerate(lines): + if line != target: + continue + block: list[str] = [] + for candidate in lines[index + 1 :]: + stripped = candidate.strip() + if not stripped or stripped.startswith("#"): + block.append(candidate) + continue + candidate_indent = len(candidate) - len(candidate.lstrip(" ")) + if candidate_indent <= indent: + break + block.append(candidate) + return block + return [] + + +def _list_values(lines: list[str], header: str, indent: int) -> set[str]: + """Return literal scalar list items nested under the requested mapping key.""" + block = _indented_block(lines, header, indent) + item_prefix = " " * (indent + 2) + "- " + return { + line[len(item_prefix) :].strip() + for line in block + if line.startswith(item_prefix) and line[len(item_prefix) :].strip() + } + + +def _list_item_blocks(lines: list[str], header: str, indent: int) -> list[list[str]]: + """Split one YAML-like sequence block into its top-level item blocks.""" + block = _indented_block(lines, header, indent) + item_prefix = " " * (indent + 2) + "- " + items: list[list[str]] = [] + current: list[str] = [] + for line in block: + if line.startswith(item_prefix): + if current: + items.append(current) + current = [line] + elif current: + current.append(line) + if current: + items.append(current) + return items + + +def _step_action(step: list[str]) -> str | None: + """Return the action reference from a workflow step, if the step uses one.""" + for line in step: + stripped = line.strip() + if stripped.startswith("- uses:"): + return stripped.removeprefix("- uses:").strip() + if stripped.startswith("uses:"): + return stripped.removeprefix("uses:").strip() + return None + + +def _yaml_scalar(value: str) -> str | None: + """Normalize the simple YAML scalars used by workflow ``with`` mappings. + + A ``#`` starts an inline YAML comment only when it is outside quotes and is + separated from the scalar by whitespace. Hash characters inside quoted + values, or inside an unquoted value such as ``result#1.sarif``, are data. + """ + quote: str | None = None + escaped = False + comment_at: int | None = None + + for index, character in enumerate(value): + if quote == '"': + if escaped: + escaped = False + continue + if character == "\\": + escaped = True + continue + if character == '"': + quote = None + continue + if quote == "'": + if character == "'": + quote = None + continue + if character in {"'", '"'}: + quote = character + continue + if character == "#" and (index == 0 or value[index - 1].isspace()): + comment_at = index + break + + scalar = value[:comment_at].strip() if comment_at is not None else value.strip() + if not scalar: + return None + if len(scalar) >= 2 and scalar[0] == scalar[-1] and scalar[0] in {"'", '"'}: + return scalar[1:-1] + return scalar + + +def _mapping_value(lines: list[str], header: str, key: str) -> str | None: + """Return a scalar from a nested mapping without borrowing sibling evidence.""" + target = f"{header}:" + for index, line in enumerate(lines): + if line.strip() != target: + continue + header_indent = len(line) - len(line.lstrip(" ")) + for candidate in lines[index + 1 :]: + stripped = candidate.strip() + if not stripped or stripped.startswith("#"): + continue + candidate_indent = len(candidate) - len(candidate.lstrip(" ")) + if candidate_indent <= header_indent: + break + key_prefix = f"{key}:" + if stripped.startswith(key_prefix): + return _yaml_scalar(stripped[len(key_prefix) :].strip()) + return None + return None + + +def main() -> int: + """Require the Trivy workflow to cover PRs targeting protected branches.""" + lines = TRIVY_WORKFLOW.read_text(encoding="utf-8").splitlines() + pull_request_block = _indented_block(lines, "pull_request", 2) + pr_targets = _list_values(pull_request_block, "branches", 4) + jobs_block = _indented_block(lines, "jobs", 0) + trivy_job = _indented_block(jobs_block, "trivy-fs-scan", 2) + steps = _list_item_blocks(trivy_job, "steps", 4) + + trivy_outputs = { + output + for step in steps + if (_step_action(step) or "").startswith("aquasecurity/trivy-action@") + and _mapping_value(step, "with", "format") == "sarif" + if (output := _mapping_value(step, "with", "output")) + } + uploaded_sarif = { + sarif_file + for step in steps + if (_step_action(step) or "").startswith("github/codeql-action/upload-sarif@") + if (sarif_file := _mapping_value(step, "with", "sarif_file")) + } + + missing: list[str] = [] + if not pull_request_block: + missing.append("pull_request event") + for branch in ("develop", "main"): + if branch not in pr_targets: + missing.append(f"pull_request branch {branch!r}") + if not trivy_job: + missing.append("jobs.trivy-fs-scan") + if not trivy_outputs: + missing.append("Trivy SARIF-producing action step with an output file") + if not uploaded_sarif: + missing.append("CodeQL SARIF upload step with sarif_file") + if trivy_outputs and uploaded_sarif and trivy_outputs.isdisjoint(uploaded_sarif): + missing.append("matching Trivy output and CodeQL sarif_file") + + if missing: + print("Trivy PR code-scanning contract is incomplete:") + for item in missing: + print(f"- missing {item}") + return 1 + print("Trivy PR code-scanning contract passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 06d93ae69917ecd836f60b323579dcadc0c4bf57 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:02:01 +0900 Subject: [PATCH 17/23] test(trivy): preserve PR scan regressions --- scripts/checks/test_verify_trivy_pr_scan.py | 172 ++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 scripts/checks/test_verify_trivy_pr_scan.py diff --git a/scripts/checks/test_verify_trivy_pr_scan.py b/scripts/checks/test_verify_trivy_pr_scan.py new file mode 100644 index 000000000..aa115fc5a --- /dev/null +++ b/scripts/checks/test_verify_trivy_pr_scan.py @@ -0,0 +1,172 @@ +"""Regression checks for the Trivy pull-request workflow contract.""" + +from __future__ import annotations + +import subprocess +import sys +import tempfile +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +CHECKER = REPO_ROOT / "scripts" / "checks" / "verify_trivy_pr_scan.py" + +MISSING_PR_TARGETS = """name: trivy + +on: + push: + branches: + - develop + - main + pull_request: + types: [opened] + +jobs: + trivy-fs-scan: + steps: + - name: Run Trivy filesystem scan + uses: aquasecurity/trivy-action@0123456789abcdef + with: + format: sarif + output: trivy-results.sarif + - uses: github/codeql-action/upload-sarif@fedcba9876543210 + with: + sarif_file: trivy-results.sarif +""" + +DISCONNECTED_SARIF = """name: trivy + +on: + pull_request: + branches: + - develop + - main + +jobs: + trivy-fs-scan: + steps: + - name: Unrelated formatter + run: echo harmless + with: + format: sarif + - name: Run Trivy filesystem scan + uses: aquasecurity/trivy-action@0123456789abcdef + with: + format: table + output: trivy-results.txt + - uses: github/codeql-action/upload-sarif@fedcba9876543210 + with: + sarif_file: unrelated.sarif +""" + +MISMATCHED_SARIF = """name: trivy + +on: + pull_request: + branches: + - develop + - main + +jobs: + trivy-fs-scan: + steps: + - name: Run Trivy filesystem scan + uses: aquasecurity/trivy-action@0123456789abcdef + with: + format: sarif + output: trivy-results.sarif + - uses: github/codeql-action/upload-sarif@fedcba9876543210 + with: + sarif_file: different-results.sarif +""" + +INLINE_COMMENTED_SARIF = """name: trivy + +on: + pull_request: + branches: + - develop + - main + +jobs: + trivy-fs-scan: + steps: + - name: Run Trivy filesystem scan + uses: aquasecurity/trivy-action@0123456789abcdef + with: + format: sarif # GitHub code scanning format + output: trivy-results.sarif # produced by Trivy + - uses: github/codeql-action/upload-sarif@fedcba9876543210 + with: + sarif_file: trivy-results.sarif # upload the same result +""" + +QUOTED_HASH_SARIF = """name: trivy + +on: + pull_request: + branches: + - develop + - main + +jobs: + trivy-fs-scan: + steps: + - name: Run Trivy filesystem scan + uses: aquasecurity/trivy-action@0123456789abcdef + with: + format: "sarif" # quoted scalar with a comment + output: "trivy#results.sarif" # # inside quotes is data + - uses: github/codeql-action/upload-sarif@fedcba9876543210 + with: + sarif_file: 'trivy#results.sarif' # same path, different YAML quoting +""" + +INVALID_CASES = { + "missing protected PR targets": MISSING_PR_TARGETS, + "SARIF format detached from the Trivy action": DISCONNECTED_SARIF, + "Trivy output and upload paths disagree": MISMATCHED_SARIF, +} + +VALID_CASES = { + "equivalent SARIF paths with inline comments": INLINE_COMMENTED_SARIF, + "quoted SARIF path containing a literal hash": QUOTED_HASH_SARIF, +} + + +def _run_checker(workflow: str) -> subprocess.CompletedProcess[str]: + """Run the production checker against one isolated workflow fixture.""" + with tempfile.TemporaryDirectory() as temp_dir: + workflow_path = Path(temp_dir) / ".github" / "workflows" / "trivy.yml" + workflow_path.parent.mkdir(parents=True) + workflow_path.write_text(workflow, encoding="utf-8") + return subprocess.run( + [sys.executable, str(CHECKER)], + cwd=temp_dir, + capture_output=True, + check=False, + text=True, + ) + + +def main() -> int: + """Reject unsafe wiring without rejecting valid YAML scalar comments.""" + accepted = [ + name for name, workflow in INVALID_CASES.items() if _run_checker(workflow).returncode == 0 + ] + rejected = [ + name for name, workflow in VALID_CASES.items() if _run_checker(workflow).returncode != 0 + ] + + if accepted or rejected: + print("Trivy PR contract regression:") + for name in accepted: + print(f"- accepted malformed workflow: {name}") + for name in rejected: + print(f"- rejected valid workflow: {name}") + return 1 + print("Trivy PR contract regressions passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 0913edf31500df1d1be54157053d499900c69eea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:02:13 +0900 Subject: [PATCH 18/23] test(trivy): gate quickcheck on PR scan wiring --- scripts/harness/quickcheck.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/harness/quickcheck.sh b/scripts/harness/quickcheck.sh index f2b87e4e8..9f608335e 100755 --- a/scripts/harness/quickcheck.sh +++ b/scripts/harness/quickcheck.sh @@ -9,6 +9,8 @@ python3 scripts/checks/verify_security_notes.py python3 scripts/checks/security_gates.py python3 scripts/checks/verify_supply_chain.py python3 scripts/checks/verify_github_bootstrap_policy.py +python3 scripts/checks/test_verify_trivy_pr_scan.py +python3 scripts/checks/verify_trivy_pr_scan.py npm run lint npm run typecheck npm run test From c1f8ccb93db3891cb1c362536e59da399c78d3bc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:02:46 +0900 Subject: [PATCH 19/23] fix(trivy): scan protected pull-request heads --- .github/workflows/trivy.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index db6c3db87..415882660 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -5,6 +5,10 @@ on: branches: - develop - main + pull_request: + branches: + - develop + - main permissions: contents: read @@ -23,6 +27,8 @@ jobs: security-events: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Run Trivy filesystem scan summary uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0; SHA pinning retained as supply-chain attack mitigation, do not replace with tag. with: From a3507bf6a83c2f10f4cc0e9ef7cf9abf42145df4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:14:08 +0900 Subject: [PATCH 20/23] chore(codeql): drop Trivy regression from canonical lifecycle lane --- scripts/checks/test_verify_trivy_pr_scan.py | 172 -------------------- 1 file changed, 172 deletions(-) delete mode 100644 scripts/checks/test_verify_trivy_pr_scan.py diff --git a/scripts/checks/test_verify_trivy_pr_scan.py b/scripts/checks/test_verify_trivy_pr_scan.py deleted file mode 100644 index aa115fc5a..000000000 --- a/scripts/checks/test_verify_trivy_pr_scan.py +++ /dev/null @@ -1,172 +0,0 @@ -"""Regression checks for the Trivy pull-request workflow contract.""" - -from __future__ import annotations - -import subprocess -import sys -import tempfile -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[2] -CHECKER = REPO_ROOT / "scripts" / "checks" / "verify_trivy_pr_scan.py" - -MISSING_PR_TARGETS = """name: trivy - -on: - push: - branches: - - develop - - main - pull_request: - types: [opened] - -jobs: - trivy-fs-scan: - steps: - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@0123456789abcdef - with: - format: sarif - output: trivy-results.sarif - - uses: github/codeql-action/upload-sarif@fedcba9876543210 - with: - sarif_file: trivy-results.sarif -""" - -DISCONNECTED_SARIF = """name: trivy - -on: - pull_request: - branches: - - develop - - main - -jobs: - trivy-fs-scan: - steps: - - name: Unrelated formatter - run: echo harmless - with: - format: sarif - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@0123456789abcdef - with: - format: table - output: trivy-results.txt - - uses: github/codeql-action/upload-sarif@fedcba9876543210 - with: - sarif_file: unrelated.sarif -""" - -MISMATCHED_SARIF = """name: trivy - -on: - pull_request: - branches: - - develop - - main - -jobs: - trivy-fs-scan: - steps: - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@0123456789abcdef - with: - format: sarif - output: trivy-results.sarif - - uses: github/codeql-action/upload-sarif@fedcba9876543210 - with: - sarif_file: different-results.sarif -""" - -INLINE_COMMENTED_SARIF = """name: trivy - -on: - pull_request: - branches: - - develop - - main - -jobs: - trivy-fs-scan: - steps: - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@0123456789abcdef - with: - format: sarif # GitHub code scanning format - output: trivy-results.sarif # produced by Trivy - - uses: github/codeql-action/upload-sarif@fedcba9876543210 - with: - sarif_file: trivy-results.sarif # upload the same result -""" - -QUOTED_HASH_SARIF = """name: trivy - -on: - pull_request: - branches: - - develop - - main - -jobs: - trivy-fs-scan: - steps: - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@0123456789abcdef - with: - format: "sarif" # quoted scalar with a comment - output: "trivy#results.sarif" # # inside quotes is data - - uses: github/codeql-action/upload-sarif@fedcba9876543210 - with: - sarif_file: 'trivy#results.sarif' # same path, different YAML quoting -""" - -INVALID_CASES = { - "missing protected PR targets": MISSING_PR_TARGETS, - "SARIF format detached from the Trivy action": DISCONNECTED_SARIF, - "Trivy output and upload paths disagree": MISMATCHED_SARIF, -} - -VALID_CASES = { - "equivalent SARIF paths with inline comments": INLINE_COMMENTED_SARIF, - "quoted SARIF path containing a literal hash": QUOTED_HASH_SARIF, -} - - -def _run_checker(workflow: str) -> subprocess.CompletedProcess[str]: - """Run the production checker against one isolated workflow fixture.""" - with tempfile.TemporaryDirectory() as temp_dir: - workflow_path = Path(temp_dir) / ".github" / "workflows" / "trivy.yml" - workflow_path.parent.mkdir(parents=True) - workflow_path.write_text(workflow, encoding="utf-8") - return subprocess.run( - [sys.executable, str(CHECKER)], - cwd=temp_dir, - capture_output=True, - check=False, - text=True, - ) - - -def main() -> int: - """Reject unsafe wiring without rejecting valid YAML scalar comments.""" - accepted = [ - name for name, workflow in INVALID_CASES.items() if _run_checker(workflow).returncode == 0 - ] - rejected = [ - name for name, workflow in VALID_CASES.items() if _run_checker(workflow).returncode != 0 - ] - - if accepted or rejected: - print("Trivy PR contract regression:") - for name in accepted: - print(f"- accepted malformed workflow: {name}") - for name in rejected: - print(f"- rejected valid workflow: {name}") - return 1 - print("Trivy PR contract regressions passed") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) From 513f05141f599cac8591b0b51a43722b643eae6d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:14:21 +0900 Subject: [PATCH 21/23] chore(codeql): remove duplicate Trivy verifier lane --- scripts/checks/verify_trivy_pr_scan.py | 179 ------------------------- 1 file changed, 179 deletions(-) delete mode 100644 scripts/checks/verify_trivy_pr_scan.py diff --git a/scripts/checks/verify_trivy_pr_scan.py b/scripts/checks/verify_trivy_pr_scan.py deleted file mode 100644 index 4aa55b4b6..000000000 --- a/scripts/checks/verify_trivy_pr_scan.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Fail closed when Trivy code scanning cannot run on pull-request heads.""" - -from pathlib import Path - -TRIVY_WORKFLOW = Path(".github/workflows/trivy.yml") - - -def _indented_block(lines: list[str], header: str, indent: int) -> list[str]: - """Return the YAML-like block nested under an exact-indentation mapping key.""" - prefix = " " * indent - target = f"{prefix}{header}:" - for index, line in enumerate(lines): - if line != target: - continue - block: list[str] = [] - for candidate in lines[index + 1 :]: - stripped = candidate.strip() - if not stripped or stripped.startswith("#"): - block.append(candidate) - continue - candidate_indent = len(candidate) - len(candidate.lstrip(" ")) - if candidate_indent <= indent: - break - block.append(candidate) - return block - return [] - - -def _list_values(lines: list[str], header: str, indent: int) -> set[str]: - """Return literal scalar list items nested under the requested mapping key.""" - block = _indented_block(lines, header, indent) - item_prefix = " " * (indent + 2) + "- " - return { - line[len(item_prefix) :].strip() - for line in block - if line.startswith(item_prefix) and line[len(item_prefix) :].strip() - } - - -def _list_item_blocks(lines: list[str], header: str, indent: int) -> list[list[str]]: - """Split one YAML-like sequence block into its top-level item blocks.""" - block = _indented_block(lines, header, indent) - item_prefix = " " * (indent + 2) + "- " - items: list[list[str]] = [] - current: list[str] = [] - for line in block: - if line.startswith(item_prefix): - if current: - items.append(current) - current = [line] - elif current: - current.append(line) - if current: - items.append(current) - return items - - -def _step_action(step: list[str]) -> str | None: - """Return the action reference from a workflow step, if the step uses one.""" - for line in step: - stripped = line.strip() - if stripped.startswith("- uses:"): - return stripped.removeprefix("- uses:").strip() - if stripped.startswith("uses:"): - return stripped.removeprefix("uses:").strip() - return None - - -def _yaml_scalar(value: str) -> str | None: - """Normalize the simple YAML scalars used by workflow ``with`` mappings. - - A ``#`` starts an inline YAML comment only when it is outside quotes and is - separated from the scalar by whitespace. Hash characters inside quoted - values, or inside an unquoted value such as ``result#1.sarif``, are data. - """ - quote: str | None = None - escaped = False - comment_at: int | None = None - - for index, character in enumerate(value): - if quote == '"': - if escaped: - escaped = False - continue - if character == "\\": - escaped = True - continue - if character == '"': - quote = None - continue - if quote == "'": - if character == "'": - quote = None - continue - if character in {"'", '"'}: - quote = character - continue - if character == "#" and (index == 0 or value[index - 1].isspace()): - comment_at = index - break - - scalar = value[:comment_at].strip() if comment_at is not None else value.strip() - if not scalar: - return None - if len(scalar) >= 2 and scalar[0] == scalar[-1] and scalar[0] in {"'", '"'}: - return scalar[1:-1] - return scalar - - -def _mapping_value(lines: list[str], header: str, key: str) -> str | None: - """Return a scalar from a nested mapping without borrowing sibling evidence.""" - target = f"{header}:" - for index, line in enumerate(lines): - if line.strip() != target: - continue - header_indent = len(line) - len(line.lstrip(" ")) - for candidate in lines[index + 1 :]: - stripped = candidate.strip() - if not stripped or stripped.startswith("#"): - continue - candidate_indent = len(candidate) - len(candidate.lstrip(" ")) - if candidate_indent <= header_indent: - break - key_prefix = f"{key}:" - if stripped.startswith(key_prefix): - return _yaml_scalar(stripped[len(key_prefix) :].strip()) - return None - return None - - -def main() -> int: - """Require the Trivy workflow to cover PRs targeting protected branches.""" - lines = TRIVY_WORKFLOW.read_text(encoding="utf-8").splitlines() - pull_request_block = _indented_block(lines, "pull_request", 2) - pr_targets = _list_values(pull_request_block, "branches", 4) - jobs_block = _indented_block(lines, "jobs", 0) - trivy_job = _indented_block(jobs_block, "trivy-fs-scan", 2) - steps = _list_item_blocks(trivy_job, "steps", 4) - - trivy_outputs = { - output - for step in steps - if (_step_action(step) or "").startswith("aquasecurity/trivy-action@") - and _mapping_value(step, "with", "format") == "sarif" - if (output := _mapping_value(step, "with", "output")) - } - uploaded_sarif = { - sarif_file - for step in steps - if (_step_action(step) or "").startswith("github/codeql-action/upload-sarif@") - if (sarif_file := _mapping_value(step, "with", "sarif_file")) - } - - missing: list[str] = [] - if not pull_request_block: - missing.append("pull_request event") - for branch in ("develop", "main"): - if branch not in pr_targets: - missing.append(f"pull_request branch {branch!r}") - if not trivy_job: - missing.append("jobs.trivy-fs-scan") - if not trivy_outputs: - missing.append("Trivy SARIF-producing action step with an output file") - if not uploaded_sarif: - missing.append("CodeQL SARIF upload step with sarif_file") - if trivy_outputs and uploaded_sarif and trivy_outputs.isdisjoint(uploaded_sarif): - missing.append("matching Trivy output and CodeQL sarif_file") - - if missing: - print("Trivy PR code-scanning contract is incomplete:") - for item in missing: - print(f"- missing {item}") - return 1 - print("Trivy PR code-scanning contract passed") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) From ef6f3b1b6ab69bf8431f9dbaa0b4e98fb0a8a899 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:14:42 +0900 Subject: [PATCH 22/23] chore(codeql): restore Trivy ownership boundary --- .github/workflows/trivy.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 415882660..db6c3db87 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -5,10 +5,6 @@ on: branches: - develop - main - pull_request: - branches: - - develop - - main permissions: contents: read @@ -27,8 +23,6 @@ jobs: security-events: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - persist-credentials: false - name: Run Trivy filesystem scan summary uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0; SHA pinning retained as supply-chain attack mitigation, do not replace with tag. with: From c0d4ebc35d79cef27df7f0c636db23727db24f52 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:15:01 +0900 Subject: [PATCH 23/23] chore(codeql): remove duplicate Trivy quickcheck wiring --- scripts/harness/quickcheck.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/harness/quickcheck.sh b/scripts/harness/quickcheck.sh index 9f608335e..f2b87e4e8 100755 --- a/scripts/harness/quickcheck.sh +++ b/scripts/harness/quickcheck.sh @@ -9,8 +9,6 @@ python3 scripts/checks/verify_security_notes.py python3 scripts/checks/security_gates.py python3 scripts/checks/verify_supply_chain.py python3 scripts/checks/verify_github_bootstrap_policy.py -python3 scripts/checks/test_verify_trivy_pr_scan.py -python3 scripts/checks/verify_trivy_pr_scan.py npm run lint npm run typecheck npm run test