inblock && !/\/\/ indirect/ { if ($1 != "") print $1 " " $2 }
The audit (direct-dep-filtered go list -u -m): parse only the require block from go.mod, drop // indirect lines, cross-reference against go list -u -m all, and group results by module family so coordinated bumps can be batched safely.
#!/usr/bin/env bash
# audit-deps.sh — find actionable (direct + outdated) deps
set -euo pipefail
GOMOD="${1:-go.mod}"
# 1) Direct deps: require block minus // indirect (handles block + single-line forms)
awk -v gomod="$GOMOD" '
/^require \(/ {inblock=1; next}
inblock && /^\)/ {inblock=0; next}
inblock && !/\/\/ indirect/ { if ($1 != "") print $1 " " $2 }
!inblock && /^require / && !/\/\/ indirect/ { print $2 " " $3 }
' "$GOMOD" > /tmp/direct-deps.txt
# 2) Modules with available updates: "mod curver [latest]"
go list -u -m all | awk '$3 ~ /^\[/ { gsub(/[\[\]]/,"",$3); print $1, $2, $3 }' > /tmp/updates.txt
# 3) Join: direct AND outdated = actionable
awk 'NR==FNR {direct[$1]=$2; next}
$1 in direct { printf "%-70s %-12s -> %s\n", $1, $2, $3 }' /tmp/direct-deps.txt /tmp/updates.txt
The fix — batch the whole otel family in one go get invocation (one MVS resolution pass → consistent module graph + go.sum), then tidy and run the full gate:
#!/usr/bin/env bash
# bump-otel.sh — coordinated same-family minor bump
set -euo pipefail
go get \
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp@v0.70.0 \
go.opentelemetry.io/otel@v1.45.0 \
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric@v1.45.0 \
go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.45.0 \
go.opentelemetry.io/otel/metric@v1.45.0 \
go.opentelemetry.io/otel/sdk@v1.45.0 \
go.opentelemetry.io/otel/sdk/metric@v1.45.0 \
go.opentelemetry.io/otel/trace@v1.45.0
go mod tidy # prune stale indirects + go.sum
go build ./... && go vet ./... && go test ./...
This is safe to batch because every target is a same-family coordinated minor bump (otel 1.44.x→1.45.x, otelhttp 0.69.x→0.70.x) with a single shared release line — verified by the family grouping in the audit output. Avoid go get -u ./... here: it would bump unrelated graph deps (e.g. golang.org/x/net) that the direct-dep filter deliberately left alone.
The bug — .gitleaks.toml auto-generated by gitreins init masks every first-party doc/spec/markdown file:
title = "gitleaks config"
[extend]
useDefault = true
[allowlist]
description = "auto-generated by gitreins init"
paths = [ # <- path-targeted; docs/specs/markdown are FIRST-PARTY, must be scanned
'''specs/''',
'''docs/''',
'''.*\.spec\.md$''',
'''.*\.md$''',
]
The fix — keep only dependency dirs (vendor/, third_party/), anchored so a first-party file merely named vendor-* is not masked:
title = "gitleaks config"
[extend]
useDefault = true
[allowlist]
description = "Narrowed: dependency dirs only; first-party docs/specs/markdown are scanned"
paths = [
'''(^|/)vendor/''',
'''(^|/)third_party/''',
# add '''(^|/)node_modules/''' for JS repos — nothing else
]
Then re-scan before trusting PASS: gitleaks detect --source . --config .gitleaks.toml --log-opts=-1 and confirm exit 1 (leaks found) or a report with zero findings only after the real secrets are remediated. Note for gitleaks v8.30: path allowlisting goes in paths = [...]; regexTarget = "path" is rejected (only match/line), so don't try to express these as regexes.
Reproduced the full scenario in `/tmp/audit-demo` (Go 1.26) and `/tmp/secrets-guard-demo` (gitleaks v8.30.1, real `ghp_` tokens that the default rules fire on). | # | Check | Result | |---|-------|--------| | 1 | Audit finds actionable direct otel deps: 7× `otel v1.44.0 → v1.45.0`, `otelhttp v0.69.0 → v0.70.0` | 8 found (demo has more direct modules than the 5+1 in the report; same mechanism) | | 2 | Direct-dep filter excludes indirect `go.opentelemetry.io/otel/metric/x v0.66.0 → v0.67.0` | Excluded (not in `require` without `// indirect`) | | 3 | Family grouping prints `go.opentelemetry.io/otel → v1.45.0` etc. | Single target per family → batch-safe | | 4 | Batch `go get` (one pass) applies all 8 to target versions | All 8 at `v1.45.0`/`v0.70.0`; `auto/sdk` untouched | | 5 | `go build ./...` after bump | OK | | 6 | `go vet ./...` after bump | OK | | 7 | `go test ./...` (`TestTracerStartSpan`, real otel v1.45 SDK) | PASS | | 8 | Re-run audit after bump | Zero actionable | | 9 | Parser edge: single-line `require mod vX` + mixed block form | Only non-indirect extracted correctly | | 10 | gitreins config: `gitleaks detect` masks `docs/deployment.md`, `specs/api.spec.md`, `README.md` secrets; code leak still caught | 3 masked (findings list: only `main.go`, `vendor/`, `third_party/`) | | 11 | False-PASS variant: only doc/spec secrets present → guard reports **"no leaks found" exit 0** | Vulnerability proven | | 12 | Narrowed config: same repo → `docs/deployment.md`, `specs/api.spec.md`, `README.md`, `main.go` all caught; `vendor/` + `third_party/` still skipped | exit 1, 4 findings, zero vendor findings | | 13 | After remediating doc/spec/README tokens, scan of current tree (`--log-opts=-1`) | "no leaks found", exit 0 → PASS now trustworthy | | 14 | Anchored regex edge: first-party `docs/vendor-migration.md` containing a token | **Caught** (exit 1) — `(^|/)vendor/` does not match `vendor-` in a filename | Key edge cases covered: indirect deps, single-line vs block `require`, already-latest deps (re-audit), git history scanning vs HEAD-only (why `--log-opts=-1` for post-remediation verification), gitleaks config schema (`paths` vs `regexTarget="path"`), and anchored vs unanchored path regexes. ---
{"model": "deepseek-v4-flash", "problem_class": "go-deps-audit-and-gitleaks-allowlist", "result": "passed", "tests": 14}