netsec

package
v0.21.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package netsec provides centralized network security primitives used by every HTTP-based provider in SpeechKit (STT, TTS, LLM, downloads).

It addresses the audit findings S1-S5 (2026-04-16):

  • SSRF via unvalidated user-supplied BaseURL configuration
  • Missing TLS hardening on HTTP clients
  • Bearer tokens leaking through default loggers

All HTTP provider constructors MUST route user-supplied URLs through ValidateProviderURL and MUST obtain their *http.Client via NewSafeHTTPClient.

Index

Constants

View Source
const RedactedHeadersKey ctxKey = 1

RedactedHeadersKey is the context key used to store redacted headers.

Variables

View Source
var (
	ErrEmptyURL          = errors.New("netsec: empty URL")
	ErrInvalidURL        = errors.New("netsec: URL could not be parsed")
	ErrMissingScheme     = errors.New("netsec: URL must have a scheme")
	ErrMissingHost       = errors.New("netsec: URL must have a host")
	ErrUnsupportedScheme = errors.New("netsec: scheme must be http or https")
	ErrInsecureHTTP      = errors.New("netsec: plain http:// not allowed for this host")
	ErrLoopbackBlocked   = errors.New("netsec: loopback addresses not allowed")
	ErrPrivateBlocked    = errors.New("netsec: private / link-local / ULA addresses not allowed")
	ErrInvalidHost       = errors.New("netsec: URL host could not be resolved as a literal IP or name")
	ErrUserInfoForbidden = errors.New("netsec: URL user-info (user:pass@) is not permitted")
)

Validation errors. Callers can match on these with errors.Is for UX.

Functions

func BuildEndpoint

func BuildEndpoint(baseURL, path string, opts ValidationOptions) (string, error)

BuildEndpoint validates baseURL and joins path safely. It prevents a malicious baseURL like "https://legit.example.com/.." from escaping the host, and normalises trailing slashes.

func NewSafeHTTPClient

func NewSafeHTTPClient(opts ClientOptions) *http.Client

NewSafeHTTPClient returns an *http.Client with explicit TLS 1.2+ minimum, sensible timeouts, and a RedactingRoundTripper wrapper so that any subsequent call to httputil.DumpRequest will not leak Authorization headers.

The client is safe to share across goroutines.

func RedactBearer

func RedactBearer(value string) string

RedactBearer returns a safe rendering of an "Authorization: Bearer xxx" header value for logs. It always returns "Bearer [REDACTED]" when the input is non-empty, and "" otherwise.

func RedactHeaders

func RedactHeaders(h http.Header) http.Header

RedactHeaders returns a copy of h where sensitive header values are replaced with the literal "[REDACTED]". Safe for logging.

func ValidateProviderURL

func ValidateProviderURL(raw string, opts ValidationOptions) error

ValidateProviderURL parses raw and rejects URLs that would expose the caller to SSRF. It does not make network calls — hostnames that are not IP literals are accepted as public, on the assumption that DNS resolution happens inside the stdlib http.Transport. If you need resolve-time protection, combine with a RestrictedDialer (see httpclient.go).

Types

type ClientOptions

type ClientOptions struct {
	// Timeout is the per-request timeout. Zero disables it (discouraged).
	Timeout time.Duration

	// TLSMinVersion pins the minimum TLS version (default: tls.VersionTLS12).
	TLSMinVersion uint16

	// InnerTransport, if non-nil, is wrapped by the RedactingRoundTripper.
	// Normally nil — the function constructs a hardened *http.Transport.
	InnerTransport http.RoundTripper

	// DisableKeepAlives turns off connection reuse. Leave false for best perf.
	DisableKeepAlives bool
}

ClientOptions configures a SpeechKit HTTP client.

type RedactingRoundTripper

type RedactingRoundTripper struct {
	Base http.RoundTripper
}

RedactingRoundTripper is an http.RoundTripper that records redacted copies of the outgoing request header set on the *http.Request context so that downstream logging middleware can inspect non-sensitive headers only.

It does NOT rewrite the outgoing request — the real Authorization header is sent over TLS as expected. Redaction applies only to what observability paths can access via ctx.Value(RedactedHeadersKey).

func (*RedactingRoundTripper) RoundTrip

func (r *RedactingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements http.RoundTripper.

type ValidationOptions

type ValidationOptions struct {
	// AllowLoopback permits http:// and https:// URLs whose host is a
	// literal loopback address (127.0.0.0/8, ::1, or the name "localhost").
	// Enable for local whisper-server / Ollama / dev endpoints.
	AllowLoopback bool

	// AllowPrivate permits URLs whose host is in RFC1918 / RFC6598 /
	// link-local / unique-local IPv6 ranges. Enable only for self-hosted
	// VPS scenarios where the user explicitly opts in.
	AllowPrivate bool

	// AllowHTTP permits http:// for non-loopback hosts. Leave false unless
	// an operator has a documented reason (e.g. test harness).
	AllowHTTP bool
}

ValidationOptions controls how strict URL validation behaves. The zero value is the safe default: HTTPS-only, no loopback, no private IPs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL