Security Guide
Security is foundational — Agentic Workflows inherits GitHub Actions’ sandboxing model, scoped permissions, and auditable execution. The attack surface of agentic automation can be subtle (prompt injection, tool invocation side‑effects, data exfiltration), so we bias toward explicit constraints over implicit trust: least‑privilege tokens, allow‑listed tools, and execution paths that always leave human‑visible artifacts (comments, PRs, logs) instead of silent mutation.
A core reason for building Agentic Workflows as a research demonstrator is to closely track emerging security controls in agentic engines under near‑identical inputs, so differences in behavior and guardrails are comparable. Alongside engine evolution, we are working on our own mechanisms: highly restricted substitutions, MCP proxy filtering, and hooks‑based security checks that can veto or require review before effectful steps run.
We aim for strong, declarative guardrails — clear policies the workflow author can review and version — rather than opaque heuristics. Lock files are fully reviewable so teams can see exactly what was resolved and executed. This will keep evolving; we would love to hear ideas and critique from the community on additional controls, evaluation methods, and red‑team patterns.
This material documents some notes on the security of using partially-automated agentic workflows.
Before You Begin
Section titled “Before You Begin”When working with agentic workflows, thorough review is essential:
- Review workflow contents before installation, particularly third-party workflows that may contain unexpected automation. Treat prompt templates and rule files as code.
- Assess compiled workflows (
.lock.ymlfiles) to understand the actual permissions and operations being performed - Understand GitHub’s security model - GitHub Actions provides built-in protections like read-only defaults for fork PRs and restricted secret access. These apply to agentic workflows as well. See GitHub Actions security and permissions documentation
- Remember permission defaults - when you specify any permission explicitly, all unspecified permissions default to
none - Check repository access restrictions - By default, agentic workflows restrict execution to users with
admin,maintainer, orwriterepository permissions. Useroles: allcarefully, especially in public repositories where any user can potentially trigger workflows
Threat Model
Section titled “Threat Model”Understanding the security risks in agentic workflows helps inform protective measures:
Primary Threats
Section titled “Primary Threats”- Command execution: Agentic workflows are, executed in the partially-sandboxed environment of GitHub Actions. By default, they are configured to disallow the execution of arbitrary shell commands. However, they may optionally be manually configured to allow specific commands, and if so they will not ask for confirmation before executing these specific commands as part of the GitHub Actions workflow run. If these configuration options are used inappropriately, or on sensitive code, an attacker might use this capability to make the coding agent fetch and run malicious code to exfiltrate data or perform unauthorized execution within this environment.
- Malicious inputs: Attackers can craft inputs that poison an coding agent. Agentic workflows often pull data from many sources, including GitHub Issues, PRs, comments and code. If considered untrusted, e.g. in an open source setting, any of those inputs could carry a hidden payload for AI. Agentic workflows are designed to minimize the risk of malicious inputs by restricting the expressions that can be used in workflow markdown content. This means inputs such as GitHub Issues and Pull Requests must be accessed via the GitHub MCP, however the returned data can, in principle, be used to manipulate the AI’s behavior if not properly assessed and sanitized.
- Tool exposure: By default, Agentic Workflows are configured to have no access to MCPs except the GitHub MCP in read-only mode. However unconstrained use of 3rd-party MCP tools can enable data exfiltration or privilege escalation.
- Supply chain attacks and other generic GitHub Actions threats: Unpinned Actions, npm packages and container images are vulnerable to tampering. These threats are generic to all GitHub Actions workflows, and Agentic Workflows are no exception.
Core Security Principles
Section titled “Core Security Principles”The fundamental principle of security for Agentic Workflows is that they are GitHub Actions workflows and should be reviewed with the same rigour and rules that are applied to all GitHub Actions. See GitHub Actions security.
This means they inherit the security model of GitHub Actions, which includes:
- Isolated copy of the repository - each workflow runs in a separate copy of the repository, so it cannot access other repositories or workflows
- Read-only defaults for forked PRs
- Restricted secret access - secrets are not available in forked PRs by default
- Explicit permissions - all permissions default to
noneunless explicitly set
In addition, the compilation step of Agentic Workflows enforces additional security measures:
- Expression restrictions - only a limited set of expressions are allowed in the workflow frontmatter, preventing arbitrary code execution
- Highly restricted commands - by default, no commands are allowed to be executed, and any commands that are allowed must be explicitly specified in the workflow
- Explicit tool allowlisting - only tools explicitly allowed in the workflow can be used
- Engine network restrictions - control network access for AI engines using domain allowlists
- Limit workflow longevity - workflows can be configured to stop triggering after a certain time period
- Limit chat iterations - workflows can be configured to limit the number of chat iterations per run, preventing runaway loops and excessive resource consumption
Apply these principles consistently across all workflow components:
- Least privilege by default - elevate permissions only when required, scoped to specific jobs or steps
- Default-deny approach - explicitly allowlist tools
- Separation of concerns - implement “plan” and “apply” phases with approval gates for risky operations
- Supply chain integrity - pin all dependencies (Actions, containers) to immutable SHAs
Implementation Guidelines
Section titled “Implementation Guidelines”Workflow Permissions and Triggers
Section titled “Workflow Permissions and Triggers”Configure GitHub Actions with defense in depth:
Permission Configuration
Section titled “Permission Configuration”Set minimal permissions for the agentic processing:
# Applies to the agentic processingpermissions: issues: write contents: readProduction workflows: Consider using strict mode to enforce additional security constraints:
# Enable strict mode for production workflowsstrict: truepermissions: contents: read # Write permissions are blocked in strict modetimeout_minutes: 10network: allowed: - "api.example.com"Strict mode prevents write permissions (contents:write, issues:write, pull-requests:write) and requires explicit network configuration. Use safe-outputs configuration instead for controlled GitHub API interactions. See Strict Mode Validation for details.
Human in the Loop
Section titled “Human in the Loop”GitHub Actions workflows are designed to be steps within a larger process. Some critical operations should always involve human review:
- Approval gates: Use manual approval steps for high-risk operations like deployments, secret management, or external tool invocations
- Pull requests require humans: GitHub Actions cannot approve or merge pull requests. This means a human will always be involved in reviewing and merging pull requests that contain agentic workflows.
- Plan-apply separation: Implement a “plan” phase that generates a preview of actions before execution. This allows human reviewers to assess the impact of changes. This is usually done via an output issue or pull request.
- Review and audit: Regularly review workflow history, permissions, and tool usage to ensure compliance with security policies.
Limit operations
Section titled “Limit operations”Strict Mode Validation
Section titled “Strict Mode Validation”For production workflows, use strict mode to enforce enhanced security and reliability constraints:
# Enable strict mode declaratively in frontmatterstrict: truepermissions: contents: readnetwork: allowed: - "api.example.com"Or enable for all workflows during compilation:
gh aw compile --strictStrict mode enforces:
- Write Permissions Blocked: Refuses
contents:write,issues:write, andpull-requests:write(usesafe-outputsinstead) - Network Configuration Required: Must explicitly configure network access (cannot rely on defaults)
- No Network Wildcards: Cannot use wildcard
*innetwork.alloweddomains - MCP Network Configuration: Custom MCP servers with containers must have network configuration
Benefits:
- Security: Minimizes attack surface by blocking write permissions and requiring explicit network access
- Compliance: Ensures workflows meet organizational security standards
- Auditability: Clear security requirements make workflows easier to review
Behavior:
- CLI
--strictflag applies to all workflows during compilation - Frontmatter
strict: trueenables strict mode for individual workflows - CLI flag takes precedence over frontmatter settings
- Default is non-strict mode for backward compatibility
See the Frontmatter Reference for complete strict mode documentation.
Limit workflow longevity by stop-after:
Section titled “Limit workflow longevity by stop-after:”Use stop-after: in the on: section to limit the time of operation of an agentic workflow. For example, using
on: schedule: - cron: "0 9 * * 1" stop-after: "+7d"will mean the agentic workflow no longer operates 7 days after time of compilation.
For complete documentation on stop-after: configuration and supported formats, see Trigger Events.
Limit workflow runs by engine max-turns:
Section titled “Limit workflow runs by engine max-turns:”Use max-turns: in the engine configuration to limit the number of chat iterations per run. This prevents runaway loops and excessive resource consumption. For example:
engine: id: claude max-turns: 5This limits the workflow to a maximum of 5 interactions with the AI engine per run.
Monitor costs by gh aw logs
Section titled “Monitor costs by gh aw logs”Use gh aw logs to monitor the costs of running agentic workflows. This command provides insights into the number of turns, tokens used, and other metrics that can help you understand the cost implications of your workflows. Reported information may differ based on the AI engine used (e.g., Claude vs. Codex).
Repository Access Control
Section titled “Repository Access Control”Agentic workflows include built-in access control to prevent unauthorized execution:
By default, workflows restrict execution to users with admin, maintainer, or write permissions:
- Default roles:
admin,maintainer, andwriterepository permissions are required - Automatic enforcement: Permission checks are automatically added to workflows with potentially unsafe triggers (
push,issues,pull_request, etc.) - Safe trigger exceptions: Workflows that only use “safe” triggers (
schedule,workflow_run) skip permission checks by default - workflow_dispatch is treated as safe only when
writeis in the allowed roles, since workflow_dispatch can be triggered by users with write access
Use the roles: frontmatter field to customize who can trigger workflows:
# Default (admin, maintainer, or write access)roles: [admin, maintainer, write]
# More restrictive - only admin/maintainer (use for sensitive operations)roles: [admin, maintainer]
# Only write accessroles: [write]
# Disable restrictions entirely (high risk in public repos)roles: allworkflow_dispatch Examples:
# Permission check REQUIRED - write role not allowedon: workflow_dispatch:roles: [admin, maintainer] # Users with write access will be denied
# Permission check SKIPPED - write role allowed (default)on: workflow_dispatch:roles: [admin, maintainer, write] # Users with write access allowed
# Permission check SKIPPED - all users allowedon: workflow_dispatch:roles: allSecurity Behavior
Section titled “Security Behavior”- Permission checks happen at workflow runtime, not when the workflow is installed
- Failed permission checks automatically cancel the workflow with a logged warning
- Users see the workflow start but then immediately stop if they lack permissions
- All permission check results are visible in the Actions tab for debugging
Important: Use roles: all with extreme caution, especially in public repositories where any authenticated user can potentially trigger workflows through issues, comments, or pull requests.
Authorization and Token Management
Section titled “Authorization and Token Management”GitHub Agentic Workflows support flexible token configuration for different execution contexts and security requirements.
GitHub Token Precedence
Section titled “GitHub Token Precedence”Workflows use a hierarchical token precedence system that allows you to configure authentication tokens at different levels. The precedence order from highest to lowest is:
- Individual safe-output
github-token- Token specified for a specific safe-output (e.g.,create-issue.github-token) - Safe-outputs global
github-token- Token specified at thesafe-outputslevel - Top-level
github-token- Token specified in workflow frontmatter (new) - Default fallback -
${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
This allows you to set a default token for the entire workflow while still allowing specific safe-outputs to use different tokens when needed.
Token Configuration Examples
Section titled “Token Configuration Examples”Top-level token for entire workflow:
---on: pushgithub-token: ${{ secrets.CUSTOM_PAT }}engine: claudetools: github: allowed: [list_issues]---This token will be used for:
- Engine authentication (GitHub MCP server, etc.)
- Checkout steps (in trial mode)
- Safe-output operations (unless overridden)
Safe outputs with custom tokens:
---on: pushgithub-token: ${{ secrets.DEFAULT_PAT }}safe-outputs: github-token: ${{ secrets.SAFE_OUTPUT_PAT }} create-issue: github-token: ${{ secrets.ISSUE_SPECIFIC_PAT }} create-pull-request: # Uses safe-outputs global token---In this example:
create-issueusesISSUE_SPECIFIC_PAT(highest precedence)create-pull-requestusesSAFE_OUTPUT_PAT(safe-outputs level)- Engine and other operations use
DEFAULT_PAT(top-level)
Per-job token configuration (legacy):
jobs: agentic-task: runs-on: ubuntu-latest env: GH_AW_GITHUB_TOKEN: ${{ secrets.ENHANCED_PAT }} steps: # Workflow steps use the enhanced tokenSecurity Considerations
Section titled “Security Considerations”When using custom tokens:
- Principle of least privilege: Grant only the minimum permissions required
- Token rotation: Regularly rotate Personal Access Tokens
- Scope limitation: Use fine-grained PATs when possible to limit repository access
- Audit logging: Monitor token usage through GitHub’s audit logs
- Secret management: Store tokens as GitHub repository or organization secrets, never in code
MCP Tool Hardening
Section titled “MCP Tool Hardening”Model Context Protocol tools require strict containment:
Sandboxing and Isolation
Section titled “Sandboxing and Isolation”Run MCP servers in explicit sandboxes to constrain blast radius:
- Container isolation: Prefer running each MCP server in its own container with no shared state between workflows, repos, or users.
- Non-root, least-capability: Use non-root UIDs, drop Linux capabilities, and apply seccomp/AppArmor where supported. Disable privilege escalation.
- Supply-chain sanity: Use pinned images/binaries (digest/SHAs), run vulnerability scans, and track SBOMs for MCP containers.
Example (pinned container with minimal allowances):
tools: web: mcp: container: "ghcr.io/example/web-mcp@sha256:abc123..." # Pinned image digest allowed: [fetch]Tool Allow/Disallow Examples
Section titled “Tool Allow/Disallow Examples”Configure explicit allow-lists for tools. See also docs/tools.md for full options.
- Minimal GitHub tool set (read + specific writes):
tools: github: allowed: [get_issue, add_issue_comment]- Restricted Claude bash and editing:
engine: claudetools: edit: bash: ["echo", "git status"] # keep tight; avoid wildcards- Patterns to avoid:
tools: github: allowed: ["*"] # Too broad bash: [":*"] # Unrestricted shell accessEgress Filtering
Section titled “Egress Filtering”A critical guardrail is strict control over outbound network connections. Agentic Workflows now supports declarative network allowlists for containerized MCP servers.
Example (domain allowlist):
mcp-servers: fetch: container: mcp/fetch network: allowed: - "example.com" allowed: ["fetch"]Enforcement details:
- Compiler generates a per‑tool Squid proxy and Docker network; MCP egress is forced through the proxy via iptables.
- Only listed domains are reachable; all others are denied at the network layer.
- Applies to
mcp.containerstdio servers. Non‑container stdio andtype: httpservers are not supported and will cause compilation errors.
Operational guidance:
- Use bare domains (no scheme). Explicitly list each domain you intend to permit.
- Prefer minimal allowlists; review the compiled
.lock.ymlto verify proxy setup and rules.
Agent Security and Prompt Injection Defense
Section titled “Agent Security and Prompt Injection Defense”Protect against model manipulation through layered defenses:
Sanitized Context Text Usage
Section titled “Sanitized Context Text Usage”CRITICAL: Always use ${{ needs.activation.outputs.text }} instead of raw github.event fields when accessing user-controlled content.
Raw context fields like ${{ github.event.issue.title }}, ${{ github.event.issue.body }}, and ${{ github.event.comment.body }} contain unsanitized user input that can:
- Inject malicious prompts to manipulate AI behavior
- Trigger unintended @mentions and bot commands
- Include XML/HTML content that could affect output processing
- Contain excessive content leading to resource exhaustion
The needs.activation.outputs.text provides the same content but with security protections:
- @mentions are neutralized:
@userbecomes`@user` - Bot triggers are escaped:
fixes #123becomes`fixes #123` - XML tags converted to safe parentheses format
- Only HTTPS URIs from trusted domains allowed
- Content size limited to 0.5MB and 65k lines maximum
- Control characters and ANSI sequences removed
# SECURE: Use sanitized contextAnalyze this content: "${{ needs.activation.outputs.text }}"
# INSECURE: Raw user input (vulnerable to injection)Title: "${{ github.event.issue.title }}"Body: "${{ github.event.issue.body }}"Policy Enforcement
Section titled “Policy Enforcement”- Input sanitization: Always use sanitized context text for user-controlled content
- Action validation: Implement a plan-validate-execute flow where policy layers check each tool call against risk thresholds
Safe Outputs Security Model
Section titled “Safe Outputs Security Model”Safe outputs provide a security-first approach to GitHub API interactions by separating AI processing from write operations. The agentic portion of workflows runs with minimal read-only permissions, while separate jobs handle validated GitHub API operations like creating issues, comments, or pull requests.
This architecture ensures the AI never has direct write access to your repository, preventing unauthorized changes while still enabling automated actions. All agent output is automatically sanitized and validated before processing.
See the Safe Outputs Reference for complete configuration details and available output types.
Threat Detection
Section titled “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.
The system uses AI-powered analysis with workflow source context to distinguish between legitimate actions and threats, helping reduce false positives while maintaining strong security controls.
Configuration Options:
Threat detection is automatically enabled when safe outputs are configured, but can be customized:
safe-outputs: create-pull-request: threat-detection: enabled: true # Enable/disable (default: true) prompt: "Focus on SQL injection" # Custom analysis instructions engine: # Custom detection engine id: claude model: claude-sonnet-4 steps: # Additional security scanning - name: Run TruffleHog uses: trufflesecurity/trufflehog@mainCustom Detection Tools:
Add specialized security scanners like Ollama/LlamaGuard, Semgrep, or TruffleHog alongside AI analysis for defense-in-depth security.
See the Threat Detection Guide for comprehensive documentation, configuration examples, and the LlamaGuard integration pattern.
Automated Security Scanning
Section titled “Automated Security Scanning”GitHub Agentic Workflows supports automated security scanning of compiled workflows using zizmor, a specialized security scanner for GitHub Actions workflows. Zizmor runs during compilation to identify potential vulnerabilities before workflows are deployed.
Enable security scanning:
gh aw compile --zizmorZizmor analyzes generated .lock.yml files for common security issues:
- Excessive or overly broad permissions
- Insecure workflow practices
- Supply chain vulnerabilities
- Workflow misconfigurations
Security findings are reported in IDE-parseable format:
./.github/workflows/workflow.lock.yml:7:5: warning: [Medium] excessive-permissions: overly broad permissions5 | steps:6 | - uses: actions/checkout@v47 | permissions: ^^^^^^^^^^^^^8 | contents: write9 | issues: writeEach finding includes:
- Severity level: Low, Medium, High, or Critical
- File location: Clickable line and column numbers for IDE navigation
- Context: Surrounding code lines showing the issue
- Description: Clear explanation of the security concern
Enforcement with strict mode:
Combine --zizmor with --strict to block compilation when security issues are found:
gh aw compile --strict --zizmor # Fails if security findings existThis ensures workflows meet security standards before deployment to production environments. In non-strict mode, findings are reported as warnings but do not prevent compilation.
Requirements:
- Docker must be installed and running
- The scanner runs in a container (
ghcr.io/zizmorcore/zizmor:latest) - Each
.lock.ymlfile is scanned independently
Best practices:
- Run
--zizmorduring development to catch security issues early - Use
--strict --zizmorin CI/CD pipelines for production workflows - Review and address all High and Critical severity findings
- Consider Medium and Low findings for defense-in-depth improvements
Network Isolation
Section titled “Network Isolation”Network isolation in GitHub Agentic Workflows operates at two layers to prevent unauthorized network access:
- MCP Tool Network Controls: Containerized tools with network-level domain allowlisting
- AI Engine Network Permissions: Configurable network access controls for AI engines
See the Network Reference for detailed configuration options and the Engine Network Permissions section below for engine-specific controls.
Engine Network Permissions
Section titled “Engine Network Permissions”Overview
Section titled “Overview”Engine network permissions provide fine-grained control over network access for AI engines themselves, separate from MCP tool network permissions. This feature uses Claude Code’s hook system to enforce domain-based access controls.
Security Benefits
Section titled “Security Benefits”- Defense in Depth: Additional layer beyond MCP tool restrictions
- Compliance: Meet organizational security requirements for AI network access
- Audit Trail: Network access attempts are logged through Claude Code hooks
- Principle of Least Privilege: Only grant network access to required domains
Implementation Details
Section titled “Implementation Details”Engine network permissions are implemented differently based on the AI engine:
Claude Engine:
- Hook-Based Enforcement: Uses Claude Code’s PreToolUse hooks to intercept network requests
- Runtime Validation: Domain checking happens at request time, not compilation time
- Error Handling: Blocked requests receive clear error messages with allowed domains
- Performance Impact: Minimal overhead (~10ms per network request)
Copilot Engine with AWF:
- Firewall Wrapper: Uses AWF (Agent Workflow Firewall) from github.com/githubnext/gh-aw-firewall
- Domain Allowlisting: Enforces network access at the process level via
--allow-domainsflag - Execution Wrapping: AWF wraps the entire Copilot CLI execution command
- Activity Logging: All network activity is logged for audit purposes
- Configuration: Configure via
network.firewallin workflow frontmatter
See Copilot Engine - Network Permissions for detailed AWF configuration.
Best Practices
Section titled “Best Practices”- Start with Minimal Access: Begin with
defaultsand add only needed ecosystems - Use Ecosystem Identifiers: Prefer
python,node, etc. over listing individual domains - Use Wildcards Carefully:
*.example.commatches any subdomain including nested ones (e.g.,api.example.com,nested.api.example.com) - ensure this broad access is intended - Test Thoroughly: Verify that all required domains/ecosystems are included in allowlist
- Monitor Usage: Review workflow logs to identify any blocked legitimate requests
- Document Reasoning: Comment why specific domains/ecosystems are required for maintenance
Permission Modes
Section titled “Permission Modes”-
No network permissions: Defaults to basic infrastructure only (backwards compatible)
engine:id: claude# No network block - defaults to basic infrastructure -
Basic infrastructure only: Explicit basic infrastructure access
engine:id: claudenetwork: defaults # Or use "allowed: [defaults]" -
Ecosystem-based access: Use ecosystem identifiers for common development tools
engine:id: claudenetwork:allowed:- defaults # Basic infrastructure- python # Python/PyPI ecosystem- node # Node.js/NPM ecosystem- containers # Container registries -
Granular domain control: Specific domains only
engine:id: claudenetwork:allowed:- "api.github.com"- "*.company-internal.com" -
Complete denial: No network access
engine:id: claudenetwork: {} # Deny all network access
Engine Security Guide
Section titled “Engine Security Guide”Different agentic engines have distinct defaults and operational surfaces.
engine: claude
Section titled “engine: claude”- Restrict
claude.allowedto only the needed capabilities (Edit/Write/WebFetch/Bash with a short list) - Keep
allowed_toolsminimal in the compiled step; review.lock.ymloutputs - Use engine network permissions with ecosystem identifiers to grant access to only required development tools
Security posture differences across engines
Section titled “Security posture differences across engines”Copilot and Claude expose richer default tools and optional Bash; Codex relies more on CLI behaviors. In all cases, tool allow-lists, network restrictions, and pinned dependencies are your primary controls.
See also
Section titled “See also”- Threat Detection Guide - Comprehensive threat detection configuration and examples
- Safe Outputs Reference
- Network Configuration
- Tools
- MCPs
- Workflow Structure
References
Section titled “References”- Model Context Protocol: Security Best Practices (2025-06-18) — https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices