Threat Detection
GitHub Agentic Workflows includes automatic threat detection to analyze agent output and code changes for potential security issues before they are applied. When safe outputs are configured, a threat detection job automatically runs to identify prompt injection attempts, secret leaks, and malicious code patches.
How It Works
Section titled “How It Works”Threat detection provides an additional security layer by analyzing agent output for malicious content, scanning code changes for suspicious patterns, using workflow context to distinguish legitimate actions from threats, and running automatically after the main job completes but before safe outputs are applied.
Security Architecture:
┌─────────────────┐│ Agentic Job │ (Read-only permissions)│ Generates ││ Output & Patches│└────────┬────────┘ │ artifacts ▼┌─────────────────┐│ Threat Detection│ (Analyzes for security issues)│ Job │└────────┬────────┘ │ approved/blocked ▼┌─────────────────┐│ Safe Output Jobs│ (Write permissions, only if safe)│ Create Issues, ││ PRs, Comments │└─────────────────┘Default Configuration
Section titled “Default Configuration”Threat detection is automatically enabled when safe outputs are configured:
safe-outputs: create-issue: # Threat detection enabled automatically create-pull-request:The default configuration uses AI-powered analysis to detect prompt injection (malicious instructions manipulating AI behavior), secret leaks (exposed API keys, tokens, passwords, credentials), and malicious patches (code changes introducing vulnerabilities, backdoors, or suspicious patterns).
Configuration Options
Section titled “Configuration Options”Basic Enabled/Disabled
Section titled “Basic Enabled/Disabled”Control threat detection with a boolean flag:
safe-outputs: create-issue: threat-detection: true # Explicitly enable (default when safe-outputs exist)
# Or disable entirely:safe-outputs: create-pull-request: threat-detection: false # Disable threat detection entirelyThe features.gh-aw-detection flag controls the detection implementation, not
whether threat detection runs. The external threat-detect implementation is
the default; set features.gh-aw-detection: false to select the legacy inline
engine implementation. Compiled workflows embed the reviewed detector release
tag and per-architecture SHA-256 digests. Installation verifies the downloaded
binary against those compiler-controlled pins instead of a runtime checksum file.
If installation does not complete verification, the detection job neither runs nor
concludes with a detector binary: analysis is skipped and the conclusion is reported
as an agent_failure (a warning in warn mode, a hard failure in strict mode).
Advanced Configuration
Section titled “Advanced Configuration”Use object syntax for fine-grained control:
safe-outputs: create-issue: threat-detection: enabled: true # Enable/disable detection prompt: "Focus on SQL injection" # Additional analysis instructions steps: # Custom steps run before engine execution - name: Setup Security Gateway run: echo "Connecting to security gateway..." post-steps: # Custom steps run after engine execution - name: Custom Security Check run: echo "Running additional checks"Configuration Fields:
| Field | Type | Description |
|---|---|---|
enabled | boolean | Enable or disable detection (default: true when safe-outputs exist) |
prompt | string | Custom instructions appended to default detection prompt |
engine | string/object/false | AI engine config ("copilot", full config object, or false for no AI) |
runs-on | string/array/object | Runner for the detection job (default: inherits from workflow runs-on) |
artifact-base-url | string | HTTPS base URL for a mirror of the pinned detector release assets. The compiler appends the pinned release tag and asset name; the embedded digest cannot be overridden. |
steps | array | Additional GitHub Actions steps to run before AI analysis (pre-steps) |
post-steps | array | Additional GitHub Actions steps to run after AI analysis (post-steps) |
max-ai-credits | integer | AI Credits cap for the detection run, independent of the main agent budget. Defaults to 400 when unset, with runtime override via vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS. Accepts plain integers; -1 disables the detection budget. |
continue-on-error | boolean | When true (default), detection warnings/failures produce a caution notice instead of blocking safe outputs. |
report-as-issue | boolean | When true (default), detection warnings/failures create or update the [aw] Detection Runs tracking issue. Set to false to keep threat detection and its enforcement enabled while skipping the tracking issue; results remain visible in the GitHub Actions run logs. |
Use an artifact mirror
Section titled “Use an artifact mirror”Set artifact-base-url when detector binaries must be downloaded through an
approved HTTPS mirror:
safe-outputs: create-issue: threat-detection: artifact-base-url: https://artifacts.example.com/gh-aw-threat-detection/releases/downloadThe configured URL is the base of this required layout:
<artifact-base-url>/<version>/threat-detect-linux-amd64<artifact-base-url>/<version>/threat-detect-linux-arm64For example, the current v0.5.2 pin resolves the amd64 asset to
https://artifacts.example.com/gh-aw-threat-detection/releases/download/v0.5.2/threat-detect-linux-amd64.
The mirrored bytes must exactly match the SHA-256 digests embedded by the
compiler. The mirror changes only the source of the bytes; it cannot override
the release tag or expected digest. The mirror must be reachable from the
threat-detection runner. artifact-base-url currently has no documented
authentication mechanism, so use a runner-accessible HTTPS endpoint that does
not require credentials.
Update the mirror contents whenever a gh-aw release changes the pinned detector
version or digests. Populate the new <version> directory with both Linux assets
before compiling workflows that use the new gh-aw release.
Maintain detector release pins
Section titled “Maintain detector release pins”Review the promoted gh-aw-threat-detection release, then run the updater from
the gh-aw repository root:
make update-threat-detect-pins THREAT_DETECT_VERSION=<version>For example, <version> is v0.5.2 for the current pin. The updater downloads
the release checksums.txt and all four Linux and Darwin assets. It rejects
malformed, missing, duplicate, or unexpected manifest entries, verifies every
download against the manifest, and atomically updates
DefaultThreatDetectVersion with the complete DefaultThreatDetectSHA256
matrix and synchronizes the reviewed-literal compiler test. It does not update
source files if any validation fails.
The make target also runs make fmt and make recompile so the generated
workflow locks carry the new Linux digests. Review
pkg/constants/version_constants.go, pkg/constants/version_constants_test.go,
and the generated lock-file diff in the same pull request.
Detection Budget
Section titled “Detection Budget”Threat-detection runs have their own AI Credits budget, separate from the main agent’s max-ai-credits. Detection does not inherit the main agent’s budget — both caps apply independently to their respective jobs.
Set safe-outputs.threat-detection.max-ai-credits to override the per-run detection budget:
safe-outputs: create-pull-request: threat-detection: max-ai-credits: 750When unset, the compiler emits a runtime resolution that falls back to the built-in default of 400:
${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}Set the org-wide default with the GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS GitHub Actions variable. A value of -1 disables AWF budget steering for detection runs.
AI-Based Detection (Default)
Section titled “AI-Based Detection (Default)”By default, threat detection uses the same AI engine as your main workflow to analyze output for security threats.
Example with Default AI Detection:
---on: pushsafe-outputs: create-pull-request: threat-detection: true # Uses Claude for analysis---
# Code Review Agent
Analyze code and create pull requests with improvements.The AI engine receives the workflow source context and analyzes agent output items (issues, comments, PRs), git patch files with code changes, and workflow intent to distinguish legitimate actions.
Output Format:
AI detection produces a structured JSON response:
{ "prompt_injection": false, "secret_leak": false, "malicious_patch": false, "reasons": []}If any threat is detected (true), the workflow fails and safe outputs are blocked.
Custom Prompt Instructions
Section titled “Custom Prompt Instructions”Enhance AI analysis with domain-specific guidance:
safe-outputs: create-pull-request: threat-detection: prompt: | Focus on these additional security concerns: - SQL injection vulnerabilities in database queries - Cross-site scripting (XSS) in user input handling - Unsafe deserialization patterns - Hardcoded credentials in configuration filesThe custom prompt is appended to the default threat detection instructions, providing specialized context for your workflow’s domain.
Custom Engine Configuration
Section titled “Custom Engine Configuration”Override the main workflow engine for threat detection:
String Format:
safe-outputs: create-pull-request: threat-detection: engine: copilot # Use Copilot instead of main workflow engineObject Format:
safe-outputs: create-pull-request: threat-detection: engine: id: copilot max-turns: 3Disable AI Engine:
safe-outputs: create-pull-request: threat-detection: engine: false # No AI analysis, only custom steps steps: - name: Custom Scanning run: | # Run your own security tools ./security-scan.shWorkflows Using a Custom Engine
Section titled “Workflows Using a Custom Engine”Threat detection only runs on the built-in engines. When a workflow uses a custom engine, detection runs on copilot by default (so it needs Copilot credentials) and the compiler emits a warning. Set safe-outputs.threat-detection.engine to another built-in engine, or to false to skip AI analysis.
Custom engine definitions can ship their own default with the detection-engine key (copilot, claude, or codex), which removes the warning for every workflow using that engine:
engine: id: my-engine detection-engine: claude behaviors: # ...An explicit safe-outputs.threat-detection.engine in a workflow always wins over detection-engine.
Custom Detection Steps
Section titled “Custom Detection Steps”Add specialized security scanning tools alongside or instead of AI detection. You can run steps before the AI engine (for setup, gateway connections, etc.) and steps after (for additional scanning based on AI results).
Pre-Steps (steps:)
Section titled “Pre-Steps (steps:)”Steps defined under steps: run before the AI engine executes. Use these for setup tasks such as connecting to a private AI gateway, installing security tools, or preparing artifacts.
safe-outputs: create-pull-request: threat-detection: steps: - name: Connect to Security Gateway run: | echo "Setting up secure connection to analysis gateway..." # Authentication and connection setupPost-Steps (post-steps:)
Section titled “Post-Steps (post-steps:)”Steps defined under post-steps: run after the AI engine completes its analysis. Use these for additional security scanning, reporting, or cleanup.
safe-outputs: create-pull-request: threat-detection: post-steps: - name: Run Security Scanner run: | echo "Scanning agent output for threats..." # Your custom security logic here
- name: Check for Secrets uses: trufflesecurity/trufflehog@main with: path: /tmp/gh-aw/threat-detection/Available Artifacts: Custom steps have access to /tmp/gh-aw/threat-detection/prompt.txt (workflow prompt), agent_output.json (safe output items), and aw.patch (git patch file). gh-aw also stages /tmp/gh-aw/threat-detection/aw-prompts/prompt-template.txt, /tmp/gh-aw/threat-detection/aw-prompts/prompt-import-tree.json, /tmp/gh-aw/threat-detection/aw_info.json, and any restored /tmp/gh-aw/threat-detection/comment-memory/*.md files so detectors can analyze prompt structure, activation context, and persisted comment memory.
Execution Order: Download artifacts → Stage detection inputs → Execute pre-steps (steps:) → Run AI analysis (if enabled) → Execute post-steps (post-steps:) → Upload detection log.
Example: LlamaGuard Integration
Section titled “Example: LlamaGuard Integration”Use Ollama with LlamaGuard 3 for specialized threat detection running after AI analysis:
---on: pushengine: copilotsafe-outputs: create-pull-request: threat-detection: post-steps: - name: Ollama LlamaGuard 3 Scan uses: actions/github-script@v8 with: script: | // Install and start Ollama service await exec.exec('curl', ['-fsSL', 'https://ollama.com/install.sh', '-o', '/tmp/install.sh']); await exec.exec('sh', ['/tmp/install.sh']); exec.exec('ollama', ['serve'], { detached: true });
// Pull model and scan output await exec.exec('ollama', ['pull', 'llama-guard3:1b']); const content = require('fs').readFileSync('/tmp/gh-aw/threat-detection/agent_output.json', 'utf8'); const response = await exec.getExecOutput('curl', [ '-X', 'POST', 'http://localhost:11434/api/chat', '-H', 'Content-Type: application/json', '-d', JSON.stringify({ model: 'llama-guard3:1b', messages: [{ role: 'user', content }] }) ]);
const result = JSON.parse(response.stdout); const isSafe = result.message?.content.toLowerCase().includes('safe'); if (!isSafe) core.setFailed('LlamaGuard detected threat');
timeout-minutes: 20---
# Code Review AgentCombined AI and Custom Detection
Section titled “Combined AI and Custom Detection”Use both AI analysis and custom tools for defense-in-depth:
safe-outputs: create-pull-request: threat-detection: prompt: "Check for authentication bypass vulnerabilities" engine: copilot post-steps: - name: Static Analysis run: | # Run static analysis tool semgrep --config auto /tmp/gh-aw/threat-detection/
- name: Secret Scanner uses: trufflesecurity/trufflehog@main with: path: /tmp/gh-aw/threat-detection/aw.patchExample: Private AI Gateway
Section titled “Example: Private AI Gateway”Connect to a private AI gateway before running the detection engine:
safe-outputs: create-pull-request: threat-detection: steps: - name: Connect to AI Gateway run: | # Authenticate and set up connection to private AI gateway echo "Setting up gateway connection..." ./scripts/setup-gateway.sh engine: id: copilotError Handling
Section titled “Error Handling”When Threats Are Detected:
The threat detection job fails with a clear error message and safe output jobs are skipped:
✗ Threat detected: Potential SQL injection in code changesReasons:- Unsanitized user input in database query- Missing parameterized query patternWhen Detection Fails:
If the detection process itself fails (e.g., network issues, tool errors), the workflow stops and safe outputs are not applied. This fail-safe approach prevents potentially malicious content from being processed.
When Detection Returns a Warning:
A warning is a lower-severity signal than a hard threat: the safe output is allowed to proceed, but human review is required before merge. When create-pull-request is the safe output, the handler submits a REQUEST_CHANGES pull request review whose body includes the detection reason and a link to the workflow run logs. If a request_review protected-files gate also fires in the same run, both signals are composed into a single review body separated by a horizontal rule.
Opting Out of Tracking Issues:
By default, a warning or failure conclusion also creates or updates a [aw] Detection Runs tracking issue in the repository and posts a comment describing the run. This is useful for auditing automation health, but not every repository wants detector diagnostics (such as parse_error reports) surfaced alongside user-facing issues.
Set report-as-issue: false to keep threat detection and its enforcement fully active while skipping the tracking issue entirely:
safe-outputs: create-issue: threat-detection: report-as-issue: falseDetection still runs, continue-on-error behavior is unchanged, and results remain available in GitHub Actions diagnostics and logs.
Supply Chain Protection (Protected Files)
Section titled “Supply Chain Protection (Protected Files)”Beyond AI-powered threat detection, GitHub Agentic Workflows includes a static, rule-based protection layer that guards against supply chain attacks — cases where an AI agent could (intentionally or accidentally) modify files that control how software is built, tested, or deployed.
The Threat
Section titled “The Threat”An AI agent operating in a repository can be tricked (through prompt injection or misconfigured tasks) into modifying:
- Dependency manifests (
package.json,go.mod,requirements.txt,Gemfile,pom.xml, etc.) — changing what third-party code is installed. - CI/CD configuration (
.github/workflows/*.yml,.github/dependabot.yml, etc.) — altering how and when pipelines run, potentially exfiltrating secrets or bypassing security checks. - Agent instruction files (
AGENTS.md,CLAUDE.md,.claude/settings.json,.agents/, etc.) — redirecting the AI agent’s behavior on subsequent runs.
Default Remediation
Section titled “Default Remediation”Protected file protection is enabled by default for create-pull-request and push-to-pull-request-branch. Any patch that touches a protected file or directory causes the safe output to fail with a clear error:
Cannot create pull request: patch modifies protected files (package.json).Set protected-files: fallback-to-issue to create a review issue instead.This error is also surfaced as a Protected Files section in the agent failure issue or comment created by the conclusion job.
Policy Options
Section titled “Policy Options”Configure how each safe output handles protected file changes using the protected-files field:
| Value | Behavior |
|---|---|
request_review (default) | Create the pull request and submit a REQUEST_CHANGES review listing the protected files. A human reviewer must approve before merge. |
blocked | Hard-block: the safe output fails with an error message |
allowed | No restriction — all protected file changes are permitted |
fallback-to-issue | Create a review issue instead of a PR / push, so a human can inspect and apply the changes manually |
safe-outputs: create-pull-request: protected-files: fallback-to-issue # human review required for protected file changes
push-to-pull-request-branch: protected-files: fallback-to-issue # create issue instead of pushing protected file changesProtected Files
Section titled “Protected Files”The protection list is composed of four sources:
- Runtime dependency manifests — one entry per supported package manager (npm, Go, Python, Ruby, Java, Rust, Elixir, Haskell, .NET, Bun, Deno, uv).
- Engine instruction files — added automatically based on the active AI engine:
- Copilot:
AGENTS.md - Claude:
CLAUDE.md; directory prefix.claude/ - Codex:
AGENTS.md; directory prefix.codex/
- Copilot:
- Repository security configuration — the
.github/and.agents/path prefixes (.github/covers GitHub Actions workflows, Dependabot config;.agents/covers generic agent instruction and configuration files). - Repository access control files — matched by filename anywhere in the repository:
CODEOWNERS(governs required code reviewers; valid at the repository root,.github/, ordocs/).
Troubleshooting
Section titled “Troubleshooting”| Issue | Solution |
|---|---|
| AI detection always fails | Review custom prompt for overly strict instructions, check if legitimate patterns trigger detection, adjust prompt context, or temporarily disable to test |
| Custom steps not running | Verify YAML indentation, ensure steps array is properly formatted, review compilation output, check if AI detection failed first |
| Large patches cause timeouts | Increase jobs.detection.timeout-minutes (10 minutes by default, or vars.GH_AW_DEFAULT_DETECTION_JOB_TIMEOUT_MINUTES), configure max-patch-size, truncate content before analysis, or split changes into smaller PRs |
| False positives | Refine prompt with specific exclusions, adjust tool thresholds, add workflow context explaining patterns, review detection logs |
Learn More
Section titled “Learn More”- Safe Outputs Reference - Complete safe outputs configuration
- Security Guide - Overall security best practices
- Custom Safe Outputs - Creating custom output types
- Frontmatter Reference - All configuration options