feat(allowedsymbols): replace AST checker internals with go/analysis.Analyzer#160
feat(allowedsymbols): replace AST checker internals with go/analysis.Analyzer#160
Conversation
…nternals Replace the duplicated AST-walking logic in symbols_test.go with shared helper functions in a new analyzer.go file that also exposes a proper go/analysis.Analyzer (NewAnalyzer) for go vet integration. - Add allowedsymbols/analyzer.go with NewAnalyzer, buildAllowlistSets, checkFileImports, checkFileSelectors, reportUnused, and fileLineReporter helpers - Rewrite checkAllowedSymbols in symbols_test.go to use those helpers instead of its own inline AST walking; all existing test signatures and behaviors are preserved - Add golang.org/x/tools v0.43.0 as a direct dependency All 35 existing tests continue to pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… helper
Three code-quality fixes to analyzer.go and symbols_test.go:
- Replace interface{} with any in the NewAnalyzer run func signature
- Remove unused listName parameter from reportUnused; callers already
embed the list name in the closure message they pass in
- Move fileLineReporter from analyzer.go (production code) into
symbols_test.go where it is actually used; keeps production API clean
All 35 tests continue to pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
02fa95b to
35d8b77
Compare
The new allowedsymbols package imports golang.org/x/tools, which was added to go.mod but missing from the third-party license file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
thieman
left a comment
There was a problem hiding this comment.
Review: allowedsymbols — go/analysis.Analyzer + checker refactor
This PR adds a go/analysis.Analyzer implementation (NewAnalyzer / AnalyzerConfig) alongside a refactor that extracts the core checking logic from the test-only checkAllowedSymbols into shared helpers (buildAllowlistSets, checkFileImports, checkFileSelectors, reportUnused) usable by both the test harness and the new analyzer. All existing tests pass; the package compiles cleanly with go vet and the race detector. License and dependency bookkeeping are up to date.
Overall the extraction is clean and the refactoring is safe. One functional bug in the new analyzer and one correctness gap deserve attention before this is wired up to go vet.
Findings
P1 — inspect.Analyzer declared in Requires but never used
analyzer.go line 71 adds inspect.Analyzer to Requires, which tells the go/analysis framework to run the inspect pass first and make its *inspector.Inspector result available via pass.ResultOf[inspect.Analyzer]. However, the Run function never calls pass.ResultOf — it uses ast.Inspect (stdlib) directly, just as the test harness does.
This is a latent bug: the unused Requires entry imposes unnecessary ordering overhead on the analysis framework, and the doc comment on lines 37-38 falsely describes the implementation as using "the inspect pass for efficient AST traversal." If the intent was to use inspector.Inspector's filtered pre-order walk, the pass.ResultOf call is missing; if the intent is to keep ast.Inspect, the inspect.Analyzer dependency and import should be removed.
P2 — NewAnalyzer is exported but has zero call sites and zero tests
NewAnalyzer and AnalyzerConfig are the primary deliverables of this PR (the package doc says it "integrates with go vet"), but there are no tests that call NewAnalyzer or run it via analysistest.Run. The unique code path at line 59 (pass.Files[0].Package position for unused-symbol reporting) is not exercised by any existing test. There is also no cmd/ entry point or Makefile target that wires the analyzer into go vet. The README does not mention how to invoke it.
Since the public API is untested and not yet integrated, there is a risk the go/analysis path diverges silently from the test harness as the codebase evolves.
P2 — Silent skip on malformed allowlist entry in buildAllowlistSets diverges from test harness
buildAllowlistSets silently skips entries where dot <= 0 (line 84 of analyzer.go). The test harness calls buildAllowlistSets first and then separately validates entries with t.Fatalf (lines 59-64 of symbols_test.go). Through the analyzer path (NewAnalyzer), a malformed entry is silently dropped with no diagnostic, so a maintainer who makes a typo would get no error and the entry would simply not be enforced.
P3 — Package doc comment overstates current integration state
The package-level doc comment says the package "integrates with go vet" and reports "file:line:col diagnostics." This is accurate only if NewAnalyzer is wired into a go vet/golangci-lint pipeline, which this PR does not do. The README correctly describes the implementation as test-based AST analysis. Consider aligning the package doc with reality until integration is completed.
P3 — checkFileImports doc comment refers to caller by filename
Lines 98-102 of analyzer.go name symbols_test.go as an example caller in the doc comment. This tight coupling will rot as the code evolves; prefer describing the contract (pos == token.NoPos means no position prefix) without referencing a specific caller.
Code path coverage table
| Code path | Covered? |
|---|---|
buildAllowlistSets — normal entries |
yes (test harness + verification tests) |
buildAllowlistSets — malformed entry (dot <= 0) |
test harness: t.Fatalf; analyzer: silently dropped, not tested |
checkFileImports — permanently banned (exact) |
yes (verification test) |
checkFileImports — permanently banned (prefix) |
yes (verification test) |
checkFileImports — blank/dot import |
yes (verification test) |
checkFileImports — package not in allowlist |
yes (verification test) |
checkFileImports — exempt import |
yes (verification test) |
checkFileImports — aliased import |
yes (verification test) |
checkFileSelectors — unlisted symbol |
yes (verification test) |
reportUnused — unused symbol detected |
yes (verification test) |
NewAnalyzer — any invocation |
not tested |
NewAnalyzer Run — unused-symbol position |
not tested |
NewAnalyzer Run — malformed allowlist entry |
not tested, silently dropped |
Security assessment
This PR touches only the allowedsymbols analysis tooling — it is not a builtin command, does not run during shell execution, and has no impact on the sandbox at runtime. The refactoring is conservative: behavior of the test harness path is preserved (verified by all existing tests passing under -race). No new os.Open, os/exec, network, or unsafe code is introduced. The golang.org/x/tools dependency is a well-known, widely-audited Go module used exclusively in this analysis-time package.
No sandbox-integrity or security concerns identified.
| return &analysis.Analyzer{ | ||
| Name: "allowedsymbols", | ||
| Doc: "enforces symbol-level import restrictions via an allowlist", | ||
| Requires: []*analysis.Analyzer{inspect.Analyzer}, |
There was a problem hiding this comment.
inspect.Analyzer is declared in Requires but its result is never retrieved via pass.ResultOf[inspect.Analyzer] — the code uses ast.Inspect (stdlib) directly throughout. This adds unnecessary framework overhead and the doc comment on lines 37-38 incorrectly describes the implementation as using the inspect pass for efficient AST traversal.
Either remove the inspect import and Requires entry (keeping ast.Inspect), or switch to pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) and use inspector.Preorder for actual efficiency gains.
There was a problem hiding this comment.
[Claude Sonnet 4.6] Fixed — removed inspect.Analyzer from Requires and the unused inspect import. Updated the doc comment to accurately describe the ast.Inspect-based traversal. (commit a437e30)
| allowedPkgs := make(map[string]bool) | ||
| for _, entry := range symbols { | ||
| dot := strings.LastIndexByte(entry, '.') | ||
| if dot <= 0 { |
There was a problem hiding this comment.
buildAllowlistSets silently skips malformed entries (dot <= 0) when called from the analyzer path. The test harness catches this with t.Fatalf, but the NewAnalyzer Run function has no equivalent guard, so a malformed allowlist entry would be silently dropped and not enforced. Consider adding a diagnostic or returning an error when encountering a malformed entry through the analyzer path.
There was a problem hiding this comment.
[Claude Sonnet 4.6] Fixed — NewAnalyzer now panics at construction time if any allowlist entry is missing a dot separator, matching the test-harness t.Fatalf behaviour. Errors surface immediately rather than being silently dropped by buildAllowlistSets. (commit a437e30)
| // import restrictions described by cfg. The analyzer uses the inspect pass for | ||
| // efficient AST traversal and reports violations via pass.Reportf so they | ||
| // appear as go vet diagnostics with proper file:line:col positions. | ||
| func NewAnalyzer(cfg AnalyzerConfig) *analysis.Analyzer { |
There was a problem hiding this comment.
NewAnalyzer has no tests via analysistest.Run or any other mechanism. The unique code paths in the Run function (especially the pass.Files[0].Package position used for unused-symbol reporting at line 59) are not covered by any existing test. Consider adding at least one analysistest-based test before this API is considered production-ready.
There was a problem hiding this comment.
[Claude Sonnet 4.6] Fixed — added analyzer_test.go with six analysistest.Run-based tests covering: metadata (correct Name, empty Requires), malformed-entry panic, allowed imports (no diagnostics), banned imports, disallowed imports, and unused allowlist symbols. (commit a437e30)
…dd tests Three issues from code review: 1. inspect.Analyzer was declared in Requires but never used — the run function uses ast.Inspect directly. Remove the Requires entry and the inspect import to avoid unnecessary framework overhead. 2. Malformed allowlist entries (no dot separator) were silently dropped by buildAllowlistSets in the analyzer path, unlike the test harness which calls t.Fatalf. NewAnalyzer now panics on construction if any entry is malformed, matching the test-harness behaviour and surfacing programmer errors early. 3. NewAnalyzer had no tests. Add analysistest.Run-based tests covering: metadata (correct Name, empty Requires), malformed-entry panic, allowed imports (no diagnostics), banned imports, disallowed imports, and unused allowlist symbols. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g/x/mod The four testdata Go files added for analysistest were missing the required Apache 2.0 license header. Also adds golang.org/x/mod (pulled in as an indirect dependency of analysistest) to LICENSE-3rdparty.csv. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Replaces the manual
go/parser+go/astwalking insideallowedsymbols/symbols_test.gowith a propergo/analysis.Analyzer(allowedsymbols/analyzer.go).What changed:
allowedsymbols/analyzer.go: ago/analysis.Analyzerbuilt ongo/analysis/passes/inspectthat enforces all existing rules — permanently banned packages, per-package allowlists, per-command sublists, blank/dot import rejection, unused allowlist entry reporting — and emits violations aspass.Reportf()diagnostics with properfile:line:colpositionsallowedsymbols/symbols_test.go: shared logic (buildAllowlistSets,checkFileImports,checkFileSelectors,reportUnused) extracted from the old duplicated functions; test harness now calls these shared helpers so the analyzer and the test-time checks stay in syncgolang.org/x/tools v0.43.0as a direct dependency (needed forgo/analysisandgo/analysis/passes/inspect)symbols_builtins.go,symbols_interp.go, etc.) are unchangedWhat this enables:
go vetfor editor/gopls integrationanalysis.AnalyzerpassesThe
allowed-symbols.ymllabel gate is unaffected — it still triggers on changes toallowedsymbols/and requiresverified/allowed_symbolsbefore merge.Test plan
go test ./allowedsymbols/ -v— all 35 tests pass (including verification tests that inject invalid symbols)go build ./allowedsymbols/...— cleanallowed-symbols.ymlstill detects changes to the allowedsymbols/ directory🤖 Generated with Claude Code