Security Best Practices
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”Review workflow contents before installation, treating prompt templates and rule files as code. Assess compiled .lock.yml files to understand actual permissions and operations.
GitHub Actions’ built-in protections apply to agentic workflows: read-only defaults for fork PRs, restricted secret access, and explicit permissions (unspecified permissions default to none). See GitHub Actions security.
By default, workflows restrict execution to users with admin, maintainer, or write permissions. Use roles: all carefully in public repositories.
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: Workflows run in GitHub Actions’ partially-sandboxed environment. Arbitrary shell commands are disallowed by default; specific commands require manual allowlisting. Misconfiguration enables malicious code execution and data exfiltration.
-
Malicious inputs: Workflows pull data from Issues, PRs, comments, and code that may contain hidden AI payloads. Risk is minimized by restricting expressions in markdown and requiring GitHub MCP access, though returned data can still manipulate AI behavior.
-
Tool exposure: Default access is GitHub MCP in read-only mode. Unconstrained 3rd-party MCP tools enable data exfiltration or privilege escalation.
-
Supply chain: Unpinned Actions, npm packages, and container images are vulnerable to tampering.
Core Security Principles
Section titled “Core Security Principles”Agentic Workflows inherit GitHub Actions’ security model: isolated repository copies, read-only defaults for forked PRs, restricted secret access, and explicit permissions (default none). See GitHub Actions security.
Compilation-time security measures include:
- Expression restrictions in frontmatter
- Command allowlisting (explicit only)
- Tool allowlisting
- Engine network restrictions via domain allowlists
- Workflow longevity and iteration limits
Apply defense-in-depth consistently: least privilege by default, default-deny approach, separation of concerns (plan/apply with approval gates), and supply chain integrity (pin 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 read-only permissions for agentic processing; use safe-outputs for write operations:
permissions: contents: read actions: read
safe-outputs: create-issue: add-comment:Fork Protection for Pull Request Triggers
Section titled “Fork Protection for Pull Request Triggers”Pull request workflows block forks by default. Workflows triggered by pull_request execute only for same-repository PRs unless explicitly configured:
on: pull_request: types: [opened, synchronize] # Default: blocks all forks
# Allow specific patterns: forks: ["trusted-org/*"]
# Allow all (use with caution): # forks: ["*"]The compiler generates repository ID comparison conditions (github.event.pull_request.head.repo.id == github.repository_id) for reliable fork detection unaffected by repository renames.
workflow_run Trigger Security
Section titled “workflow_run Trigger Security”Workflows triggered by workflow_run include automatic protections against cross-repository attacks and fork execution. The compiler injects repository ID and fork detection checks:
on: workflow_run: workflows: ["CI"] types: [completed] branches: [main, develop] # Required to prevent execution on all branchesThe generated safety condition prevents execution if the triggering workflow_run is from a different repository or fork:
if: > (user_condition) && ((github.event_name != 'workflow_run') || ((github.event.workflow_run.repository.id == github.repository_id) && (!github.event.workflow_run.repository.fork)))This prevents cross-repository attacks, blocks fork execution, and combines with user conditions via AND logic. Without branch restrictions, compilation emits warnings (or errors in strict mode).
Production workflows should use strict mode:
strict: truepermissions: contents: readtimeout-minutes: 10network: allowed: ["api.example.com"]Strict mode blocks write permissions and requires explicit network configuration. Use safe-outputs for GitHub API interactions. See Strict Mode Validation.
Human in the Loop
Section titled “Human in the Loop”Critical operations require human review. Use manual-approval to require approval before execution—configure environment protection rules in repository settings. See Manual Approval Gates.
GitHub Actions cannot approve or merge PRs, ensuring human involvement. Implement plan-apply separation for previewing changes via output issues or PRs. Regularly audit workflow history, permissions, and tool usage.
Limit operations
Section titled “Limit operations”Strict Mode Validation
Section titled “Strict Mode Validation”Enable strict mode for production workflows via frontmatter or CLI (gh aw compile --strict):
strict: truepermissions: contents: readnetwork: allowed: ["api.example.com"]Strict mode enforces:
- Blocks write permissions (
contents:write,issues:write,pull-requests:write)—usesafe-outputsinstead - Requires explicit network configuration (no defaults)
- Refuses wildcard
*in network domains - Requires network config for custom MCP containers
- Enforces Action pinning to commit SHAs
- Refuses deprecated frontmatter fields
CLI flag takes precedence over frontmatter. See Frontmatter Reference.
Limit workflow longevity
Section titled “Limit workflow longevity”Use stop-after: to set workflow expiration:
on: schedule: - cron: "0 9 * * 1" stop-after: "+7d"This workflow expires 7 days after compilation. See Trigger Events.
Monitor costs
Section titled “Monitor costs”Use gh aw logs to monitor workflow costs—turns, tokens, and other metrics that help track resource usage.
Repository Access Control
Section titled “Repository Access Control”Workflows restrict execution to users with admin, maintainer, or write permissions by default. Checks auto-apply to unsafe triggers (push, issues, pull_request) but skip safe triggers (schedule, workflow_run).
Customize via roles::
roles: [admin, maintainer, write] # Defaultroles: [admin, maintainer] # Recommended for sensitive operationsroles: all # High risk in public reposPermission checks occur at runtime. Failed checks auto-cancel with warnings. Use roles: all with caution.
Authorization and Token Management
Section titled “Authorization and Token Management”Token precedence (highest to lowest): individual safe-output github-token → safe-outputs global → top-level → default (${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}).
github-token: ${{ secrets.CUSTOM_PAT }}
safe-outputs: github-token: ${{ secrets.SAFE_OUTPUT_PAT }} create-issue: github-token: ${{ secrets.ISSUE_SPECIFIC_PAT }}Use least privilege, rotate PATs regularly, prefer fine-grained PATs, monitor via audit logs, and store as secrets only.
MCP Tool Hardening
Section titled “MCP Tool Hardening”Run MCP servers in sandboxed containers: non-root UIDs, dropped capabilities, seccomp/AppArmor profiles, no privilege escalation. Pin images to digests, scan for vulnerabilities, track SBOMs.
tools: web: mcp: container: "ghcr.io/example/web-mcp@sha256:abc123..." allowed: [fetch]Tool Allow/Disallow
Section titled “Tool Allow/Disallow”Configure explicit allow-lists:
tools: github: allowed: [issue_read, add_issue_comment] bash: ["echo", "git status"]
# Avoid: ["*"] or [":*"] (unrestricted access)Egress Filtering
Section titled “Egress Filtering”Declarative network allowlists for containerized MCP servers:
mcp-servers: fetch: container: mcp/fetch network: allowed: ["example.com"] allowed: ["fetch"]The compiler generates per-tool Squid proxies; MCP egress is forced through iptables. Only listed domains are reachable. Applies to mcp.container stdio servers only.
Agent Security and Prompt Injection Defense
Section titled “Agent Security and Prompt Injection Defense”Sanitized Context Text Usage
Section titled “Sanitized Context Text Usage”CRITICAL: Always use ${{ needs.activation.outputs.text }} instead of raw github.event fields. Raw fields enable prompt injection, @mentions, bot triggers, and XML/HTML injection.
Sanitized output provides neutralized @mentions, safe XML format, HTTPS URIs from trusted domains only, 0.5MB/65k line limits, and removed control characters.
# SECUREAnalyze: "${{ needs.activation.outputs.text }}"
# INSECURETitle: "${{ github.event.issue.title }}"Safe Outputs Security Model
Section titled “Safe Outputs Security Model”Safe outputs separate AI processing from write operations. The agentic portion runs with minimal read-only permissions, while separate jobs handle validated GitHub API operations.
This ensures AI never has direct write access to your repository, preventing unauthorized changes while enabling automation. Agent output is automatically sanitized and validated.
Threat Detection
Section titled “Threat Detection”Automatic threat detection analyzes agent output for prompt injection, secret leaks, and malicious patches. Auto-enabled with safe outputs; uses AI-powered analysis to reduce false positives.
safe-outputs: create-pull-request: threat-detection: enabled: true prompt: "Focus on SQL injection" # Optional steps: # Optional additional scanning - name: Run TruffleHog uses: trufflesecurity/trufflehog@mainAdd specialized scanners for defense-in-depth. See Threat Detection Guide.
Automated Security Scanning
Section titled “Automated Security Scanning”zizmor scans compiled workflows:
gh aw compile --zizmor # Scan with warningsgh aw compile --strict --zizmor # Block on findingsAnalyzes .lock.yml for excessive permissions, insecure practices, supply chain vulnerabilities, and misconfigurations. Reports include severity, location, and context in IDE-parseable format. Requires Docker. Best practices: run during development, use --strict --zizmor in CI/CD, address High/Critical findings.
Network Isolation
Section titled “Network Isolation”Network isolation operates at two layers:
- MCP Tool Network Controls: Containerized tools with domain allowlisting
- AI Engine Network Permissions: Configurable network access for engines
See Network Reference and Engine Network Permissions.
Engine Network Permissions
Section titled “Engine Network Permissions”Fine-grained control over AI engine network access, separate from MCP tool permissions.
Copilot Engine with AWF: Uses AWF firewall wrapper for process-level domain allowlisting, execution wrapping, and activity logging. See Copilot Engine - Network Permissions.
Best Practices: Start with defaults, add needed ecosystems; prefer ecosystem identifiers over individual domains; listing a domain includes all subdomains; test thoroughly and monitor logs.
Permission Modes
Section titled “Permission Modes”# Basic infrastructure (default)engine: id: copilotnetwork: defaults
# Ecosystem-basednetwork: allowed: [defaults, python, node, containers]
# Granular domainsnetwork: allowed: - "api.github.com" - "*.company-internal.com"
# Complete denialnetwork: {}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