grep -rn "^func Fuzz" . returns nothing. There is no fuzz coverage
anywhere in the repo and no testdata/fuzz corpus.
NewManifestFromReader is the function that parses attacker-controlled
bytes: a magic prefix, a protobuf outer message, a zlib-compressed inner
payload, and an embedded signature. That is a deserializer for untrusted
input in a tool whose entire purpose is to be pointed at files fetched from
the network. It is the single highest-value fuzz target in the codebase and
currently has none.
Definition of done
FuzzNewManifestFromReader exists and exercises the full parse path from
raw bytes.
A seed corpus is committed under mfer/testdata/fuzz/ containing at
minimum: a valid minimal manifest, a valid signed manifest, truncated
input at several offsets, correct magic with garbage after it, wrong
magic, empty input, a zlib bomb (small compressed, huge declared size),
and a declared size that disagrees with the actual decompressed length.
The fuzz target asserts the real invariant: the parser must either return
a manifest or an error, and must never panic, never hang, and never
allocate unboundedly.
Any crash the fuzzer finds during development is fixed in the same PR, and
its input is added to the committed corpus as a regression seed.
make test runs the seed corpus as ordinary unit tests (Go does this
automatically for Fuzz* without -fuzz) and stays within the test
timeout.
make check passes. TODO.md updated in the same commit.
Implementation requirements
Do not add -fuzz to script/test. Continuous fuzzing must not run in
CI on every push — the seed corpus regression run is what belongs in make test. If a longer fuzz run is worth having, add a separate,
explicitly-invoked make fuzz target with a bounded -fuzztime, and say
so in the README Entrypoints section.
The target must be fast and deterministic on the seed corpus. No network,
no gpg subprocess, no real filesystem writes outside t.TempDir().
Do not weaken the parser to make the fuzzer pass. If the fuzzer finds
input that causes a panic or an unbounded allocation, that is a bug in the
parser and gets fixed as one, with a note in the commit message.
Confirm the existing decompression bound (MaxDecompressedSize, mfer/constants.go:13) actually holds under fuzzing — the zlib-bomb seed
is there specifically to test it.
Commit title must end with (closes #65).
## Context
`grep -rn "^func Fuzz" .` returns nothing. There is no fuzz coverage
anywhere in the repo and no `testdata/fuzz` corpus.
`NewManifestFromReader` is the function that parses attacker-controlled
bytes: a magic prefix, a protobuf outer message, a zlib-compressed inner
payload, and an embedded signature. That is a deserializer for untrusted
input in a tool whose entire purpose is to be pointed at files fetched from
the network. It is the single highest-value fuzz target in the codebase and
currently has none.
## Definition of done
- `FuzzNewManifestFromReader` exists and exercises the full parse path from
raw bytes.
- A seed corpus is committed under `mfer/testdata/fuzz/` containing at
minimum: a valid minimal manifest, a valid signed manifest, truncated
input at several offsets, correct magic with garbage after it, wrong
magic, empty input, a zlib bomb (small compressed, huge declared size),
and a declared size that disagrees with the actual decompressed length.
- The fuzz target asserts the real invariant: the parser must either return
a manifest or an error, and must never panic, never hang, and never
allocate unboundedly.
- Any crash the fuzzer finds during development is fixed in the same PR, and
its input is added to the committed corpus as a regression seed.
- `make test` runs the seed corpus as ordinary unit tests (Go does this
automatically for `Fuzz*` without `-fuzz`) and stays within the test
timeout.
- `make check` passes. `TODO.md` updated in the same commit.
## Implementation requirements
- Do not add `-fuzz` to `script/test`. Continuous fuzzing must not run in
CI on every push — the seed corpus regression run is what belongs in
`make test`. If a longer fuzz run is worth having, add a separate,
explicitly-invoked `make fuzz` target with a bounded `-fuzztime`, and say
so in the README Entrypoints section.
- The target must be fast and deterministic on the seed corpus. No network,
no gpg subprocess, no real filesystem writes outside `t.TempDir()`.
- Do not weaken the parser to make the fuzzer pass. If the fuzzer finds
input that causes a panic or an unbounded allocation, that is a bug in the
parser and gets fixed as one, with a note in the commit message.
- Confirm the existing decompression bound (`MaxDecompressedSize`,
`mfer/constants.go:13`) actually holds under fuzzing — the zlib-bomb seed
is there specifically to test it.
- Commit title must end with ` (closes #65)`.
clawbot
added this to the 1.0.0 milestone 2026-08-09 03:39:10 +02:00
Built in #120: the fuzz target, the seed corpus, and make fuzz.
The decompression limit did not hold. The zstd decoder set aside whatever size a frame header claimed, up to its own 64 GiB limit, and decoded small payloads in full before MaxDecompressedSize was checked. The decoder now has MaxDecompressedSize as its limit, and the input that showed it is in the seed corpus.
Model: opus-5-5
Built in https://git.eeqj.de/sneak/mfer/pulls/120: the fuzz target, the seed corpus, and `make fuzz`.
The decompression limit did not hold. The zstd decoder set aside whatever size a frame header claimed, up to its own 64 GiB limit, and decoded small payloads in full before `MaxDecompressedSize` was checked. The decoder now has `MaxDecompressedSize` as its limit, and the input that showed it is in the seed corpus.
Model: opus-5-5
Labelled critical: the fuzzing for this issue found that a 92-byte manifest makes any mfer command that reads it (check, fetch, list) try to allocate 8 GiB, so anyone who opens a manifest from an untrusted source can have mfer run the machine out of memory; the fix is in #120.
Model: opus-5-5
Labelled critical: the fuzzing for this issue found that a 92-byte manifest makes any `mfer` command that reads it (`check`, `fetch`, `list`) try to allocate 8 GiB, so anyone who opens a manifest from an untrusted source can have `mfer` run the machine out of memory; the fix is in https://git.eeqj.de/sneak/mfer/pulls/120.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
grep -rn "^func Fuzz" .returns nothing. There is no fuzz coverageanywhere in the repo and no
testdata/fuzzcorpus.NewManifestFromReaderis the function that parses attacker-controlledbytes: a magic prefix, a protobuf outer message, a zlib-compressed inner
payload, and an embedded signature. That is a deserializer for untrusted
input in a tool whose entire purpose is to be pointed at files fetched from
the network. It is the single highest-value fuzz target in the codebase and
currently has none.
Definition of done
FuzzNewManifestFromReaderexists and exercises the full parse path fromraw bytes.
mfer/testdata/fuzz/containing atminimum: a valid minimal manifest, a valid signed manifest, truncated
input at several offsets, correct magic with garbage after it, wrong
magic, empty input, a zlib bomb (small compressed, huge declared size),
and a declared size that disagrees with the actual decompressed length.
a manifest or an error, and must never panic, never hang, and never
allocate unboundedly.
its input is added to the committed corpus as a regression seed.
make testruns the seed corpus as ordinary unit tests (Go does thisautomatically for
Fuzz*without-fuzz) and stays within the testtimeout.
make checkpasses.TODO.mdupdated in the same commit.Implementation requirements
-fuzztoscript/test. Continuous fuzzing must not run inCI on every push — the seed corpus regression run is what belongs in
make test. If a longer fuzz run is worth having, add a separate,explicitly-invoked
make fuzztarget with a bounded-fuzztime, and sayso in the README Entrypoints section.
no gpg subprocess, no real filesystem writes outside
t.TempDir().input that causes a panic or an unbounded allocation, that is a bug in the
parser and gets fixed as one, with a note in the commit message.
MaxDecompressedSize,mfer/constants.go:13) actually holds under fuzzing — the zlib-bomb seedis there specifically to test it.
(closes #65).Built in #120: the fuzz target, the seed corpus, and
make fuzz.The decompression limit did not hold. The zstd decoder set aside whatever size a frame header claimed, up to its own 64 GiB limit, and decoded small payloads in full before
MaxDecompressedSizewas checked. The decoder now hasMaxDecompressedSizeas its limit, and the input that showed it is in the seed corpus.Model: opus-5-5
Labelled critical: the fuzzing for this issue found that a 92-byte manifest makes any
mfercommand that reads it (check,fetch,list) try to allocate 8 GiB, so anyone who opens a manifest from an untrusted source can havemferrun the machine out of memory; the fix is in #120.Model: opus-5-5