Skip to content

fix(security): resolve gosec findings and pin Go toolchain to 1.26.5 - #630

Merged
CybotTM merged 1 commit into
mainfrom
fix/gosec-and-toolchain
Jul 28, 2026
Merged

fix(security): resolve gosec findings and pin Go toolchain to 1.26.5#630
CybotTM merged 1 commit into
mainfrom
fix/gosec-and-toolchain

Conversation

@CybotTM

@CybotTM CybotTM commented Jul 28, 2026

Copy link
Copy Markdown
Member

gosec became blocking in the shared workflow (netresearch/.github#312) and flagged six findings here; govulncheck failed on four stdlib advisories. None of the gosec findings turned out to be a real defect — so each one carries a reason a reviewer can check, not a bare suppression.

Rule Where Verdict
G402 TLS verification disabled internal/web/server.go opt-in, not a shipped bypass
G704 SSRF cmd/ldap-manager/main.go ×2 URL is operator config, not request data
G404 weak randomness internal/retry/retry.go backoff jitter only
G103 unsafe internal/ldap_cache/cachetest/helpers.go ×2 test fixture writer

G402 is the one worth stating plainly: TLSSkipVerify is a plain bool that only LDAP_TLS_SKIP_VERIFY / --tls-skip-verify sets. Its zero value is false, a test pins that default, and enabling it logs a warning at startup. Verification is not disabled unless an operator asks for it.

G404: rand.Float64 supplies jitter in addJitter. It produces no token, nonce or session ID — crypto/rand would buy nothing.

G103: the helpers write simple-ldap-go's unexported Object.dn/cn through reflection when building test fixtures. That library sets those fields only in objectFromEntry and exposes no constructor taking them, so a fixture cannot be built through its public API — filed as simple-ldap-go#191.

An earlier revision of this PR moved the scaffolding into a _test.go file, the way raybeam#254 did, which removes unsafe from the binary entirely. That does not work here: the tests of both internal/web and internal/ldap_cache import these helpers, and Go does not share _test.go declarations across package boundaries. Duplicating them into each package cleared gosec but tripped SonarCloud's duplication gate at 20.8% — correctly, since it was duplication. So the shared package stays, and its comment now explains why it is an ordinary package instead of asserting "test-only". It is compiled in, reachable from no production path.

govulncheck

GO-2026-5856 (crypto/tls), GO-2026-5039 (net/textproto), GO-2026-5037 (crypto/x509), GO-2026-4971 (net). The module moves to go 1.26 with toolchain go1.26.5, matching ofelia.

Verification

  • gosec: Issues : 0, exit 0
  • govulncheck: 0 affecting vulnerabilities, exit 0
  • go build, go vet, go test ./... all pass

Copilot AI review requested due to automatic review settings July 28, 2026 18:29
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file tests labels Jul 28, 2026
github-actions[bot]
github-actions Bot previously approved these changes Jul 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Mutation Testing Results

Mutation Score: 0% (threshold: 60%)

⚠️ Score is below threshold. Consider improving test coverage or test quality.

What is mutation testing?

Mutation testing measures test quality by introducing small changes (mutations) to the code and checking if tests detect them. A higher score means better test effectiveness.

  • Killed mutants: Tests caught the mutation (good!)
  • Survived mutants: Tests missed the mutation (needs improvement)

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.92%. Comparing base (ba604b6) to head (c7e4933).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #630   +/-   ##
=======================================
  Coverage   70.92%   70.92%           
=======================================
  Files          36       36           
  Lines        3570     3570           
=======================================
  Hits         2532     2532           
  Misses        876      876           
  Partials      162      162           
Flag Coverage Δ
e2e 58.99% <ø> (ø)
unittests 71.40% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

gosec became blocking in the shared workflow and flagged six findings here;
govulncheck failed on four stdlib advisories. None of the gosec findings was a
real defect, so each carries a reason that can be checked rather than a bare
suppression.

G402, TLS verification disabled (internal/web/server.go): opt-in, not a
shipped bypass. TLSSkipVerify is a plain bool that only LDAP_TLS_SKIP_VERIFY /
--tls-skip-verify sets, its zero value is false, a test pins that default, and
enabling it logs a warning at startup.

G704, SSRF (cmd/ldap-manager/main.go): the URL is the LDAP server address from
operator configuration, never derived from request data.

G404, weak randomness (internal/retry/retry.go): rand.Float64 supplies backoff
jitter. It produces no token, nonce or session ID.

G103, unsafe (internal/ldap_cache/cachetest/helpers.go): reflection writes to
simple-ldap-go's unexported Object.dn/cn when building test fixtures. That
library sets those fields only in objectFromEntry and exposes no constructor
taking them, so a fixture cannot be built through its public API — filed as
netresearch/simple-ldap-go#191.

The scaffolding cannot move into a _test.go file the way it did in raybeam:
the tests of both internal/web and internal/ldap_cache import it, and Go does
not share _test.go declarations across package boundaries. It therefore stays
an ordinary package, compiled in but reachable from no production path, and
the comment now says why instead of asserting "test-only".

govulncheck: GO-2026-5856 (crypto/tls), GO-2026-5039 (net/textproto),
GO-2026-5037 (crypto/x509), GO-2026-4971 (net). The module moves to go 1.26
with toolchain go1.26.5, matching ofelia.

Verified: gosec 0 issues, govulncheck 0 affecting vulnerabilities, go vet
clean, tests pass.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@sonarqubecloud

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

@CybotTM
CybotTM added this pull request to the merge queue Jul 28, 2026
Merged via the queue into main with commit aec1962 Jul 28, 2026
30 checks passed
@CybotTM
CybotTM deleted the fix/gosec-and-toolchain branch July 28, 2026 18:55
CybotTM added a commit to netresearch/simple-ldap-go that referenced this pull request Jul 28, 2026
`Object.cn` and `Object.dn` are unexported and written only by
`objectFromEntry`, so a consumer cannot build a `User`, `Group` or
`Computer` fixture with a DN through the public API. Both downstream
repos do it with reflection plus an `unsafe` write to the unexported
fields, which gosec flags as G103 now that it is blocking in the shared
workflow.

```go
// NewObject builds an Object with the given common name and distinguished name.
func NewObject(cn, dn string) Object
```

## Why a constructor, and only this one

The fields are unexported on purpose — an `Object` mirrors what the
directory returned, and it should not be editable afterwards into
something the directory never said. Setters would give that away
permanently for the sake of test fixtures. A constructor taking both
values at build time does not: after `NewObject` returns, the object is
as immutable as one decoded from an entry, and the only way to produce a
DN that disagrees with the directory is to state it explicitly at
construction, in a fixture, which is the point.

The issue left open whether `NewUser`/`NewGroup`/`NewComputer` belong
here too. They don't. Every other field on those three types is already
exported, so the embedded `Object` is the only part a caller cannot set,
and `ldap.User{Object: ldap.NewObject(cn, dn), SAMAccountName: …}`
covers the whole case with one function instead of four. Adding per-type
constructors would also mean a positional or option-based mirror of
every field on `User` — an API surface that has to track the struct
forever, to solve a problem the struct literal already solves. Same
reasoning against a separate `ldaptest` subpackage: it would be a second
import path and a second thing to version for a single three-line
function, and the constructor is legitimate outside tests anyway (any
caller assembling an `Object` from a source that is not a
`*ldap.Entry`).

The one cost is that `cn` and `dn` are both strings and adjacent, so a
swapped call compiles. The argument order follows the field order and
the issue's sketch, and the doc comment names it.

Additive and non-breaking: nothing existing changes behaviour or
signature, and the `go 1.25.0` directive is untouched.

## Tests

`TestNewObject` covers the `DN()`/`CN()` round-trip including empty and
unicode values; `TestNewObjectMatchesObjectFromEntry` asserts a
constructed Object equals one decoded from the equivalent `*ldap.Entry`;
`TestNewObjectComposesIntoFixtures` builds a `User`, a `Group` and a
`Computer` from it and reads DN/CN back through the embedded methods.

## Follow-up, not this PR

Removing the `unsafe` from [ldap-manager
`internal/ldap_cache/cachetest/helpers.go`](https://github.com/netresearch/ldap-manager/blob/main/internal/ldap_cache/cachetest/helpers.go)
and [raybeam
`internal/server/test_helpers_test.go`](https://github.com/netresearch/raybeam/blob/main/internal/server/test_helpers_test.go)
needs a simple-ldap-go release and a dependency bump in each repo first,
so it is out of scope here. ldap-manager is the one that actually gains
something: its helpers are shared across two packages and so cannot live
in a `_test.go` file, which is why that repo currently compiles `unsafe`
into the binary behind a `#nosec`
([ldap-manager#630](netresearch/ldap-manager#630)).

Closes #191
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants