This repository was archived by the owner on Aug 12, 2026. It is now read-only.
fix: five audit findings — origin term shape, non-string keys, a quadratic scan, and two unfollowable documents - #22
Merged
Merged
Conversation
…ratic scan, and two documents that could not be followed
ORIGIN TERM SHAPE. INV-013 says the grammar has exactly four productions. The
sequence was matched against them, and the sealed CONSTRUCTOR was not: both
implementations accepted `{sealed: {template: $t, path: $.a, extra: junk}}` and
`{sealed: {template: [a, b], path: $.a}}`. A term carrying a third member, or a
template that is a sequence, is no production the grammar contains. The same
presence-versus-shape mistake the sequence check fixed one level up, one level
down. Both now require exactly two members, both scalars.
NON-STRING MAPPING KEYS. Go read a key through the YAML node's textual value
without looking at its tag, so `1: x` arrived as the name "1" —
indistinguishable from `'1': x`, which is a different key in YAML, in a model
whose purpose is unique addressing. Rust refused such documents outright, so the
two did not agree on which names a document contains. Go now rejects the key
FORM, and the quoted spelling is still refused for the ordinary reason: nothing
declares it.
The Go test asserted the old behaviour — INV-029, "the key is not declared" —
which was a rejection for the wrong reason, and one of its cases was explicitly
left unpinned with a comment saying pinning it would pin behaviour nobody
designed. It is designed now.
A QUADRATIC SCAN, TWICE. The duplicate-key pre-scan carried a comment claiming
it is linear in the input's own size. It ran `Vec::contains` before every push —
n(n-1)/2 comparisons — and `Map::insert` searched the whole vector again per
key. Two quadratics, one comment.
before 10k 1.0s 20k 2.9s 40k 11.6s (389 KB)
after 10k 0.20s 20k 0.26s 40k 0.45s 80k 0.75s
A set for the scan, and an append for the parser, which is safe because the scan
has already refused duplicates so there is nothing to overwrite. 40k keys went
from 11.6 seconds to 0.45, and the curve is linear.
THE DESCRIPTOR COULD NOT VALIDATE AGAINST ITS OWN SCHEMA. project.yaml declares
`repo_type: spec`; project.schema.yaml permitted `schema`, `workflow`, `module`.
Added, and worth saying why it survived: nothing runs that validation.
TWO DOCUMENTS THAT COULD NOT BE FOLLOWED. docs/{en,hu}/workflow.md and the
cheatsheets instructed `make release-dependency` and `make release-schema`.
Neither target has EVER existed here — both inherited from the base template —
so `make -n` returns "No rule to make target" and anyone following either page
could not begin. Replaced with the real release path: subject, verify, external
review, then the pull request into main. Each page says what it used to say and
why that was impossible, rather than quietly becoming correct.
make ci passes. Go 90.6%.
---
[signing-metadata]
key = cic-my-sign-key
signature = vault:v1:MEUCIQCie0vnRd5jTopKqdpfsxuVzqniT9SdPSlik91jQSPODwIgdDWdw8elW6pjKuJGAFlf06H2ZfV5yExrDF2w7fczq5I=
hash-algorithm = sha256
digest = 7g9KFBiLsyVJqZXEP28xOWsGf9aksb3XqtEl7i3/Gfg=
[certificate]
-----BEGIN CERTIFICATE-----
MIICBjCCAaygAwIBAgIUSnRMR6RPnEbg296XWPOqq/u5PCwwCgYIKoZIzj0EAwIw
QzELMAkGA1UEBhMCSFUxGTAXBgNVBAoMEENlbnRyYWxJbmZyYUNvcmUxGTAXBgNV
BAMMEENJQyBEZXZlbG9wZXIgQ0EwHhcNMjYwMzIwMTMyMjU5WhcNMjYxMjMxMTMy
MjU5WjBFMQswCQYDVQQGEwJIVTEZMBcGA1UECgwQQ2VudHJhbEluZnJhQ29yZTEb
MBkGA1UEAwwSR2Fib3IgWm9sdGFuIFNpbmtvMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAEIG2CVmTfmLB9pLLclj7YmP2eedAjklpy4LGrU2ijoiy6Xqpuybv7OgJe
i+ez31s65NEV8+X/ByeX1cstR988z6N8MHowCQYDVR0TBAIwADAdBgNVHQ4EFgQU
yZN6AIX/TNnIJ9GwAa/NRN3ujHAwHwYDVR0jBBgwFoAUXn6CHYzPUqU4JVP8g+OS
WeDYjhcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF
BQcDBDAKBggqhkjOPQQDAgNIADBFAiEA+bFzXRoJ4PCQbhAAtpkcMjt0vNj5rEW0
lOMBGDNyaWkCIB1vmM7PcZzv/c9bIrxF5kqv6QXomouhByUfeNUTbpKW
-----END CERTIFICATE-----
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. The origin term's shape (adversarial, additional gaps)
INV-013 says the grammar has exactly four productions. The sequence was matched against them; the sealed constructor was not. Both implementations accepted:
A term with a third member, or a
templatethat is a sequence, is no production the grammar contains. The same presence-versus-shape mistake the sequence check fixed one level up — one level down. Both now require exactly two members, both scalars.2. Non-string mapping keys (adversarial, additional gaps)
Go read a key through the YAML node's textual
.Valuewithout looking at its tag, so1: xarrived as the name"1"— indistinguishable from'1': x, which is a different key in YAML, in a model whose purpose is unique addressing. Rust refused such documents outright.The two did not agree on which names a document contains.
Go now rejects the key form; the quoted spelling is still refused for the ordinary reason (nothing declares it).
The Go test asserted the old behaviour —
INV-029, "the key is not declared" — a rejection for the wrong reason. One of its cases was explicitly left unpinned, with a comment saying pinning it would pin behaviour nobody designed. It is designed now.3. A quadratic scan, twice (adversarial F-08)
The duplicate-key pre-scan carried a comment claiming it is linear in the input's own size. It ran
Vec::containsbefore every push —n(n-1)/2comparisons — andMap::insertsearched the whole vector again per key. Two quadratics, one comment.A set for the scan, and an append for the parser — safe because the scan has already refused duplicates, so there is nothing to overwrite.
4. The descriptor could not validate against its own schema (claim F-02)
project.yamldeclaresrepo_type: spec;project.schema.yamlpermittedschema,workflow,module. Added — and worth saying why it survived: nothing runs that validation.5. Two documents that could not be followed (claim F-09)
docs/{en,hu}/workflow.mdand both cheatsheets instructedmake release-dependencyandmake release-schema. Neither target has ever existed here — both inherited from the base template — somake -nreturns "No rule to make target", and anyone following either page could not begin.Replaced with the real path: subject → verify → external review → pull request into
main. Each page says what it used to say and why that was impossible, rather than quietly becoming correct.make cipasses. Go 90.6%. 15 of 37 audit findings closed.