test: add copilot network-isolation egress smoke test - #6330
Conversation
Adds a CI-integrated smoke test that runs the Copilot engine under the AWF network-isolation topology (sandbox.agent.sudo: false, isolation: true) and asserts the egress allowlist is enforced from inside the sandbox: - egress ALLOW: api.github.com is reachable - egress DENY: example.com is blocked - agent completion: add_comment safe-output validated by post-steps Wires the new workflow into the ready-for-aw label matrix and the copilot smoke output-requirement tests. Closes the network-isolation CI coverage gap tracked in #6326 (Gap 2). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b593fd45-255a-49a2-9d21-fd78108650a2
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
There was a problem hiding this comment.
Pull request overview
Adds a CI-integrated Copilot smoke test for AWF network-isolation egress enforcement.
Changes:
- Adds allow/deny egress checks inside the sandbox.
- Validates required safe-output usage.
- Registers and compiles the new workflow.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/smoke-copilot-network-isolation.md |
Defines the smoke test. |
.github/workflows/smoke-copilot-network-isolation.lock.yml |
Compiled workflow using local AWF. |
scripts/ci/smoke-copilot-workflow.test.ts |
Registers output-requirement checks. |
scripts/ci/ready-for-aw-workflows.test.ts |
Registers activation-gating checks. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 4/4 changed files
- Comments generated: 3
- Review effort level: Medium
| - name: Validate safe outputs were invoked | ||
| run: | |
There was a problem hiding this comment.
Fixed in d8d1df9. The post-step now requires a machine-readable EGRESS_RESULT allow=<pass|fail> deny=<pass|fail> marker in the safe-output body and fails the job unless it's exactly allow=pass deny=pass. The prompt instructs the agent to emit the real observed outcome of both curl checks, so a broken allowlist (allowed host unreachable) or a leak (denied host reachable) now turns the workflow red instead of leaving it green.
| add-comment: | ||
| hide-older-comments: true | ||
| add-labels: | ||
| allowed: [smoke-copilot-network-isolation] |
There was a problem hiding this comment.
Created the smoke-copilot-network-isolation label in the repo (color ededed, matching the per-workflow label convention used by smoke-copilot-byok, smoke-copilot-pat, etc.), so add_labels no longer 422s.
| fi | ||
| # shellcheck disable=SC1003,SC2016,SC2086 | ||
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --build-local \ | ||
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log |
Explain what gVisor is (application kernel, Sentry, netstack, Systrap/KVM platforms) and how AWF runs the agent under the runsc OCI runtime: the compose executionModel, runtime field wiring, the netstack DNS workaround (extra_hosts + chroot /host/etc/hosts patching), iptables DNAT compat, and the Bun JIT shim. Includes guidance for adding other compose-model runtimes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 23717692-af7a-4e03-a156-5b696c3f01bd
Address PR review feedback on the workflow .md: - Post-step now parses a machine-readable EGRESS_RESULT marker from the safe-output body and fails the workflow unless both the allow and deny egress checks passed, instead of only asserting a safe-output tool was called. Prompt updated to require the agent to emit the marker. - The smoke-copilot-network-isolation label is created in the repo (matching the per-workflow label convention) so add_labels no longer 422s. Recompiled the lock with gh-aw v0.82.12 and re-ran the smoke postprocess. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e846bbd4-4c40-4e7b-869d-c3c4d7e6fa0f
|
✅ Copilot review passed with no inline comments. @lpcox Add the |
…n-smoke # Conflicts: # docs/gvisor-integration.md
Summary
Closes the network-isolation CI coverage gap tracked in #6326 (Gap 2).
Today every Copilot smoke test already runs under the AWF network-isolation topology (
isolation: true), but the only egress check runs on the runner (a pre-step), not inside the sandbox — and nothing asserts that a non-allowlisted host is actually blocked. So the egress-enforcement path has no automated regression coverage.This adds a dedicated CI-integrated smoke test,
smoke-copilot-network-isolation, that runs the Copilot engine under network isolation (sandbox.agent.sudo: false,isolation: true) and asserts the egress allowlist is enforced from inside the sandbox:curl https://api.github.com/zensucceeds (allowlisted)curl https://example.comis blocked (not allowlisted)add_commentsafe-output, validated bypost-stepsBoth
curlchecks are executed by the agent via thebashtool, so they exercise the real Docker-network egress model rather than the unfiltered runner.Wiring
ready-for-awlabel_command on PRs +schedule(every 12h) +workflow_dispatch, so it runs automatically in CI.ready-for-aw-workflows.test.ts(gating) andsmoke-copilot-workflow.test.ts(output-requirement) test lists.scripts/ci/postprocess-smoke-workflows.ts, so CI dogfoods the repo own AWF source (--build-local).Validation
gh aw compile— 0 errorsnpx tsx scripts/ci/postprocess-smoke-workflows.ts— lock rewritten to local buildjestready-for-aw + smoke-copilot + postprocess suites — all passnpm run type-check,markdownlint— cleanRefs #6326