How to Configure Cross-Repository Safe Output Handlers Securely in gh-aw

Configure cross-repository safe output handlers securely by defining a target-repo for default operations and an allowed-repos list for additional repositories, which enables runtime repository selection while enforcing automatic allow-list validation that rejects unauthorized cross-repo requests with error E004.

The github/gh-aw repository implements a strict security model for agentic workflows that need to interact with GitHub APIs across repository boundaries. When you configure cross-repository safe output handlers securely, you leverage a defense-in-depth strategy that combines static configuration validation, runtime allow-list enforcement, and continuous integration checks to prevent unauthorized repository access.

Understanding Cross-Repository Safe Output Configuration

The Role of target-repo and allowed-repos

At the core of gh-aw's security model are two configuration fields defined in pkg/workflow/safe_output_builder.go (lines 17-19):

  • target-repo: Specifies the default repository for the safe-output operation (format: owner/repo)
  • allowed-repos: Defines an explicit allow-list of additional repositories that may be targeted at runtime

When allowed-repos is populated, the compiler automatically injects a repo input parameter into the safe-output handler. This dynamic parameter allows agentic scripts to select different permitted repositories during execution while maintaining security boundaries.

Security Layers for Cross-Repository Operations

Allow-List Validation and Error E004

Every cross-repository safe-output handler performs mandatory allow-list validation before executing GitHub API calls. The system verifies that the requested repository matches either the target-repo or appears in the allowed-repos list.

If validation fails, the operation aborts immediately with error E004 ("cross-repository operation not allowed"). This default-deny posture ensures that unauthorized repository access attempts are blocked at runtime.

Global GitHub Reference Restrictions

Workflows may optionally define a top-level allowed-github-references array in the workflow schema (pkg/parser/schemas/main_workflow_schema.json, line 1195). This global allow-list acts as an additional security boundary:

  • If defined, all cross-repo targets must appear in this global list unless explicitly overridden by the safe-output's own allowed-repos configuration
  • Provides defense-in-depth for organizations managing multiple workflows

CI Conformance Checks

The repository includes scripts/check-safe-outputs-conformance.sh (lines 163-172), which enforces security requirement SEC-005. This script scans all safe-output handlers during continuous integration and fails the build if any handler supporting target-repo lacks proper allow-list validation.

Running this script locally ensures your configurations meet the repository's security standards before deployment.

Configuring allowed-repos for Dynamic Repository Selection

The compiler logic in pkg/workflow/safe_outputs_config_generation.go (lines 735-983) handles the conditional injection of the repo parameter. When allowed-repos contains entries, the generated GitHub Action step includes:

  1. An additional repo input in the schema
  2. Runtime validation logic comparing the provided repo value against the configured allow-list

The test suite in pkg/workflow/safe_outputs_tools_test.go (lines 663-708) verifies this behavior, confirming that the repo parameter appears only when allowed-repos is defined and that unauthorized repositories trigger the appropriate error handling.

Example: Multi-Repository Configuration

---
engine: copilot
safe-outputs:
  - type: create-issue
    target-repo: "myorg/main-repo"
    allowed-repos:
      - "myorg/documentation"
      - "partner-org/shared-assets"
    input:
      title: "Automated workflow report"
      body: "{{.analysis.results}}"
---

In this configuration:

  • The default target is myorg/main-repo
  • The workflow may optionally target myorg/documentation or partner-org/shared-assets by passing the repo parameter at runtime
  • Attempts to target any other repository result in error E004

Practical Configuration Examples

Minimal Single-Repository Setup

For workflows that only interact with the repository that triggered the action, omit allowed-repos to enforce strict single-repo access:

---
safe-outputs:
  - type: add-comment
    target-repo: "owner/current-repo"
    input:
      issue-number: 1
      body: "Processing complete"
---

Multi-Repository with Runtime Selection

Enable dynamic repository selection by defining allowed-repos and using the injected repo parameter:

---
engine: copilot
---
{{/* Runtime selection from allowed list */}}
{{ $targetRepo := "owner/primary-repo" }}
{{ if eq .event.action "sync-docs" }}
  {{ $targetRepo = "owner/docs-repo" }}
{{ end }}

{{ .gh.aw.safe_output "create-pull-request"
     (dict "repo" $targetRepo
           "title" "Sync documentation"
           "body" "Automated sync") }}

Global Allow-List Fallback

Combine workflow-level restrictions with safe-output specific permissions:

---
allowed-github-references:
  - "github.com/enterprise/core-repo"
  - "github.com/enterprise/shared-libs"
safe-outputs:
  - type: create-issue
    target-repo: "enterprise/core-repo"
    allowed-repos:
      - "enterprise/shared-libs"
    input:
      title: "Security alert"
---

Summary

  • Configure cross-repository safe output handlers securely by defining both target-repo (default target) and allowed-repos (permitted alternatives) in your workflow configuration.
  • The repo input parameter is automatically injected only when allowed-repos is populated, enabling runtime repository selection while maintaining security boundaries.
  • Three security layers protect against unauthorized access: runtime allow-list validation (error E004), optional global allowed-github-references restrictions, and CI conformance checks (SEC-005) enforced by scripts/check-safe-outputs-conformance.sh.
  • Source code locations: configuration struct in pkg/workflow/safe_output_builder.go (lines 17-19), compiler logic in pkg/workflow/safe_outputs_config_generation.go (lines 735-983), and validation tests in pkg/workflow/safe_outputs_tools_test.go (lines 663-708).

Frequently Asked Questions

What happens if I omit the allowed-repos field?

If you omit allowed-repos, the safe-output handler restricts operations exclusively to the repository specified in target-repo. The compiler will not inject a repo input parameter, preventing runtime selection of alternative repositories. Any attempt to specify a different repository at runtime results in error E004.

How does gh-aw enforce the allow-list at runtime?

The enforcement occurs in the generated GitHub Action step code within pkg/workflow/safe_outputs_config_generation.go. Before executing the GitHub API call, the handler validates that the requested repository matches either the target-repo or one of the entries in allowed-repos. If validation fails, the operation aborts immediately with error E004.

Can I use wildcards or patterns in allowed-repos?

No, the allowed-repos field requires explicit, fully-qualified repository names in the format owner/repo. The validation logic performs exact string matching against the allow-list. If you need to permit multiple repositories under the same organization, you must list each one explicitly or use the global allowed-github-references workflow setting for broader organizational policies.

What is error E004 and how do I resolve it?

Error E004 indicates "cross-repository operation not allowed." This error occurs when a safe-output handler attempts to interact with a repository that is neither the configured target-repo nor included in the allowed-repos list. To resolve it, either add the target repository to your safe-output's allowed-repos array or ensure your workflow targets only the permitted repositories defined in your configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →