Repository navigation
chore(ci): gocyclo's filesystem walk in ci.yml scans frontend/node_modules when deps are installed #177
Description
Summary
ci.yml's complexity check walks the filesystem:
# .github/workflows/ci.yml (lint job)
COMPLEXITY_ISSUES=$(gocyclo -over 10 -ignore "_test\.go" . 2>&1 || true)frontend/node_modules contains vendored Go source — e.g. frontend/node_modules/flatted/golang/pkg/flatted/flatted.go ships Stringify at complexity 32 and Parse at 16. Any run of that command in a workspace where npm dependencies are installed reports those as findings.
CI is NOT currently affected — established, not assumed
I checked both jobs that run gocyclo before filing:
| workflow / job | gocyclo invocation | npm install in the same job? | affected |
|---|---|---|---|
ci.yml / lint |
gocyclo -over 10 -ignore "_test\.go" . (filesystem walk) |
no (no setup-node, no npm ci) |
no — node_modules never exists in that workspace |
pre-commit.yml / pre-commit |
via the gocyclo hook: gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/) |
yes (npm ci at line 233, before pre-commit run --all-files at line 279) |
no — git ls-files only lists tracked files, and frontend/node_modules/ is gitignored (.gitignore:97) |
So the two invocations are safe today for opposite reasons: one has no node_modules, the other cannot see it.
Why file it anyway
1. It is a live trap for local runs and agents. The documented, copy-pasteable command is the ci.yml one, and anyone who runs it after npm ci in the same checkout gets findings that belong to a third-party npm package. I hit this during review of PR LeanerCloud/cloud-commitments-cli#1758 and initially attributed 2 findings to the PR that were my own — which is exactly how it would present to anyone else, including a CI-reproduction agent trying to match the lint job locally.
2. The two invocations disagree about scope, and only one of them is robust. ci.yml's . walk is safe purely because that job happens not to install npm dependencies. Adding setup-node + npm ci to the lint job — or a cache restore that materialises node_modules — breaks it immediately, with a failure pointing at a vendored file nobody in this repo owns. The pre-commit form is structurally immune because it derives its file list from git.
Note ci.yml's form also lacks the vendor/ exclusion the pre-commit form has. That costs nothing today (git ls-files "*/vendor/*.go" "vendor/*.go" returns 0 — no tracked vendor tree), but it is the same latent gap.
Suggested fix
Make ci.yml derive its file list the same way the pre-commit hook does, so the two checks agree and neither depends on what happens to be on disk:
COMPLEXITY_ISSUES=$(gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/) 2>&1 || true)Or, minimally, extend the ignore pattern: -ignore "_test\.go|node_modules|vendor/".
Reproduction
cd frontend && npm ci && cd ..
gocyclo -over 10 -ignore "_test\.go" .
32 flatted Stringify frontend/node_modules/flatted/golang/pkg/flatted/flatted.go:16:1
16 flatted Parse frontend/node_modules/flatted/golang/pkg/flatted/flatted.go:145:1
gocyclo -over 10 -ignore "_test\.go|node_modules" .
(no output, exit 0)
Found during independent adversarial review of PR LeanerCloud/cloud-commitments-cli#1758.
Findings from the 2026-09-02 codebase audit
Added by an automated audit of 3c0f8ac94048a2c36fce5ccddee54e6c4849a5cd (tip of origin/main). Each item below was reported by one reviewer and independently confirmed by a second that did not write it. Full report: docs/audits/codebase-audit-2026-09-02.md.
A15-003 (medium)
The table in this issue clears pre-commit.yml on the grounds that its gocyclo hook enumerates via git ls-files and so cannot see node_modules. That holds for gocyclo but not for the Go hooks beside it. .pre-commit-config.yaml:16-21 defines the go-vet hook as bash -c 'go vet ./...', and Go's ./... skips only testdata and paths beginning with . or _, never node_modules. Reproduced in a clean worktree: after npm ci, go list ./... emits github.com/LeanerCloud/CUDly/frontend/node_modules/flatted/golang/pkg/flatted, because frontend/node_modules/flatted/golang/pkg/flatted/flatted.go has no go.mod of its own and is absorbed into the CUDly module. In .github/workflows/pre-commit.yml the "Install frontend deps" step at :235 runs before "Run pre-commit" at :241, so this fires in CI. go vet happens to pass on that package today, so nothing is broken; the exposure is that a future flatted release reddens this repo's own vet, test and govulncheck gates on code it does not own. Same fix shape as the gocyclo one: have the Go ./... invocations enumerate real packages, e.g. go vet $(go list ./... | grep -v /node_modules/). Audit finding A15-003.
Activity