From fa8fe341457ff3743ac5aa5061e791d59b51dc5b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 23:25:41 +0900 Subject: [PATCH 1/2] fix(security): scan custom requirements manifests Signed-off-by: Seongho Bae --- .github/workflows/scheduled-security-scan.yml | 2 + .github/workflows/security-scan.yml | 2 + ...y-custom-requirements-manifest-coverage.md | 43 +++++++++++++++++++ ...ode_scanning_required_workflow_contract.py | 19 ++++++++ 4 files changed, 66 insertions(+) create mode 100644 docs/doctoring/trivy-custom-requirements-manifest-coverage.md diff --git a/.github/workflows/scheduled-security-scan.yml b/.github/workflows/scheduled-security-scan.yml index 6b6a90aa36..16a989c0d3 100644 --- a/.github/workflows/scheduled-security-scan.yml +++ b/.github/workflows/scheduled-security-scan.yml @@ -118,6 +118,8 @@ jobs: persist-credentials: false - name: Trivy filesystem scan uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 + env: + TRIVY_FILE_PATTERNS: 'pip:requirements-.*\.txt' with: scan-type: fs scan-ref: . diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 500e22b4ab..df5a6e30a8 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -548,6 +548,8 @@ jobs: echo "SECURITY_CHECKOUT scanner=trivy-fs revision=head repository=${EXPECTED_CHECKOUT_REPOSITORY} expected_sha=${EXPECTED_CHECKOUT_SHA} actual_sha=${actual_sha}" - name: Trivy filesystem scan uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 + env: + TRIVY_FILE_PATTERNS: 'pip:requirements-.*\.txt' with: scan-type: fs scan-ref: . diff --git a/docs/doctoring/trivy-custom-requirements-manifest-coverage.md b/docs/doctoring/trivy-custom-requirements-manifest-coverage.md new file mode 100644 index 0000000000..952221a55b --- /dev/null +++ b/docs/doctoring/trivy-custom-requirements-manifest-coverage.md @@ -0,0 +1,43 @@ +# Trivy custom requirements manifest coverage + +Status: `active_pr` until the required and periodic workflows are present on +protected `main`; thereafter `implemented_on_protected_main`. + +## Root cause and repair + +Trivy's default pip analyzer does not discover every generated or purpose-named +`requirements-*.txt` file. A Naruon merge-ref scan using Trivy 0.74.0 found four +dependency manifests with default discovery and eleven after adding the pip +pattern. The expanded scan exposed `CVE-2026-69244` in +`requirements-strix-ci-hashes.txt`; the installed aiohttp 3.14.1 is affected and +3.14.3 is the patched release. + +Both central filesystem-scan owners set +`TRIVY_FILE_PATTERNS=pip:requirements-.*\.txt` directly on the pinned Trivy +action step. This is trusted workflow configuration, not a configuration file +from the repository being scanned. It adds custom pip manifest discovery while +retaining Trivy's default detection. The required PR scan and the scheduled +default-branch backstop therefore use the same manifest boundary without adding +a workflow, job, step, or scanner invocation. + +The severity set, unfixed-vulnerability policy, zero scanner exit used to +preserve SARIF, hard-fail SARIF parser, and upload behavior are unchanged. + +## Verification boundary + +The regression contract requires the trusted environment setting on both +existing action calls. A local Trivy 0.74.0 fixture scan separately demonstrates +that `requirements-strix-ci-hashes.txt` is absent from default results and +present when the pattern is set. Protected-main and consumer merge-ref runs are +still required runtime evidence. + +## References + +Aqua Security. (2026). *Filtering and custom file handling*. Trivy +documentation v0.74.0. +https://github.com/aquasecurity/trivy/blob/v0.74.0/docs/guide/configuration/skipping.md + +aiohttp project. (2026). *Out-of-bounds heap read in C HTTP parser when +constructing an error message for malformed responses* (GHSA-cq5v-8q36-5273; +CVE-2026-69244). +https://github.com/aio-libs/aiohttp/security/advisories/GHSA-cq5v-8q36-5273 diff --git a/tests/test_code_scanning_required_workflow_contract.py b/tests/test_code_scanning_required_workflow_contract.py index 19933303d8..c771afac61 100644 --- a/tests/test_code_scanning_required_workflow_contract.py +++ b/tests/test_code_scanning_required_workflow_contract.py @@ -2,6 +2,8 @@ from pathlib import Path +import pytest + from scripts.ci import audit_central_required_workflows as audit @@ -38,6 +40,23 @@ def test_consolidated_security_scan_preserves_osv_and_scorecard_evidence() -> No assert "Upload Scorecard SARIF to code scanning" in workflow +@pytest.mark.parametrize( + "workflow_name", + ("security-scan.yml", "scheduled-security-scan.yml"), +) +def test_trivy_scans_custom_pip_requirements_manifests(workflow_name: str) -> None: + """Required and periodic scans must include generated requirements locks.""" + workflow = ( + REPOSITORY_ROOT / ".github/workflows" / workflow_name + ).read_text(encoding="utf-8") + step = workflow.split(" - name: Trivy filesystem scan\n", 1)[1].split( + "\n - name:", 1 + )[0] + + assert "uses: aquasecurity/trivy-action@" in step + assert "TRIVY_FILE_PATTERNS: 'pip:requirements-.*\\.txt'" in step + + def test_ruleset_requires_dispatch_safe_codeql_pr() -> None: """Restore the central gate without reintroducing forbidden CodeQL actions.""" workflow_path = ".github/workflows/codeql-pr.yml" From 61033f205ce058ac3692260dc7a52e4daf05b64b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 23:29:15 +0900 Subject: [PATCH 2/2] test(security): verify Trivy step configuration Signed-off-by: Seongho Bae --- ...y-custom-requirements-manifest-coverage.md | 61 +++++++++---------- ...ode_scanning_required_workflow_contract.py | 17 +++++- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/docs/doctoring/trivy-custom-requirements-manifest-coverage.md b/docs/doctoring/trivy-custom-requirements-manifest-coverage.md index 952221a55b..c91b1cc8c2 100644 --- a/docs/doctoring/trivy-custom-requirements-manifest-coverage.md +++ b/docs/doctoring/trivy-custom-requirements-manifest-coverage.md @@ -1,43 +1,42 @@ -# Trivy custom requirements manifest coverage +# Trivy 사용자 정의 requirements manifest 탐지 범위 -Status: `active_pr` until the required and periodic workflows are present on -protected `main`; thereafter `implemented_on_protected_main`. +상태: 필수 workflow와 주기 workflow가 protected `main`에 반영되기 전까지 +`active_pr`, 반영된 뒤에는 `implemented_on_protected_main`. -## Root cause and repair +## 원인과 수리 -Trivy's default pip analyzer does not discover every generated or purpose-named -`requirements-*.txt` file. A Naruon merge-ref scan using Trivy 0.74.0 found four -dependency manifests with default discovery and eleven after adding the pip -pattern. The expanded scan exposed `CVE-2026-69244` in -`requirements-strix-ci-hashes.txt`; the installed aiohttp 3.14.1 is affected and -3.14.3 is the patched release. +Trivy 기본 pip analyzer는 생성된 파일이나 용도별로 이름 붙인 +`requirements-*.txt`를 모두 찾지 못한다. Trivy 0.74.0으로 Naruon merge-ref를 +검사했을 때 기본 탐지는 dependency manifest 4개를 찾았고, pip pattern을 +추가한 뒤에는 11개를 찾았다. 확장 검사는 +`requirements-strix-ci-hashes.txt`의 `CVE-2026-69244`를 드러냈다. 설치된 +aiohttp 3.14.1은 영향받으며 3.14.3에서 수정됐다. -Both central filesystem-scan owners set -`TRIVY_FILE_PATTERNS=pip:requirements-.*\.txt` directly on the pinned Trivy -action step. This is trusted workflow configuration, not a configuration file -from the repository being scanned. It adds custom pip manifest discovery while -retaining Trivy's default detection. The required PR scan and the scheduled -default-branch backstop therefore use the same manifest boundary without adding -a workflow, job, step, or scanner invocation. +중앙의 두 filesystem scan owner는 pinned Trivy action step에 +`TRIVY_FILE_PATTERNS=pip:requirements-.*\.txt`를 직접 설정한다. 검사 대상 +저장소의 configuration file이 아니라 trusted workflow 설정이다. Trivy의 +기본 탐지는 유지하면서 사용자 정의 pip manifest 탐지만 추가한다. 따라서 +필수 PR 검사와 default branch 주기 backstop은 workflow, job, step, scanner +호출을 늘리지 않고 같은 manifest 경계를 사용한다. -The severity set, unfixed-vulnerability policy, zero scanner exit used to -preserve SARIF, hard-fail SARIF parser, and upload behavior are unchanged. +severity 집합, 수정판이 없는 취약점 처리 정책, SARIF를 보존하기 위한 scanner +exit 0, hard-fail SARIF parser, upload 동작은 바꾸지 않았다. -## Verification boundary +## 검증 경계 -The regression contract requires the trusted environment setting on both -existing action calls. A local Trivy 0.74.0 fixture scan separately demonstrates -that `requirements-strix-ci-hashes.txt` is absent from default results and -present when the pattern is set. Protected-main and consumer merge-ref runs are -still required runtime evidence. +회귀 계약은 기존 action 호출 두 곳의 실제 `env.TRIVY_FILE_PATTERNS` 값을 +검사하고, pinned action이 지원하지 않는 `with.file-patterns` 입력이 없음을 +확인한다. 별도의 로컬 Trivy 0.74.0 fixture 검사는 기본 결과에 +`requirements-strix-ci-hashes.txt`가 없고 환경값을 설정한 결과에는 있음을 +실증한다. protected `main`과 consumer merge-ref 실행은 여전히 필요한 runtime +증거다. -## References +## 참고문헌 -Aqua Security. (2026). *Filtering and custom file handling*. Trivy -documentation v0.74.0. +Aqua Security. (2026). *Customizing file handling*. Trivy documentation +v0.74.0, “Filtering”. https://github.com/aquasecurity/trivy/blob/v0.74.0/docs/guide/configuration/skipping.md -aiohttp project. (2026). *Out-of-bounds heap read in C HTTP parser when -constructing an error message for malformed responses* (GHSA-cq5v-8q36-5273; -CVE-2026-69244). +aiohttp project. (2026). *Out-of-bounds heap read in C HTTP response parser may +lead to DoS* (GHSA-cq5v-8q36-5273; CVE-2026-69244). https://github.com/aio-libs/aiohttp/security/advisories/GHSA-cq5v-8q36-5273 diff --git a/tests/test_code_scanning_required_workflow_contract.py b/tests/test_code_scanning_required_workflow_contract.py index c771afac61..f498a7edfa 100644 --- a/tests/test_code_scanning_required_workflow_contract.py +++ b/tests/test_code_scanning_required_workflow_contract.py @@ -52,9 +52,24 @@ def test_trivy_scans_custom_pip_requirements_manifests(workflow_name: str) -> No step = workflow.split(" - name: Trivy filesystem scan\n", 1)[1].split( "\n - name:", 1 )[0] + environment = step.split("\n env:\n", 1)[1].split( + "\n with:\n", 1 + )[0] + action_inputs = step.split("\n with:\n", 1)[1] + environment_values = dict( + line.strip().split(": ", 1) + for line in environment.splitlines() + if line.startswith(" ") + ) + input_names = { + line.strip().split(":", 1)[0] + for line in action_inputs.splitlines() + if line.startswith(" ") and not line.lstrip().startswith("#") + } assert "uses: aquasecurity/trivy-action@" in step - assert "TRIVY_FILE_PATTERNS: 'pip:requirements-.*\\.txt'" in step + assert environment_values["TRIVY_FILE_PATTERNS"] == "'pip:requirements-.*\\.txt'" + assert "file-patterns" not in input_names def test_ruleset_requires_dispatch_safe_codeql_pr() -> None: