From 9baacde7475f33e0326b3af1253b6bd95f95fbb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 18:13:06 +0000 Subject: [PATCH 1/8] chore(deps): bump github/codeql-action from 4.35.1 to 4.35.4 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.1 to 4.35.4. - [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/c10b8064de6f491fea524254123dbe5e09572f13...68bde559dea0fdcac2102bfdf6230c5f70eb485e) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.35.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index df227792f..eb599e570 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,12 +29,12 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Initialize CodeQL - uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 97e353ba6..47031cdf6 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -37,6 +37,6 @@ jobs: publish_results: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: results.sarif From d7f5aa58cbe49b2d735ff3191703b04dddefd700 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 17:48:34 +0900 Subject: [PATCH 2/8] Fix Strix gate placeholder-secret fixture Use a fake PR changed file that exists in the self-test workspace so the placeholder-secret fallback scenario exercises Strix instead of being skipped as unscannable. --- scripts/ci/test_strix_quick_gate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index cccb069ed..078d8027b 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -5888,7 +5888,7 @@ run_gate_case "pr-primary-ratelimit-fallback-placeholder-secret" \ "1200" \ "0" \ "pull_request" \ - "backend/app/auth.py" \ + "backend/services/email_client.py" \ "" \ "" \ "0" \ From c3aae8183323d6d47e2a2072ebefce62ff3fa36e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 19:59:19 +0900 Subject: [PATCH 3/8] Fix stale Strix evidence handling --- .github/workflows/opencode-review.yml | 76 ++++++++++++++++++--- scripts/ci/collect_failed_check_evidence.sh | 48 ++++++++++++- scripts/ci/test_strix_quick_gate.sh | 56 +++++++++++++++ 3 files changed, 171 insertions(+), 9 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 5d776fbc9..366616711 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -1383,7 +1383,7 @@ jobs: local runs_json runs_json="$(mktemp)" - if ! gh api -X GET "repos/${GH_REPOSITORY}/actions/workflows/strix.yml/runs?event=pull_request_target&per_page=30" >"$runs_json"; then + if ! gh api -X GET "repos/${GH_REPOSITORY}/actions/workflows/strix.yml/runs?per_page=100" >"$runs_json"; then rm -f "$runs_json" return 1 fi @@ -1394,7 +1394,7 @@ jobs: (.workflow_runs // []) | map( select((.head_sha // "") == $head_sha) - | select((.event // "") == "pull_request_target") + | select((.event // "") == "pull_request_target" or (.event // "") == "workflow_dispatch") | select((.status // "") == "completed") | select((.conclusion // "" | ascii_upcase) as $c | ["FAILURE","TIMED_OUT","ACTION_REQUIRED","CANCELLED","STARTUP_FAILURE"] | index($c)) | "- Strix Security Scan/strix workflow run: " + (.conclusion // "unknown") + (if (.html_url // "") != "" then " (" + .html_url + ")" else "" end) @@ -1407,7 +1407,7 @@ jobs: (.workflow_runs // []) | map( select((.head_sha // "") == $head_sha) - | select((.event // "") == "pull_request_target") + | select((.event // "") == "pull_request_target" or (.event // "") == "workflow_dispatch") | select((.status // "") != "completed") | "- Strix Security Scan/strix workflow run: " + (.status // "unknown") + (if (.html_url // "") != "" then " (" + .html_url + ")" else "" end) ) @@ -1423,14 +1423,67 @@ jobs: rm -f "$runs_json" } + collect_current_head_strix_success_run_ids() { + local output_file="$1" + + HEAD_SHA="$HEAD_SHA" gh run list \ + --repo "$GH_REPOSITORY" \ + --commit "$HEAD_SHA" \ + --limit 100 \ + --json databaseId,workflowName,status,conclusion,event,headSha \ + --jq ' + .[] + | select((.event // "") == "pull_request_target" or (.event // "") == "workflow_dispatch") + | select((.headSha // "") == env.HEAD_SHA) + | select((.workflowName // "") == "Strix Security Scan" or (.workflowName // "") == "Strix") + | select((.status // "") == "completed") + | select((.conclusion // "" | ascii_downcase) == "success") + | ((.databaseId // "") | tostring) + ' >"$output_file" + } + + filter_superseded_strix_failure_lines() { + local input_file="$1" + local output_file="$2" + local latest_success_run_id="$3" + + if [ -z "$latest_success_run_id" ]; then + cat "$input_file" >"$output_file" + return 0 + fi + + awk -v latest_success_run_id="$latest_success_run_id" ' + /Strix Security Scan/ { + line = $0 + run_id = 0 + if (line ~ /actions\/runs\/[0-9]+/) { + sub(/^.*actions\/runs\//, "", line) + sub(/[^0-9].*$/, "", line) + run_id = line + 0 + } + if (run_id > 0 && run_id < (latest_success_run_id + 0)) { + next + } + } + { print } + ' "$input_file" >"$output_file" + } + collect_failed_github_checks() { local output_file="$1" local owner="${GH_REPOSITORY%%/*}" local name="${GH_REPOSITORY#*/}" local rollup_file local strix_runs_file + local combined_file + local filtered_file + local strix_success_run_ids_file + local latest_strix_success_run_id rollup_file="$(mktemp)" strix_runs_file="$(mktemp)" + combined_file="$(mktemp)" + filtered_file="$(mktemp)" + strix_success_run_ids_file="$(mktemp)" # shellcheck disable=SC2016 if ! gh api graphql \ -f owner="$owner" \ @@ -1485,20 +1538,27 @@ jobs: ) | .[] ' >"$rollup_file"; then - rm -f "$rollup_file" "$strix_runs_file" + rm -f "$rollup_file" "$strix_runs_file" "$combined_file" "$filtered_file" "$strix_success_run_ids_file" return 1 fi if ! collect_current_head_strix_workflow_runs "$strix_runs_file" failed; then - rm -f "$rollup_file" "$strix_runs_file" + rm -f "$rollup_file" "$strix_runs_file" "$combined_file" "$filtered_file" "$strix_success_run_ids_file" return 1 fi if grep -Fq -- "Strix Security Scan/strix:" "$rollup_file"; then - cat "$rollup_file" >"$output_file" + cat "$rollup_file" >"$combined_file" else - cat "$rollup_file" "$strix_runs_file" >"$output_file" + cat "$rollup_file" "$strix_runs_file" >"$combined_file" fi - rm -f "$rollup_file" "$strix_runs_file" + if collect_current_head_strix_success_run_ids "$strix_success_run_ids_file" && [ -s "$strix_success_run_ids_file" ]; then + latest_strix_success_run_id="$(sort -n "$strix_success_run_ids_file" | tail -n 1)" + filter_superseded_strix_failure_lines "$combined_file" "$filtered_file" "$latest_strix_success_run_id" + cat "$filtered_file" >"$output_file" + else + cat "$combined_file" >"$output_file" + fi + rm -f "$rollup_file" "$strix_runs_file" "$combined_file" "$filtered_file" "$strix_success_run_ids_file" } diff --git a/scripts/ci/collect_failed_check_evidence.sh b/scripts/ci/collect_failed_check_evidence.sh index 4305ab7ec..dc5c413ac 100755 --- a/scripts/ci/collect_failed_check_evidence.sh +++ b/scripts/ci/collect_failed_check_evidence.sh @@ -176,12 +176,53 @@ owner="${GH_REPOSITORY%%/*}" repo="${GH_REPOSITORY#*/}" failed_contexts="$(mktemp)" workflow_run_contexts="$(mktemp)" -tmp_files=("$failed_contexts" "$workflow_run_contexts") +strix_success_run_ids="$(mktemp)" +tmp_files=("$failed_contexts" "$workflow_run_contexts" "$strix_success_run_ids") cleanup() { rm -f "${tmp_files[@]}" } trap cleanup EXIT +collect_current_head_strix_success_run_ids() { + local output_file="$1" + + HEAD_SHA="$HEAD_SHA" gh run list \ + --repo "$GH_REPOSITORY" \ + --commit "$HEAD_SHA" \ + --limit 100 \ + --json databaseId,workflowName,status,conclusion,event,headSha \ + --jq ' + .[] + | select((.event // "") == "pull_request_target" or (.event // "") == "workflow_dispatch") + | select((.headSha // "") == env.HEAD_SHA) + | select((.workflowName // "") == "Strix Security Scan" or (.workflowName // "") == "Strix") + | select((.status // "") == "completed") + | select((.conclusion // "" | ascii_downcase) == "success") + | ((.databaseId // "") | tostring) + ' >"$output_file" +} + +filter_superseded_strix_failures() { + local contexts_file="$1" + local latest_success_run_id="$2" + local filtered_contexts + + if [ -z "$latest_success_run_id" ]; then + return 0 + fi + + filtered_contexts="$(mktemp)" + tmp_files+=("$filtered_contexts") + awk -F '\t' -v latest_success_run_id="$latest_success_run_id" ' + BEGIN { OFS = FS } + $2 ~ /Strix/ && $5 ~ /^[0-9]+$/ && ($5 + 0) < (latest_success_run_id + 0) { + next + } + { print } + ' "$contexts_file" >"$filtered_contexts" + cp "$filtered_contexts" "$contexts_file" +} + # shellcheck disable=SC2016 gh api graphql \ -f owner="$owner" \ @@ -287,6 +328,11 @@ while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id; d printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$kind" "$label" "$conclusion" "$details_url" "$run_id" "$check_run_id" >>"$failed_contexts" done <"$workflow_run_contexts" +if collect_current_head_strix_success_run_ids "$strix_success_run_ids" && [ -s "$strix_success_run_ids" ]; then + latest_strix_success_run_id="$(sort -n "$strix_success_run_ids" | tail -n 1)" + filter_superseded_strix_failures "$failed_contexts" "$latest_strix_success_run_id" +fi + { printf '# Failed GitHub Check Evidence\n\n' printf -- '- PR: #%s\n' "$PR_NUMBER" diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 078d8027b..b8b2a920f 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -386,6 +386,9 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "Strix model attempt and finding summary" "failed-check evidence collector summarizes every Strix model attempt" assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "Strix vulnerability report window" "failed-check evidence collector preserves Strix vulnerability report windows" assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "When Strix logs contain multiple" "failed-check evidence collector requires all model-reported vulnerabilities" + assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "filter_superseded_strix_failures" "failed-check evidence collector ignores older Strix failures after newer current-head success" + assert_file_contains "$workflow_file" "collect_current_head_strix_success_run_ids" "opencode review workflow can find current-head Strix success evidence" + assert_file_contains "$workflow_file" "filter_superseded_strix_failure_lines" "opencode review workflow ignores older Strix failure lines after newer current-head success" assert_file_contains "$workflow_file" "If bounded failed GitHub Check evidence is present, treat it as a blocker until diagnosed." "opencode review prompt forces failed-check diagnosis" assert_file_contains "$workflow_file" "include every model-reported vulnerability as a separate evidence-backed finding" "opencode review prompt requires all Strix model findings" assert_file_contains "$workflow_file" "Multiple Strix model reports must not be collapsed" "opencode review prompt prevents collapsing multiple Strix model reports" @@ -571,6 +574,57 @@ EOF rm -rf "$tmp_dir" } +assert_failed_check_evidence_ignores_superseded_strix_runs() { + local tmp_dir + local evidence_file + local rc + tmp_dir="$(mktemp -d)" + mkdir -p "$tmp_dir/bin" + evidence_file="$tmp_dir/evidence.md" + + cat >"$tmp_dir/bin/gh" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +if [ "$1" = "api" ] && [ "${2:-}" = "graphql" ]; then + printf 'check_run\tStrix Security Scan/strix\tfailure\thttps://github.com/example/repo/actions/runs/100/job/200\t100\t200\n' + exit 0 +fi + +if [ "$1" = "run" ] && [ "${2:-}" = "list" ]; then + case " $* " in + *"databaseId,workflowName,status,conclusion,url,event,headSha"*) + printf 'workflow_run\tStrix Security Scan\tfailure\thttps://github.com/example/repo/actions/runs/101\t101\t\n' + exit 0 + ;; + *"databaseId,workflowName,status,conclusion,event,headSha"*) + printf '102\n' + exit 0 + ;; + esac +fi + +printf 'unexpected gh arguments: %s\n' "$*" >&2 +exit 1 +EOF + chmod +x "$tmp_dir/bin/gh" + + set +e + PATH="$tmp_dir/bin:$PATH" \ + GH_REPOSITORY="example/repo" \ + PR_NUMBER="123" \ + HEAD_SHA="abc123" \ + bash "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "$evidence_file" >"$tmp_dir/out" 2>"$tmp_dir/err" + rc=$? + set -e + + assert_equals "0" "$rc" "failed-check evidence collector accepts superseded stale Strix failures" + assert_file_contains "$evidence_file" "No completed failed GitHub Checks were present" "superseded Strix failures do not block current-head approval" + assert_file_not_contains "$evidence_file" "## Failed check:" "superseded Strix failures are removed from detailed evidence" + + rm -rf "$tmp_dir" +} + assert_internal_pr_scope_targets() { local target_log_file="$1" local repo_root_dir="$2" @@ -5018,6 +5072,8 @@ assert_opencode_review_gate_rejects_line_zero_findings assert_opencode_failed_check_review_validator_rejects_unrelated_findings +assert_failed_check_evidence_ignores_superseded_strix_runs + run_pull_request_target_head_scope_case \ "pull-request-target-modified-file-uses-head-blob" \ "src/app.py" \ From 43952b3560dd61f150e2e4d455421bb4012c5209 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 20:47:19 +0900 Subject: [PATCH 4/8] Allow PR-baseline Strix findings after fallback --- scripts/ci/strix_quick_gate.sh | 14 ++++++-- scripts/ci/test_strix_quick_gate.sh | 54 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 26ce7a889..159e2eb14 100644 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -1934,6 +1934,16 @@ evaluate_pull_request_findings() { return 1 } +pull_request_findings_allow_current_change() { + case "$PR_FINDINGS_DECISION" in + allow_absent_placeholder_secret | allow_baseline | allow_manifest_only) + return 0 + ;; + esac + + return 1 +} + fallback_models_raw_for_model() { local model="$1" @@ -3074,7 +3084,7 @@ run_current_target_scan() { fi if evaluate_pull_request_findings; then - if [ "$PR_FINDINGS_DECISION" = "allow_absent_placeholder_secret" ]; then + if pull_request_findings_allow_current_change; then return 0 fi if [ "$strict_primary_provider_fallback" -eq 0 ]; then @@ -3137,7 +3147,7 @@ run_current_target_scan() { fi if evaluate_pull_request_findings; then - if [ "$PR_FINDINGS_DECISION" = "allow_absent_placeholder_secret" ]; then + if pull_request_findings_allow_current_change; then return 0 fi if [ "$strict_fallback_provider_signal" -eq 0 ]; then diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index b8b2a920f..d5fac8392 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -1175,6 +1175,30 @@ EOS ;; esac ;; + github-models-fallback-provider-signal-baseline-only) + case "${STRIX_LLM:-}" in + openai/gpt-5) + echo "LLM CONNECTION FAILED" + echo "Could not establish connection to the language model." + echo "Error: litellm.RateLimitError: RateLimitError: OpenAIException - Too many requests." + exit 1 + ;; + deepseek/deepseek-r1-0528) + mkdir -p "$STRIX_REPORTS_DIR/fake-pr-baseline-provider-signal/vulnerabilities" + cat >"$STRIX_REPORTS_DIR/fake-pr-baseline-provider-signal/vulnerabilities/vuln-0001.md" <<'EOS' +Severity: CRITICAL +Location 1: +sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java:5 +EOS + echo "Warning: fallback model emitted provider failure-signal output" + exit 2 + ;; + *) + echo "Error: GitHub Models provider-signal baseline-only path unexpected (${STRIX_LLM:-})" >&2 + exit 38 + ;; + esac + ;; gemini-high-demand-retry-same-model-success) case "${STRIX_LLM:-}" in gemini/retry-high-demand-primary) @@ -5551,6 +5575,36 @@ run_gate_case "github-models-fallback-provider-signal-tries-next" \ "deepseek/deepseek-r1-0528 deepseek/deepseek-v3-0324" \ "1" +run_gate_case "github-models-fallback-provider-signal-baseline-only" \ + "openai/gpt-5" \ + "" \ + "0" \ + "Strix findings are limited to unchanged files in this pull request; allowing pipeline continuation." \ + "2" \ + "openai/gpt-5|deepseek/deepseek-r1-0528" \ + "https://models.github.ai/inference|https://models.github.ai/inference" \ + "openai" \ + "https://models.github.ai/inference" \ + "" \ + "0" \ + "CRITICAL" \ + "0" \ + "" \ + "" \ + "1200" \ + "0" \ + "pull_request" \ + "sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" \ + "" \ + "" \ + "0" \ + "" \ + "" \ + "" \ + "__SAME_AS_FALLBACK_MODELS__" \ + "deepseek/deepseek-r1-0528" \ + "1" + run_gate_case_allow_provider_signal "gemini-high-demand-retry-same-model-success" \ "gemini/retry-high-demand-primary" \ "vertex_ai/fallback-one vertex_ai/fallback-two" \ From aa23aa4aeaec4f557d098d1cf3d384f63e94ae09 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 20:55:37 +0900 Subject: [PATCH 5/8] Preserve Strix fallback retry before baseline allow --- scripts/ci/strix_quick_gate.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 159e2eb14..02345d671 100644 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -3084,7 +3084,7 @@ run_current_target_scan() { fi if evaluate_pull_request_findings; then - if pull_request_findings_allow_current_change; then + if pull_request_findings_allow_current_change && [ "$strict_primary_provider_fallback" -eq 0 ]; then return 0 fi if [ "$strict_primary_provider_fallback" -eq 0 ]; then @@ -3147,7 +3147,7 @@ run_current_target_scan() { fi if evaluate_pull_request_findings; then - if pull_request_findings_allow_current_change; then + if pull_request_findings_allow_current_change && [ "$strict_fallback_provider_signal" -eq 0 ]; then return 0 fi if [ "$strict_fallback_provider_signal" -eq 0 ]; then @@ -3175,6 +3175,10 @@ run_current_target_scan() { fi done + if pull_request_findings_allow_current_change; then + return 0 + fi + if should_fail_pull_request_infra_zero_findings; then return 1 fi From d1e848a7b6230ee3f252ffe414be039bfa0ed6b3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 21:54:41 +0900 Subject: [PATCH 6/8] Ignore stale OpenCode self-check cancellations --- .github/workflows/opencode-review.yml | 7 ++++++- scripts/ci/collect_failed_check_evidence.sh | 6 +++++- scripts/ci/test_strix_quick_gate.sh | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 366616711..ce54f047a 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -1527,10 +1527,15 @@ jobs: | map( if .__typename == "CheckRun" then select((.status // "") == "COMPLETED") + | select((.name // "") != "opencode-review") + | select((.checkSuite.workflowRun.workflow.name // "") != "OpenCode PR Review") + | select((.checkSuite.workflowRun.workflow.name // "") != "OpenCode Review") | select((.conclusion // "" | ascii_upcase) as $c | ["FAILURE","TIMED_OUT","ACTION_REQUIRED","CANCELLED","STARTUP_FAILURE"] | index($c)) | "- " + ((.checkSuite.workflowRun.workflow.name // "") + "/" + (.name // "check") | gsub("^/"; "")) + ": " + (.conclusion // "unknown") + (if (.detailsUrl // "") != "" then " (" + .detailsUrl + ")" else "" end) elif .__typename == "StatusContext" then - select((.state // "" | ascii_upcase) as $s | ["FAILURE","ERROR"] | index($s)) + select((.context // "") != "opencode-review") + | select((.context // "") != "OpenCode Review") + | select((.state // "" | ascii_upcase) as $s | ["FAILURE","ERROR"] | index($s)) | "- " + (.context // "status") + ": " + (.state // "unknown") + (if (.targetUrl // "") != "" then " (" + .targetUrl + ")" else "" end) else empty diff --git a/scripts/ci/collect_failed_check_evidence.sh b/scripts/ci/collect_failed_check_evidence.sh index dc5c413ac..d931d7a4b 100755 --- a/scripts/ci/collect_failed_check_evidence.sh +++ b/scripts/ci/collect_failed_check_evidence.sh @@ -268,6 +268,9 @@ gh api graphql \ | map( if .__typename == "CheckRun" then select((.status // "") == "COMPLETED") + | select((.name // "") != "opencode-review") + | select((.checkSuite.workflowRun.workflow.name // "") != "OpenCode PR Review") + | select((.checkSuite.workflowRun.workflow.name // "") != "OpenCode Review") | select((.conclusion // "" | ascii_upcase) as $c | ["FAILURE","TIMED_OUT","ACTION_REQUIRED","CANCELLED","STARTUP_FAILURE"] | index($c)) | [ "check_run", @@ -278,7 +281,8 @@ gh api graphql \ ((.databaseId // "") | tostring) ] elif .__typename == "StatusContext" then - select((.state // "" | ascii_upcase) as $s | ["FAILURE","ERROR"] | index($s)) + select((.context // "") != "opencode-review") + | select((.state // "" | ascii_upcase) as $s | ["FAILURE","ERROR"] | index($s)) | [ "status_context", (.context // "status"), diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index d5fac8392..e52f04e8a 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -387,6 +387,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "Strix vulnerability report window" "failed-check evidence collector preserves Strix vulnerability report windows" assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "When Strix logs contain multiple" "failed-check evidence collector requires all model-reported vulnerabilities" assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" "filter_superseded_strix_failures" "failed-check evidence collector ignores older Strix failures after newer current-head success" + assert_file_contains "$REPO_ROOT/scripts/ci/collect_failed_check_evidence.sh" 'select((.name // "") != "opencode-review")' "failed-check evidence collector ignores stale OpenCode self-check failures" assert_file_contains "$workflow_file" "collect_current_head_strix_success_run_ids" "opencode review workflow can find current-head Strix success evidence" assert_file_contains "$workflow_file" "filter_superseded_strix_failure_lines" "opencode review workflow ignores older Strix failure lines after newer current-head success" assert_file_contains "$workflow_file" "If bounded failed GitHub Check evidence is present, treat it as a blocker until diagnosed." "opencode review prompt forces failed-check diagnosis" From f3d7893fa5b4da52b23d055b97bf8bd10d7f7fd9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 15 Jun 2026 22:46:25 +0900 Subject: [PATCH 7/8] Handle absent Strix source locations --- scripts/ci/strix_quick_gate.sh | 109 ++++++++++++++++++++++++++-- scripts/ci/test_strix_quick_gate.sh | 74 +++++++++++++++++++ 2 files changed, 177 insertions(+), 6 deletions(-) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 02345d671..a70f1cdd3 100644 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -438,7 +438,7 @@ copy_pr_head_blob_to_file() { is_supported_source_file() { case "$1" in - *.java | *.kt | *.kts | *.groovy | *.scala | *.py | *.js | *.jsx | *.ts | *.tsx | *.vue | *.yaml | *.yml | *.sh | *.sql | *.xml | *.json | *.html | *.css | *.md) + *.java | *.kt | *.kts | *.groovy | *.scala | *.py | *.js | *.jsx | *.ts | *.tsx | *.vue | *.yaml | *.yml | *.sh | *.sql | *.xml | *.json | *.html | *.css | *.md | *.lock) return 0 ;; *) @@ -449,7 +449,7 @@ is_supported_source_file() { is_dependency_manifest_path() { case "$1" in - pom.xml | */pom.xml | package.json | */package.json | package-lock.json | */package-lock.json | pnpm-lock.yaml | */pnpm-lock.yaml | yarn.lock | */yarn.lock | pyproject.toml | */pyproject.toml | requirements.txt | */requirements.txt | requirements-*.txt | */requirements-*.txt | uv.lock | */uv.lock) + pom.xml | */pom.xml | package.json | */package.json | package-lock.json | */package-lock.json | pnpm-lock.yaml | */pnpm-lock.yaml | yarn.lock | */yarn.lock | pyproject.toml | */pyproject.toml | requirements.txt | */requirements.txt | requirements.lock | */requirements.lock | requirements-*.txt | */requirements-*.txt | uv.lock | */uv.lock) return 0 ;; *) @@ -1629,6 +1629,7 @@ import sys text = Path(sys.argv[1]).read_text(encoding='utf-8', errors='replace') patterns = [ re.compile(r'(?P/workspace/[^`\r\n]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./ \[\]-]+\.[A-Za-z0-9_]+):\d+'), + re.compile(r'(?im)^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Location[ \t]+\d+:(?:\*\*)?[ \t]*`?(?P/workspace/[^`\r\n│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./ \[\]-]+\.[A-Za-z0-9_]+)`?'), re.compile(r'\s*(?P/workspace/[^<`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)\s*'), re.compile(r'^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Target:(?:\*\*)?[ \t]*(?:File:[ \t]*)?(?P/workspace/[^`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)', re.MULTILINE), re.compile(r'^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Endpoint:(?:\*\*)?[ \t]*(?P/workspace/[^`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)', re.MULTILINE), @@ -1805,14 +1806,14 @@ evaluate_pull_request_findings() { if [ "$rank" -lt "$threshold_rank" ]; then continue fi - if vulnerability_file_is_retryable_model_inconsistency "$vuln_file"; then - found_retryable_model_inconsistency=1 - continue - fi if vulnerability_file_has_absent_placeholder_secret_claim "$vuln_file"; then found_absent_placeholder_secret_finding=1 continue fi + if vulnerability_file_is_retryable_model_inconsistency "$vuln_file"; then + found_retryable_model_inconsistency=1 + continue + fi mapfile -t vulnerability_locations < <(extract_vulnerability_locations "$vuln_file") if [ "${#vulnerability_locations[@]}" -eq 0 ]; then PR_FINDINGS_DECISION="block_unmapped" @@ -2833,6 +2834,99 @@ PY return 0 } +vulnerability_file_has_only_absent_source_locations() { + local vuln_file="$1" + if [ ! -f "$vuln_file" ] || [ -L "$vuln_file" ]; then + return 1 + fi + + local resolved_target_root="" + resolved_target_root="$(resolve_current_target_path "$TARGET_PATH" 2>/dev/null || true)" + + python3 - "$vuln_file" "$REPO_ROOT" "$REPO_NAME" "$resolved_target_root" <<'PY' +from pathlib import Path +from urllib.parse import unquote +import re +import sys + +vuln_file = Path(sys.argv[1]) +repo_root = Path(sys.argv[2]).resolve(strict=True) +repo_name = sys.argv[3] +target_root = Path(sys.argv[4]).resolve(strict=True) if sys.argv[4] else None +target_workspace_prefix = f"/workspace/{target_root.name}/" if target_root else "" + +text = vuln_file.read_text(encoding="utf-8", errors="replace") +lowered = text.lower() +if ( + ("hardcoded secret" in lowered or "hardcoded secrets" in lowered) + and ("dbpassword123" in text or "sk_live_1234567890" in text) +): + raise SystemExit(1) + +patterns = [ + re.compile( + r"(?im)^[^\S\r\n│]*[│]?[ \t]*(?:\*\*)?Location[ \t]+\d+:(?:\*\*)?[ \t]*`?(?P/workspace/[^`\r\n│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./ \[\]-]+\.[A-Za-z0-9_]+)`?" + ), + re.compile( + r"\s*(?P/workspace/[^<`│]*\.[A-Za-z0-9_]+|[A-Za-z0-9_./\[\]-][A-Za-z0-9_./ \[\]-]*\.[A-Za-z0-9_]+)\s*" + ), +] + +raw_candidates = [] +seen = set() +for pattern in patterns: + for match in pattern.finditer(text): + value = unquote(match.group("path").strip().strip("`")) + if value and value not in seen: + seen.add(value) + raw_candidates.append(value) + +if not raw_candidates: + raise SystemExit(1) + +def safe_relative(value): + if not value or value.startswith("/"): + return None + rel = Path(value) + if rel.is_absolute() or ".." in rel.parts: + return None + if "/" not in rel.as_posix(): + return None + return rel + +def relative_from_workspace(value): + prefixes = [f"/workspace/{repo_name}/"] + if target_workspace_prefix: + prefixes.append(target_workspace_prefix) + for prefix in prefixes: + if value.startswith(prefix): + return safe_relative(value[len(prefix):]) + if value.startswith("/workspace/"): + return None + return safe_relative(value) + +for raw in raw_candidates: + rel = relative_from_workspace(raw) + if rel is None: + raise SystemExit(1) + + possible_paths = [repo_root / rel] + if target_root is not None: + possible_paths.append(target_root / rel) + + if any(possible_path.exists() for possible_path in possible_paths): + raise SystemExit(1) + +raise SystemExit(0) +PY + local rc=$? + if [ "$rc" -eq 0 ]; then + echo "Detected Strix report structured source location(s) absent from the repository; treating as retryable model inconsistency." >&2 + return 0 + fi + return 1 +} + is_hallucinated_endpoint_finding() { local latest_report_dir if ! latest_report_dir="$(latest_strix_report_dir)"; then @@ -2978,6 +3072,9 @@ PY vulnerability_file_is_retryable_model_inconsistency() { local vuln_file="$1" + if vulnerability_file_has_only_absent_source_locations "$vuln_file"; then + return 0 + fi if vulnerability_file_has_absent_endpoint_finding "$vuln_file"; then return 0 fi diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index e52f04e8a..88d947d61 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -1200,6 +1200,48 @@ EOS ;; esac ;; + github-models-fallback-provider-signal-absent-source-baseline) + case "${STRIX_LLM:-}" in + openai/gpt-5) + echo "LLM CONNECTION FAILED" + echo "Could not establish connection to the language model." + echo "Error: litellm.RateLimitError: RateLimitError: OpenAIException - Too many requests." + exit 1 + ;; + deepseek/deepseek-r1-0528) + mkdir -p "$STRIX_REPORTS_DIR/fake-pr-absent-source-baseline/vulnerabilities" + cat >"$STRIX_REPORTS_DIR/fake-pr-absent-source-baseline/vulnerabilities/vuln-0001.md" <<'EOS' +# Cross-Site Scripting (XSS) Risk in User-Controlled Data Rendering + +**Severity:** HIGH + +## Code Analysis + +**Location 1:** `frontend/src/components/ProjectDisplay.jsx` (line 15) + Potential XSS risk when rendering project name + +**Location 2:** `frontend/src/components/MemberList.jsx` (line 22) + Potential XSS risk when rendering member subject +EOS + cat >"$STRIX_REPORTS_DIR/fake-pr-absent-source-baseline/vulnerabilities/vuln-0002.md" <<'EOS' +# Vulnerable Dependencies in Backend + +**Severity:** CRITICAL + +## Code Analysis + +**Location 1:** `backend/requirements.lock` (line 181) + Vulnerable cryptography package +EOS + echo "Warning: fallback model emitted provider failure-signal output" + exit 2 + ;; + *) + echo "Error: GitHub Models provider-signal absent-source baseline path unexpected (${STRIX_LLM:-})" >&2 + exit 38 + ;; + esac + ;; gemini-high-demand-retry-same-model-success) case "${STRIX_LLM:-}" in gemini/retry-high-demand-primary) @@ -2416,6 +2458,8 @@ EOF mkdir -p "$repo_root_dir/backend/services" echo 'async def send_email(*args, **kwargs): return None' >"$repo_root_dir/backend/services/email_client.py" echo 'def parse_eml(*args): return {}' >"$repo_root_dir/backend/services/email_parser.py" + mkdir -p "$repo_root_dir/backend" + echo 'cryptography==45.0.0' >"$repo_root_dir/backend/requirements.lock" if [ -n "$current_pr_number" ]; then cat >"$event_payload_file" < Date: Mon, 15 Jun 2026 22:59:15 +0900 Subject: [PATCH 8/8] Cover Strix absent source fixture --- scripts/ci/test_strix_quick_gate.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 88d947d61..c0ed0b173 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -2453,6 +2453,8 @@ EOF echo 'class ChangedJwtUtil {}' >"$repo_root_dir/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" mkdir -p "$repo_root_dir/frontend/src/app/labels/[slug]" echo 'export default function Page() { return null }' >"$repo_root_dir/frontend/src/app/labels/[slug]/page.tsx" + mkdir -p "$repo_root_dir/.github/workflows" + echo 'name: OpenCode Review' >"$repo_root_dir/.github/workflows/opencode-review.yml" mkdir -p "$repo_root_dir/src" echo 'print("unsafe name")' >"$repo_root_dir/src/unsafe name.py" mkdir -p "$repo_root_dir/backend/services"