Created by GitHub Ace · View Session
Summary
buildExternalDetectorExecutionStep (pkg/workflow/threat_detection_external.go:392-405) builds its env block exclusively from the engine's own execution step:
executionSteps := engine.GetExecutionSteps(threatDetectionData, constants.ThreatDetectionLogPath)
envLines := extractStepEnvLines(executionSteps[0])
Five env vars that gh-aw already computes are set only on the Setup threat detection github-script step (buildThreatDetectionAnalysisStep) and on the conclude step — never on the step that actually runs the binary.
Verified upstream against a freshly compiled smoke-copilot-standalone.lock.yml (gh-aw v0.84.3):
| Step |
GH_AW_DETECTION_CONTINUE_ON_ERROR |
HAS_PATCH |
WORKFLOW_NAME |
| Setup threat detection (JS) |
✅ |
✅ |
✅ |
Execute threat detection with AWF (runs threat-detect) |
❌ |
❌ |
❌ |
| Conclude threat detection |
✅ |
— |
— |
The invocation passes no equivalent flags either:
threat-detect --engine copilot --output /tmp/gh-aw/threat-detection/detection_result.json /tmp/gh-aw/threat-detection
Impact
safe-outputs.threat-detection.prompt is silently ignored
CUSTOM_PROMPT is appended verbatim by the detector as ## Additional Instructions. Because it never arrives, a documented, user-facing configuration key has no effect and emits no warning on the external path. Anyone relying on custom detection instructions loses them without knowing.
Every external detection run reports the workflow as unnamed
The prompt renders Unnamed Workflow / No description provided and the runlog records workflow_name_defaulted=true. The detection prompt's own security guidelines instruct the model to use workflow intent to separate legitimate behavior from threats, so removing it should measurably increase false positives.
Strict/warn mode disagreement between stages
GH_AW_DETECTION_CONTINUE_ON_ERROR reaches gh-aw's JS setup step but not the detector. After v0.4.0 (#751) the detector performs its own artifact validation gated on this var, so a workflow configured with threat-detection: continue-on-error: false gets strict validation in the JS setup and lenient validation in the detector. This matters increasingly as the JS setup step is retired. HAS_PATCH is part of the same validation input set.
No regression exists today — absent vars mean the detector sees warn mode, its documented default.
Proposed change
In buildExternalDetectorExecutionStep, append to the execution step's env block:
GH_AW_DETECTION_CONTINUE_ON_ERROR: <same expr already emitted for setup/conclude>
HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
WORKFLOW_NAME: <workflow name>
WORKFLOW_DESCRIPTION: <workflow description>
CUSTOM_PROMPT: <safe-outputs.threat-detection.prompt, when configured>
Reuse the existing c.buildWorkflowContextEnvVars(data) and the conditional CUSTOM_PROMPT: %q logic from buildThreatDetectionAnalysisStep rather than duplicating it.
Flags vs env — recommendation
Upstream #742 added --workflow-name, --workflow-description, --custom-prompt, and --custom-prompt-file specifically so this plumbing cannot be dropped by an env filter. Upstream's stated preference for the custom prompt is --custom-prompt-file, since it survives env-passthrough filtering and avoids shell-quoting an arbitrary user-supplied string into a command line.
Recommendation: use flags for the three prompt-context values (writing the custom prompt to a file staged in the detection dir), and env for GH_AW_DETECTION_CONTINUE_ON_ERROR / HAS_PATCH which are boolean-ish and already env-shaped. Passing user-controlled text as a flag value on a command line assembled by the compiler warrants the file indirection.
If env is chosen instead: the detection step runs awf --env-all, so step-level env reaches the sandboxed process and no AWF change is needed. Still verify the three names survive ComputeAWFExcludeEnvVarNames.
Acceptance criteria
References
Summary
buildExternalDetectorExecutionStep(pkg/workflow/threat_detection_external.go:392-405) builds its env block exclusively from the engine's own execution step:Five env vars that gh-aw already computes are set only on the
Setup threat detectiongithub-script step (buildThreatDetectionAnalysisStep) and on the conclude step — never on the step that actually runs the binary.Verified upstream against a freshly compiled
smoke-copilot-standalone.lock.yml(gh-aw v0.84.3):GH_AW_DETECTION_CONTINUE_ON_ERRORHAS_PATCHWORKFLOW_NAMEthreat-detect)The invocation passes no equivalent flags either:
Impact
safe-outputs.threat-detection.promptis silently ignoredCUSTOM_PROMPTis appended verbatim by the detector as## Additional Instructions. Because it never arrives, a documented, user-facing configuration key has no effect and emits no warning on the external path. Anyone relying on custom detection instructions loses them without knowing.Every external detection run reports the workflow as unnamed
The prompt renders
Unnamed Workflow/No description providedand the runlog recordsworkflow_name_defaulted=true. The detection prompt's own security guidelines instruct the model to use workflow intent to separate legitimate behavior from threats, so removing it should measurably increase false positives.Strict/warn mode disagreement between stages
GH_AW_DETECTION_CONTINUE_ON_ERRORreaches gh-aw's JS setup step but not the detector. After v0.4.0 (#751) the detector performs its own artifact validation gated on this var, so a workflow configured withthreat-detection: continue-on-error: falsegets strict validation in the JS setup and lenient validation in the detector. This matters increasingly as the JS setup step is retired.HAS_PATCHis part of the same validation input set.No regression exists today — absent vars mean the detector sees warn mode, its documented default.
Proposed change
In
buildExternalDetectorExecutionStep, append to the execution step's env block:Reuse the existing
c.buildWorkflowContextEnvVars(data)and the conditionalCUSTOM_PROMPT: %qlogic frombuildThreatDetectionAnalysisSteprather than duplicating it.Flags vs env — recommendation
Upstream #742 added
--workflow-name,--workflow-description,--custom-prompt, and--custom-prompt-filespecifically so this plumbing cannot be dropped by an env filter. Upstream's stated preference for the custom prompt is--custom-prompt-file, since it survives env-passthrough filtering and avoids shell-quoting an arbitrary user-supplied string into a command line.Recommendation: use flags for the three prompt-context values (writing the custom prompt to a file staged in the detection dir), and env for
GH_AW_DETECTION_CONTINUE_ON_ERROR/HAS_PATCHwhich are boolean-ish and already env-shaped. Passing user-controlled text as a flag value on a command line assembled by the compiler warrants the file indirection.If env is chosen instead: the detection step runs
awf --env-all, so step-level env reaches the sandboxed process and no AWF change is needed. Still verify the three names surviveComputeAWFExcludeEnvVarNames.Acceptance criteria
--workflow-name/--workflow-description/--custom-prompt-file) vs env.prompt_builtshowsworkflow_name_defaulted=falseandcustom_prompt_applied=truewhen configured.GH_AW_DETECTION_CONTINUE_ON_ERRORandHAS_PATCHset on the execution step.Execute threat detection with AWF.CUSTOM_PROMPT/--custom-prompt-fileis omitted whensafe-outputs.threat-detection.promptis unset.make recompile.References
threat-detection.promptis silently ignored gh-aw-threat-detection#688