diff --git a/Makefile b/Makefile index cafa49b..59cf4c5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help build run test lint clean docker-up docker-down migrate-up migrate-down generate test-cover frontend-build +.PHONY: help build run test lint verify clean docker-up docker-down migrate-up migrate-down generate test-cover frontend-build # Default target - show help help: @@ -15,6 +15,7 @@ help: @echo " make test-verbose Run tests with verbose output" @echo " make lint Run linter (golangci-lint)" @echo " make fmt Format code with gofmt" + @echo " make verify Pre-commit gate: fmt + vet + lint + test (CI-parity)" @echo "" @echo "Database:" @echo " make migrate-up Run migrations (requires DB_URL)" @@ -97,6 +98,24 @@ vet: @echo "Running go vet..." go vet ./... +# verify: aggregate pre-commit gate. Mirrors what CI enforces, so +# running `make verify` locally before committing prevents the +# class of breakages that ship green-locally / red-on-CI (e.g. +# Bundle-9's ST1018 invisible-Unicode-literal hits, which `go vet` +# alone cannot catch — staticcheck under golangci-lint does). +verify: + @echo "==> fmt" + @go fmt ./... | { ! grep -q '.'; } || (echo "gofmt produced changes — commit them" && exit 1) + @echo "==> go vet ./..." + @go vet ./... + @echo "==> golangci-lint run ./... (incl. staticcheck ST*)" + @which golangci-lint > /dev/null || (echo "Installing golangci-lint..." && go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest) + @golangci-lint run ./... --timeout 5m + @echo "==> go test -short ./..." + @go test -short -count=1 ./... + @echo "" + @echo "verify: PASS — safe to commit" + # Database targets (requires migrate tool) migrate-up: @echo "Running migrations..." diff --git a/internal/connector/issuer/local/bundle9_coverage_test.go b/internal/connector/issuer/local/bundle9_coverage_test.go index adcfab0..7f3bed0 100644 --- a/internal/connector/issuer/local/bundle9_coverage_test.go +++ b/internal/connector/issuer/local/bundle9_coverage_test.go @@ -395,7 +395,7 @@ func TestValidateCSRUnicode_RejectsCNHomograph(t *testing.T) { func TestValidateCSRUnicode_RejectsDNSNameRTL(t *testing.T) { csr := &x509.CertificateRequest{ Subject: pkix.Name{CommonName: "ok.com"}, - DNSNames: []string{"good‮evil.com"}, + DNSNames: []string{"good\u202Eevil.com"}, } err := validateCSRUnicode(csr, nil) if err == nil { @@ -409,7 +409,7 @@ func TestValidateCSRUnicode_RejectsDNSNameRTL(t *testing.T) { func TestValidateCSRUnicode_RejectsEmailZeroWidth(t *testing.T) { csr := &x509.CertificateRequest{ Subject: pkix.Name{CommonName: "ok.com"}, - EmailAddresses: []string{"good​bad@example.com"}, + EmailAddresses: []string{"good\u200Bbad@example.com"}, } err := validateCSRUnicode(csr, nil) if err == nil { @@ -424,7 +424,7 @@ func TestValidateCSRUnicode_RejectsAdditionalSAN(t *testing.T) { csr := &x509.CertificateRequest{ Subject: pkix.Name{CommonName: "ok.com"}, } - err := validateCSRUnicode(csr, []string{"good‮evil.com"}) + err := validateCSRUnicode(csr, []string{"good\u202Eevil.com"}) if err == nil { t.Fatal("expected rejection for additional SAN RTL") } diff --git a/internal/validation/unicode_test.go b/internal/validation/unicode_test.go index 39907e1..4c20498 100644 --- a/internal/validation/unicode_test.go +++ b/internal/validation/unicode_test.go @@ -36,15 +36,15 @@ func TestValidateUnicodeSafe_RejectsRTLOverride(t *testing.T) { name string in string }{ - {"LRE", "good‪com"}, - {"RLE", "good‫com"}, - {"PDF", "good‬com"}, - {"LRO", "good‭com"}, - {"RLO", "good‮com"}, - {"LRI", "good⁦com"}, - {"RLI", "good⁧com"}, - {"FSI", "good⁨com"}, - {"PDI", "good⁩com"}, + {"LRE", "good\u202Acom"}, + {"RLE", "good\u202Bcom"}, + {"PDF", "good\u202Ccom"}, + {"LRO", "good\u202Dcom"}, + {"RLO", "good\u202Ecom"}, + {"LRI", "good\u2066com"}, + {"RLI", "good\u2067com"}, + {"FSI", "good\u2068com"}, + {"PDI", "good\u2069com"}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -64,10 +64,10 @@ func TestValidateUnicodeSafe_RejectsZeroWidth(t *testing.T) { name string in string }{ - {"ZWSP", "good​com"}, - {"ZWNJ", "good‌com"}, - {"ZWJ", "good‍com"}, - {"WJ", "good⁠com"}, + {"ZWSP", "good\u200Bcom"}, + {"ZWNJ", "good\u200Ccom"}, + {"ZWJ", "good\u200Dcom"}, + {"WJ", "good\u2060com"}, {"BOM", "good\uFEFFcom"}, } for _, c := range cases { @@ -141,7 +141,7 @@ func TestValidateUnicodeSafe_AcceptsPureNonASCII(t *testing.T) { } func TestValidateUnicodeSafe_ErrorMentionsByteOffset(t *testing.T) { - in := "good‮evil.com" + in := "good\u202Eevil.com" err := ValidateUnicodeSafe(in) if err == nil { t.Fatal("expected rejection")