Skip to content

fix(safeoutputs): prevent symlink exfiltration in create-pr Stage 3#549

Draft
Copilot wants to merge 2 commits into
mainfrom
copilot/fix-symlink-exfiltration
Draft

fix(safeoutputs): prevent symlink exfiltration in create-pr Stage 3#549
Copilot wants to merge 2 commits into
mainfrom
copilot/fix-symlink-exfiltration

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 15, 2026

Stage 3's create-pr file collection used is_file() (follows symlinks), allowing an agent to plant a symlink (e.g. ln -s /proc/self/environ secrets.txt) that would be read and uploaded to ADO as PR file content — exfiltrating SYSTEM_ACCESSTOKEN and any other Stage 3 environment secrets.

Changes

Runtime fix — symlink-blind file collection (collect_changes_from_worktree, collect_changes_from_diff_tree)

  • All 8 is_file() call sites replaced with tokio::fs::symlink_metadata() checks
  • Symlinks are skipped (with a warn! log) rather than followed; only is_file() on symlink_metadata proceeds to read_file_change
// Before (follows symlinks — vulnerable):
if full_path.is_file() {
    changes.push(read_file_change("add", file_path, &full_path).await?);
}

// After (lstat — does not follow symlinks):
match tokio::fs::symlink_metadata(&full_path).await {
    Ok(meta) if meta.file_type().is_file() => {
        changes.push(read_file_change("add", file_path, &full_path).await?);
    }
    Ok(meta) if meta.file_type().is_symlink() => {
        warn!("Skipping symlink in worktree: {} (symlink-following attack prevention)", file_path);
    }
    _ => {}
}

Belt-and-suspenders — patch validation (validate_patch_paths)

  • Added rejection of git symlink mode lines (new file mode 120000, old mode 120000, new mode 120000) before git apply is attempted

Test plan

Two new unit tests:

  • test_validate_patch_paths_symlink_rejected — a new file mode 120000 patch is rejected
  • test_validate_patch_paths_symlink_mode_change_rejected — a mode-change-to-symlink patch is rejected

cargo test — all 43 create_pr unit tests pass; full suite clean.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • spsprodeus21.vssps.visualstudio.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-3136f96127ec4700 /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-3136f96127ec4700 .0ag02syuw6fxga7uyy8gkdeoq.04b7qv3.rcgu.o .0dqxcxfrctfhbygie7bf4453v.04b7qv3.rcgu.o .0ekitu3uiyv7m2a3d2uhqo7a5.04b7qv3.rcgu.o .0fr3fpdav92c9j5l5rzozflt2.04b7qv3.rcgu.o .0oqdct0dawlu5ppp8kj2hz2tc.04b7qv3.rcgu.o .0vbn6yl3fvqqwl6lz3do6ahcx.04b7qv3.rcgu.o .13xtrqyqsed61wm8zhn8j1hjv.04b7qv3.rcgu.o .1hmtumlb9ewwrni2k8hoy0oeu.04b7qv3.rcgu.o .1khtvf86e0i1q24ff28w1acpo.04b7qv3.rcgu.o .1ztrh6pi1jfscthf06p0r33pf.04b7qv3.rcgu.o .237ycq9ev86ic9az9gs80xi0k.04b7qv3.rcgu.o .24x�� .2eic40pkfx5wikhd73nzwyiy4.04b7qv3.rcgu.o .2s1x3nf0f4deblv1kjt23ls7h.04b7qv3.rcgu.o mbols.o -a69babd28d2d3b2bash -a69babd28d2d3b2--norc -a69babd28d2d3b2--noprofile -a69babd28d2d3b2-C (dns block)
  • spsprodweu4.vssps.visualstudio.com
    • Triggering command: /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-3136f96127ec4700 /home/REDACTED/work/ado-aw/ado-aw/target/debug/deps/ado_aw-3136f96127ec4700 .0ag02syuw6fxga7uyy8gkdeoq.04b7qv3.rcgu.o .0dqxcxfrctfhbygie7bf4453v.04b7qv3.rcgu.o .0ekitu3uiyv7m2a3d2uhqo7a5.04b7qv3.rcgu.o .0fr3fpdav92c9j5l5rzozflt2.04b7qv3.rcgu.o .0oqdct0dawlu5ppp8kj2hz2tc.04b7qv3.rcgu.o .0vbn6yl3fvqqwl6lz3do6ahcx.04b7qv3.rcgu.o .13xtrqyqsed61wm8zhn8j1hjv.04b7qv3.rcgu.o .1hmtumlb9ewwrni2k8hoy0oeu.04b7qv3.rcgu.o .1khtvf86e0i1q24ff28w1acpo.04b7qv3.rcgu.o .1ztrh6pi1jfscthf06p0r33pf.04b7qv3.rcgu.o .237ycq9ev86ic9az9gs80xi0k.04b7qv3.rcgu.o .24x�� .2eic40pkfx5wikhd73nzwyiy4.04b7qv3.rcgu.o .2s1x3nf0f4deblv1kjt23ls7h.04b7qv3.rcgu.o mbols.o -a69babd28d2d3b2bash -a69babd28d2d3b2--norc -a69babd28d2d3b2--noprofile -a69babd28d2d3b2-C (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Replace is_file() (follows symlinks) with symlink_metadata() checks in
collect_changes_from_worktree and collect_changes_from_diff_tree so
that symlinks in the applied patch are silently skipped rather than
followed to arbitrary filesystem paths (e.g. /proc/self/environ).

Also add symlink mode (120000) detection to validate_patch_paths as a
belt-and-suspenders defence: patches that create or convert files to
symlinks are rejected before git apply is attempted.

Adds two unit tests covering the new rejection paths.

Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/99d4d7f5-eaea-4a34-87e3-ac34bab53ddb

Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix symlink vulnerability in create-pr patch fix(safeoutputs): prevent symlink exfiltration in create-pr Stage 3 May 15, 2026
Copilot AI requested a review from jamesadevine May 15, 2026 06:25
@jamesadevine
Copy link
Copy Markdown
Collaborator

@copilot Fix up the duplicate code here

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.

🔴 Red Team Audit — High: create-pr patch can contain symlink to exfiltrate Stage 3 write tokens

2 participants