Repository navigation
Threat detection: apply the warn-mode safe-outputs policy when the detection engine fails - #70325
Conversation
…fails Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
There was a problem hiding this comment.
🔵 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
…ADR 29031 Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
|
End-to-end verification of Option B. The sandbox runs were compiled against Detection engine failure in warn mode (private sandbox
Before this PR, the same failure concluded Tests and review. At The latest commit's PR workflows, including the review bots, are waiting for "Approve and run". After they pass, this is ready to merge. |
|
🎉 This pull request is included in a new release. Release: |

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-levelcontinue-on-errorhid that, and downstream code only applies the warn-mode policy forwarning. This PR implements Option B, as decided on the issue: treat warn-mode engine failures aswarning, so WTD1a (annotate), WTD2 (convert) and WTD3 (abort) apply.agent_failure/parse_error, detection step failedfailure, outputs applied with no restrictionwarning: WTD1a annotation, WTD2 conversion, WTD3 abortthreat_detectedwarningsafe_outputsskippedConclude paths (both now produce the same outputs)
parse_threat_detection_results.cjs): removed themustFailbranch that threat-detection: delegate all conclude branches tothreat-detect concludeon the external path #50854 reintroduced. In warn mode every failure now concludeswarningwithoutsetFailed.buildResultsParsingScriptRequire,threat_detection_steps.go): same rule. This changes 51 lock files.conclude_threat_detection.sh):threat-detect concludev0.5.2 still concludesfailureand exits 1 in this case. In warn mode, the wrapper now reads the lastconclusion/reasonit wrote to$GITHUB_OUTPUT. If that isfailurewith a tooling reason, it appendsconclusion=warning(the last value wins) andGH_AW_DETECTION_CONCLUSION=warning, then exits 0. Every other result keeps its exit code.Safe-outputs consumers (defence in depth)
getEffectiveDetectionConclusion(conclusion, reason)inthreat_detection_warning.cjs:safe_outputsruns only whenneeds.detection.result == 'success', so afailureit sees can only come from warn mode.safe_output_handler_manager.cjs,push_to_pull_request_branch.cjs,messages_footer.cjs(getDetectionCautionAlert) andgenerate_footer.cjs.failureconclusion, witherrorCode: threat_detection_failure_blocked.WTD1a annotation
threat_detection_engine_error.mdnow matches the spec text, including the requiredthreat detection engine errorlabel string. It was missing before this PR (see [setup-security] Environment Variable Injection via OTLP Token in GitHub Actions Step #70120). This also changes the visible text of the existingwarning+ tooling-reason annotation.Docs and spec
reference/threat-detection.md: "When Detection Fails" now describes warn mode (annotate, convert, abort) and strict mode. Thecontinue-on-errorrow in the options table matches.failurewith a tooling reason, which must be handled aswarning.Not changed (per the decision comment)
update_cache_memorygate.report_detector_unavailable.Follow-up for github/gh-aw-threat-detection
In warn mode,
concluder.fail(TD-20b) should concludewarningfor tooling failures. Once that ships, the downgrade inconclude_threat_detection.shcan 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.