Skip to content

Commit feec51b

Browse files
Harden manifest boundary guard: Flux, Git library/exec, recursion (#5)
Close coverage gaps found in review of the boundary conformance guard: - Flux was effectively unguarded: `\bflux\b` never matches the tokens Flux actually carries (`github.com/fluxcd/...`, `*.toolkit.fluxcd.io`, `kind: GitRepository`). Match `fluxcd` and the GitRepository kind. - Git integration via the go-git library or an exec of the git binary slipped past a pattern that only matched the prose "git clone". Match `go-git` and `exec.Command[Context]("git"...)`. - Runtime scan was non-recursive, so a future subpackage would silently escape. Enumerate compiled runtime source recursively via a testable helper, excluding test files and toolchain-ignored dirs. Add behavior tests pinning the detector to the real integration vectors (plus benign lines that must not trip it) and proving the enumerator recurses while filtering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 55ab657 commit feec51b

1 file changed

Lines changed: 134 additions & 14 deletions

File tree

‎conformance_test.go‎

Lines changed: 134 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"io/fs"
55
"os"
6+
"path/filepath"
67
"regexp"
78
"strconv"
89
"strings"
@@ -14,24 +15,34 @@ import (
1415
// renders deterministic Kubernetes output from normalized inputs; how that
1516
// output is transported, reviewed, or reconciled belongs to a separate
1617
// promotion driver, not here. Each entry is matched case-insensitively.
18+
//
19+
// Patterns target the tokens these concepts actually carry in Go source and
20+
// Kubernetes manifests — import paths, API groups, resource kinds, and
21+
// process invocations — not the plain-English word, which real integrations
22+
// never emit. "flux" appears only as "fluxcd" (the module path and the
23+
// *.toolkit.fluxcd.io API groups); Git integration arrives as the go-git
24+
// library or an exec of the git binary, never as the prose "git clone".
1725
var transportOwnershipPatterns = []*regexp.Regexp{
1826
regexp.MustCompile(`(?i)gitops`),
1927
regexp.MustCompile(`(?i)repourl`),
2028
regexp.MustCompile(`(?i)targetrevision`),
2129
regexp.MustCompile(`(?i)appproject`),
2230
regexp.MustCompile(`(?i)argocd`),
2331
regexp.MustCompile(`(?i)argoproj`),
24-
regexp.MustCompile(`(?i)\bflux\b`),
32+
regexp.MustCompile(`(?i)fluxcd`),
2533
regexp.MustCompile(`(?i)kubeconfig`),
2634
regexp.MustCompile(`(?i)reconcil`),
35+
regexp.MustCompile(`(?i)go-git`),
36+
regexp.MustCompile(`(?i)exec\.Command(?:Context)?\([^"]*"git"`),
2737
regexp.MustCompile(`(?i)\bgit\s+(clone|push|commit|checkout|fetch|remote|tag|init|pull)\b`),
2838
regexp.MustCompile(`(?i)\bpull\s+request\b`),
2939
regexp.MustCompile(`(?i)kind:\s*Application\b`),
3040
regexp.MustCompile(`(?i)kind:\s*AppProject\b`),
41+
regexp.MustCompile(`(?i)kind:\s*GitRepository\b`),
3142
}
3243

33-
// scan reports every line in content that matches a forbidden pattern,
34-
// formatted as "<name>:<line>: <trimmed source>".
44+
// scanTransportOwnership reports every line in content that matches a
45+
// forbidden pattern, formatted as "<name>:<line>: <trimmed source>".
3546
func scanTransportOwnership(name, content string) []string {
3647
var hits []string
3748
for i, line := range strings.Split(content, "\n") {
@@ -45,22 +56,49 @@ func scanTransportOwnership(name, content string) []string {
4556
return hits
4657
}
4758

59+
// isIgnoredSourceDir reports whether a directory is excluded from a module's
60+
// compiled runtime source. This mirrors the Go toolchain's own rule:
61+
// directories named "testdata" and those beginning with "." or "_" are never
62+
// built.
63+
func isIgnoredSourceDir(name string) bool {
64+
return name == "testdata" || strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_")
65+
}
66+
67+
// runtimeGoFiles returns every compiled runtime Go file under root: all
68+
// non-test .go files, recursively, excluding toolchain-ignored directories.
69+
// Test files are excluded because boundary guards (this file included) name
70+
// the forbidden concepts as data.
71+
func runtimeGoFiles(root string) ([]string, error) {
72+
var files []string
73+
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
74+
if err != nil {
75+
return err
76+
}
77+
if d.IsDir() {
78+
if path != root && isIgnoredSourceDir(d.Name()) {
79+
return fs.SkipDir
80+
}
81+
return nil
82+
}
83+
if strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go") {
84+
files = append(files, path)
85+
}
86+
return nil
87+
})
88+
return files, err
89+
}
90+
4891
// TestRuntimeSourceIsTransportNeutral enforces the manifest-bundle boundary
4992
// on this plugin's runtime source: it must not integrate with Git
5093
// repositories, GitHub/pull requests, Argo CD/Flux reconcilers, kubeconfig,
5194
// or own reconciliation objects (repoURL, targetRevision, Application,
52-
// AppProject). Test files are excluded — this guard itself names the
53-
// forbidden concepts.
95+
// AppProject). The scan is recursive so a future subpackage cannot escape it.
5496
func TestRuntimeSourceIsTransportNeutral(t *testing.T) {
55-
entries, err := os.ReadDir(".")
97+
files, err := runtimeGoFiles(".")
5698
if err != nil {
57-
t.Fatalf("read package dir: %v", err)
99+
t.Fatalf("enumerate runtime source: %v", err)
58100
}
59-
for _, e := range entries {
60-
name := e.Name()
61-
if e.IsDir() || !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
62-
continue
63-
}
101+
for _, name := range files {
64102
src, err := os.ReadFile(name)
65103
if err != nil {
66104
t.Fatalf("read %s: %v", name, err)
@@ -73,8 +111,8 @@ func TestRuntimeSourceIsTransportNeutral(t *testing.T) {
73111

74112
// TestOwnedManifestsAreTransportNeutral enforces the same boundary on the
75113
// plugin-owned deployment templates: they carry only workload/resources,
76-
// never reconciliation control-plane objects (Argo Application/AppProject)
77-
// or repository source bindings (repoURL, targetRevision).
114+
// never reconciliation control-plane objects (Argo Application/AppProject,
115+
// Flux GitRepository) or repository source bindings (repoURL, targetRevision).
78116
func TestOwnedManifestsAreTransportNeutral(t *testing.T) {
79117
err := fs.WalkDir(deploymentFS, ".", func(path string, d fs.DirEntry, err error) error {
80118
if err != nil {
@@ -96,3 +134,85 @@ func TestOwnedManifestsAreTransportNeutral(t *testing.T) {
96134
t.Fatalf("walk deployment templates: %v", err)
97135
}
98136
}
137+
138+
// TestScanTransportOwnershipDetectsRealVectors pins the detector to the forms
139+
// transport ownership actually takes in Go source and manifests — library
140+
// imports, API groups, resource kinds, and process invocations — and to a set
141+
// of benign lines that must not trip it.
142+
func TestScanTransportOwnershipDetectsRealVectors(t *testing.T) {
143+
forbidden := []string{
144+
`_ "github.com/fluxcd/pkg/apis/meta"`,
145+
`apiVersion: source.toolkit.fluxcd.io/v1`,
146+
`kind: GitRepository`,
147+
`_ "github.com/argoproj/argo-cd/v2/pkg/apis"`,
148+
`import gogit "github.com/go-git/go-git/v5"`,
149+
`r, _ := exec.Command("git", "clone", url).Output()`,
150+
`out, _ := exec.CommandContext(ctx, "git", "push").CombinedOutput()`,
151+
`kind: Application`,
152+
`spec: { repoURL: https://example.com, targetRevision: main }`,
153+
`// reconcile the cluster state`,
154+
}
155+
for _, line := range forbidden {
156+
if hits := scanTransportOwnership("x.go", line); hits == nil {
157+
t.Errorf("expected forbidden line to be flagged: %q", line)
158+
}
159+
}
160+
161+
benign := []string{
162+
`import _ "github.com/codefly-dev/core/agents/services"`,
163+
`s.Wool.Debug("git status is clean")`,
164+
`kind: Deployment`,
165+
`kind: Service`,
166+
`newTag: {{.Image.Tag}}`,
167+
`// influx of requests handled by the digit parser`,
168+
}
169+
for _, line := range benign {
170+
if hits := scanTransportOwnership("x.go", line); hits != nil {
171+
t.Errorf("benign line falsely flagged: %q -> %v", line, hits)
172+
}
173+
}
174+
}
175+
176+
// TestRuntimeGoFilesRecursesAndFilters proves the enumerator descends into
177+
// subpackages (so no future package escapes the boundary check) while
178+
// excluding test files and toolchain-ignored directories.
179+
func TestRuntimeGoFilesRecursesAndFilters(t *testing.T) {
180+
root := t.TempDir()
181+
write := func(rel string) {
182+
p := filepath.Join(root, rel)
183+
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
184+
t.Fatal(err)
185+
}
186+
if err := os.WriteFile(p, []byte("package p\n"), 0o644); err != nil {
187+
t.Fatal(err)
188+
}
189+
}
190+
write("top.go")
191+
write("sub/pkg/deep.go")
192+
write("sub/pkg/deep_test.go")
193+
write("testdata/fixture.go")
194+
write(".hidden/skip.go")
195+
196+
files, err := runtimeGoFiles(root)
197+
if err != nil {
198+
t.Fatalf("runtimeGoFiles: %v", err)
199+
}
200+
got := map[string]bool{}
201+
for _, f := range files {
202+
rel, _ := filepath.Rel(root, f)
203+
got[filepath.ToSlash(rel)] = true
204+
}
205+
206+
want := []string{"top.go", "sub/pkg/deep.go"}
207+
for _, w := range want {
208+
if !got[w] {
209+
t.Errorf("expected %s to be scanned, got %v", w, files)
210+
}
211+
}
212+
excluded := []string{"sub/pkg/deep_test.go", "testdata/fixture.go", ".hidden/skip.go"}
213+
for _, e := range excluded {
214+
if got[e] {
215+
t.Errorf("%s should have been excluded from the scan", e)
216+
}
217+
}
218+
}

0 commit comments

Comments
 (0)