Skip to content

fix: recover toolchain env vars from $GITHUB_ENV file#1977

Merged
lpcox merged 2 commits intomainfrom
fix/github-env-toolchain-fallback
Apr 14, 2026
Merged

fix: recover toolchain env vars from $GITHUB_ENV file#1977
lpcox merged 2 commits intomainfrom
fix/github-env-toolchain-fallback

Conversation

@lpcox
Copy link
Copy Markdown
Collaborator

@lpcox lpcox commented Apr 14, 2026

Summary

Fixes toolchain environment variables (GOROOT, CARGO_HOME, JAVA_HOME, etc.) being lost when AWF runs via sudo, which strips non-standard env vars.

Problem

When actions/setup-go (or setup-java, setup-dotnet, etc.) runs before AWF, it exports GOROOT via core.exportVariable(), which writes to the $GITHUB_ENV file. The Actions runner injects these into the step env. But sudo awf --env-all strips them because sudo only preserves a small set of vars.

AWF already solved this for PATH via readGitHubPathEntries() which reads $GITHUB_PATH directly. This PR adds the equivalent for $GITHUB_ENV.

Changes

src/docker-manager.ts

  • parseGitHubEnvFile(content) — Parses $GITHUB_ENV file content. Supports both KEY=VALUE (with = in values) and heredoc (KEY<<DELIM) formats. Handles CRLF.
  • readGitHubEnvEntries() — Reads the file at $GITHUB_ENV path, returns parsed key-value map
  • TOOLCHAIN_ENV_VARS — Allowlisted set: GOROOT, CARGO_HOME, RUSTUP_HOME, JAVA_HOME, DOTNET_ROOT, BUN_INSTALL
  • Toolchain loop — Replaces 6 individual if (process.env.X) blocks with a loop that falls back to $GITHUB_ENV when process.env is empty

Tests (14 new)

  • parseGitHubEnvFile: simple values, values with =, heredoc, CRLF, mixed, empty, unterminated heredoc
  • readGitHubEnvEntries: file missing, file exists, GITHUB_ENV unset
  • Integration: GOROOT recovered from GITHUB_ENV, process.env takes priority

Security

The $GITHUB_ENV fallback is narrowly allowlisted to TOOLCHAIN_ENV_VARS only — it does NOT broadly merge all entries into the container env. This avoids reintroducing secrets or proxy settings that sudo intentionally stripped.

Closes #1958

When AWF runs via sudo, non-standard env vars like GOROOT, CARGO_HOME,
JAVA_HOME are stripped. Add readGitHubEnvEntries() to read the
$GITHUB_ENV file directly (analogous to existing readGitHubPathEntries
for $GITHUB_PATH) and use it as a fallback for toolchain variables.

Key changes:
- Add parseGitHubEnvFile() supporting KEY=VALUE and heredoc formats
- Add readGitHubEnvEntries() reading from $GITHUB_ENV file path
- Replace individual process.env checks with TOOLCHAIN_ENV_VARS loop
  that falls back to $GITHUB_ENV when process.env is empty
- 14 new tests covering parser, file reader, and integration

Closes #1958

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 14, 2026 21:22
@lpcox lpcox requested a review from Mossaka as a code owner April 14, 2026 21:22
@github-actions
Copy link
Copy Markdown
Contributor

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 85.18% 85.41% 📈 +0.23%
Statements 85.08% 85.30% 📈 +0.22%
Functions 87.85% 87.92% 📈 +0.07%
Branches 77.82% 77.95% 📈 +0.13%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 86.3% → 87.0% (+0.78%) 85.9% → 86.7% (+0.78%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 addresses toolchain environment variables (e.g., GOROOT, JAVA_HOME) being lost when AWF is invoked via sudo by recovering allowlisted values from the GitHub Actions $GITHUB_ENV file, analogous to the existing $GITHUB_PATH handling.

Changes:

  • Added $GITHUB_ENV reading/parsing helpers to recover exported env vars even if sudo strips process.env.
  • Introduced an allowlist (TOOLCHAIN_ENV_VARS) and refactored toolchain env propagation into a loop.
  • Added unit/integration-style tests covering $GITHUB_ENV parsing/reading and precedence vs process.env.
Show a summary per file
File Description
src/docker-manager.ts Adds $GITHUB_ENV parsing/reading and uses it as a fallback source for allowlisted toolchain vars when constructing container env.
src/docker-manager.test.ts Adds tests for $GITHUB_ENV parsing/reading and verifies toolchain fallback + precedence behavior.

Copilot's findings

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

@github-actions

This comment has been minimized.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@github-actions github-actions bot mentioned this pull request Apr 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

GitHub MCP#1976 "feat: add upstream corporate proxy support for self-hosted runners" / #1974 "optimize(secret-digger-claude): default threat detection to Haiku, drop version-reporting import"
Playwright — github.com title contains "GitHub"
File Write/tmp/gh-aw/agent/smoke-test-claude-24423751372.txt created
Bash — file content verified

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

🔥 Smoke Test Results — @lpcox

Test Result
GitHub MCP (list_pull_requests) ✅ PR #1976: "feat: add upstream corporate proxy support for self-hosted runners"
GitHub.com HTTP connectivity ✅ HTTP 200
File write/read ⚠️ Pre-step template vars not expanded

Overall: PARTIAL PASS — MCP and network connectivity confirmed; file test data unavailable due to unexpanded workflow template variables.

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: GitHub Actions Services Connectivity ✅

All checks passed:

Check Result
Redis PINGhost.docker.internal:6379 PONG
PostgreSQL pg_isreadyhost.docker.internal:5432 ✅ accepting connections
PostgreSQL SELECT 1 on smoketest db ✅ returned 1

Note: redis-cli was not available (no sudo), connectivity verified via Python socket.

🔌 Service connectivity validated by Smoke Services

@github-actions
Copy link
Copy Markdown
Contributor

Smoke test results:

  • feat: add upstream corporate proxy support for self-hosted runners ✅
  • optimize(secret-digger-claude): default threat detection to Haiku, drop version-reporting import ✅
  • GitHub MCP review ✅ | safeinputs-gh query ❌ | Playwright title ✅ | Tavily search ❌
  • Temp file write/read ✅ | Discussion oracle comment ❌ | Build (npm ci && npm run build) ✅
    Overall status: FAIL

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.14.1 v20.20.2
Go go1.22.12 go1.22.12

Result: Not all versions match. Go matches, but Python (3.12.13 vs 3.12.3) and Node.js (v24.14.1 vs v20.20.2) differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

Security Review: $GITHUB_ENV as a new toolchain path source

Finding — Expanded shell-injection attack surface (moderate)

Files: src/docker-manager.ts (new readGitHubEnvEntries) + containers/agent/entrypoint.sh (pre-existing, unchanged)

What the PR does: When AWF runs under sudo and a toolchain var (e.g. GOROOT) is absent from process.env (stripped by sudo), it now falls back to reading $GITHUB_ENV directly. That value is then passed into the container as AWF_GOROOT.

Where the injection lands: entrypoint.sh writes toolchain paths into a shell script via unquoted interpolation:

echo "export PATH=\"${AWF_GOROOT}/bin:\$PATH\"" >> "/host${SCRIPT_FILE}"
echo "export GOROOT=\"${AWF_GOROOT}\"" >> "/host${SCRIPT_FILE}"

If AWF_GOROOT contains a newline or unescaped ", arbitrary shell lines are injected into the generated script and executed (as awfuser inside the chroot).

Why $GITHUB_ENV expands the risk: Before this PR, toolchain values were gated by sudo's env-stripping, which acts as an implicit sanitization boundary — a prior workflow step can't inject into process.env when sudo clears it. After this PR, any workflow run: step (or third-party action) can write to $GITHUB_ENV and influence these values, including steps that process untrusted external data such as ${{ github.event.pull_request.title }}.

Suggested mitigation: Validate values read from $GITHUB_ENV before use — reject strings containing newlines, ", $, `, or other shell metacharacters. A strict allowlist (e.g. ^[a-zA-Z0-9/_.\-]+$) would be appropriate for filesystem paths:

const SAFE_PATH_RE = /^[a-zA-Z0-9/_.\-]+$/;
const rawValue = githubEnvEntries[varName];
if (rawValue && SAFE_PATH_RE.test(rawValue)) {
  environment[`AWF_${varName}`] = rawValue;
} else if (rawValue) {
  logger.warn(`Skipping unsafe value for ${varName} from $GITHUB_ENV`);
}

Note: the same sanitization should be applied to values from process.env[varName] for consistency, as the shell injection in entrypoint.sh is pre-existing for that path too.

Generated by Security Guard for issue #1977 · ● 101.3K ·

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Note (Java): Maven's default local repository directory (~/.m2/repository) was root-owned and could not be created. Worked around by using -Dmaven.repo.local=/tmp/gh-aw/agent/mvn-repo. All tests passed successfully.

Generated by Build Test Suite for issue #1977 · ● 773.4K ·

@lpcox lpcox merged commit 06b99eb into main Apr 14, 2026
53 of 55 checks passed
@lpcox lpcox deleted the fix/github-env-toolchain-fallback branch April 14, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[awf] docker-manager: GOROOT not propagated to agent container despite --env-all

2 participants