Skip to content
Closed
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
25 changes: 25 additions & 0 deletions .design/project-log/2026-06-02-fix-test-hub-credential-leak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Fix: Test suite leaking Hub credentials (issue #123)

**Date**: 2026-06-02
**PR**: #125
**Issue**: #123

## Problem

When `go test` runs inside an agent container, tests inherit live Hub env vars. `TestInitCommand_Integration` builds and spawns a real sciontool binary that inherits these vars, causing the child process to report status to the real Hub and corrupt agent state (resetting phase to "starting"). This is how dev-issue-71b got stuck.

## Fix

1. Added `scrubHubEnv(t)` helpers using `t.Setenv` for automatic cleanup in:
- `cmd/sciontool/commands/init_test.go` (primary subprocess fix)
- `pkg/sciontool/hooks/handlers/hub_test.go` (env var hygiene)
- `pkg/sciontool/hub/client_test.go` (env var hygiene)

2. Added `filterHubEnv(env)` to explicitly strip Hub vars from subprocess environments.

3. Converted all `os.Setenv`/`os.Unsetenv` patterns to `t.Setenv` in hub-related test files for crash-safe env isolation.

## Observations

- The Hub env var list (`SCION_HUB_ENDPOINT`, `SCION_HUB_URL`, `SCION_AUTH_TOKEN`, `SCION_AGENT_ID`, `SCION_AGENT_MODE`) is defined in `pkg/sciontool/hub/client.go:45-56`. The `scrubHubEnv` helpers are inlined in each test file rather than shared, to avoid importing `testing` into production code.
- Pre-existing CI issue: `pkg/hub/resource_import_handler_test.go` has an undefined `mockRoundTripper` symbol that causes `go vet ./...` to fail — not related to this change.
47 changes: 46 additions & 1 deletion cmd/sciontool/commands/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,45 @@ import (
"testing"
)

// hubEnvVars lists the environment variables used by the Hub client.
// Leaking these to a subprocess (e.g., sciontool init) causes the child
// to talk to the real Hub and corrupt agent state. See issue #123.
var hubEnvVars = []string{
"SCION_HUB_ENDPOINT",
"SCION_HUB_URL",
"SCION_AUTH_TOKEN",
"SCION_AGENT_ID",
"SCION_AGENT_MODE",
}

// scrubHubEnv clears all Hub-related environment variables for the
// duration of the test, preventing accidental communication with a
// real Hub when tests run inside an agent container.
func scrubHubEnv(t *testing.T) {
t.Helper()
for _, key := range hubEnvVars {
t.Setenv(key, "")
}
}

// filterHubEnv returns a copy of the environment with all Hub-related
// variables removed. Use when constructing exec.Cmd.Env to prevent
// credential leakage to child processes.
func filterHubEnv(env []string) []string {
blocked := make(map[string]bool, len(hubEnvVars))
for _, key := range hubEnvVars {
blocked[key] = true
}
var filtered []string
for _, e := range env {
key, _, _ := strings.Cut(e, "=")
if !blocked[key] {
filtered = append(filtered, e)
}
}
return filtered
}

// TestInitProjectDataIsolation is a canary test that verifies sciontool source code
// does NOT import the pkg/config package, which contains project path resolution logic.
// This is a compile-time guarantee that in-container code cannot access project data paths.
Expand Down Expand Up @@ -135,14 +174,20 @@ func TestInitCommand_Integration(t *testing.T) {
t.Skip("skipping integration test in short mode")
}

// Clear Hub env vars so the subprocess cannot talk to the real Hub
// and corrupt agent state. See issue #123.
scrubHubEnv(t)

// Build sciontool if needed for integration testing
cmd := exec.Command("go", "build", "-buildvcs=false", "-o", "/tmp/sciontool-test", "../")
if err := cmd.Run(); err != nil {
t.Skipf("failed to build sciontool for integration test: %v", err)
}

// Test running a simple command
// Test running a simple command — filter Hub env vars from the
// subprocess environment as belt-and-suspenders protection.
testCmd := exec.Command("/tmp/sciontool-test", "init", "--", "echo", "hello")
testCmd.Env = filterHubEnv(os.Environ())
output, err := testCmd.CombinedOutput()
if err != nil {
t.Errorf("init command failed: %v\nOutput: %s", err, output)
Expand Down
Loading
Loading