Files
felhom-controller/controller/internal/dockerexec/dockerexec_test.go
T
admin 1040cfe225 R-35 (D4): dashboard sign-ins survive the controller's own restart; disk holds only a fingerprint
Sessions are keyed by sha256(cookie) and persisted to dashboard-sessions.json
(0600, tmp+fsync+rename) in the data dir: fingerprint, expiry, CSRF token.
Loaded in NewServer; expired rows dropped at load and save. Logout and
invalidateAllSessions (password change, claim reset) write the file at once.
Corrupt/unreadable file = start with no sessions (never fatal).

Red-proof: with load/save as no-ops the restart test fails ('the old cookie
no longer signs in'); with the raw token as the key the file test fails
('the sessions file holds the cookie value').

Also: TestR650_NoBareDockerExec skips a non-.go file that vanished mid-walk
(a parallel stacks test's update-journal.json.tmp raced it in a full run).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-08 14:49:27 +02:00

111 lines
3.8 KiB
Go

package dockerexec
import (
"context"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
)
// TestR650_RealDockerIsRefusedUnderGoTest is the decoy: a harmless-looking `docker ps` with the
// real PATH must NOT run. The consequence asserted is that Run returns the refusal, naming the
// command — and that nothing was started (ProcessState stays nil).
func TestR650_RealDockerIsRefusedUnderGoTest(t *testing.T) {
t.Setenv(OptInEnv, "")
for _, name := range []string{"docker", "docker-compose", "/usr/bin/docker"} {
cmd := Command(name, "ps", "-a")
err := cmd.Run()
if err == nil || !strings.Contains(err.Error(), "R-650") || !strings.Contains(err.Error(), "ps -a") {
t.Fatalf("%s: want an R-650 refusal naming the command, got %v", name, err)
}
if cmd.ProcessState != nil {
t.Fatalf("%s: a process was started", name)
}
c2 := CommandContext(context.Background(), name, "volume", "create", "r650-decoy")
if _, err := c2.CombinedOutput(); err == nil || !strings.Contains(err.Error(), "volume create r650-decoy") {
t.Fatalf("%s: CommandContext not refused: %v", name, err)
}
}
}
// TestR650_StubOnPathIsAllowed — a test's own fake in t.TempDir() is a seam, not Docker.
func TestR650_StubOnPathIsAllowed(t *testing.T) {
bin := t.TempDir()
if err := os.WriteFile(filepath.Join(bin, "docker"), []byte("#!/bin/sh\necho stub:$*\n"), 0o755); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", bin)
out, err := Command("docker", "ps").CombinedOutput()
if err != nil || strings.TrimSpace(string(out)) != "stub:ps" {
t.Fatalf("stub should run: out=%q err=%v", out, err)
}
}
// TestR650_OptInAndProductionAndNonDockerPassThrough — the guard refuses nothing else.
func TestR650_OptInAndProductionAndNonDockerPassThrough(t *testing.T) {
t.Setenv(OptInEnv, "1")
if err := refusal("docker", []string{"ps"}); err != nil {
t.Fatalf("opt-in must allow: %v", err)
}
t.Setenv(OptInEnv, "")
if err := refusal("du", []string{"-sb", "/"}); err != nil {
t.Fatalf("a non-docker command must pass: %v", err)
}
old := underTest
underTest = func() bool { return false }
defer func() { underTest = old }()
if err := refusal("docker", []string{"ps"}); err != nil {
t.Fatalf("the production binary must never refuse: %v", err)
}
if Command("docker", "ps").Err != nil {
t.Fatal("production Command carried an error")
}
}
// TestR650_NoBareDockerExec pins the invariant the package doc states: every production path that
// builds a docker process goes through this package. A new `exec.Command("docker", …)` in
// non-test code fails here, naming the file and line.
func TestR650_NoBareDockerExec(t *testing.T) {
root := filepath.Join("..", "..")
bare := regexp.MustCompile(`\bexec\.Command(Context)?\(([^,()]+, )?"docker`)
var hits []string
n := 0
err := filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
if err != nil {
// Another package's test, running in parallel, may create and rename a scratch file (for example
// internal/stacks/update-journal.json.tmp) between the directory read and the lstat. A file that vanished
// is not a Go source file; skip it. Seen 2026-10-08 in a full `go test ./...`.
if os.IsNotExist(err) && !strings.HasSuffix(p, ".go") {
return nil
}
return err
}
if info.IsDir() || !strings.HasSuffix(p, ".go") || strings.HasSuffix(p, "_test.go") {
return nil
}
b, err := os.ReadFile(p)
if err != nil {
return err
}
n++
for i, line := range strings.Split(string(b), "\n") {
if bare.MatchString(line) {
hits = append(hits, p+":"+strconv.Itoa(i+1)+": "+strings.TrimSpace(line))
}
}
return nil
})
if err != nil {
t.Fatal(err)
}
if n < 100 {
t.Fatalf("scope: only %d Go files walked — the sweep is not looking at the tree", n)
}
if len(hits) > 0 {
t.Fatalf("bare docker exec outside dockerexec (R-650):\n%s", strings.Join(hits, "\n"))
}
}