Closes #97.
Why
A push decides how much of this code runs. Frontmatter becomes the object every page renders, and workflow and image YAML becomes the job the runner executes. Both accept bytes from anyone allowed to push, and neither was fuzzed.
What changed
Four targets, beside the parsers they drive:
| Target | Property |
|---|---|
FuzzParseIssue |
An accepted issue renders and re-parses identically — header, body, labels — and a second write does not change a byte |
FuzzParseComment |
Same, for comments, which hold code blocks and pasted frontmatter |
FuzzParseWorkflow |
An accepted workflow is one the runner can act on: right version, at least one job, every job naming an image; and the same file validates the same way twice |
FuzzParseImage |
An accepted definition renders a Dockerfile with exactly the lines its env and run account for, so a value cannot add an instruction |
The round-trip property is the one that matters: the server rewrites the file on every edit, so an issue it can read but not write back loses whatever it could not express. Refusing is always allowed; accepting and then failing to write is not.
The one asymmetry found, and why it stays
FuzzParseComment failed in 0.37 s on a hand-written comment with no trailing
newline: the body came back as "0", and after one write as "0\n".
That is the format working as designed rather than a defect. Doc.Bytes ends a
non-empty file with a newline, Split keeps it in the body, and every writer
trims trailing newlines before it writes (renderIssue, RenderComment,
EditIssueComment). So the file the server wrote is a fixed point, and a
hand-written file is normalized once. Nothing compares raw bodies for equality, so
nothing observable depends on the extra newline.
The property now says that in the test: the body comparison is on the trimmed
form, and a second write must be byte-identical. The failing input is committed as
metadata/testdata/fuzz/FuzzParseComment/bb6303671997e95a, so it runs as a seed
on every push.
The YAML bomb
TestParseRefusesUnboundedAliasing asserts two different things, because one would
have hidden the other:
- Through
Parse, a nine-level bomb is refused — but on its unknown keys, not on the aliasing, sinceKnownFields(true)rejects the top-level anchors before expansion is reached. - Through
yaml.v3directly, the decoder refuses withdocument contains excessive aliasing. That limit is what covers a bomb placed inside a field the schema does know, and it is the reason this code can decode repository YAML at all.
Verification
- 60 seconds per target:
FuzzParseIssue4.2 M execs,FuzzParseComment6.7 M,FuzzParseWorkflow346 K,FuzzParseImage5.3 M — all clean after the normalization above. vole workflow run verifyin the pinned image: green, corpus included.CONTRIBUTING.mdnow namesmetadataandciamong the packages whose seed corpora gate a push.