chore(security): fix reachable x/crypto ssh vulns, scan driver modules in CI - #14
Merged
Conversation
…s in CI govulncheck resolves imports per module, so the shared go-ci.yml security job — which runs at the repository root — never scanned the four driver sub-modules. Every third-party dependency the project ships lives there, so they were entirely unscanned while Dependabot reported 39 alerts against them. Scanning each module directly found five reachable advisories in golang.org/x/crypto/ssh from sftpdriver. Reachable, not merely present: Open calls ssh.Dial, ssh.ParsePrivateKey and sftp.NewClient directly, so the traces resolve to called symbols rather than being call-graph over-approximations. Bumped to v0.52.0, which clears all five. The go directive is unchanged — v0.52.0 needs only Go 1.25.0. The new CI step gates on reachable findings in third-party modules and reports standard-library findings without failing. A stdlib finding tracks the runner's Go patch release rather than anything here, so gating on it would red every branch whenever a new Go version lands. gcsdriver, s3driver and azuredriver still have reachable findings, all indirect. Rather than leave the new gate non-blocking, they are recorded in .github/govulncheck-allowlist.txt so the gate blocks anything new from today. That file is a backlog, not an exemption: removing a line is how a fix gets enforced, and adding one requires a reason.
A reusable workflow reports a single conclusion for all of its jobs, so when ci / Security failed, the whole `ci` job was marked failed and every job declaring `needs: ci` skipped: drivers, extension, bench and lint. The driver sub-modules and the extension module were therefore not being built or tested at all. A skipped job reports as neither pass nor fail, so this left no red check anywhere — the run looked healthy while four jobs did nothing. It also made the govulncheck step added in the previous commit unreachable, since it lives in the drivers job. The current trigger is two standard-library advisories describing the runner's Go patch release, which has no bearing on whether an independent module compiles or its tests pass. Keep `needs: ci` for ordering and run these jobs unless the workflow was actually cancelled. No gate is weakened: each job still reports its own result, and ci / Security still fails on its own findings.
Every informative line went to GITHUB_STEP_SUMMARY, so a passing scan left no trace in stdout. Reading the step back with `gh run view --log` showed the script being echoed and nothing else, which is indistinguishable from a step that parsed nothing and passed vacuously. Report both places: the summary for the run page, stdout for anyone debugging from the CLI or reading a failed job's log.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Items 1 and 2 from the Dependabot triage. This is independent of #13 and touches only
go.mod,go.sumand CI.The blind spot
govulncheck resolves imports per module. The
securityjob comes from the sharedxraph/workflows/go-ci.yml@v1and runs at the repository root, so it only ever scanned the root module. The four driver sub-modules, which is where every third-party dependency the project ships actually lives, were never scanned at all.That's why CI kept reporting two vulnerabilities while Dependabot reported thirty-nine against those same modules. The
driversmatrix job built and tested each one but ran no vuln scan.What turned up
Scanning each module directly found five reachable advisories in
golang.org/x/crypto/sshfromsftpdriver. Reachable, not merely present, so these are not call-graph over-approximations.Bumping x/crypto from v0.37.0 to v0.52.0 clears all five. Reachable non-stdlib findings for
sftpdrivergo from five to zero.x/syscame along as an indirect bump, and the module'sgodirective is untouched, since v0.52.0 asks only for Go 1.25.0 and CI already builds with 1.25.7.The gate, and why it isn't just "fail on findings"
Two deliberate choices, both aimed at keeping the check meaningful instead of ambient noise.
Standard-library findings report but never gate. A stdlib finding tracks the runner's Go patch release, not anything in this repository, so gating on it reds every branch the moment a new Go version ships. That is exactly what's happening to
ci / Securityright now: go1.25.12, fixed in 1.25.13, and both traces run throughio.ReadFullandsync.Once.Do, which merge every implementation in the binary. Worth knowing that the runner reports fifteen stdlib findings per driver where a local Go 1.26.3 shows eight. Gate on those and all four driver jobs stay permanently red.Only symbol-level findings gate. When a finding's trace stops at module or package level, the dependency is present but the vulnerable code never runs. That's Dependabot's job, not a build gate's.
The allowlist
gcsdriver,s3driverandazuredriverstill have reachable findings, all of them indirect through their cloud SDK trees. A naive blocking gate would have turned three of the four driver jobs red on day one and blocked every unrelated PR after that.Making the gate non-blocking would have made it decorative, so instead the known findings go in
.github/govulncheck-allowlist.txt:The gate blocks anything new from today while the existing backlog stays visible and attributed to a module. Treat that file as a backlog rather than an exemption. Removing a line is how you enforce a fix: bump the dependency, delete the entry, and the job goes green on its own.
sftpdriveris deliberately absent from it. Its findings were fixed, not listed.Verification
The gate logic was rehearsed locally against all four modules before any of this was committed.
Pulling
GO-2026-6061back out of the allowlist correctly failsgcsdriverwithNEW (gating): GO-2026-6061, which is the check that the gate isn't vacuous.sftpdriverbuilds, vets and passesgo test -raceafter the bump.Two things this run exposed
The
drivers,extension,benchandlintjobs were skipping wheneverci / Securityfailed. A reusable workflow reports one conclusion for all of its jobs, so a stdlib finding at the root marked the wholecijob failed and skipped everything declaringneeds: ci. The driver sub-modules and the extension module were not being built or tested at all, and a skipped job reports as neither pass nor fail, so the coverage vanished without a red check anywhere. It also left the govulncheck step above permanently unreachable. They run now unless the workflow is actually cancelled, andneeds: cistays for ordering.The scan step also wrote every informative line to
GITHUB_STEP_SUMMARYand nothing to stdout, so reading it back withgh run view --logshowed the script echoed and no decisions. It reports to both now.Not in here
The root
securityjob still gates on stdlib findings, soci / Securitykeeps going red on Go patch releases. The same split applied here would fix it, but that config lives in the sharedxraph/workflowsrepo.Seventy-two of the hundred and eleven alerts belong to the
docs/Next.js site:next,postcss,js-yaml,sharp. Build-only, not shipped, not part of any Go module. They deserve their owndependabot.ymlcadence so they stop drowning the alerts that affect shipped code.The three allowlisted backlogs need grpc v1.71 to v1.82.1,
aws-sdk-go-v2/service/s3v1.79.3 to v1.97.3, plus x/net and x/text. Those are bigger jumps across cloud SDKs and are better done per module against their integration tests.One note: this branch and #13 both add a section under
## [Unreleased]inCHANGELOG.md, so whichever lands second hits a trivial conflict there.