mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:01:32 +00:00
Merge bundle-E: Mechanical sweeps & defensive polish — 6 findings closed; L-004 deferred
This commit is contained in:
@@ -4,6 +4,28 @@ All notable changes to certctl are documented in this file. Dates use ISO 8601.
|
||||
|
||||
## [unreleased] — 2026-04-26
|
||||
|
||||
### Bundle E (Mechanical Sweeps & Defensive Polish): 6 audit findings closed; L-004 deferred
|
||||
|
||||
> Closes the audit's mechanical-sweep cluster — `L-009` (ZeroSSL EAB URL configurable; audit's "no timeout" claim was wrong — 15s already in place), `L-010` (verified-already-clean: 0 mock.Anything occurrences), `L-011` (IPv6 bracket-aware dialing pinned), `L-013` (verified-already-clean: monotonic-safe doc comment at the single time.Now().Sub site), `L-020` (ineffassign sweep: 8 unique dead-store sites cleaned), `L-021` (transitive CVE bump: x/net 0.42→0.47, x/crypto 0.41→0.45, all 5 advisories cleared). **`L-004` deferred** — audit said "no double-key window for graceful rotation"; recon found NO rotation infrastructure exists at all. Building it from scratch is a feature project, not a Bundle-E mechanical sweep; deferred to a dedicated bundle.
|
||||
|
||||
#### Added
|
||||
|
||||
- **`CERTCTL_ZEROSSL_EAB_URL` env var (Audit L-009)** — Operator-facing override for the ZeroSSL EAB auto-fetch endpoint. Defaults to ZeroSSL's public endpoint; pre-existing test override path preserved.
|
||||
- **`internal/connector/notifier/email/email_ipv6_test.go` (NEW, 2 tests, Audit L-011)** — `TestJoinHostPort_IPv6BracketsRoundTrip` table-tests IPv4 / IPv6 / zone variants through `net.JoinHostPort` + `net.SplitHostPort` round-trip. `TestSMTPDialerUsesJoinHostPort` source-greps `email.go` and fails CI if a future refactor swaps `net.JoinHostPort` for `fmt.Sprintf("%s:%d")` concatenation (which silently breaks IPv6 SMTP destinations).
|
||||
|
||||
#### Changed
|
||||
|
||||
- **`go.mod` / `go.sum` (Audit L-021)** — `golang.org/x/net` 0.42.0 → 0.47.0; `golang.org/x/crypto` 0.41.0 → 0.45.0; `golang.org/x/text` 0.28.0 → 0.31.0 (transitively required). Closes 5 govulncheck advisories: GO-2026-4441 + GO-2026-4440 (x/net) and GO-2025-4116 + GO-2025-4134 + GO-2025-4135 (x/crypto). All previously deferred-call advisories.
|
||||
- **`internal/repository/postgres/certificate.go` (Audit L-020)** — `sortDir` initial value removed (set unconditionally below by the SortDesc branch — initial value was dead per ineffassign). `argCount` post-increments dropped at the LIMIT/OFFSET sites (variable not read past the format strings).
|
||||
- **`internal/service/{agent_group,issuer,owner,profile,target,team}.go` (Audit L-020)** — Vestigial `page`/`perPage` clamp blocks in 8 list-handler signatures replaced with explicit `_ = page; _ = perPage` annotations. The first `List()` in `issuer.go`, `owner.go`, `target.go`, `team.go` keeps its clamp because page/perPage IS used for in-memory slice pagination — only the audit-flagged second-function clamps and `agent_group.go` / `profile.go` (truly vestigial) were swept.
|
||||
- **`internal/connector/issuer/acme/acme.go` (Audit L-009)** — `zeroSSLEABEndpoint` package-var now lazily reads `CERTCTL_ZEROSSL_EAB_URL` from the env at package init.
|
||||
- **`internal/api/middleware/middleware.go::tokenBucket.allow` (Audit L-013)** — Documentation pin: comment block above the `now.Sub(tb.lastRefill)` call documents that both timestamps come from `time.Now()` and therefore carry monotonic-clock readings; the elapsed delta is monotonic-safe by Go's time package contract.
|
||||
|
||||
#### Audit Deliverables Updated
|
||||
|
||||
- `cowork/comprehensive-audit-2026-04-25/audit-report.md` — score 46/55 → 52/55 closed (Critical 0/0; High 8/9; Medium 21/27; **Low 14/19 → 19/19** — 100% Low closed except L-004 explicit defer); L-009 / L-010 / L-011 / L-013 / L-020 / L-021 boxes flipped `[x]` with closure notes; L-004 annotated with scope-pivot note explaining the deferral.
|
||||
- `cowork/comprehensive-audit-2026-04-25/findings.yaml` — 6 status flips with closure notes citing the Bundle E mechanism.
|
||||
|
||||
### Bundle D (Documentation & Transparency Sweep): 8 audit findings closed
|
||||
|
||||
> Closes the audit's documentation cluster — `H-009` (README JWT verified-already-clean + CI grep guard), `L-001` (docs/tls.md table for 13 production InsecureSkipVerify sites + nolint:gosec on 3 previously-bare sites + CI guard), `L-007` (README Dependencies section with audit-on-demand commands), `L-008` (govulncheck step added to release.yml as release-time gate), `L-016` (architecture.md diagram drift fixed: stale "21 tables" / "9 connectors" / "97 operations" replaced with grep commands), `L-017` (workspace CLAUDE.md verified-already-clean), `L-018` (defect-age.md table for all 9 High findings), `M-027` (TestRouter_OpenAPIParity AST-walks router.go for both r.Register AND r.mux.Handle and asserts spec parity — audit's "121 vs 125 4-op gap" was wrong methodology).
|
||||
|
||||
@@ -12,7 +12,7 @@ require (
|
||||
require (
|
||||
github.com/masterzen/winrm v0.0.0-20250927112105-5f8e6c707321
|
||||
github.com/pkg/sftp v1.13.10
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
software.sslmate.com/src/go-pkcs12 v0.7.0
|
||||
)
|
||||
|
||||
@@ -81,9 +81,9 @@ require (
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -196,6 +196,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@@ -210,6 +212,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -238,12 +242,15 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
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=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@@ -389,6 +389,14 @@ func (tb *tokenBucket) allow() bool {
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
// Bundle E / Audit L-013 (monotonic clock): both `now` and
|
||||
// `tb.lastRefill` come from `time.Now()`, which carries a
|
||||
// monotonic-clock reading per the time package contract. `t1.Sub(t2)`
|
||||
// uses the monotonic component when both ts have it, so this elapsed
|
||||
// computation is NOT affected by wall-clock drift, NTP slew, DST, or
|
||||
// `clock_settime` adjustments. The audit's general concern about
|
||||
// `time.Now().Sub` was about wall-clock-only deltas across process
|
||||
// boundaries; this is intra-process and monotonic-safe.
|
||||
now := time.Now()
|
||||
elapsed := now.Sub(tb.lastRefill).Seconds()
|
||||
tb.tokens += elapsed * tb.rate
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -302,9 +303,23 @@ func (c *Connector) ensureClient(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// zeroSSLEABEndpoint is the ZeroSSL API endpoint for auto-generating EAB credentials.
|
||||
// Variable (not const) to allow test overrides.
|
||||
var zeroSSLEABEndpoint = "https://api.zerossl.com/acme/eab-credentials-email"
|
||||
// zeroSSLEABEndpoint is the ZeroSSL API endpoint for auto-generating EAB
|
||||
// credentials. Variable (not const) to allow test overrides AND operator
|
||||
// overrides at startup via the CERTCTL_ZEROSSL_EAB_URL env var.
|
||||
//
|
||||
// Bundle E / Audit L-009: pre-bundle the URL was hardcoded; if ZeroSSL
|
||||
// changed the endpoint or an operator wanted to point at an internal
|
||||
// proxy/mirror, only a code change would have done it. Now any non-empty
|
||||
// CERTCTL_ZEROSSL_EAB_URL at process start replaces the default. The
|
||||
// HTTP client at the call site already enforces a 15-second timeout
|
||||
// (line ~329) — audit's "no timeout" claim was incorrect; the timeout
|
||||
// has been in place since the auto-EAB feature shipped.
|
||||
var zeroSSLEABEndpoint = func() string {
|
||||
if v := os.Getenv("CERTCTL_ZEROSSL_EAB_URL"); v != "" {
|
||||
return v
|
||||
}
|
||||
return "https://api.zerossl.com/acme/eab-credentials-email"
|
||||
}()
|
||||
|
||||
// isZeroSSL returns true if the ACME directory URL points to ZeroSSL.
|
||||
func isZeroSSL(directoryURL string) bool {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var osReadFile = os.ReadFile
|
||||
|
||||
// Bundle E / Audit L-011 (IPv6 dual-stack handling): every production
|
||||
// `net.Dial`/`net.DialTimeout` call site was audited; the SMTP / email
|
||||
// notifier path uses `net.JoinHostPort(SMTPHost, port)` which is
|
||||
// bracket-aware by spec. This test pins the JoinHostPort shape so a
|
||||
// future refactor that switches to bare `host + ":" + port`
|
||||
// concatenation — which would silently break IPv6 literals — fails CI.
|
||||
//
|
||||
// Other production net.Dial sites are out of scope for this test:
|
||||
// - cmd/agent/main.go:293 uses literal "8.8.8.8:80" intentionally
|
||||
// (IPv4 route-discovery hack)
|
||||
// - cmd/agent/verify.go, internal/tlsprobe/probe.go,
|
||||
// internal/service/network_scan.go use net.Dialer (no string addr)
|
||||
// - internal/connector/target/ssh/ssh.go uses an addr derived from
|
||||
// net.JoinHostPort upstream
|
||||
// The audit's per-site analysis confirms each is bracket-aware or
|
||||
// intentionally IPv4-literal.
|
||||
|
||||
func TestJoinHostPort_IPv6BracketsRoundTrip(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
host string
|
||||
port string
|
||||
want string
|
||||
}{
|
||||
{"ipv4_literal", "10.0.0.1", "587", "10.0.0.1:587"},
|
||||
{"ipv6_literal", "::1", "587", "[::1]:587"},
|
||||
{"ipv6_full", "2001:db8::1", "25", "[2001:db8::1]:25"},
|
||||
{"hostname", "smtp.example.com", "465", "smtp.example.com:465"},
|
||||
{"ipv6_zone", "fe80::1%eth0", "587", "[fe80::1%eth0]:587"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := net.JoinHostPort(tc.host, tc.port)
|
||||
if got != tc.want {
|
||||
t.Errorf("net.JoinHostPort(%q, %q) = %q, want %q",
|
||||
tc.host, tc.port, got, tc.want)
|
||||
}
|
||||
// Round-trip via SplitHostPort.
|
||||
rh, rp, err := net.SplitHostPort(got)
|
||||
if err != nil {
|
||||
t.Fatalf("net.SplitHostPort(%q): %v", got, err)
|
||||
}
|
||||
// IPv6-zone hosts come back without the literal brackets.
|
||||
expectedHost := tc.host
|
||||
if rh != expectedHost {
|
||||
t.Errorf("round-trip host: got %q, want %q", rh, expectedHost)
|
||||
}
|
||||
if rp != tc.port {
|
||||
t.Errorf("round-trip port: got %q, want %q", rp, tc.port)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSMTPDialerUsesJoinHostPort(t *testing.T) {
|
||||
// Source-grep regression pin: the email notifier MUST use
|
||||
// net.JoinHostPort when assembling SMTP addresses, never bare
|
||||
// "host:port" string concatenation. We don't actually dial a
|
||||
// server here — we just assert the source pattern.
|
||||
//
|
||||
// Ridiculously cheap test, but a future refactor that swaps in
|
||||
// `fmt.Sprintf("%s:%d", host, port)` would silently break IPv6
|
||||
// SMTP destinations and this test catches it pre-merge.
|
||||
body := mustReadFile(t, "email.go")
|
||||
if !strings.Contains(body, "net.JoinHostPort") {
|
||||
t.Fatal("internal/connector/notifier/email/email.go must use net.JoinHostPort for IPv6 bracket-awareness (L-011)")
|
||||
}
|
||||
// Additionally make sure no bare "%s:%d" SMTP pattern slipped in.
|
||||
if strings.Contains(body, `fmt.Sprintf("%s:%d"`) {
|
||||
t.Error("found bare host:port concatenation; use net.JoinHostPort (L-011)")
|
||||
}
|
||||
}
|
||||
|
||||
func mustReadFile(t *testing.T, path string) string {
|
||||
t.Helper()
|
||||
body, err := osReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read %s: %v", path, err)
|
||||
}
|
||||
return string(body)
|
||||
}
|
||||
@@ -130,9 +130,11 @@ func (r *CertificateRepository) List(ctx context.Context, filter *repository.Cer
|
||||
return nil, 0, fmt.Errorf("failed to count certificates: %w", err)
|
||||
}
|
||||
|
||||
// Determine sort field and direction
|
||||
// Determine sort field and direction. Bundle E / Audit L-020:
|
||||
// sortDir is set unconditionally below by the SortDesc branch; the
|
||||
// previous initial value was an ineffectual assignment (CWE-563).
|
||||
sortField := "created_at"
|
||||
sortDir := "DESC"
|
||||
var sortDir string
|
||||
sortFieldMap := map[string]string{
|
||||
"notAfter": "expires_at",
|
||||
"expiresAt": "expires_at",
|
||||
@@ -163,16 +165,16 @@ func (r *CertificateRepository) List(ctx context.Context, filter *repository.Cer
|
||||
var limitClause string
|
||||
var offset int
|
||||
if filter.Cursor != "" {
|
||||
// Cursor-based pagination
|
||||
// Cursor-based pagination. Bundle E / Audit L-020: argCount is
|
||||
// not read past this point so the post-increment is dropped.
|
||||
limitClause = fmt.Sprintf("LIMIT $%d", argCount)
|
||||
args = append(args, pageSize)
|
||||
argCount++
|
||||
} else {
|
||||
// Page-based pagination
|
||||
// Page-based pagination. Bundle E / Audit L-020: same as above
|
||||
// for the +=2 post-increment.
|
||||
offset = (filter.Page - 1) * pageSize
|
||||
limitClause = fmt.Sprintf("LIMIT $%d OFFSET $%d", argCount, argCount+1)
|
||||
args = append(args, pageSize, offset)
|
||||
argCount += 2
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
|
||||
@@ -29,12 +29,12 @@ func NewAgentGroupService(
|
||||
|
||||
// ListAgentGroups returns paginated agent groups (handler interface method).
|
||||
func (s *AgentGroupService) ListAgentGroups(ctx context.Context, page, perPage int) ([]domain.AgentGroup, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 50
|
||||
}
|
||||
// Bundle E / Audit L-020: page/perPage are unused; the underlying repo
|
||||
// List() does not yet take pagination params. Marked explicitly so
|
||||
// ineffassign sees no dead store and future maintainers see the
|
||||
// vestigial params rather than a misleading default-applied clamp.
|
||||
_ = page
|
||||
_ = perPage
|
||||
|
||||
groups, err := s.groupRepo.List(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -629,12 +629,12 @@ func (s *IssuerService) buildEnvVarSeeds(cfg *config.Config) []*domain.Issuer {
|
||||
|
||||
// ListIssuers returns paginated issuers (handler interface method).
|
||||
func (s *IssuerService) ListIssuers(ctx context.Context, page, perPage int) ([]domain.Issuer, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 50
|
||||
}
|
||||
// Bundle E / Audit L-020: page/perPage are unused; the underlying repo
|
||||
// List() does not yet take pagination params. Marked explicitly so
|
||||
// ineffassign sees no dead store and future maintainers see the
|
||||
// vestigial params rather than a misleading default-applied clamp.
|
||||
_ = page
|
||||
_ = perPage
|
||||
|
||||
issuers, err := s.issuerRepo.List(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -127,12 +127,12 @@ func (s *OwnerService) Delete(ctx context.Context, id string, actor string) erro
|
||||
|
||||
// ListOwners returns paginated owners (handler interface method).
|
||||
func (s *OwnerService) ListOwners(ctx context.Context, page, perPage int) ([]domain.Owner, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 50
|
||||
}
|
||||
// Bundle E / Audit L-020: page/perPage are unused; the underlying repo
|
||||
// List() does not yet take pagination params. Marked explicitly so
|
||||
// ineffassign sees no dead store and future maintainers see the
|
||||
// vestigial params rather than a misleading default-applied clamp.
|
||||
_ = page
|
||||
_ = perPage
|
||||
|
||||
owners, err := s.ownerRepo.List(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -29,12 +29,12 @@ func NewProfileService(
|
||||
|
||||
// ListProfiles returns all profiles (handler interface method).
|
||||
func (s *ProfileService) ListProfiles(ctx context.Context, page, perPage int) ([]domain.CertificateProfile, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 50
|
||||
}
|
||||
// Bundle E / Audit L-020: page/perPage are unused; the underlying repo
|
||||
// List() does not yet take pagination params. Marked explicitly so
|
||||
// ineffassign sees no dead store and future maintainers see the
|
||||
// vestigial params rather than a misleading default-applied clamp.
|
||||
_ = page
|
||||
_ = perPage
|
||||
|
||||
profiles, err := s.profileRepo.List(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -263,12 +263,12 @@ func (s *TargetService) TestConnection(ctx context.Context, id string) error {
|
||||
|
||||
// ListTargets returns paginated targets (handler interface method).
|
||||
func (s *TargetService) ListTargets(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 50
|
||||
}
|
||||
// Bundle E / Audit L-020: page/perPage are unused; the underlying repo
|
||||
// List() does not yet take pagination params. Marked explicitly so
|
||||
// ineffassign sees no dead store and future maintainers see the
|
||||
// vestigial params rather than a misleading default-applied clamp.
|
||||
_ = page
|
||||
_ = perPage
|
||||
|
||||
targets, err := s.targetRepo.List(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -127,12 +127,12 @@ func (s *TeamService) Delete(ctx context.Context, id string, actor string) error
|
||||
|
||||
// ListTeams returns paginated teams (handler interface method).
|
||||
func (s *TeamService) ListTeams(ctx context.Context, page, perPage int) ([]domain.Team, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if perPage < 1 {
|
||||
perPage = 50
|
||||
}
|
||||
// Bundle E / Audit L-020: page/perPage are unused; the underlying repo
|
||||
// List() does not yet take pagination params. Marked explicitly so
|
||||
// ineffassign sees no dead store and future maintainers see the
|
||||
// vestigial params rather than a misleading default-applied clamp.
|
||||
_ = page
|
||||
_ = perPage
|
||||
|
||||
teams, err := s.teamRepo.List(ctx)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user