Skip to content

Threat detection: apply the warn-mode safe-outputs policy when the detection engine fails - #70325

Merged
SivaKesava1 merged 5 commits into
mainfrom
copilot/threat-detection-failures
Oct 19, 2026
Merged

SivaKesava1 merged 5 commits into
mainfrom
copilot/threat-detection-failures

Conversation

Copilot AI commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

In warn mode, a failed detection engine (for example, a retired model) applied safe outputs with no restriction. The conclude step returned failure/agent_failure, the step-level continue-on-error hid that, and downstream code only applies the warn-mode policy for warning. This PR implements Option B, as decided on the issue: treat warn-mode engine failures as warning, so WTD1a (annotate), WTD2 (convert) and WTD3 (abort) apply.

Case Before After
Warn mode, agent_failure/parse_error, detection step failed failure, outputs applied with no restriction warning: WTD1a annotation, WTD2 conversion, WTD3 abort
Warn mode, threat_detected warning unchanged
Strict mode, engine failure detection job fails, safe_outputs skipped unchanged
Detection passed or skipped no restriction unchanged

Conclude paths (both now produce the same outputs)

  • Inline (parse_threat_detection_results.cjs): removed the mustFail branch that threat-detection: delegate all conclude branches to threat-detect conclude on the external path #50854 reintroduced. In warn mode every failure now concludes warning without setFailed.
  • Inline module-load fallback (buildResultsParsingScriptRequire, threat_detection_steps.go): same rule. This changes 51 lock files.
  • External (conclude_threat_detection.sh): threat-detect conclude v0.5.2 still concludes failure and exits 1 in this case. In warn mode, the wrapper now reads the last conclusion/reason it wrote to $GITHUB_OUTPUT. If that is failure with a tooling reason, it appends conclusion=warning (the last value wins) and GH_AW_DETECTION_CONCLUSION=warning, then exits 0. Every other result keeps its exit code.

Safe-outputs consumers (defence in depth)

  • New getEffectiveDetectionConclusion(conclusion, reason) in threat_detection_warning.cjs:
    if (conclusion === "failure" && isToolingFailureReason(reason)) return "warning";
    safe_outputs runs only when needs.detection.result == 'success', so a failure it sees can only come from warn mode.
  • The helper is used by safe_output_handler_manager.cjs, push_to_pull_request_branch.cjs, messages_footer.cjs (getDetectionCautionAlert) and generate_footer.cjs.
  • The handler now blocks every message for any remaining failure conclusion, with errorCode: threat_detection_failure_blocked.

WTD1a annotation

Docs and spec

  • reference/threat-detection.md: "When Detection Fails" now describes warn mode (annotate, convert, abort) and strict mode. The continue-on-error row in the options table matches.
  • Safe-outputs spec, WTD1a: now explicitly covers a warn-mode failure with a tooling reason, which must be handled as warning.

Not changed (per the decision comment)

  • update_cache_memory gate.
  • report_detector_unavailable.
  • How detection picks its model.

Follow-up for github/gh-aw-threat-detection

In warn mode, concluder.fail (TD-20b) should conclude warning for tooling failures. Once that ships, the downgrade in conclude_threat_detection.sh can be removed.

Not yet verified end to end

The agent token cannot trigger workflow runs. A workflow on this branch with a detection engine forced to fail in warn mode still needs a manual run to confirm the WTD1a annotation on the created issue.

…fails

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix detection engine failure handling in warn mode Threat detection: apply the warn-mode safe-outputs policy when the detection engine fails Oct 19, 2026
Copilot AI requested a review from SivaKesava1 October 19, 2026 19:00
@SivaKesava1
SivaKesava1 marked this pull request as ready for review October 19, 2026 20:20
Copilot AI max review requested due to automatic review settings October 19, 2026 20:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Security-sensitive enforcement retains an unresolved failure path and lacks end-to-end validation.

2 open issues
What changed in this PR

Updates gh-aw threat detection so warn-mode engine failures apply safe-output restrictions rather than allowing unrestricted outputs, while preserving strict-mode behavior.

Changes:

  • Normalize tooling failures across inline and external detection paths.
  • Update warning annotations, regression tests, and policy documentation.
  • Regenerate 51 workflow lock files with the revised fallback.
File Description
pkg/​workflow/​threat_detection_steps.go Updates module-load fallback.
pkg/​workflow/​threat_detection_conclude_script_test.go Tests external conclusion handling.
docs/​src/​content/​docs/​specs/​safe-outputs-specification.md Clarifies tooling-failure policy.
docs/​src/​content/​docs/​reference/​threat-detection.md Documents warn and strict behavior.
actions/​setup/​sh/​conclude_threat_detection.sh Normalizes external tooling failures.
actions/​setup/​md/​threat_detection_engine_error.md Updates engine-error annotation.
actions/​setup/​js/​threat_detection_warning.cjs Adds shared conclusion normalization.
actions/​setup/​js/​safe_output_handler_manager.test.cjs Tests output enforcement.
actions/​setup/​js/​safe_output_handler_manager.cjs Applies policy and blocks remaining failures.
actions/​setup/​js/​push_to_pull_request_branch.test.cjs Tests review-PR conversion.
actions/​setup/​js/​push_to_pull_request_branch.cjs Normalizes conclusions before conversion.
actions/​setup/​js/​parse_threat_detection_results.test.cjs Tests warn and strict failures.
actions/​setup/​js/​parse_threat_detection_results.cjs Removes warn-mode hard-fail exception.
actions/​setup/​js/​messages.test.cjs Tests engine-error alerts.
actions/​setup/​js/​messages_footer.cjs Normalizes conclusions for alerts.
actions/​setup/​js/​handle_agent_failure.test.cjs Updates annotation fixture.
actions/​setup/​js/​generate_footer.test.cjs Tests tooling-failure footers.
actions/​setup/​js/​generate_footer.cjs Normalizes conclusions for footers.
.github/​workflows/​smoke-workflow-call.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-workflow-call-with-inputs.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-work-queue.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-update-cross-repo-pr.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-test-tools.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-temporary-id.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-service-ports.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-repo-memory-ledger.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-pydantic.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-project.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-pi.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-pi-sub-agents.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-pi-routed.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-pi-routed-claude.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-otel-backends.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-opencode.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-multi-pr.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-kiro.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-goose.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-drive.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-deepseek-harness.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-cursor.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-crush.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-create-cross-repo-pr.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-sub-agents.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-small.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-sdk.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-sdk-routed.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-sdk-routed-claude.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-routed-steering.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-routed-claude.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-mai.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-dynamic-workflow.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-auto.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-arm.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-aoai-entra.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-copilot-aoai-apikey.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-codex.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-claude.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-claude-routed.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-checkout-pr-dispatch.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-call-workflow.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-builtin-ledgers.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-aider.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-agent-scoped-approved.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-agent-public-none.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-agent-public-approved.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-agent-all-none.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​smoke-agent-all-merged.lock.yml Regenerates warn-mode fallback.
.github/​workflows/​daily-github-docs-seo-optimizer.lock.yml Regenerates warn-mode fallback.

Review effort: Max

Comment thread actions/setup/js/parse_threat_detection_results.cjs
Comment thread actions/setup/sh/conclude_threat_detection.sh
…ADR 29031

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
@SivaKesava1

Copy link
Copy Markdown
Collaborator

End-to-end verification of Option B. The sandbox runs were compiled against 4b544d7fdd. The later commit 434ca552df only merges main. Recompiling all 344 workflows at 434ca552df gives no lock-file changes, so the UI merge left no stale locks.

Detection engine failure in warn mode (private sandbox githubnext/gh-aw-routing-sandbox, temporary workflows). The main agent runs on live claude-haiku-4.5. Detection uses safe-outputs.threat-detection.engine: {id: copilot, model: claude-sonnet-4.6}, which is not in that org's live catalogue, so the detection engine fails:

Detector Run Detection result Run Created issue
External threat-detect (default) 39335411023 THREAT_DETECTION_STATUS: reason=engine_error exit=2 → GH_AW_DETECTION_CONCLUSION: warning, GH_AW_DETECTION_REASON: agent_failure, GH_AW_RUN_DETECTION: true all jobs succeeded [routing-test] … with the WTD1a block (> [!WARNING] / threat detection engine error / tooling-failure text) and <!-- gh-aw-threat-engine-error -->
Inline (features.gh-aw-detection: false) 39335416651 No THREAT_DETECTION_RESULT found → warning, parse_error, GH_AW_RUN_DETECTION: true all jobs succeeded same WTD1a block and marker

Before this PR, the same failure concluded failure/agent_failure, and create_issue published with no annotation (#70316, run 39191157830). Neither created issue carries the CAUTION marker (gh-aw-threat-detected), so a tooling failure is not reported as a threat.

Tests and review. At 4b544d7fdd, go test ./pkg/workflow -run 'ThreatDetection|Detection|ConcludeThreat' and the focused JS tests pass (parse_threat_detection_results, safe_output_handler_manager, push_to_pull_request_branch, messages_footer, generate_footer, messages). handle_agent_failure.test.cjs fails locally both here and on main (5676aa8016), so it isn't caused by this PR. All 305 changed lock files carry the same run_detection output and GH_AW_RUN_DETECTION change, and 51 also have the inline conclusion update. Both review threads are resolved.

The latest commit's PR workflows, including the review bots, are waiting for "Approve and run". After they pass, this is ready to merge.

@SivaKesava1
SivaKesava1 merged commit 9d21287 into main Oct 19, 2026
49 checks passed
@SivaKesava1
SivaKesava1 deleted the copilot/threat-detection-failures branch October 19, 2026 22:42
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.92.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Threat detection: in warn mode, a detection engine failure applies safe outputs with no restriction (neither fail-closed nor WTD policy)

3 participants