diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0146bbc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + # Keep the SHA-pinned GitHub Actions current. Pinning to a SHA is only safe + # if something moves the pin forward when the action publishes a fix. + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + commit-message: + prefix: "ci" + + - package-ecosystem: gomod + directory: "/" + schedule: + interval: weekly + commit-message: + prefix: "deps" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b32ca5b..e69f519 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,10 @@ on: branches: [main] pull_request: branches: [main] + schedule: + # Surface newly-published CVEs against an otherwise unchanged codebase + # without waiting for the next push/PR. + - cron: "0 6 * * 1" permissions: contents: read @@ -32,7 +36,7 @@ jobs: - name: Lint uses: golangci/golangci-lint-action@9fae48acfc02a90574d7c304a1758ef9895495fa # v7.0.1 with: - version: v2.12.2 + version: v2.13.1 sonarqube: name: SonarQube @@ -58,6 +62,27 @@ jobs: env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + govulncheck: + name: Vulnerability scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version-file: go.mod + cache: true + + # govulncheck is a go.mod "tool" dependency (go 1.24+), so `go tool` runs + # a go.sum-verified, checksummed version instead of an unpinned + # `go install ...@latest` (which trips SonarCloud's S8545 + # dependency-pinning check). Dependabot's gomod ecosystem tracks and + # bumps it like any other module requirement. The vulnerability + # database itself is still fetched fresh at run time over the network, + # independent of the tool's pinned version, so scans stay current. + - name: Run govulncheck + run: go tool govulncheck ./... + cross-compile: runs-on: ubuntu-latest needs: build-test-lint diff --git a/README.md b/README.md index 59ecf92..f33aa19 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ see [`SECURITY.md`](SECURITY.md) for the full threat model. ## Building -Requires Go 1.22+. +Requires Go 1.26+. ```sh make build # native build, for local development -> bin/pimonitor diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 08fd1d0..7345d47 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -410,6 +410,10 @@ separate cross-compile job (build-only, `arm`/`arm64`) so a target-platform buil caught even though the runner itself is `amd64` and cannot execute Pi-only code paths. A `sonarqube` job (skipped for Dependabot PRs, which don't receive repository secrets) regenerates coverage as a Go coverage profile and uploads it to SonarCloud together with -the sources, per `sonar-project.properties` at the repo root. +the sources, per `sonar-project.properties` at the repo root. A `govulncheck` job runs +`govulncheck ./...` (reachability-based, so it only fails on vulnerabilities actually +reachable from this code) on every push/PR plus a weekly schedule, so a newly-published CVE +against an unchanged codebase is surfaced without waiting for the next commit. Actions are pinned to commit SHAs (not floating tags) so a compromised or rewritten action -release can't silently change what CI executes. +release can't silently change what CI executes; `.github/dependabot.yml` keeps those pins +and `go.mod` dependencies current. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 84beae6..69bf6f8 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -13,14 +13,21 @@ installed it, you can download it [here](https://git-scm.com/downloads) or, if y a GUI-based approach, try [GitHub Desktop](https://desktop.github.com/). Once Git is installed, you'll also need the Go version this project targets (currently -**Go 1.22+**, see [`go.mod`](../go.mod)). Instructions and downloads for your preferred OS +**Go 1.26+**, see [`go.mod`](../go.mod)). Instructions and downloads for your preferred OS can be found [here](https://go.dev/dl/). -For linting, install `golangci-lint` matching the version CI uses (currently `v2.12.2`, +> [!NOTE] +> The `go` directive in `go.mod` tracks a currently-supported Go release (Go supports the +> two most recent major releases). Since PiMonitor ships as a single statically-linked +> binary, the toolchain version is a dependency-security property, not just a build detail +> — it's raised whenever the declared version falls out of that support window, independent +> of any new language features being adopted. + +For linting, install `golangci-lint` matching the version CI uses (currently `v2.13.1`, see [`.github/workflows/ci.yml`](../.github/workflows/ci.yml)): ```sh -go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 +go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1 ``` > [!IMPORTANT] @@ -84,9 +91,10 @@ When a PR is related to an issue, use the `Closes #issuenumber` syntax so the is to the PR automatically and closes when the PR is merged. Follow the PR template in [`.github/pull_request_template.md`](../.github/pull_request_template.md). -Run `make build`, `go vet ./...`, `make test`, and `make lint` locally before opening the -PR — CI runs the same checks (plus a cross-compile check for `arm`/`arm64`) and will not -merge on a red build. +Run `make build`, `go vet ./...`, `make test`, `make lint`, and `govulncheck ./...` (install +via `go install golang.org/x/vuln/cmd/govulncheck@latest`) locally before opening the PR — +CI runs the same checks (plus a cross-compile check for `arm`/`arm64`) and will not merge on +a red build. ## Code style diff --git a/go.mod b/go.mod index fd7be98..65f1a87 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,16 @@ module github.com/larslaskowski/pimonitor -go 1.22 +go 1.26.7 require gopkg.in/yaml.v3 v3.0.1 + +require ( + golang.org/x/mod v0.39.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5 // indirect + golang.org/x/tools v0.49.0 // indirect + golang.org/x/vuln v1.7.0 // indirect +) + +tool golang.org/x/vuln/cmd/govulncheck diff --git a/go.sum b/go.sum index a62c313..64ac71b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,25 @@ +github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU= +github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= +golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5 h1:ZUSxONxc981v7AW7QUg+I9WwZzSTTJ019ENBYr5pV/Q= +golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5/go.mod h1:LVehoXe41cL5SCVQilsV7Gg6BNG+Js6P9PhSbYTIUkQ= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/vuln v1.7.0 h1:4MQBuhmXbz2uepNJrf3v+aaZLGDqw1JluwYboegA1qg= +golang.org/x/vuln v1.7.0/go.mod h1:Xw7zvU3e1bsCYYBXu+w4wcn2Kgn27f34WBCTw8LL5Us= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=