Skip to content

build: harden CI, release and dependencies; require Go 1.27.2 - #112

Merged
euskadi31 merged 9 commits into
masterfrom
fix/audit-ci-deps
Oct 13, 2026
Merged

euskadi31 merged 9 commits into
masterfrom
fix/audit-ci-deps

Conversation

@euskadi31

@euskadi31 euskadi31 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the following findings of the internal security audit of 2026-10-12 (commit c5c2ac3): DEP-01, DEP-02, DEP-03, CI-01, CI-02.

It also completes two maintainer decisions: Go 1.27.2 everywhere (with a govulncheck job), and Dependabot PRs #101–#110 merged with Dependabot extended. The first release of every module is v1.0.0.

DEP-01 — unpatched toolchain, no vulnerability scanning

Problem.

  • CI tested with go1.26.5 (go.work), and the modules declared go 1.26.0/1.26.5.
  • go1.26.5 carries standard-library advisories reachable from the library's own call paths: HTTP/2 server crashes and request smuggling in net/http, crypto/tls (post-handshake KeyUpdate, ECH), encoding/asn1 recursion, net/url, encoding/xml.
  • Nothing enforced a patched toolchain, and nothing scanned for vulnerabilities.

Fix.

  • Every go.mod (tool modules included) and go.work declare go 1.27.2. Since Go 1.21 that line is a hard minimum for consumers:
    • GOTOOLCHAIN=auto switches to go1.27.2, which I verified: go1.27.1 with an empty module cache printed go: downloading go1.27.2;
    • a GOTOOLCHAIN=local toolchain stops with a clear message.
  • CI installs the version go.work names.
  • A new govulncheck workflow (v1.9.0, pinned) scans every module outside the workspace, tests included, on every change and weekly. The release workflow scans the released module itself.
  • On go1.27.2: 0 reachable advisories in all 25 modules. The only remaining item is informational, at module level: GO-2026-5932 (golang.org/x/crypto/openpgp is unmaintained, no fix). No module imports openpgp.

Breaking: consumers need Go ≥ 1.27.2. See the new MIGRATION.md section "Toolchain: every module requires Go 1.27.2", which also lists the Go 1.27 behaviour the floor brings:

  • encoding/json on the v2 implementation, with different error texts;
  • HTTP/1 response bodies drained on Close;
  • SSL_CERT_* honoured on darwin and windows.

Side effect of Go 1.27 found here. outbound's TestResponseSizeLimit/gzip_bomb_counted_after_decompression fails on every Go 1.27 toolchain: the new compress/flate encoder makes the payload 16,438 B, over an arbitrary 16 KiB precondition. CI hid it by running go1.26.5. The precondition is now the invariant the case relies on: compressed size < MaxResponseBytes.

DEP-02 — golang.org/x/net v0.58.0 in grpc

Already fixed on master by the Dependabot merge #109: its go.work update raised x/net to v0.60.0 in grpc and examples. Verified: govulncheck reports grpc and examples clean.

The root cause was that Dependabot version updates skip indirect requirements. It is now covered by the govulncheck job, plus the maintainer step "enable Dependabot alerts and security updates" (below).

DEP-03 — mockery in every consumer's module graph

Problem.

  • The core declared tool github.com/vektra/mockery/v2, so a consumer importing only the core resolved 50 modules (mockery, cobra, viper, zerolog, x/tools…).
  • The tool was also unusable: .mockery.yaml is mockery v3 syntax.

Fix.

  • One module per tool under tools/, outside go.work, so no tool dependency reaches a library go.mod:
    • tools/golangci-lint v2.14.0: Go 1.27 support, built with upstream's own x/tools v0.50.0;
    • tools/govulncheck v1.9.0;
    • tools/goveralls v0.0.12: coverage upload;
    • tools/mockery v3.8.1: the release that reads Go 1.27.2 export data.
  • make tools installs them, checksum-verified by each go.sum.
  • The core consumer graph drops to 23 modules, none of them a tool. Both counts include the consumer module. Checked with a consumer module and go list -m all.

Mocks (maintainer decision: kept, used, generated by CI, never committed).

  • make generate writes in-package mock_<interface>_test.go files (testify template, EXPECT() API; 58 today).
  • These are test files, so no library build ever sees them, and every module builds with GOWORK=off without them.
  • test, lint, vuln, tidy and CI generate first; stale generated files are deleted beforehand.
  • testify/mock pulls stretchr/objx, while Dependabot and go mod tidy never generate mocks. So each of the 16 modules that get mocks keeps a committed mockery_test.go importing testify/mock, which makes go.mod/go.sum identical with and without the mocks. check-modules verifies both states.
  • Generated mocks/ packages were rejected: Dependabot runs a strict go mod tidy in every workspace module, and a committed import of a not-generated package would break it.
  • New tests use the mocks; existing hand-written fakes migrate when their test is touched.
  • Consequence, documented in CLAUDE.md, docs/releasing.md and LIMITATIONS.md: a published module's tests compile only from a repository checkout after make generate, because a module zip has no mocks. The library builds are unaffected.

CI-01 — CI/release supply chain

Problem. The workflows:

  • ran with the repository's write-scoped default token;
  • used mutable action references (coverallsapp/github-action@master was even handed GITHUB_TOKEN);
  • gave contents: write to the whole release job, tests and a third-party action included;
  • persisted checkout credentials and interpolated github.ref_name into a script.

Moreover:

  • the release */v* filter never matched modules nested two levels deep (oidc/client, saml/sp, oauth2/store/sql…);
  • make lint piped curl …/HEAD/install.sh | sh.

Fix.

  • The token is read-only by default (workflow-level permissions: contents: read); only the release publish job gets contents: write.
  • Every action is pinned to a full commit SHA with a version comment, each looked up from its latest release: checkout v7.0.1, setup-go v7.0.0. No third-party action is left.
  • persist-credentials: false everywhere.
  • concurrency: pull requests are grouped by ref, with superseded runs cancelled. Every other run is grouped by commit, so a master commit always keeps its CI result.
  • go.yml: tools → make generate → check-modules → build → test → lint → make coveralls.
    • goveralls is built from tools/goveralls, with COVERALLS_TOKEN set to the read-only workflow token, as goveralls documents for GitHub Actions.
    • The upload step has continue-on-error, so a Coveralls outage never fails the build.
    • No binary is downloaded at run time any more.
  • govulncheck.yml: every module, tests included; on every change, plus a weekly run.
  • release.yml: tags v[0-9]* and **/v[0-9]*, split in two jobs:
    • verify, read-only, no cache read: scripts/release-check.sh, check-modules, build, test, lint, and govulncheck on the released module's own code;
    • publish: contents: write, environment release, no checkout, no third-party action. It runs gh release create --verify-tag with notes since the module's previous tag, --prerelease for -rc, and --latest=false for sub-modules.
      The tag reaches scripts only through env.
  • Dependabot:
  • actionlint: 0 findings. zizmor 1.30.1 (online; regular, pedantic and auditor personas): 0 findings, versus 18 + 5 suppressed on master.

CI-02 — module build hygiene and real versions at release

Problem.

  • Mixed go lines made modules unbuildable with GOWORK=off.
  • Every sub-module requires the in-repo modules at the placeholder v0.0.0-00010101000000-000000000000 behind replace. Consumers ignore replace, so a tag cut from this tree could not be resolved, and a GOWORK=off build does not notice: the module's own replace still applies.

Fix.

  • make check-modules (CI and release) fails unless:

    • go.work and every go.mod share one go line, with no toolchain line;
    • go.work lists every module (Dependabot coverage);
    • go work sync changes nothing;
    • every module (and tool module) is tidy with GOWORK=off without the generated mocks;
    • every module builds, vets and is still tidy with them.
  • make release-check TAG=… (scripts/release-check.sh, also in verify) accepts only a canonical v1.MINOR.PATCH[-PRERELEASE] tag of a released module (no build metadata, no v0/v2+). It refuses:

    • placeholder requirements;
    • a module path mismatch;
    • a commit that is not on origin/master;
    • any replace other than the in-repo ones, and any exclude: consumers ignore both.

    It then builds and vets a copy of the module without its in-repo replace directives, dropped by their exact path@version, outside the workspace. That is what a consumer resolves.

  • docs/releasing.md: v1.0.0 first; release order; one release commit setting real requirements; tags pushed in dependency order; consumer check; toolchain patch policy; unfixable-advisory policy; Dependabot; tests of published modules; repository settings.

  • The "v2 stack / first v2 release" wording becomes v1 everywhere (docs-only commit): the module paths carry no /vN, so the go command would ignore a v2 tag.

  • The dead git-flow release Makefile target is removed (no develop branch exists).

Dependabot PRs #101–#110

All merged, each on a Dependabot-rebased head with green CI:

PR Merge commit
#102 7084908
#107 227bc1a
#104 4f7b955
#105 18828b4
#108 b765173
#109 1e70d05
#110 c3990cf
#103 9ab63c9
#101 958ab7a

Tests

  • Gzip test.
    • Red phase on go1.27.2: "16438" is not less than "16384".
    • Green on go1.27.2 and go1.26.5.
    • Mutants:
      • cap below the compressed size → fails;
      • outbound counting wire bytes (no transparent decompression, limiter before gzip.NewReader) → fails with Expected error with "outbound: response body too large" in chain but got nil.
  • check-modules: 7 mutants, all killed: go-line drift, toolchain line, module missing from go.work, version drift (sync), missing mock anchor, untidy module, untidy tool module.
  • release-check dry run. It ran in a throwaway clone (never pushed) against a local file:// module proxy holding fake in-repo versions.
    • Refused:
      • malformed, non-canonical, +build, v0 and v2 tags;
      • examples, internal, tools and unknown directories;
      • placeholder requirements (incl. oidc/client, saml/sp/store/sql);
      • wrong release order;
      • a missing sibling tag (…@v1.0.1 … 404);
      • a commit off master;
      • a third-party replace providing a symbol: keeps replace directives its consumers ignore: go.opentelemetry.io/otel => ./zz_otelfork, while its GOWORK=off build passes;
      • an exclude.
    • Accepted:
      • v1.0.0, v1.0.0-rc.1, oauth2/v1.0.0, oidc/client/v1.0.0;
      • saml/sp/store/sql/v1.0.0, once saml/sp is published;
      • a versioned in-repo replace, dropped by its path@version.
    • The plan review's scenario: oauth2 uses a core symbol that the required core lacks. It is refused with undefined: security.ReleaseCheckProbe, with an unversioned or a versioned in-repo replace, while GOWORK=off go build through replace passes.
    • $GITHUB_OUTPUT is right:
      • the previous tag of the same module, sorted with versionsort.suffix=- (v1.0.0 → v1.0.0-rc.1);
      • the pre-release flag.
    • The publish step's shell was dry-run with a stub gh for the core, a nested module and an -rc.
  • goveralls: run on the real coverage profile against a local capture endpoint, with the GitHub Actions variables. Result: service_name=github, run ID, git head and branch, 173 repository-relative source files, 10,891 of 11,213 lines covered.
  • oidc/provider's import-policy test allows testify/mock in tests only, so a non-test mock would still be caught.

Checks (Go 1.27.2)

Step Result
make sync no change
make tools / make generate 4 tools / 58 mocks
make check-modules ok (25 modules + 4 tool modules)
make build / make test ok
make lint (v2.14.0) 0 issues
make vuln 25/25 clean
actionlint 0
zizmor (online: regular / pedantic / auditor) 0 / 0 / 0
shellcheck (scripts/*.sh, publish step) clean

Coverage of the touched packages:

Package Coverage
outbound 100.0 %
security (core) 98.2 %
oidc/provider 98.3 %

No library code changed.

Follow-ups

  • After merge, run "Check for updates" on each Dependabot entry (Insights → Dependency graph → Dependabot) and read the logs:

    • the root entry: every gomod job downloads go1.27.2 on Dependabot's go1.27.0 image, so look for a toolchain download error;
    • /tools/*: check its indirect allow-list.

    The fallbacks are in docs/releasing.md ("Dependabot").

  • Once Dependabot security updates are enabled: their PRs run in a sub-module directory without go.work, so push make tidy onto them before merging. check-modules fails otherwise (documented in docs/releasing.md).

  • Optionally exercise release.yml end to end on a throwaway fork with v1.0.0-rc.0, oauth2/v1.0.0-rc.0, oidc/client/v1.0.0-rc.0 and examples/v1.0.0 (the last must fail). A fork's tags never reach the real module path.

  • 12 test files predating this PR are not gofmt-clean (oauth2/server_test.go, basic/authenticator_test.go…). Fix them after the audit-fix campaign, to avoid conflicts.

Notes for the maintainer

Repository settings to change by hand (not done by this PR):

  1. Settings → Actions → General:
    • Workflow permissions → Read repository contents;
    • untick Allow GitHub Actions to create and approve pull requests. This matters more since allow_auto_merge is on;
    • enable Require actions to be pinned to a full-length commit SHA.
  2. Rulesets:
    • master: pull request + required status check Build & test (workspace). Keep govulncheck non-required: an advisory without a fix must not block every merge. The scheduled run and the release gate cover it;
    • tags v* and **/v*: creation, update and deletion restricted to maintainers. A * does not match /, hence **/v*.
  3. Settings → General → Releases: enable release immutability, so a published release's tag can no longer be moved or deleted.
  4. Settings → Environments → release (created on first use): add required reviewers to approve each publication before the write token is minted.
  5. Settings → Code security: enable Dependabot alerts, Dependabot security updates, secret scanning and push protection.

Coverage upload: goveralls is built from its pinned module (tools/goveralls), verified by its go.sum and the checksum database like the other tools, and authenticates with the read-only workflow token. No binary is downloaded at run time. A Coveralls outage does not fail the build.

Convention change for open PRs:

  • The per-step gate is make test lint check-modules, plus make vuln before a PR.
  • make sync/make tidy results must now be committed: CI fails if go work sync or tidy would change a file.
  • make generate comes first; run it once before calling go test directly.
  • New Go 1.27 vet checks (stdversion in go test; printf %w with a pointer-to-error operand) and lint v2.14.0 (gosec 2.29 re-enables G407) apply.
  • To resolve go.mod conflicts: take either side → make tidy → make check-modules → commit.

The documentation called the rewrite the "v2 stack" and announced a
"first v2 release", but every module path lacks a `/v2` suffix: under
Go's semantic import versioning the go command ignores a `v2.x.y` tag on
such a path, so a "v2" release could not be consumed. No tag exists yet
(neither on GitHub nor on proxy.golang.org), so the first release of
every module is `v1.0.0` and the paths stay as they are.

Reword the occurrences accordingly: CLAUDE.md, MIGRATION.md (the
breaking-change heading becomes "before the first release"),
LIMITATIONS.md, CHANGELOG.md (header; the "Authorization v2" bullet
named the stack, not an API version), docs/migration-from-v0.md (v0 to
v1) and the oauth2 example. The release-tag guard that enforces v1 comes
with the release checklist.

Audit: CI-02.
TestResponseSizeLimit/gzip_bomb_counted_after_decompression checked that
8 MiB of zeros compress to fewer than 16 KiB before serving them. Go 1.27
changed the compress/flate encoder output and the payload now compresses
to 16,438 bytes, so the test fails on every Go 1.27 toolchain (CI hid it
by running go1.26.5).

The bound was arbitrary. What the case relies on is that the bytes on the
wire stay under Policy.MaxResponseBytes (64 KiB here), so only the
decompressed size can trip the cap. Assert exactly that, once the policy
is built; the ErrResponseTooLarge assertion is unchanged.

Mutation check: making outbound count the compressed wire bytes (no
transparent decompression, limiter before gzip.NewReader) makes the test
fail with "Expected error ... response body too large ... but got nil".

Audit: DEP-01.
The core module declared `tool github.com/vektra/mockery/v2`, which put
mockery and ~45 tool modules (cobra, viper, zerolog, x/tools, ...) into
the module graph of every consumer: one importing only the core resolved
50 modules. The tool was also unusable: `.mockery.yaml` uses mockery v3
syntax. And `make lint` piped an unpinned install script
(`curl .../golangci-lint/HEAD/install.sh | sh`).

Pin each tool in its own module, outside go.work, so that no tool
dependency reaches a library go.mod and each tool builds with exactly the
dependency set its upstream tests:
- tools/golangci-lint: v2.14.0 (Go 1.27 support; built with x/tools
  v0.50.0, upstream's);
- tools/govulncheck: golang.org/x/vuln v1.9.0;
- tools/mockery: v3.8.1 (reads Go 1.27.2 export data).
`make tools` installs them into build/bin, checksum-verified by each
module's go.sum. The core now resolves 22 modules for a consumer.

mockery is kept and used, and its output is never committed: `make
generate` writes in-package mock_<interface>_test.go files (testify
template), which exist only in test binaries, so no library build sees
them and every module builds with GOWORK=off without them. test, lint,
vuln, bench and tidy generate first; stale generated files are removed.
Dependabot and `go mod tidy` never generate mocks, so every module that
gets mocks keeps a mockery_test.go importing testify/mock: go.mod and
go.sum stay identical with or without them (objx becomes an indirect
requirement; checked with GOWORK=off build, vet and tidy -diff on all
25 modules, with 58 mocks generated and with none). The oidc/provider
import policy allows testify/mock in tests.

Add `make vuln` (govulncheck -test on every module, GOWORK=off) and make
`make tidy` sync, tidy every module and tool, then sync again.

Audit: DEP-03, CI-01.
Every module declared go 1.26.0 or 1.26.5 and CI tested with go1.26.5,
which carries standard-library advisories reachable from the library's
call paths (HTTP/2 server crashes and request smuggling in net/http,
crypto/tls post-handshake and ECH issues, encoding/asn1 recursion,
net/url, encoding/xml...). Nothing in the build enforced a patched
toolchain, and the mixed go lines made `GOWORK=off go build` fail in the
modules at 1.26.0 ("updates to go.mod needed").

Set `go 1.27.2` in every go.mod (the tool modules included) and in
go.work. Since Go 1.21 the go line is a minimum, so no consumer can build
the library with an unpatched toolchain (GOTOOLCHAIN=auto switches to
go1.27.2, verified by the checksum database; a GOTOOLCHAIN=local
toolchain stops with a clear message), and CI follows go.work. govulncheck
reports no reachable advisory on go1.27.2 in any of the 25 modules; the
only remaining item is GO-2026-5932 (x/crypto/openpgp is unmaintained, no
fix), at module level, never imported.

go mod tidy merges the duplicate require blocks of password and
internal/interop/saml (Go 1.27 behaviour). make sync is a no-op; every
module builds, vets and is tidy with GOWORK=off, with and without the
generated mocks. MIGRATION.md documents the floor and the Go 1.27
behaviour it brings.

Audit: DEP-01, CI-02.
Nothing checked that a module builds outside the workspace, and the
workspace hid two release problems:
- the mixed go lines made some modules unbuildable with GOWORK=off;
- every sub-module requires the in-repo modules at the placeholder
  v0.0.0-00010101000000-000000000000, made to work by `replace`
  directives. Consumers ignore those, so a tag cut from this tree could
  not be resolved.
A GOWORK=off build alone would not catch the second problem, since the
module's own replace directives still apply.

`make check-modules` (scripts/check-modules.sh), for CI and every release,
fails unless:
- go.work and every go.mod carry one go line and no toolchain line;
- go.work lists every module (Dependabot's go.work mode covers exactly
  those);
- `go work sync` changes nothing;
- every module and tool module is tidy with GOWORK=off without the
  generated mocks (what go mod tidy and Dependabot see);
- every module builds, vets and is still tidy with them.
Seven mutants (go line drift, toolchain line, module missing from go.work,
version drift, missing mock anchor, untidy module and tool module) each
make it fail.

`make release-check TAG=...` (scripts/release-check.sh, also run by the
release workflow) refuses:
- a tag that is not a canonical v1.MINOR.PATCH[-PRERELEASE] (the module
  paths carry no /vN, and the go command skips build metadata);
- the examples, internal and tool modules;
- a module path mismatch, a placeholder requirement, and a commit off
  origin/master.
It then builds and vets a copy of the module without its in-repo replace
directives, outside the workspace: what a consumer resolves. A dry run
against a local module proxy refuses a module using a core symbol its
required core version lacks ("undefined: security.ReleaseCheckProbe"),
although its GOWORK=off build through replace passes.

docs/releasing.md is the checklist: v1.0.0 first, dependency order, one
release commit setting real requirements, the toolchain patch policy, and
the repository settings the maintainer sets once. The git-flow `release`
target (no develop branch exists) is removed.

Audit: CI-02.
…lease

The workflows had several supply-chain weaknesses:
- they ran with the repository's default write-scoped token;
- third-party actions were referenced by mutable tags or a branch
  (coverallsapp/github-action@master, handed GITHUB_TOKEN explicitly);
- release.yml gave `contents: write` to the whole job, tests and a
  third-party release action included, kept the checkout credentials and
  interpolated `github.ref_name` into a script. Its `*/v*` filter missed
  every module nested two levels deep (oidc/client, saml/sp,
  oauth2/store/sql...);
- nothing scanned for vulnerabilities.

Changes:
- workflow-level `permissions: contents: read`, every action pinned to
  a full commit SHA with its version in a comment (looked up from the
  releases: checkout v7.0.1, setup-go v7.0.0, upload-artifact v7.0.2,
  download-artifact v8.0.2, coverallsapp/github-action v2.3.8),
  `persist-credentials: false` everywhere;
- go.yml builds the pinned tools, generates the mocks, then runs
  check-modules, build, test and lint. Coveralls uploads from a separate,
  non-blocking job without Go caches, with a pinned reporter;
- govulncheck.yml runs `make vuln` on every change and weekly;
- release.yml triggers on `v[0-9]*` and `**/v[0-9]*` and is split:
  - `verify`, read-only: release-check, then the whole gate, with no
    cache read;
  - `publish`: `contents: write`, environment `release`, no checkout, no
    third-party action, `gh release create --verify-tag`, notes since the
    module's previous tag, never "latest" for a sub-module.
  The tag only reaches scripts through `env`;
- Dependabot: the `/` gomod entry already covers the 25 workspace modules
  (go.work mode: #103, #104, #105, #108 and #110 bumped dependencies only
  sub-modules require); added the tool modules (allowed by name, since
  tool requirements are indirect), groups for OpenTelemetry and actions,
  and a 7-day cooldown.

actionlint: 0 findings. zizmor 1.30.1, online, default and pedantic
personas: 0 findings (master: 18 findings + 5 suppressed). Repository
settings stay manual (docs/releasing.md).

Audit: CI-01, DEP-01, DEP-02.
release-check only dropped the in-repo replace directives, and only in
their unversioned form, before building the "consumer view" of a module:
- a third-party replace, e.g. a local fork providing a symbol, or a
  patched golang.org/x/net, still applied. The check passed while
  consumers, who ignore every replace and exclude directive of a
  dependency, failed or got the vulnerable version;
- a versioned in-repo replace (`replace …/security v1.0.0 => ../`) was
  left in place. It was refused only because `../` stops resolving from
  the temporary copy, with a misleading message.

Read `go mod edit -json` in the copy, drop each in-repo replace by its
exact Old.Path[@Old.Version], then refuse any remaining replace and any
exclude: a release must not need them. In a dry run:
- the reviewer's probe, a third-party replace providing a symbol, is now
  refused ("keeps replace directives its consumers ignore");
- a versioned in-repo replace is dropped, so a missing core symbol is
  reported as "undefined: security.ReleaseCheckProbe";
- an exclude is refused.

Sort the previous tag with versionsort.suffix=- so that v1.0.0-rc.1
precedes v1.0.0 (release notes start from the right tag), and give the
anchor hint in check-modules' last step too.

Audit: CI-02.
Coverage: the Coveralls action ran a reporter binary downloaded at run
time from a mutable release, verified only against a checksum file of the
same release. That ran on master pushes, on a hosted runner where any
code can read ACTIONS_RUNTIME_TOKEN and seed caches later runs restore.
Upload with goveralls instead, pinned in tools/goveralls (v0.0.12, built
from its own go.sum and the checksum database, like the other tools),
from the build job with the read-only token as COVERALLS_TOKEN, as
goveralls documents for GitHub Actions; a Coveralls outage does not fail
the build. The artifact actions and the coverage job go away: the
workflows now use only actions/checkout and actions/setup-go.

Concurrency: one group per ref meant that a push to master cancelled the
run still waiting behind another, so a master commit could end up with no
build, test or govulncheck result. Group pull requests by ref (cancelling
superseded runs) and everything else by commit.

Release gate: scanning every module with -test let one unfixable advisory
in a test-only dependency block every release. The release now scans the
released module's own code outside the workspace; the full scan stays in
the govulncheck workflow, which must be green on the release commit.

Dependabot allows goveralls in /tools/*. actionlint: 0 findings; zizmor
(online; regular, pedantic and auditor personas): 0 findings.

Audit: CI-01, DEP-01.
Code-review fixes to the documentation of the build and release changes:

- CLAUDE.md:
  - the per-step gate is `make test lint check-modules` (and `make vuln`
    before a pull request);
  - commit what `make tidy` / `go work sync` change, and how to resolve a
    go.mod conflict;
  - run `make generate` once before running `go test` directly;
  - the tests of a published module need `make generate` in a repository
    checkout.
- docs/releasing.md:
  - release-check also refuses replace and exclude directives;
  - the release gate scans the released module;
  - a Dependabot section. Security updates run in a sub-module directory
    without go.work, so `make tidy` must be pushed onto those PRs before
    merging. Every entry is checked after a configuration change (the
    gomod jobs download the toolchain), with a manual fallback;
  - the tests of published modules.
- LIMITATIONS.md: the same limit for published modules' tests.
- docs/security-considerations.md: checksum verification is disabled by
  `GOSUMDB=off` or by a matching `GOPRIVATE`/`GONOSUMDB` pattern. The
  page named a variable that does not exist and a flag removed from the
  go command.
- MIGRATION.md, CHANGELOG.md:
  - the token is read-only by default, and only the release publish job
    gets `contents: write`;
  - coverage goes through goveralls;
  - a consumer of the core resolves 23 modules instead of 50, both counts
    including the consumer;
  - two Markdown lists were split by a blank line.
- .mockery.yaml: why unexported interfaces get mocks too.

Audit: CI-01, CI-02, DEP-03.
@euskadi31
euskadi31 merged commit 273969d into master Oct 13, 2026
3 checks passed
@euskadi31
euskadi31 deleted the fix/audit-ci-deps branch October 13, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant