Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/govulncheck-allowlist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Known-reachable dependency vulnerabilities in the driver sub-modules.
#
# The govulncheck step in ci.yml's `drivers` job fails on any reachable
# vulnerability in a third-party module that is NOT listed here. This file is
# therefore a backlog, not an exemption: an entry means "we know, it is
# tracked, it does not block the next unrelated PR" — never "this is fine".
#
# The gate exists to stop NEW findings landing. Removing a line is how you
# enforce a fix: bump the dependency, delete the entry, and the job goes green
# on its own. Adding a line requires a reason.
#
# Standard-library findings are reported but never gate, because they track
# the runner's Go patch release rather than anything in this repository — the
# toolchain moving would otherwise red every branch on a schedule nobody here
# controls.
#
# Format: <driver> <OSV-ID>
#
# Audited 2026-08-13 against govulncheck v1.6.0. sftpdriver is deliberately
# absent: its x/crypto/ssh findings were reachable through ssh.Dial and
# ssh.ParsePrivateKey in Open, and were fixed by bumping to v0.52.0 rather
# than being listed here.

# gcsdriver — all indirect, via cloud.google.com/go/storage's dependency tree.
gcsdriver GO-2026-6061 # grpc: xDS RBAC + HTTP/2 server. Needs grpc v1.82.1.
gcsdriver GO-2026-4394 # otel/sdk: PATH hijacking → arbitrary code execution.
gcsdriver GO-2026-4918 # x/net: HTTP/2 infinite loop on bad SETTINGS_MAX_FRAME_SIZE.
gcsdriver GO-2026-5026 # x/net/idna: accepts ASCII-only Punycode labels.
gcsdriver GO-2026-5970 # x/text: infinite loop on invalid input.

# s3driver — one finding, surfaced through two module paths.
s3driver GO-2026-5764 # aws eventstream decoder panic. Needs service/s3 v1.97.3.

# azuredriver — all indirect, via azure-sdk-for-go's dependency tree.
azuredriver GO-2026-4918 # x/net: HTTP/2 infinite loop on bad SETTINGS_MAX_FRAME_SIZE.
azuredriver GO-2026-5026 # x/net/idna: accepts ASCII-only Punycode labels.
azuredriver GO-2026-5970 # x/text: infinite loop on invalid input.
86 changes: 86 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ jobs:
drivers:
name: Driver (${{ matrix.driver }})
runs-on: ubuntu-latest
# `needs: ci` orders this after the root module's checks, but a reusable
# workflow reports one conclusion for all of its jobs — so any failure
# inside it, including a govulncheck finding about the runner's Go patch
# release, skipped this job entirely. These are independent modules whose
# build and tests have nothing to do with the root module's scan result,
# and a skipped job reports as neither pass nor fail, so the coverage
# disappeared silently. Run unless the workflow was actually cancelled.
if: ${{ !cancelled() }}
needs: ci
strategy:
fail-fast: false
Expand Down Expand Up @@ -69,10 +77,84 @@ jobs:
working-directory: drivers/${{ matrix.driver }}
run: go test -race -count=1 ./...

# govulncheck resolves imports per module, so the shared go-ci.yml
# `security` job — which runs at the repository root — never sees these
# sub-modules at all. Every third-party dependency the project ships
# lives here, so without this step they were entirely unscanned.
#
# Module findings gate; standard-library findings only report. A stdlib
# finding tracks the runner's Go patch release rather than anything in
# this repository, so gating on it reds every branch whenever a new Go
# release lands, regardless of whether this code is affected.
- name: Vulnerability scan (govulncheck)
working-directory: drivers/${{ matrix.driver }}
env:
GOVULNCHECK_VERSION: v1.6.0
DRIVER: ${{ matrix.driver }}
ALLOWLIST: ${{ github.workspace }}/.github/govulncheck-allowlist.txt
run: |
set -euo pipefail
go install "golang.org/x/vuln/cmd/govulncheck@$GOVULNCHECK_VERSION"

REPORT="$RUNNER_TEMP/govulncheck-$DRIVER.json"
# JSON mode is for programmatic use and does not signal findings
# through its exit code; the parse below is what decides.
govulncheck -format json ./... > "$REPORT" || true

# A finding gates only when its trace reaches a called symbol.
# Findings at module or package level mean the dependency is present
# but the vulnerable code is never invoked.
reachable() {
jq -sr --arg scope "$1" '
[ .[]
| select(has("finding")) | .finding
| select(.trace[0].function != null)
| select(if $scope == "stdlib"
then .trace[0].module == "stdlib"
else .trace[0].module != "stdlib" end)
| .osv ] | unique | .[]
' "$REPORT"
}

STDLIB=$(reachable stdlib)
MODULE=$(reachable module)
ALLOWED=$(grep -E "^$DRIVER[[:space:]]" "$ALLOWLIST" 2>/dev/null | awk '{print $2}' | sort -u || true)
NEW=$(comm -23 <(printf '%s\n' "$MODULE" | sed '/^$/d') <(printf '%s\n' "$ALLOWED" | sed '/^$/d'))

# Report to both the job log and the run summary. The summary is for
# humans reading the run page; stdout is what `gh run view --log`
# and anyone debugging a failure actually gets. A step whose
# reasoning appears only in the summary looks like it did nothing.
emit() {
echo "govulncheck[$DRIVER] $1"
echo "- \`$DRIVER\` $1" >> "$GITHUB_STEP_SUMMARY"
}

if [ -n "$STDLIB" ]; then
emit "stdlib (toolchain, not gating): $(echo $STDLIB)"
fi
if [ -n "$ALLOWED" ]; then
emit "known backlog (allowlisted): $(echo $ALLOWED)"
fi

if [ -n "$NEW" ]; then
emit "**new reachable dependency vulnerabilities**: $(echo $NEW)"
echo "::error::new reachable dependency vulnerabilities in $DRIVER: $(echo $NEW)"
echo "Fix by upgrading the dependency. To accept it for now, add it to"
echo ".github/govulncheck-allowlist.txt with a reason."
govulncheck ./... || true
exit 1
fi

emit "no new reachable dependency vulnerabilities"

# ─── Extension Module ─────────────────────────────────────────────────
extension:
name: Extension
runs-on: ubuntu-latest
# See the drivers job: a reusable-workflow failure must not silently
# skip an independent module's tests.
if: ${{ !cancelled() }}
needs: ci

steps:
Expand Down Expand Up @@ -104,6 +186,8 @@ jobs:
bench:
name: Bench
runs-on: ubuntu-latest
# See the drivers job.
if: ${{ !cancelled() }}
needs: ci

steps:
Expand Down Expand Up @@ -135,6 +219,8 @@ jobs:
lint:
name: Lint
runs-on: ubuntu-latest
# See the drivers job.
if: ${{ !cancelled() }}
needs: ci

steps:
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to Trove are documented in this file.

## [Unreleased]

### Dependencies and Supply Chain

#### Fixed
- **`sftpdriver`: `golang.org/x/crypto` v0.37.0 → v0.52.0.** Five advisories in `x/crypto/ssh` were reachable, not merely present — `Open` calls `ssh.Dial`, `ssh.ParsePrivateKey`, and `sftp.NewClient` directly: infinite loop on large channel writes (GO-2026-5020), FIDO/U2F physical-interaction bypass (GO-2026-5019), DoS from pathological RSA/DSA parameters (GO-2026-5018), client-triggered server deadlock (GO-2026-5017), and a byte-arithmetic underflow panic (GO-2026-5013). The module's `go` directive is unchanged; x/crypto v0.52.0 requires only Go 1.25.0.

#### Added
- **govulncheck now runs against each driver sub-module** in CI's `drivers` matrix job. It resolves imports per module, so the shared `go-ci.yml` `security` job — which runs at the repository root — never scanned `azuredriver`, `gcsdriver`, `s3driver`, or `sftpdriver` at all. Every third-party dependency the project ships lives in those modules, which is why the x/crypto findings above were invisible to CI while Dependabot reported them.
- **The `drivers`, `extension`, `bench` and `lint` jobs no longer skip when the shared root-module workflow fails.** A reusable workflow reports a single conclusion for all of its jobs, so a `ci / Security` failure — currently two standard-library advisories about the runner's Go patch release — marked the whole `ci` job failed and skipped every job that declared `needs: ci`. The driver sub-modules and the extension module were therefore 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. They now run unless the workflow is cancelled; `needs: ci` is kept for ordering.
- **`.github/govulncheck-allowlist.txt`** records the reachable findings that remain in `gcsdriver`, `s3driver`, and `azuredriver`, all of them indirect. It is a backlog rather than an exemption: the gate fails on anything not listed, so removing a line is how a fix gets enforced. Standard-library findings are reported but never gate, since they track the runner's Go patch release rather than this repository, and gating on them would red every branch whenever a new Go version lands.

### Phase 8: Cloud Drivers, Middleware, and Benchmarks

#### Added — Storage Drivers
Expand Down
4 changes: 2 additions & 2 deletions drivers/sftpdriver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ require (
github.com/pkg/sftp v1.13.7
github.com/stretchr/testify v1.11.1
github.com/xraph/trove v0.0.0
golang.org/x/crypto v0.37.0
golang.org/x/crypto v0.52.0
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/sys v0.45.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

Expand Down
12 changes: 6 additions & 6 deletions drivers/sftpdriver/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -39,15 +39,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
Expand Down
Loading