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

> Securely configure cross-repository safe output handlers using target-repo and allowed-repos lists. Enforce runtime validation to block unauthorized requests with error E004.

- Repository: [GitHub/gh-aw](https://github.com/github/gh-aw)
- Tags: how-to-guide
- Published: 2026-02-16

---

**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`](https://github.com/github/gh-aw/blob/main/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`](https://github.com/github/gh-aw/blob/main/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`](https://github.com/github/gh-aw/blob/main/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`](https://github.com/github/gh-aw/blob/main/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`](https://github.com/github/gh-aw/blob/main/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

```yaml
---
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:

```yaml
---
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:

```yaml
---
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:

```yaml
---
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`](https://github.com/github/gh-aw/blob/main/scripts/check-safe-outputs-conformance.sh).
- Source code locations: configuration struct in [`pkg/workflow/safe_output_builder.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/safe_output_builder.go) (lines 17-19), compiler logic in [`pkg/workflow/safe_outputs_config_generation.go`](https://github.com/github/gh-aw/blob/main/pkg/workflow/safe_outputs_config_generation.go) (lines 735-983), and validation tests in [`pkg/workflow/safe_outputs_tools_test.go`](https://github.com/github/gh-aw/blob/main/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`](https://github.com/github/gh-aw/blob/main/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.