Commit graph

16 commits

Author SHA1 Message Date
ad4d81fe50
store: survive corrupt stored values
apply() sliced record values at fixed offsets and getMeta, Count and
Top called Uint64 without checking length, so a truncated or
hand-corrupted value panicked the ingest loop or the stats command.
All decoders now validate lengths; corrupt records are overwritten,
corrupt meta/count values return errors.
2026-08-21 21:47:04 +05:30
d407ac0840
loglist: error when every log list source fails
FetchAll swallowed per-source errors and always returned nil, so all
three upstreams failing was indistinguishable from an empty log list
and the hourly sync silently tailed nothing. Total failure now
returns an error; partial failure logs a warning.
2026-08-21 21:44:48 +05:30
c8207996d1
serve: add HTTP timeouts and bind localhost by default
The server had no read/write/idle timeouts, so slowloris connections
held goroutines and file descriptors forever, and the default address
bound all interfaces, exposing the API to the network unintentionally.
Defaults now match the tool's local-search use case; add timeouts and
cap header size.
2026-08-21 21:43:44 +05:30
2be376e728
apex,tailer: normalize ingest names to punycode
Certificate SANs went into the store after only lowercasing, so a
unicode name like 'buecher.example.com' was stored as raw UTF-8 and
could never be found by search, which runs IDNA. Ingest now maps
names through a permissive IDNA profile (keeping DKIM-style
underscores that the strict lookup profile rejects), enforces RFC
1035 label lengths, and stores the canonical form. Normalize also no
longer lowercases before IDNA mapping, which produced wrong punycode
for decomposed unicode input.
2026-08-21 21:42:54 +05:30
bba101bbab
tailer: account for every fetched entry and log skips
fetchRange counted an entry as consumed before attempting to decode
it, so undecodable entries were skipped permanently with no record,
and the position could overshoot the requested range if a log served
more entries than asked. The position now advances by exactly the
number of received entries (clamped to the range) and skips are
logged with their index window.
2026-08-21 21:38:52 +05:30
ab31bcee3a
serve: wait for tailers to stop before closing the store
The tailers ran in a fire-and-forget goroutine, so shutdown cancelled
the context and immediately closed Pebble while a tailer could still
be inside Store.Watermark or an in-flight fetch, racing db.Close.
serve now waits (up to 10s) for the tailer supervisor to exit first.
2026-08-21 21:38:10 +05:30
5edad988b2
store: run recounts inside the ingest loop
Recount mutated totalBase/pendingTotal under the store mutex while the
ingest loop read and wrote them unlocked (a data race), and left stale
per-apex count caches that corrupted subsequent increments. Recount is
now an ingest-loop operation: it flushes pending records, rescans, and
resets loop-owned state inline, so counters have a single owner.
2026-08-21 21:37:28 +05:30
89990ec41f
server: cap rate limiter memory under key-flooding
Every distinct client key allocated a bucket retained for up to 24
hours, and keys are cheap to mint at line rate (spoofed XFF values,
IPv6 /64s), so a remote attacker could grow the map until the process
ran out of memory. Shards now evict expired buckets and then arbitrary
victims once a shard exceeds 4096 buckets.
2026-08-21 21:35:29 +05:30
620ca5cac8
store,server: cap buffered results per search query
Scan materialized every record for an apex in memory, sorted it, and
the server rebuilt the full body twice more. One query for a large
apex could buffer hundreds of megabytes three times over; a few
concurrent requests exhausted RAM. Scan now keeps only the top N
results by insertion sequence via a bounded heap (default 100000,
configurable with -max-results), preserving collection order.
2026-08-21 21:34:03 +05:30
c4b0f85c42
server: validate X-Forwarded-For before using it as limiter key
The limiter indexed the XFF chain at len(trustedHops) without checking
that the trusted suffix contained valid IPs. With an over-counted
trusted-proxy-hops setting, a single garbage header entry became the
rate limit bucket and attackers could rotate it to bypass the limit.
Bogus headers now fall back to the connection address.
2026-08-21 21:31:35 +05:30
250652a3dd
tailer: require pinned log keys and stop on STH verification failure
VerifySTH silently passed when no log key was pinned or when an STH
arrived without a signature, and a failed verification only produced a
log line while ingestion continued from the unauthenticated source.
NewClient now refuses logs without keys, the tailer skips the fetch
cycle on verification failure, and it rejects list entries whose
log_id does not match SHA-256 of the key.
2026-08-21 21:30:40 +05:30
394174763d
tailer: fix nil client dereference on malformed log key
NewClient returns (nil, err) when a log list entry has an undecodable
key, but the error path called client.ShortID() on the nil client,
panicking the tailer goroutine and crashing the process.
2026-08-21 21:24:48 +05:30
6f5b4c961f
rfc6962: parse SCT lists as raw TLS bytes, not DER
Both call sites pass the extnValue OCTET STRING contents, which per
RFC 6962 section 3.3 are already the TLS-encoded SCT list. The extra
asn1.Unmarshal failed on virtually every real certificate (the first
byte is the high byte of the uint16 length), so embedded SCT
timestamps were silently dropped and first_seen fell back to the leaf
timestamp. The test masked this by double-wrapping the value.
2026-08-21 21:24:09 +05:30
f9d1e3585a
rfc6962,apex: reject control and non-printable bytes in hostnames
Certificate SANs are attacker-controlled and were copied into store
keys unfiltered. Since 0x00 is the apex/sub key separator, a SAN like
'ab\0cd.qqq' corrupted per-apex counts during recount and made scan
bounds ambiguous. Names now must be printable ASCII at ingest, and
Normalize rejects them as defense in depth.
2026-08-21 21:23:22 +05:30
8037630e85
store: persist log watermarks atomically with ingested records
The tailer fsynced the watermark immediately after pushing records into
the async ingest batch, so a crash could persist the marker without the
data it covers, losing those entries permanently. Watermark updates now
travel through the same channel as records and are written inside the
same Pebble batch, and never move backwards.
2026-08-21 21:22:14 +05:30
9374d0a191
subidx: CT log tailer and crt.name-compatible subdomain search API 2026-08-21 14:26:32 +05:30