From f1b85ca5bd32531023c6930f1f44cb6758af4293 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 28 Sep 2026 13:30:44 +0900 Subject: [PATCH 1/2] fix(strix): map findings in added PR-head files --- scripts/ci/strix_quick_gate.sh | 13 +++++++++++-- scripts/ci/test_strix_quick_gate.sh | 26 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index ccf08f48eb..84cceeb14e 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -2060,8 +2060,12 @@ PY normalize_vulnerability_location() { local raw_location="$1" + local head_only_file=0 + if pull_request_head_blob_required; then + head_only_file=1 + fi raw_location="$({ - python3 - "$REPO_ROOT" "$REPO_NAME" "$resolved_scan_target" "$narrowed_workspace_prefix" "$raw_location" <<'PY' + python3 - "$REPO_ROOT" "$REPO_NAME" "$resolved_scan_target" "$narrowed_workspace_prefix" "$raw_location" "$head_only_file" <<'PY' from pathlib import Path from urllib.parse import unquote import sys @@ -2071,6 +2075,7 @@ repo_name = sys.argv[2] scan_target_root_raw = sys.argv[3].strip() scan_target_workspace_prefix = sys.argv[4].strip() raw_location = unquote(sys.argv[5].strip()) +head_only_file = sys.argv[6] == '1' if not raw_location: raise SystemExit(1) @@ -2099,7 +2104,7 @@ def emit_repo_relative(candidate: Path, fallback_relative: Path | None = None) - if fallback_relative is None: raise SystemExit(1) repo_candidate = (repo_root / fallback_relative).resolve(strict=False) - if not repo_candidate.exists(): + if not head_only_file and not repo_candidate.exists(): raise SystemExit(1) try: relative = repo_candidate.relative_to(repo_root) @@ -2145,6 +2150,10 @@ PY printf '%s\n' "$raw_location" return 0 fi + if [ "$head_only_file" -eq 1 ] && pr_head_regular_file_mode "$raw_location" >/dev/null; then + printf '%s\n' "$raw_location" + return 0 + fi return 1 } diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 80c4832243..ed977acf73 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -6171,6 +6171,12 @@ run_filtered_gate_case_if_requested() { "") return 0 ;; + pull-request-target-added-file-finding-maps-to-head) + run_pull_request_target_head_scope_case \ + "pull-request-target-added-file-finding-maps-to-head" \ + "src/new_module.py" "__ABSENT__" "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" \ + "0" "0" "__PR_SCOPE__" "0" "" "pull_request_target" "1" + ;; success) run_gate_case "success" \ "vertex_ai/ready-primary" \ @@ -7033,6 +7039,7 @@ run_pull_request_target_head_scope_case() { local expected_full_head_scope="${8-$disable_pr_scoping}" local expected_scope_message="${9-}" local github_event_name="${10-pull_request_target}" + local emit_finding="${11-0}" local tmp_dir tmp_dir="$(mktemp -d)" @@ -7104,6 +7111,10 @@ echo "scan ok with PR head content" mkdir -p strix_runs/current printf '%s\n' '{"status":"completed","scan_results":{"scan_completed":true,"success":true}}' >strix_runs/current/run.json printf 'Assessed %s\n' "$FAKE_STRIX_EXPECTED_CHANGED_FILE" >strix_runs/current/penetration_test_report.md +if [ "${FAKE_STRIX_EMIT_FINDING:-0}" = "1" ]; then + mkdir -p strix_runs/current/vulnerabilities + printf '**Severity:** CRITICAL\n**Target:** /workspace/%s/%s\n' "$(basename -- "$target_path")" "$FAKE_STRIX_EXPECTED_CHANGED_FILE" >strix_runs/current/vulnerabilities/vuln-0001.md +fi EOF chmod +x "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" @@ -7164,6 +7175,7 @@ EOF FAKE_STRIX_EXPECTED_UNCHANGED_FILE="docs/full-scope-context.md" \ FAKE_STRIX_EXPECTED_UNCHANGED_CONTENT="HEAD_FULL_SCOPE_CONTEXT_SHOULD_BE_SCANNED" \ FAKE_STRIX_EXPECT_FULL_HEAD_SCOPE="$expected_full_head_scope" \ + FAKE_STRIX_EMIT_FINDING="$emit_finding" \ STRIX_DISABLE_PR_SCOPING="$disable_pr_scoping" \ STRIX_LLM_FILE="$strix_llm_file" \ LLM_API_KEY_FILE="$llm_api_key_file" \ @@ -7174,7 +7186,12 @@ EOF local rc=$? set -e - assert_equals "0" "$rc" "case=$case_name exit code" + if [ "$emit_finding" = "1" ]; then + assert_equals "1" "$rc" "case=$case_name exit code" + assert_file_contains "$output_log" "Strix finding intersects files changed in this pull request (evidence_scope=pr_delta)." "case=$case_name finding scope" + else + assert_equals "0" "$rc" "case=$case_name exit code" + fi assert_file_contains "$output_log" "scan ok with PR head content" "case=$case_name output" if [ -n "$expected_scope_message" ]; then assert_file_contains "$output_log" "$expected_scope_message" "case=$case_name scope reason" @@ -9753,6 +9770,13 @@ run_pull_request_target_head_scope_case \ "__ABSENT__" \ "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" +run_pull_request_target_head_scope_case \ + "pull-request-target-added-file-finding-maps-to-head" \ + "src/new_module.py" \ + "__ABSENT__" \ + "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" \ + "0" "0" "__PR_SCOPE__" "0" "" "pull_request_target" "1" + run_pull_request_target_head_scope_case \ "pull-request-target-source-file-with-space-uses-head-blob" \ "src/unsafe name.py" \ From fb195088e2ad3dbe1b84a008c7e6eae0cef14e11 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 28 Sep 2026 13:39:59 +0900 Subject: [PATCH 2/2] test(strix): reject scanner-created finding paths --- scripts/ci/test_strix_quick_gate.sh | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index ed977acf73..31cb542ea0 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -6177,6 +6177,12 @@ run_filtered_gate_case_if_requested() { "src/new_module.py" "__ABSENT__" "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" \ "0" "0" "__PR_SCOPE__" "0" "" "pull_request_target" "1" ;; + pull-request-target-scanner-created-finding-stays-unmapped) + run_pull_request_target_head_scope_case \ + "pull-request-target-scanner-created-finding-stays-unmapped" \ + "src/new_module.py" "__ABSENT__" "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" \ + "0" "0" "__PR_SCOPE__" "0" "" "pull_request_target" "1" "1" + ;; success) run_gate_case "success" \ "vertex_ai/ready-primary" \ @@ -7040,6 +7046,7 @@ run_pull_request_target_head_scope_case() { local expected_scope_message="${9-}" local github_event_name="${10-pull_request_target}" local emit_finding="${11-0}" + local forge_finding_file="${12-0}" local tmp_dir tmp_dir="$(mktemp -d)" @@ -7113,7 +7120,12 @@ printf '%s\n' '{"status":"completed","scan_results":{"scan_completed":true,"succ printf 'Assessed %s\n' "$FAKE_STRIX_EXPECTED_CHANGED_FILE" >strix_runs/current/penetration_test_report.md if [ "${FAKE_STRIX_EMIT_FINDING:-0}" = "1" ]; then mkdir -p strix_runs/current/vulnerabilities - printf '**Severity:** CRITICAL\n**Target:** /workspace/%s/%s\n' "$(basename -- "$target_path")" "$FAKE_STRIX_EXPECTED_CHANGED_FILE" >strix_runs/current/vulnerabilities/vuln-0001.md + finding_file="$FAKE_STRIX_EXPECTED_CHANGED_FILE" + if [ "${FAKE_STRIX_FORGE_FINDING_FILE:-0}" = "1" ]; then + finding_file=src/scanner_created.py + printf 'untrusted scanner output\n' >"$target_path/$finding_file" + fi + printf '**Severity:** CRITICAL\n**Target:** /workspace/%s/%s\n' "$(basename -- "$target_path")" "$finding_file" >strix_runs/current/vulnerabilities/vuln-0001.md fi EOF chmod +x "$fake_strix" @@ -7176,6 +7188,7 @@ EOF FAKE_STRIX_EXPECTED_UNCHANGED_CONTENT="HEAD_FULL_SCOPE_CONTEXT_SHOULD_BE_SCANNED" \ FAKE_STRIX_EXPECT_FULL_HEAD_SCOPE="$expected_full_head_scope" \ FAKE_STRIX_EMIT_FINDING="$emit_finding" \ + FAKE_STRIX_FORGE_FINDING_FILE="$forge_finding_file" \ STRIX_DISABLE_PR_SCOPING="$disable_pr_scoping" \ STRIX_LLM_FILE="$strix_llm_file" \ LLM_API_KEY_FILE="$llm_api_key_file" \ @@ -7188,7 +7201,11 @@ EOF if [ "$emit_finding" = "1" ]; then assert_equals "1" "$rc" "case=$case_name exit code" - assert_file_contains "$output_log" "Strix finding intersects files changed in this pull request (evidence_scope=pr_delta)." "case=$case_name finding scope" + if [ "$forge_finding_file" = "1" ]; then + assert_file_contains "$output_log" "Unable to map Strix findings to changed files; failing closed for pull request." "case=$case_name finding scope" + else + assert_file_contains "$output_log" "Strix finding intersects files changed in this pull request (evidence_scope=pr_delta)." "case=$case_name finding scope" + fi else assert_equals "0" "$rc" "case=$case_name exit code" fi @@ -9777,6 +9794,13 @@ run_pull_request_target_head_scope_case \ "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" \ "0" "0" "__PR_SCOPE__" "0" "" "pull_request_target" "1" +run_pull_request_target_head_scope_case \ + "pull-request-target-scanner-created-finding-stays-unmapped" \ + "src/new_module.py" \ + "__ABSENT__" \ + "HEAD_ONLY_NEW_FILE_SHOULD_BE_SCANNED" \ + "0" "0" "__PR_SCOPE__" "0" "" "pull_request_target" "1" "1" + run_pull_request_target_head_scope_case \ "pull-request-target-source-file-with-space-uses-head-blob" \ "src/unsafe name.py" \