Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,382 changes: 1,382 additions & 0 deletions .github/workflows/smoke-bounded-agents-gvisor.lock.yml

Large diffs are not rendered by default.

121 changes: 121 additions & 0 deletions .github/workflows/smoke-bounded-agents-gvisor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
name: Smoke Bounded Agents gVisor
description: End-to-end smoke test for finite-schema gVisor bounded-agent enclaves
on:
schedule: every 12h
workflow_dispatch:
permissions:
contents: read
copilot-requests: write
env:
GH_TOKEN: ${{ github.token }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
engine:
id: copilot
version: 1.0.34
network:
allowed:
- defaults
- github
tools:
github:
toolsets: [context]
allowed: []
sandbox:
agent:
id: awf
version: v0.28.0
args:
- --build-local
steps:
- name: Build unreleased AWF
run: |
npm ci
npm run build
pre-agent-steps:
- name: Install gVisor
run: |
set -euo pipefail
arch="$(uname -m)"
url="https://storage.googleapis.com/gvisor/releases/release/20250707.0/${arch}"
curl -fsSL "${url}/runsc" -o "$RUNNER_TEMP/runsc"
curl -fsSL "${url}/runsc.sha512" -o "$RUNNER_TEMP/runsc.sha512"
(cd "$RUNNER_TEMP" && sha512sum -c runsc.sha512)
sudo install -m 755 "$RUNNER_TEMP/runsc" /usr/local/bin/runsc
sudo runsc install
sudo systemctl restart docker
docker info --format '{{json .Runtimes}}' | grep -F '"runsc"'
- name: Replace release bootstrap with current AWF build
run: |
mkdir -p "$HOME/.local/bin"
printf '#!/bin/bash\nexec "%s" "%s/dist/cli.js" "$@"\n' \
"$(command -v node)" "$GITHUB_WORKSPACE" > "$HOME/.local/bin/awf"
chmod +x "$HOME/.local/bin/awf"
node <<'NODE'
const fs = require("fs");
const file = `${process.env.RUNNER_TEMP}/gh-aw/awf-config.json`;
const config = JSON.parse(fs.readFileSync(file, "utf8"));
config.apiProxy = { ...(config.apiProxy || {}), targets: { openai: {} } };
config.boundedAgents = {
enabled: true,
privateRepos: [{ repo: "github/gh-aw", sensitivity: "internal" }],
runtime: "gvisor",
profile: "openai",
model: "gpt-4o-mini",
memoryLimit: "512m"
};
fs.writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
NODE
safe-outputs:
threat-detection:
enabled: false
timeout-minutes: 30
strict: false
concurrency:
group: smoke-bounded-agents-gvisor
cancel-in-progress: false
post-steps:
- name: Validate gVisor bounded-agent invocation
if: always()
env:
AUDIT_LOG: /tmp/gh-aw/sandbox/firewall/audit/bounded-agent.jsonl
TELEMETRY_LOG: /tmp/gh-aw/sandbox/firewall/audit/bounded-agent-runtime.jsonl
OUTPUTS_FILE: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
run: |
node - "$AUDIT_LOG" "$TELEMETRY_LOG" "$OUTPUTS_FILE" <<'NODE'
const fs = require("fs");
const [auditPath, telemetryPath, outputsPath] = process.argv.slice(2);
const read = (file) => fs.readFileSync(file, "utf8").trim().split("\n")
.filter(Boolean).map((line) => JSON.parse(line));
const invocations = read(auditPath).filter((record) =>
record.kind === "invocation" && record.sensitivity === "internal");
if (invocations.length !== 1 || invocations[0].outcome !== "ok") {
throw new Error(`expected one successful bounded-agent invocation, found ${invocations.length}`);
}
const successes = read(telemetryPath).filter((record) =>
record.primaryBackend === "docker" &&
record.boundedAgentBackend === "gvisor" &&
record.lifecycleClass === "invocation" &&
record.category === "success");
if (successes.length !== 1) {
throw new Error(`expected one successful gVisor telemetry record, found ${successes.length}`);
}
const outputs = fs.readFileSync(outputsPath, "utf8");
if (!outputs.includes('"noop"') || !outputs.includes("PASS")) {
throw new Error("agent did not report PASS through noop");
}
NODE
---

# Smoke Test: gVisor Bounded Agent

Use the generated `bounded-agent` skill exactly once to answer this boolean
question about `github/gh-aw`: does the repository root contain a `go.mod`
file?

Use a boolean schema. Do not use GitHub tools, network requests, shell commands,
or the current checkout to answer. The test passes only when a fresh gVisor
enclave returns `true`.

Call `noop` with `PASS true` only when the result is true. Otherwise call
`safeoutputs-missing_data`. Never report failure through `noop`.
1,369 changes: 1,369 additions & 0 deletions .github/workflows/smoke-bounded-agents.lock.yml

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions .github/workflows/smoke-bounded-agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
name: Smoke Bounded Agents
description: End-to-end smoke test for finite-schema Docker bounded-agent enclaves
on:
schedule: every 12h
workflow_dispatch:
permissions:
contents: read
copilot-requests: write
env:
GH_TOKEN: ${{ github.token }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
engine:
id: copilot
version: 1.0.34
network:
allowed:
- defaults
- github
tools:
github:
toolsets: [context]
allowed: []
sandbox:
agent:
id: awf
version: v0.28.0
args:
- --build-local
steps:
- name: Build unreleased AWF
run: |
npm ci
npm run build
pre-agent-steps:
- name: Replace release bootstrap with current AWF build
run: |
mkdir -p "$HOME/.local/bin"
printf '#!/bin/bash\nexec "%s" "%s/dist/cli.js" "$@"\n' \
"$(command -v node)" "$GITHUB_WORKSPACE" > "$HOME/.local/bin/awf"
chmod +x "$HOME/.local/bin/awf"
node <<'NODE'
const fs = require("fs");
const file = `${process.env.RUNNER_TEMP}/gh-aw/awf-config.json`;
const config = JSON.parse(fs.readFileSync(file, "utf8"));
config.apiProxy = { ...(config.apiProxy || {}), targets: { openai: {} } };
config.boundedAgents = {
enabled: true,
privateRepos: [{ repo: "github/gh-aw", sensitivity: "internal" }],
runtime: "docker",
profile: "openai",
model: "gpt-4o-mini",
memoryLimit: "512m"
};
fs.writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
NODE
safe-outputs:
threat-detection:
enabled: false
timeout-minutes: 20
strict: false
concurrency:
group: smoke-bounded-agents
cancel-in-progress: false
post-steps:
- name: Validate bounded-agent invocation
if: always()
env:
AUDIT_LOG: /tmp/gh-aw/sandbox/firewall/audit/bounded-agent.jsonl
OUTPUTS_FILE: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
run: |
node - "$AUDIT_LOG" "$OUTPUTS_FILE" <<'NODE'
const fs = require("fs");
const [auditPath, outputsPath] = process.argv.slice(2);
const records = fs.readFileSync(auditPath, "utf8").trim().split("\n")
.filter(Boolean).map((line) => JSON.parse(line));
const invocations = records.filter((record) =>
record.kind === "invocation" && record.sensitivity === "internal");
if (invocations.length !== 1 || invocations[0].outcome !== "ok") {
throw new Error(`expected one successful bounded-agent invocation, found ${invocations.length}`);
}
const serialized = JSON.stringify(records);
if (serialized.includes("github/gh-aw") || serialized.includes("SECURITY.md")) {
throw new Error("protected audit disclosed repository-derived content");
}
const outputs = fs.readFileSync(outputsPath, "utf8");
if (!outputs.includes('"noop"') || !outputs.includes("PASS")) {
throw new Error("agent did not report PASS through noop");
}
NODE
---

# Smoke Test: Docker Bounded Agent

Use the generated `bounded-agent` skill exactly once to answer this boolean
question about `github/gh-aw`: does the repository root contain a `go.mod`
file?

Use a boolean schema. Do not use GitHub tools, network requests, shell commands,
or the current checkout to answer. The test passes only when the bounded agent
returns `true`.

Call `noop` with `PASS true` only when the result is true. Otherwise call
`safeoutputs-missing_data`. Never report failure through `noop`.
93 changes: 93 additions & 0 deletions .github/workflows/test-bounded-agent-runtime-matrix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Bounded-Agent Runtime Matrix

on:
pull_request:
paths:
- 'containers/bounded-agent/**'
- 'scripts/ci/report-bounded-agent-runtime-matrix*'
- 'scripts/ci/probe-bounded-agent-primary-sbx.js'
- 'scripts/ci/smoke-bounded-agent-enclave.sh'
- 'src/bounded-agent/**'
- '.github/workflows/test-bounded-agent-runtime-matrix.yml'
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
docker:
name: Docker enclave matrix
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- run: npm ci
- run: npm run build
- name: Report all nine cells and require Docker/Docker
run: node scripts/ci/report-bounded-agent-runtime-matrix.js --require docker/docker
- name: Run live Docker enclave smoke
run: bash scripts/ci/smoke-bounded-agent-enclave.sh docker

gvisor:
name: gVisor enclave matrix
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install registered runsc
run: |
set -euo pipefail
arch="$(uname -m)"
url="https://storage.googleapis.com/gvisor/releases/release/20250707.0/${arch}"
curl -fsSL "${url}/runsc" -o "$RUNNER_TEMP/runsc"
curl -fsSL "${url}/runsc.sha512" -o "$RUNNER_TEMP/runsc.sha512"
(cd "$RUNNER_TEMP" && sha512sum -c runsc.sha512)
sudo install -m 755 "$RUNNER_TEMP/runsc" /usr/local/bin/runsc
sudo runsc install
sudo systemctl restart docker
docker info --format '{{json .Runtimes}}' | grep -F '"runsc"'
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- run: npm ci
- run: npm run build
- name: Report all nine cells and require Docker/gVisor
run: node scripts/ci/report-bounded-agent-runtime-matrix.js --require docker/gvisor
- name: Run live gVisor enclave smoke
run: bash scripts/ci/smoke-bounded-agent-enclave.sh gvisor

sbx-capability:
name: Docker Sandbox capability gate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- run: npm ci
- run: npm run build
- name: Require explicit blocked result without fallback
run: |
set -euo pipefail
report="$(node scripts/ci/report-bounded-agent-runtime-matrix.js)"
printf '%s\n' "$report"
rows="$(printf '%s\n' "$report" | grep -cE '^\| (docker|gvisor|sbx) \|')"
test "$rows" -eq 9
printf '%s\n' "$report" | grep -F '| docker | sbx | BLOCKED |'
printf '%s\n' "$report" | grep -F '| gvisor | sbx | BLOCKED |'
printf '%s\n' "$report" | grep -F '| sbx | sbx | BLOCKED |'
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ See [GitHub Actions](docs/github_actions.md) for advanced setup and `awf logs su
- [AWF config schema](docs/awf-config.schema.json) — machine-readable JSON Schema for JSON/YAML configs (also published as a [versioned release asset](https://github.com/github/gh-aw-firewall/releases/latest/download/awf-config.schema.json) for IDE autocomplete)
- [AWF config spec](docs/awf-config-spec.md) — normative processing and precedence rules for tooling/compiler integration
- [Bounded queries](docs/bounded-queries.md) — run information-budgeted queries against private repositories without exposing their contents
- [Bounded agents](docs/bounded-agents.md) — delegate finite-schema repository analysis to API-proxy-only Docker or gVisor enclaves
- [Audit log schema](schemas/audit.schema.json) — JSON Schema for L7 traffic audit records (`audit.jsonl`)
- [Token usage schema](schemas/token-usage.schema.json) — JSON Schema for per-call token usage records (`token-usage.jsonl`)
- [Schemas README](schemas/README.md) — versioning policy, record identification, and validation examples
Expand Down
Loading
Loading