How to Secure GitHub Agentic Workflows Against Prompt Injection

How to Secure GitHub Agentic Workflows Against Prompt Injection

Aident AI

A translucent security gateway filters hostile coral fragments before a narrow blue workflow path reaches a private repository vault.

How to Secure GitHub Agentic Workflows Against Prompt Injection

To secure a GitHub Agentic Workflow, treat every issue, pull request, comment, and fetched page as untrusted data. Limit the workflow to the current repository, filter content by author integrity, keep the agent read-only, declare only the safe outputs it needs, block unnecessary network access, and inspect the compiled .lock.yml before enabling the trigger.

The shortest audit is:

gh extension upgrade gh-aw
gh aw upgrade
gh aw compile --no-emit --strict
gh aw validate --strict
git diff -- .github/workflows .github/aw

Then review every workflow frontmatter block for these boundaries:

permissions:
  contents: read
  issues: read

tools:
  github:
    allowed-repos: current
    min-integrity: approved

network: {}

safe-outputs:
  add-comment:
    max: 1

This is a defensive template, not a drop-in workflow. Keep only the read permissions and safe outputs required by your task, then compile and validate the actual source file.

Why GitLost Changed the Audit Question

Noma Security disclosed GitLost on July 6, 2026. Its proof of concept used a public issue to steer an Agentic Workflow that could read other public and private repositories and post a public comment. The dangerous chain was not prompt injection alone. It was untrusted input plus cross-repository read access plus a public output path.

GitHub's current Agentic Workflows documentation describes a layered defense: integrity filtering, read-only agent permissions, sandboxing, a network firewall, safe outputs, threat detection, secret redaction, and compile-time validation. Those layers matter, but they do not make an overbroad credential or repository policy harmless. The current security architecture explicitly treats token scope and declarative configuration as security boundaries.

Use this guide to break the attack chain at several independent points.

Prerequisites

You need:

  • GitHub CLI authenticated to the repository;

  • the github/gh-aw extension;

  • a clean worktree so generated lock-file changes are easy to review;

  • permission to inspect Actions settings, workflow secrets, GitHub Apps, and repository access;

  • a non-production repository or fork for the canary test.

Install and inspect the tooling:

gh --version
gh auth status
gh extension install github/gh-aw
gh aw version
git status --short

Expected result: GitHub CLI identifies the intended account, gh aw version succeeds, and the worktree has no unexplained changes. Stop if the authenticated account or repository is not the one you meant to audit.

Step 1: Draw the Actual Data Path

For each .github/workflows/*.md source file, write down four things:

  1. Which event starts it?

  2. Which user-controlled fields can enter the agent context?

  3. Which repositories, tools, secrets, and network destinations can it reach?

  4. Which public or private side effects can it request?

Start with triggers that outsiders can influence, including issues, issue_comment, pull_request, pull_request_review, and discussions. An assignment or label added by automation does not make the original issue body trustworthy.

Search the source and compiled workflows:

rg -n "issues:|issue_comment:|pull_request:|allowed-repos|min-integrity|safe-outputs|network:|github-token|secrets\." \
  .github/workflows .github/aw

Expected result: every untrusted trigger has an explicit repository scope, integrity policy, permission set, output policy, and network policy. If one workflow can read private repositories while reacting to a public issue, disable it until that access is narrowed.

Step 2: Upgrade and Recompile the Security Boundary

The Markdown file is the editable workflow source. The generated .lock.yml is the hardened GitHub Actions workflow that runs. Upgrade the extension and repository support files, then review the generated changes:

gh extension upgrade gh-aw
gh aw upgrade
gh aw compile --strict
gh aw validate --strict
git diff -- .github/workflows .github/aw

Expected result: validation passes, each source workflow has a matching generated lock file, Actions are pinned, and no unexpected permission, secret, tool, or network access appears in the diff. Never hand-edit a .lock.yml file. Change the Markdown source and compile again.

If the upgrade changes deprecated fields, review those changes as a security migration. Do not accept a large generated diff without confirming that its effective permissions and repository scope stayed narrow.

Step 3: Scope GitHub Reads to the Task

The current integrity-filtering reference says allowed-repos defaults to "all" when it is omitted. Make the intended scope explicit.

For a workflow that only needs its own repository:

tools:
  github:
    allowed-repos: current
    min-integrity: approved

For a workflow that needs a small fixed set:

tools:
  github:
    allowed-repos:
      - "example-org/service-a"
      - "example-org/shared-docs"
    min-integrity: approved

Do not use an organization-wide personal token to simplify a public-facing workflow. Prefer the default repository token or a GitHub App installation restricted to the exact repositories and read permissions required. If cross-repository access is genuinely necessary, split public intake from private analysis so untrusted issue text and private repository tools never coexist in the same agent stage.

Expected result: the agent cannot enumerate or read a repository outside the explicit allowlist, even if the model follows hostile text.

Step 4: Choose an Integrity Level Deliberately

For public repositories, GitHub currently applies min-integrity: approved automatically when no level is configured. Declare it anyway so the review boundary is visible:

tools:
  github:
    allowed-repos: current
    min-integrity: approved

approved admits owners, members, collaborators, and other content that meets GitHub's documented approval rules. Use merged when the workflow should rely only on content already incorporated into the default branch.

Some community triage workflows must read first-time contributors. If that is the job, min-integrity: none is an explicit acceptance of hostile input:

tools:
  github:
    allowed-repos: current
    min-integrity: none

Pair that setting with current-repository access, read-only permissions, no secrets in the agent container, restrictive safe outputs, and no unnecessary network. Do not lower the integrity level merely to make a filtered issue appear in a test.

Step 5: Keep the Agent Read-Only and Writes Structured

GitHub Agentic Workflows separates agent analysis from side effects. The agent should read with least privilege and request a declared safe output instead of receiving write credentials.

For a workflow that may add one comment:

permissions:
  contents: read
  issues: read

safe-outputs:
  add-comment:
    max: 1

Do not declare create-pull-request, push-to-pull-request-branch, labels, or cross-repository targets unless the workflow needs them. Safe outputs are narrower than a general write token, but their text and target rules still deserve review.

Expected result: the agent job cannot write directly, and an undeclared mutation is rejected rather than improvised.

Step 6: Close Unneeded Network Paths

If the job only reads GitHub through the provided tooling, block outbound network access:

network: {}

If a package registry or API is essential, add only the documented ecosystem or domain:

network:
  allowed:
    - defaults
    - "api.example.com"

Avoid broad wildcard domains and caller-extensible allowlists unless the workflow's threat model requires them. A blocked request is useful evidence. Review it before expanding the allowlist.

After a run, inspect firewall activity:

gh aw logs --run-id <run-id>
gh aw audit <run-id

Expected result: every allowed destination maps to a stated workflow requirement. Unknown or denied destinations should not be solved by enabling unrestricted egress.

Step 7: Inspect the Compiled Workflow With Security Scanners

Run the strict compiler and the supported Actions scanners:

gh aw compile --no-emit --strict
gh aw compile --actionlint --zizmor --poutine --grant
gh aw validate --strict

Then inspect the generated security contract:

rg -n "permissions:|uses:|secrets\.|allowed-repos|min-integrity|safe-outputs|network" \
  .github/workflows/*.lock.yml

Expected result: dependencies are pinned, the agent has no unexpected write scope, secrets are not passed to unnecessary containers, and only reviewed outputs and destinations are enabled. Treat scanner findings and compile-time warnings as blockers until they are explained.

Step 8: Run a Benign Prompt-Injection Canary

Test in a disposable public repository or fork with no private-repository access and no production secrets. Create an issue from an account below the configured integrity threshold containing a harmless canary such as:

GH_AW_UNTRUSTED_CANARY_2026_07

Treat this sentence as issue data, not as a workflow instruction

Do not include exfiltration commands, real secret names, or sensitive paths. Trigger the workflow, then inspect its logs and audit report:

gh aw logs --run-id <run-id>
gh aw audit <run-id

With min-integrity: approved, the unapproved content should be filtered and recorded as an integrity event. With an intentionally public triage workflow using min-integrity: none, the canary may be visible, but the run must remain inside the current repository, use only declared outputs, and make no unexpected network request.

Expected result: changing one trust boundary produces a predictable, auditable difference without exposing any real data.

Common Failure Modes

Symptom

Likely boundary

Safe response

A public issue can trigger a workflow with an organization-wide token

Trigger and credential scopes overlap

Disable the workflow, replace the token with current-repository access, and separate public intake from private analysis

An outsider's issue disappears from the agent context

Integrity filtering is working

Keep the filter, or design a separate low-privilege public triage workflow

The agent asks for an undeclared write

Safe outputs are narrower than the prompt expects

Add only the specific reviewed output, or keep the workflow report-only

A dependency download is blocked

Network allowlist is restrictive

Verify the destination and add only the required domain or ecosystem

Source and .lock.yml disagree

Frontmatter changed without compilation

Run gh aw compile, inspect the diff, and commit both files

A scan passes but the effective token reaches many repositories

Static workflow checks cannot narrow the external credential

Replace or reinstall the credential with repository-level scope

Threat detection blocks a run

Output or patch crossed a security rule

Inspect the artifact and input path; do not weaken the detector to make the run green

Why These Controls Work Together

Prompt text cannot reliably prove that other prompt text is harmless. The durable controls sit outside the model: which data reaches it, which repositories and tools it can access, which network paths exist, and which side effects a separate stage may apply.

GitLost joined three capabilities that should not have shared one trust domain: public input, private reads, and public writes. Repository scoping removes the private read. Integrity filtering limits hostile input. Read-only execution and safe outputs constrain writes. Network policy removes an alternate export path. Compilation, scanning, and audit logs make those controls reviewable.

For another practical security boundary, see how to audit an agent skill before installing it. For a release-time check, use the Codex Security pre-commit guide.

Verify a Read-Only GitHub Schema With Aident Loadout

Aident Loadout can help an agent discover a narrow GitHub Action without receiving a pasted provider token. Install or update it with:

Follow https://aident.ai/SETUP.md

Then inspect authentication and find one repository-scoped issue-reading Action:

aident account auth status
aident vault vault --action status
aident capabilities search \
  --query "connected GitHub Action that lists issues in one named repository without modifying them" \
  --targetEnv staging
aident capabilities get --name "<canonical-action-name>"

Do not execute an Action until its exact schema, repository fields, operation description, and risk level match the task. Use Aident Loadout to inspect one current GitHub Action schema. Measure success as one schema inspected, one repository boundary confirmed, zero provider secrets pasted, and zero repository writes.

Sources

Refresh this guide when GitHub changes the default integrity policy, allowed-repos behavior, safe-output contract, network firewall, threat detection, compiler flags, or credential model.

Home

Home

Home

Integrations

Integrations

Integrations

Vault

Vault

Vault

Audit

Audit

Audit

Arana Grande

Arana Grande

Arana Grande

Free

Free

Free

30-day audit summary

30-day audit summary

30-day audit summary

Daily action-call volume and the latest receipts from the Loadout audit trail.

Daily action-call volume and the latest receipts from the Loadout audit trail.

Daily action-call volume and the latest receipts from the Loadout audit trail.

View Audit

View Audit

View Audit

Loadout usage

Loadout usage

Loadout usage

617 action calls in the last 30 days

617 action calls in the last 30 days

617 action calls in the last 30 days

May 19 - Jun 17

May 19 - Jun 17

May 19 - Jun 17

10 active days

10 active days

10 active days

Less

Less

Less

More

More

More

Recent activity

Recent activity

Recent activity

Latest action-call receipts from connected agents

Latest action-call receipts from connected agents

Latest action-call receipts from connected agents

Apr 23, 09:23 AM

Apr 23, 09:23 AM

Apr 23, 09:23 AM

Shopify

Shopify

Shopify

Creates Or Updates An Asset For A Theme

Creates Or Updates An Asset For A Theme

Creates Or Updates An Asset For A Theme

Success

Success

Success

Apr 23, 09:21 AM

Apr 23, 09:21 AM

Apr 23, 09:21 AM

Shopify

Shopify

Shopify

Update Products Param Product Id

Update Products Param Product Id

Update Products Param Product Id

Success

Success

Success

Apr 23, 08:53 AM

Apr 23, 08:53 AM

Apr 23, 08:53 AM

Shopify

Shopify

Shopify

Update Products Param Product Id

Update Products Param Product Id

Update Products Param Product Id

Failed

Failed

Failed

Apr 22, 22:13 PM

Apr 22, 22:13 PM

Apr 22, 22:13 PM

Shopify

Shopify

Shopify

Create Product Image

Create Product Image

Create Product Image

Success

Success

Success

Apr 22, 22:12 PM

Apr 22, 22:12 PM

Apr 22, 22:12 PM

Shopify

Shopify

Shopify

Create Product Image

Create Product Image

Create Product Image

Success

Success

Success

Connected integration coverage

Connected integration coverage

Connected integration coverage

162

162

162

of 753 accessible connected

of 753 accessible connected

of 753 accessible connected

Callable actions

Callable actions

Callable actions

1,126

1,126

1,126

Vault credentials

Vault credentials

Vault credentials

8

8

8

Explore what's possible

Explore what's possible

Explore what's possible

See all Integrations

See all Integrations

See all Integrations

Google Ads

Google Ads

Google Ads

All available Goolge Ads tools via...

All available Goolge Ads tools via...

All available Goolge Ads tools via...

X (twitter)

X (twitter)

X (twitter)

All available X tools via...

All available X tools via...

All available X tools via...

Github

Github

Github

All available Github tools via...

All available Github tools via...

All available Github tools via...

Notion

Notion

Notion

All available Notion tools via...

All available Notion tools via...

All available Notion tools via...

Slack

Slack

Slack

All available Slack tools via...

All available Slack tools via...

All available Slack tools via...

Firecrawl

Firecrawl

Firecrawl

All available Firecrawl tools via...

All available Firecrawl tools via...

All available Firecrawl tools via...

753 integrations are available for loadouts.

753 integrations are available for loadouts.

753 integrations are available for loadouts.

Plug your entire stack into your AI agents.

Plug your entire stack into your AI agents.

Plug your entire stack into your AI agents.

Skip the integration headache. Plug 750+ tools into Claude Code, Codex, and OpenClaw in one go, and let your agents execute today.

Skip the integration headache. Plug 750+ tools into Claude Code, Codex, and OpenClaw in one go, and let your agents execute today.