Skip to content

Add sink-visibility to write-sink guard policy to prevent GitLost exfiltration#8895

Merged
lpcox merged 2 commits into
mainfrom
sink-visibility-write-guard
Jul 8, 2026
Merged

Add sink-visibility to write-sink guard policy to prevent GitLost exfiltration#8895
lpcox merged 2 commits into
mainfrom
sink-visibility-write-guard

Conversation

@lpcox

@lpcox lpcox commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the sink-visibility field on the write-sink guard policy to prevent the GitLost vulnerability class — data exfiltration from private repos to public outputs via prompt injection.

The Attack

1. Attacker opens crafted issue in PUBLIC repo with prompt injection
2. Agentic workflow triggers, reads issue body
3. Injected instructions cause agent to read PRIVATE repo (agent accumulates secrecy tags)
4. Agent writes private data as PUBLIC comment via safe-outputs
5. WriteSinkGuard with accept=["*"] allows the write → data leaked

The Fix

A new sink-visibility field on write-sink guard policy:

"write-sink": {
  "accept": ["*"],
  "sink-visibility": "public"
}

When sink-visibility is "public", the WriteSinkGuard sets resource secrecy to empty — the DIFC evaluator's write check (agentSecrecy ⊆ resourceSecrecy) then fails for any agent with non-empty secrecy, blocking the exfiltration.

sink-visibility Resource Secrecy Effect
"public" [] (empty) Blocks any agent with non-empty secrecy
"private" configured accept patterns Standard subset check
"internal" configured accept patterns Same as private
(omitted) configured accept (incl. "*") Backward-compatible

Changes

  • internal/config/guard_policy.go — Add SinkVisibility field to WriteSinkPolicy struct with full documentation
  • internal/config/guard_policy_validation.go — Validate sink-visibility values (public/private/internal); case-insensitive
  • internal/guard/write_sink.goNewWriteSinkGuardWithVisibility constructor; LabelResource returns empty secrecy for public sinks
  • internal/server/guard_init.go — Pass SinkVisibility when constructing WriteSinkGuard
  • docs/CONFIGURATION.md — Full documentation of sink-visibility with examples and security rationale
  • config.example.toml — Updated examples showing sink-visibility usage
  • Tests — Comprehensive tests covering all visibility modes, backward compatibility, and the core security property

Compiler Requirement

The gh-aw compiler MUST check the safe-outputs target repository visibility and set sink-visibility accordingly. Without this, agents tricked into reading private data can exfiltrate it to public repos. The documentation makes this requirement explicit in multiple places.

…iltration

Implement the sink-visibility field on the write-sink guard policy that
declares the visibility of the safe-outputs target repository. When set to
"public", the WriteSinkGuard sets resource secrecy to EMPTY — blocking any
agent with non-empty secrecy from writing. This prevents the GitLost
vulnerability class where prompt injection causes an agent to read private
repo data and exfiltrate it to a public comment.

Changes:
- WriteSinkPolicy struct: add SinkVisibility field (public/private/internal)
- WriteSinkGuard: add NewWriteSinkGuardWithVisibility constructor
- WriteSinkGuard.LabelResource: when sink-visibility=public, resource secrecy
  is empty (blocks tainted agents); otherwise uses accept patterns
- Validation: validate sink-visibility values (public/private/internal)
- guard_init.go: pass SinkVisibility when constructing WriteSinkGuard
- Documentation: CONFIGURATION.md, config.example.toml updated with
  sink-visibility usage, security rationale, and compiler requirements
- Tests: comprehensive sink-visibility tests for guard behavior and validation

The gh-aw compiler MUST check the safe-outputs target repository visibility
and set sink-visibility accordingly to prevent data exfiltration.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 01:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the DIFC write-sink guard against the “GitLost” exfiltration pattern by introducing a sink-visibility policy field, allowing public sinks to be treated as having empty resource secrecy so tainted (non-empty secrecy) agents are blocked from writing.

Changes:

  • Add sink-visibility to write-sink guard policy config and validate allowed values (public|private|internal, case-insensitive).
  • Extend the WriteSinkGuard with visibility-aware behavior; public leaves resource secrecy empty to block tainted agents.
  • Add test coverage for public/private/internal/unset modes and update user-facing configuration documentation/examples.
Show a summary per file
File Description
internal/server/guard_init.go Passes configured SinkVisibility into the write-sink guard constructor and improves DIFC logging context.
internal/guard/write_sink.go Adds visibility-aware constructor + logic to enforce “public sink blocks tainted agents” via empty resource secrecy.
internal/guard/write_sink_test.go Adds sink-visibility-focused tests covering the core security property and backward compatibility.
internal/config/guard_policy.go Extends WriteSinkPolicy with SinkVisibility and documents semantics/requirements.
internal/config/guard_policy_validation.go Validates sink-visibility values (case-insensitive) when provided.
internal/config/config_guardpolicies_test.go Adds validation tests for sink-visibility accepted/rejected values.
docs/CONFIGURATION.md Documents sink-visibility, rationale, and examples for public/private targets.
config.example.toml Updates example configuration guidance to include sink-visibility usage.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment thread internal/config/config_guardpolicies_test.go Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@lpcox
lpcox merged commit f5bd3cf into main Jul 8, 2026
26 checks passed
@lpcox
lpcox deleted the sink-visibility-write-guard branch July 8, 2026 02:10
lpcox added a commit that referenced this pull request Jul 8, 2026
## Summary

Adds runtime repo visibility verification as defense-in-depth for the
sink-visibility feature (#8895), plus comprehensive integration tests.

## Runtime Verification

At gateway startup, when a write-sink guard has `sink-visibility`
configured, the gateway now verifies the actual repo visibility via `GET
/repos/{owner}/{repo}`:

- If the API reports the repo is **public** but config says
private/internal → **overrides to "public"** with warning:
  ```
SINK VISIBILITY OVERRIDE: configured="private" but runtime check shows
repo test-owner/test-repo is "public" — overriding to "public" to
prevent potential data exfiltration
  ```
- Catches repos made public **after** workflow compilation (stale
config)
- Gracefully skipped when `GITHUB_REPOSITORY` or GitHub token
unavailable
- Falls back to configured value on API errors (non-fatal)
- Never relaxes: if configured as "public" but repo is actually private,
keeps "public"

## Integration Tests

7 test groups with 20+ subtests covering:

| Test | What it verifies |
|------|-----------------|
| `ConfigAccepted` | Gateway starts with valid sink-visibility values
(public, private, internal, omitted, case-insensitive) |
| `InvalidValue` | Invalid values cause noop guard fallback |
| `RuntimeOverride` | Override warning emitted when repo is more public
than configured |
| `RuntimeCheckSkippedWithoutRepo` | Graceful skip when
GITHUB_REPOSITORY unset |
| `RuntimeCheckSkippedWithoutToken` | Graceful skip when no token
available |
| `RuntimeCheckAPIFailure` | Graceful fallback on API 500 errors |
| `WriteSinkGuardLogsSinkVisibility` | Effective visibility logged at
startup |

## Files

- `internal/githubhttp/visibility.go` — `FetchRepoVisibility` and
`VerifySinkVisibility` functions
- `internal/githubhttp/visibility_test.go` — Unit tests for the
visibility API client
- `internal/server/guard_init.go` — `verifySinkVisibilityAtRuntime`
method wired into guard creation
- `test/integration/sink_visibility_test.go` — Integration tests using
mock HTTP backends + mock GitHub API
- `docs/CONFIGURATION.md` — Added "Runtime Verification" documentation
section

## Related

- #8895 — Initial sink-visibility implementation
- github/gh-aw#44153 — Compiler issue to emit sink-visibility at compile
time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants