mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 20:21:29 +00:00
80cbd2db59
Phase 3 of /Users/shankar/Desktop/cowork/v2.1.0-release-gate.md surfaced
four packages below their coverage floors. All four are regressions from
new code shipped in the audit-2026-05-10/11 fix bundles that didn't get
per-function tests:
internal/auth/breakglass 87.5% -> 93.3% (floor: 90%)
+ List (was 0%) — 3 tests (disabled, empty+populated, repo err)
+ RemoveCredential, Unlock disabled-branch tests
internal/auth/oidc 89.4% -> 95.4% (floor: 90%)
+ JWKSStatus (was 0%) — 2 tests (unknown provider, after AuthRequest)
+ TestDiscovery (was 0%) — 5 tests (discovery failure, happy path,
HS256 alg-downgrade detected, missing jwks_uri, JWKS 500 fetch)
internal/auth/session 89.9% -> 94.4% (floor: 90%)
+ SetTrustedProxies (was 0%) — round-trip + clear
+ ComputeCookieHMAC (was 0%) — determinism + key/inputs differ
+ DecryptKeyMaterial (was 0%) — round-trip + wrong-passphrase
internal/api/handler 73.2% -> 75.5% (floor: 75%)
+ 6 auth_breakglass handler funcs (were all 0%) — 14 tests
(disabled/404, invalid JSON, empty fields, service err, happy
path with cookies, admin endpoints, ListCredentials no
password_hash on the wire)
+ WithPermissionChecker setter test (was 0%, Bundle 2 MED-2)
+ NewAdminCRLCacheServiceImpl + CacheRows (were 0%) — 3 tests
+ itoaForRetryAfter + challengeURLBuilder ACME helpers (were 0%) —
4 tests
All five coverage gates green:
internal/service 72.7% (floor: 70%)
internal/api/handler 75.5% (floor: 75%)
internal/api/middleware 67.9% (floor: 30%)
internal/auth 93.3% (floor: 85%)
internal/service/auth 91.8% (floor: 85%)
internal/auth/oidc 95.4% (floor: 90%)
internal/auth/oidc/groupclaim 100.0% (floor: 95%)
internal/auth/oidc/domain 97.6% (floor: 90%)
internal/auth/session 94.4% (floor: 90%)
internal/auth/session/domain 98.3% (floor: 90%)
internal/auth/breakglass 93.3% (floor: 90%)
internal/auth/breakglass/domain 100.0% (floor: 90%)
internal/auth/user/domain 96.2% (floor: 90%)
(and 6 more — all green)
Per CLAUDE.md operating rule: 'Lowering a floor REQUIRES corresponding
code-side test work — never lower the gate to make CI green.' The
floors stay at their committed values; the new tests close the gap.
90 lines
2.9 KiB
Go
90 lines
2.9 KiB
Go
package session
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"testing"
|
|
)
|
|
|
|
// Coverage fill — v2.1.0 release gate Phase 3.
|
|
//
|
|
// Three previously-uncovered surfaces:
|
|
//
|
|
// - SetTrustedProxies (cmd/server config wire)
|
|
// - ComputeCookieHMAC (pre-login cookie verifier helper)
|
|
// - DecryptKeyMaterial (pre-login HMAC-key derive)
|
|
//
|
|
// Each is a thin wrapper called by main.go or the pre-login flow that
|
|
// never exits through a unit-test fixture. The tests below run them
|
|
// directly so the coverage gate stops flagging the package.
|
|
|
|
func TestSetTrustedProxies_RoundTrip(t *testing.T) {
|
|
t.Parallel() //nolint:paralleltest // shared package-level state
|
|
// Snapshot + restore so concurrent tests don't observe the override.
|
|
prev := trustedProxyCIDRs
|
|
defer func() { trustedProxyCIDRs = prev }()
|
|
|
|
want := []string{"10.0.0.0/8", "192.0.2.1"}
|
|
SetTrustedProxies(want)
|
|
if len(trustedProxyCIDRs) != len(want) {
|
|
t.Fatalf("expected %d entries, got %d", len(want), len(trustedProxyCIDRs))
|
|
}
|
|
for i, c := range want {
|
|
if trustedProxyCIDRs[i] != c {
|
|
t.Errorf("entry %d: got %q, want %q", i, trustedProxyCIDRs[i], c)
|
|
}
|
|
}
|
|
|
|
// Empty slice clears.
|
|
SetTrustedProxies(nil)
|
|
if len(trustedProxyCIDRs) != 0 {
|
|
t.Errorf("expected nil/empty after clear; got %v", trustedProxyCIDRs)
|
|
}
|
|
}
|
|
|
|
func TestComputeCookieHMAC_Deterministic(t *testing.T) {
|
|
t.Parallel()
|
|
key := []byte("a-32-byte-key-for-hmac-test-pad!")
|
|
mac1 := ComputeCookieHMAC("ses-1", "actor-1", key)
|
|
mac2 := ComputeCookieHMAC("ses-1", "actor-1", key)
|
|
if !hmac.Equal(mac1, mac2) {
|
|
t.Errorf("HMAC must be deterministic for the same inputs")
|
|
}
|
|
// Length is sha256.Size.
|
|
if len(mac1) != sha256.Size {
|
|
t.Errorf("expected len=%d (sha256), got %d", sha256.Size, len(mac1))
|
|
}
|
|
// Differing id2 changes the HMAC.
|
|
if hmac.Equal(mac1, ComputeCookieHMAC("ses-1", "actor-2", key)) {
|
|
t.Errorf("HMAC must differ when actor changes")
|
|
}
|
|
// Differing id1 changes the HMAC.
|
|
if hmac.Equal(mac1, ComputeCookieHMAC("ses-2", "actor-1", key)) {
|
|
t.Errorf("HMAC must differ when session changes")
|
|
}
|
|
}
|
|
|
|
func TestDecryptKeyMaterial_RoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
// encryptKeyMaterial + decryptKeyMaterial are the pair; round-trip
|
|
// asserts the public DecryptKeyMaterial wrapper does not bypass
|
|
// the decryption path.
|
|
plaintext := []byte("plain-32-byte-key-for-hmac-pad!!")
|
|
const passphrase = "test-passphrase-for-key-encrypt"
|
|
ct, err := encryptKeyMaterial(plaintext, passphrase)
|
|
if err != nil {
|
|
t.Fatalf("encryptKeyMaterial: %v", err)
|
|
}
|
|
got, err := DecryptKeyMaterial(ct, passphrase)
|
|
if err != nil {
|
|
t.Fatalf("DecryptKeyMaterial: %v", err)
|
|
}
|
|
if string(got) != string(plaintext) {
|
|
t.Errorf("decrypt mismatch: got %q, want %q", got, plaintext)
|
|
}
|
|
// Wrong passphrase → error (forwarded from decryptKeyMaterial).
|
|
if _, err := DecryptKeyMaterial(ct, "wrong-passphrase"); err == nil {
|
|
t.Errorf("expected error with wrong passphrase, got nil")
|
|
}
|
|
}
|