Commit Graph

4 Commits

Author SHA1 Message Date
shankar0123 3853b7460c security: reject CRLF/NUL in email headers to prevent SMTP injection (fixes H-3)
H-3 in certctl-audit-report.md: caller-supplied From/To/Subject were
interpolated directly into the SMTP DATA payload and handed to
client.Mail / client.Rcpt with no sanitization, allowing an attacker
who controls any of those values to inject extra headers (Bcc:,
Reply-To:), split the message body (CRLFCRLF), or tamper with the
SMTP envelope. CWE-113.

Fix:
- New package helper internal/validation.ValidateHeaderValue(field,
  value). Rejects CR ("\r"), LF ("\n"), and NUL ("\x00") with an error
  that names the offending field but does NOT echo the raw value,
  so log readers cannot be attacked with injected content. Silent
  stripping was considered and rejected: authentication-relevant
  headers must fail visibly.
- Two-layer defense in internal/connector/notifier/email/email.go:
    (1) primary guard at the top of sendEmail / sendHTMLEmail, which
        blocks tampering of the SMTP envelope (client.Mail, client.Rcpt)
        since net/smtp does not sanitize those arguments; and
    (2) defense-in-depth guard inside formatEmailMessage /
        formatHTMLEmailMessage, catching any future caller that
        bypasses sendEmail. Both format functions now return an error.
- Body content is intentionally NOT validated — CR/LF in body is legal
  RFC 5322 content and net/smtp handles dot-stuffing.

Tests:
- internal/validation/headers_test.go: 3 functions (AcceptsSafeInput,
  RejectsControlCharacters, DefaultFieldName) covering plain ASCII,
  UTF-8 multibyte, tabs, typical email addresses, CRLF injection,
  lone CR, lone LF, NUL, CRLFCRLF body split, trailing CR, leading LF.
  Each reject case asserts the field name IS in the error and the
  raw offending value IS NOT (anti-log-injection).
- internal/connector/notifier/email/email_test.go: added
  TestEmail_FormatEmailMessage_RejectsCRLFInjection and
  TestEmail_FormatHTMLEmailMessage_RejectsCRLFInjection. Existing
  format tests updated for the new (bytes, error) signature.

Wire-format invariants preserved:
- SMTP DATA headers still use CRLF separators and RFC 1123Z Date
  (unchanged).
- Content-Type headers unchanged (text/plain for plain, text/html +
  MIME-Version: 1.0 for HTML).
- No change to message encoding or transport.

Verification (Go 1.25.9 linux-arm64, parent e9947dc):
- go build ./...                                 clean
- go vet ./...                                   clean
- go test -race ./internal/validation/...        ok
- go test -race ./internal/connector/notifier/email/...   ok
- go test -race ./internal/connector/notifier/webhook/... ok
- Per-layer coverage gates all pass:
    validation  95.1% (+0.7 vs baseline 94.4%)
    email       39.7% (+1.4 vs baseline 38.3%)
    service     67.8% (unchanged)
    handler     78.6% (unchanged)
    middleware  80.0% (unchanged)
    domain      92.7% (unchanged)
- govulncheck ./...                              No vulnerabilities found
- golangci-lint run ./internal/validation/... ./internal/connector/notifier/email/...
                                                 0 issues

Operational note: SMTP sends that would previously deliver a
tampered message now fail fast at the notifier with a clear error.
Operators who were relying on header-injection-shaped inputs (there
should be none in practice — all callers are internal certctl code)
will see "failed to format message: <field> contains disallowed
control character" in logs.

Scope: H-3 only. H-4 (webhook SSRF) follows in a separate commit.
2026-04-17 00:08:20 +00:00
shankar0123 fde5b39d53 fix: resolve test compilation and runtime failures across codebase
- Add context.Context to handler test mocks (agent, agent_group)
- Refactor scheduler to use local interfaces instead of concrete service types
- Wire RevocationSvc/CAOperationsSvc sub-services in integration tests
- Add context.Background() to service test calls (agent, agent_group)
- Fix repo integration tests: add FK prerequisite records (team, owner,
  issuer, renewal_policy) before creating certificates
- Set MaxOpenConns(1) on test DB to preserve SET search_path across queries
- Fix Apache/HAProxy tests: replace "echo ok"/"echo reload" with "true"
  binary to avoid macOS exec.Command PATH resolution failure
- Fix validation tests: correct error expectations for regex-first checks,
  replace null byte strings with strings.Repeat for length tests
- Fix scheduler timeout test flakiness with t.Skip fallback
- Remove unused imports (context in ca_operations_test, service in scheduler)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 22:53:46 -04:00
shankar0123 a0afa7ab6f test(security): TICKET-018 add fuzz tests for command validation and domain parsing
Added Go native fuzz tests (testing/fuzz) for security-critical input validation:

1. FuzzValidateShellCommand in internal/validation/command_fuzz_test.go
   - Tests shell command validation with injection payloads (;, |, &, $, `, etc.)
   - Seed corpus includes valid commands and dangerous metacharacters
   - Ensures function never panics under fuzzing

2. FuzzValidateDomainName in internal/validation/command_fuzz_test.go
   - Tests RFC 1123 domain validation with wildcard support
   - Seed corpus includes SQL injection, path traversal, and malformed domains
   - Ensures function never panics under fuzzing

3. FuzzValidateACMEToken in internal/validation/command_fuzz_test.go
   - Tests base64url token validation
   - Seed corpus includes injection payloads and special characters
   - Ensures function never panics under fuzzing

4. FuzzIsValidRevocationReason in internal/domain/revocation_fuzz_test.go
   - Tests RFC 5280 revocation reason validation
   - Seed corpus includes case variations, injection attempts, and null bytes
   - Ensures function never panics and returns only valid booleans

5. FuzzCRLReasonCode in internal/domain/revocation_fuzz_test.go
   - Tests CRL reason code mapping
   - Validates return codes are within 0-9 range
   - Ensures invalid reasons default to 0 (unspecified)

All fuzz tests follow Go 1.18+ testing/fuzz conventions with seed corpus
for faster discovery of edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 21:40:49 -04:00
shankar0123 3e3e68fd3a fix(security): TICKET-009 add HTTP timeouts to notifier clients
- Added TestSlack_ClientHasTimeout to verify 10-second timeout
- Added TestTeams_ClientHasTimeout to verify 10-second timeout
- Added TestPagerDuty_ClientHasTimeout to verify 10-second timeout
- Added TestOpsGenie_ClientHasTimeout to verify 10-second timeout
- All notifiers already configured with 10 second timeout in New()
- Tests verify timeout is set and matches expected value
2026-03-27 21:33:31 -04:00