Repository navigation
Tardigrade v0.7.0
[0.7.0] - 2026-09-17
Added
- Fuzz-campaign orchestration and evidence capture are now available
(#740, #741, #755) — reproducible local and Proxmox campaign runners now
record manifests, watchdog outcomes, exact crash inputs, provenance, and
preservable fuzzer state. The control plane handles detached long-running
rows, SSH keepalives, remote-stage cleanup, and evidence-integrity failures
explicitly so a clean run, a finding, and an interrupted row remain
distinguishable. The sustained #675 validation flow now pins each campaign
to an explicitly requested published release tag and its resolved commit,
rather than following a moving development branch.
Changed
-
Release security hardening closes cross-protocol ambiguity, memory, and
origin-crash paths (#756) — HTTP/1 now rejects duplicate/malformed Host fields
and absolute-form/Host authority disagreement. HTTP/2 validates the complete
pseudo-header envelope and values before its HTTP/1 adapter, strips padding,
ignores extension frames safely, validates SETTINGS, bounds HPACK state, and
enforces concurrent-stream plus aggregate request-memory limits. HTTP/3 now
applies equivalent header/framing checks and per-request plus aggregate
request-memory ceilings. Valid unregistered origin status codes no longer
trap response conversion, while malformed FastCGI/SCGI/uWSGI statuses fail
closed instead of becoming 200. Persistent session and approval credentials
are atomically written with owner-only permissions and temporary plaintext
buffers are wiped. Authorization policy time windows now reject malformed or
out-of-range hours, and device request authentication uses the documented
HMAC-SHA256 construction with constant-time MAC verification. Policy regex
compilation/execution failures and invalid approval booleans also deny the
request instead of silently skipping the affected rule. Configured ACL and
HSTS state is now prepared transactionally for startup/reload rather than
being disabled on parse/allocation failure. Policy enforcement now runs on
the production H1, H2, and H3 paths; denied H1 bodies are never mirrored,
valid identities satisfy H2/H3 required-auth locations, and H3 ACL/rate-limit
decisions use the QUIC transport peer rather than a spoofableX-Real-IP.
Malformed policy/scope/approval mappings fail configuration validation.
SMTP/IMAP upstream TLS now verifies hostname and CA without permitting
truncation, mail replies are bounded, UDP proxy reads have deadlines, and
memcached JSON/TTL parsing rejects invalid input without traps or leaks.
Failed session, approval, and TLS ticket-key persistence also removes its
owner-only temporary credential file instead of leaving secret/key material
behind after write, sync, or rename errors. Transcript files are created
owner-only atomically, closing the prior create-then-chmod exposure window.
JWT and session-derived identity fields are validated at asserted-header
boundaries, preventing CR/LF-bearing claims from injecting upstream HTTP or
SMTP headers. Geo-blocking configurations now require an explicit trusted
proxy/CDN source and reject country headers received from any other peer.
Best-effort mirror delivery now uses bounded native HTTP/TLS transport and a
bounded ignored response, preventing a stalled mirror from holding a worker
indefinitely. The SMTP asserted-identity boundary rejects any inbound
X-Tardigrade-*field in the message-header section — whether or not the
request is authenticated — so a client can neither forge an identity nor
suppress the authoritative one, with case-insensitiveDATAand header
matching. Trusted-peer matching compares parsed IP addresses instead of
truncated text, closing a bypass where two IPv6 addresses differing only in
the final hextet compared equal. Mirror targets must namehttporhttps
explicitly, rejected at startup/reload rather than silently downgraded to
cleartext. IP access-control policy is now owned by the configuration
generation a request leased, so a reload can neither free rules underneath an
in-flight request nor let an older request skip a denial its own configuration
requires. Approval persistence serializes snapshot and write under one lock so
a delayed writer cannot overwrite newer approval state, and approval restore
works at all (it previously failed on every non-empty store). Approval,
session, and device-HMAC credential copies are wiped through the canonical
secureZeropath, and the device registry is created owner-only and refuses
to append to a group/world-accessible file or serialize delimiter-bearing
credential fields. Approved tokens now expire at their configured TTL and
bind exactly to their issued method, path, and identity rather than treating
request paths or*methods as patterns. Locationauth requireddenials no
longer mirror the denied request body. HTTP/2 gates inbound HEADERS and
DATA on the stream state machine rather than request-assembly bookkeeping, so
a peer cannot open a second request on a stream whose response is still
outstanding, and CONNECT is refused at stream scope instead of failing inside
the shared HTTP/1 adapter. Approval and command state mutations now remain
atomic on allocation failure instead of retaining dangling pointers, and all
related JSON — including command envelopes sent upstream — uses structural
encoding rather than interpolating identities, paths, actors, errors, IDs,
params, or upstream bodies. Structured/application logs,
health/status responses, and Prometheus device labels likewise escape
request-controlled data, preventing forged audit records and metrics.
Authentication subrequests and approval webhooks now use bounded native
transports; webhook payloads and response heads are capped, and both trust
boundaries require certificate/URL-host verification. Auth subrequests cannot
inherit a normal origin's verification bypass or SNI override. -
Secret zeroization is now materially faster on x86_64, and the
guarantee is now Tardigrade-owned (#741) —crypto.secrets.secureZero
no longer wrapsstd.crypto.secureZero, which is@memsetover a
volatile slice. LLVM drops thevolatilethere and lowers it to an
ordinarymemsetlibcall; on x86_64-linux that binds to
compiler_rt.memset, a byte-at-a-time store loop, even with libc
linked. The clear was always performed, so this was never a correctness
or non-elision defect — but it made every bulk wipe roughly 22x more
expensive on x86_64 than on aarch64.secureZeronow writes through
wide volatile vector stores, which cannot be merged, widened, or
dropped. A QUIC connection teardown (QuicTlsAdapter.deinit, 529,944
bytes of wipeable state, reachable by connection churn) drops from
~414 µs to ~254 µs of CPU on x86_64. Every production secret wipe now
routes through the canonical helper, including
encrypted_stream.ByteQueue's plaintext cleanup, which previously used
a plain@memset. -
HTTP/2 and HTTP/3/QUIC are promoted to stable (#738) — the support
matrix and public/operator docs now describe the stable native downstream
protocol contract for HTTP/1.1, HTTP/2, and HTTP/3/QUIC. The promotion is
tied to the retained H2 baseline, #593/#699 real-H3 baseline, the v0.6.5
installed-artifact #677 sweep, ngtcp2/GnuTLS plus aioquic black-box H3
closeout, and the passing Public Homebrew Smoke run for the published
artifact path. The docs also call out the stable-contract limits explicitly:
H2 is TLS/ALPN-only downstream, H3 requires UDP reachability and
Tardigrade-owned Alt-Svc advertisement, and H3 listener-owned transport
changes require restart. -
Fuzz verification is faster and keeps leak detection enabled (#757, #758,
#774, #775) — the TLS encrypted-stream cleanup oracle now compares storage in
vector-sized byte chunks while retaining full-capacity clearing checks, and
the HTTP/3 command-sequence target uses a target-local debug allocator that
preserves leak checking without the otherwise unbounded diagnostic stack
metadata cost during sustained fuzzing.
Fixed
-
TLS and HTTP/3 error-state handling is stricter and remains bounded under
hostile transitions (#741, #755) — TLS 1.3 HelloRetryRequest key-share
violations now returnillegal_parameter; HTTP/3 stops accepting new state
after close, rejects bytes after a request FIN, skips streams reset before
first acceptance, and caps retained peer unidirectional-stream state with
H3_EXCESSIVE_LOAD. -
QUIC stream reassembly resists fragmented-input CPU and final-size attacks
(#741, #749) — per-stream out-of-order receive state now caps disjoint
segments at 256, preventing a peer from growing the repeated-scan
reassembly path into an O(n²) CPU denial of service. A STREAM FIN that
undercuts already buffered out-of-order bytes is also rejected rather than
resurrecting an invalid smaller final size. -
TLS queue discard is now amortized O(1) without retaining stale plaintext
copies (#741) —ByteQueueandPlaintextProvenanceQueueadvance a lazy
read prefix instead of shifting every remaining byte on each discard.
Subsequent compaction securely wipes the obsolete plaintext duplicate before
reusing its backing storage. -
HTTP/3/QPACK protocol validation now handles fragmented and malformed peer
input without ambiguous state (#775) — QPACK dynamic references fail when
capacity is zero; fragmented unidirectional stream-type prefixes retain
parser state; peer request streams require a header section; invalid
response pseudo-headers and status values fail closed; informational
responses do not complete a request; and transport/application close codes
remain distinguishable.
Testing
- Campaign models and their independent oracles were hardened (#741, #755,
#757, #758) — TLS resumption and transcript models now match their actual
contracts, bounded HTTP/3 stream-pressure paths have deterministic
regressions, fuzz findings are detected even when the Zig runner exits zero,
and the encrypted-stream cleanup oracle comparesboolstorage through an
exact byte view on every supported architecture.
Build
- GitHub CodeQL SARIF upload action was updated (#703).