internal/arch/execwalk_test.go
Ref: Size: 4.4 KiB History
package arch
import (
"slices"
"sort"
"strings"
"testing"
)
// TestExecViolationsDetectsWrappers pins the transitive os/exec detector on a
// fake graph: a control-plane package must be flagged whether it imports
// os/exec directly OR reaches it through an internal wrapper package (the
// loophole a direct-import check misses). Allowed packages (provisioners and
// the composition root, per R6) are sanctioned exec users: reaching os/exec
// *via* them is fine, and they are themselves exempt.
func TestExecViolationsDetectsWrappers(t *testing.T) {
const m = "github.com/a73x/eitri"
graph := map[string][]string{
m + "/internal/server/api": {m + "/internal/util/execwrap", "net/http"},
m + "/internal/util/execwrap": {"os/exec", "fmt"},
m + "/internal/server/store": {m + "/internal/transport", "database/sql"},
m + "/internal/transport": {"crypto/tls"},
m + "/internal/server/hub": {"os/exec"}, // direct violation
m + "/internal/agent/reconcile": {m + "/internal/agent/cloudhv"},
m + "/internal/agent/cloudhv": {"os/exec", m + "/internal/util/spawnhelper"}, // sanctioned (allowed)
m + "/internal/util/spawnhelper": {"os/exec"},
// multi-hop: guarded -> clean intermediate -> wrapper -> os/exec
m + "/internal/server/syncsvc": {m + "/internal/util/clean"},
m + "/internal/util/clean": {m + "/internal/util/execwrap"},
}
t.Run("server: direct and wrapper violations found, clean pkg not flagged", func(t *testing.T) {
v := execViolations(graph, m, "internal/server/", nil)
if got := v[m+"/internal/server/api"]; len(got) != 1 || got[0] != m+"/internal/util/execwrap" {
t.Errorf("api must be flagged via internal/util/execwrap, got %v", got)
}
if got := v[m+"/internal/server/hub"]; len(got) != 1 || got[0] != m+"/internal/server/hub" {
t.Errorf("hub must be flagged as a direct importer, got %v", got)
}
if got := v[m+"/internal/server/syncsvc"]; len(got) != 1 || got[0] != m+"/internal/util/execwrap" {
t.Errorf("syncsvc must be flagged via the multi-hop chain, got %v", got)
}
if len(v[m+"/internal/server/store"]) != 0 {
t.Errorf("store is clean but was flagged: %v", v[m+"/internal/server/store"])
}
})
t.Run("agent: allowed package is exempt and does not taint importers", func(t *testing.T) {
allowed := map[string]bool{
m + "/internal/agent/cloudhv": true,
// internal/cli: interactive sessions must be the real OpenSSH
// client (TTY, escapes, agent forwarding); internal/cli execs it
// deliberately.
m + "/internal/cli": true,
}
v := execViolations(graph, m, "internal/agent/", allowed)
// Includes the documented design decision: spawnhelper (a non-allowed
// exec user) is reachable ONLY through cloudhv, so it is sanctioned by
// extension — code in the plane can only reach it via cloudhv's API.
if len(v) != 0 {
t.Errorf("no agent violations expected (allowed packages and their subtrees are exempt), got %v", v)
}
})
}
// execViolations returns, for every package whose module-relative path has the
// given prefix, the set of packages through which os/exec becomes reachable:
// the package itself (direct import) and/or any transitively-reached internal
// package that imports os/exec. Walking never descends into (or flags)
// packages in allowed — their exec use is sanctioned (provisioners and the
// composition root, per R6).
//
// This closes the wrapper loophole: a direct-import check misses an internal
// package that wraps os/exec and is imported by the guarded plane.
func execViolations(graph map[string][]string, module, prefix string, allowed map[string]bool) map[string][]string {
importsExec := func(pkg string) bool {
return slices.Contains(graph[pkg], "os/exec")
}
out := map[string][]string{}
for pkg := range graph {
rel := strings.TrimPrefix(pkg, module+"/")
if !strings.HasPrefix(rel, prefix) || allowed[pkg] {
continue
}
var offenders []string
if importsExec(pkg) {
offenders = append(offenders, pkg)
}
// Walk transitive internal deps, skipping sanctioned packages.
seen := map[string]bool{}
var walk func(string)
walk = func(p string) {
for _, d := range graph[p] {
if !strings.HasPrefix(d, module+"/") || seen[d] || allowed[d] {
continue
}
seen[d] = true
if importsExec(d) {
offenders = append(offenders, d)
}
walk(d)
}
}
walk(pkg)
if len(offenders) > 0 {
sort.Strings(offenders)
out[pkg] = offenders
}
}
return out
}