Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
361 commits
Select commit Hold shift + click to select a range
a0f02b4
Let a parse_char result be compared with the only character spelling …
awsmadi Aug 26, 2026
7df7617
Report the operand a query-versus-query equality actually failed on, …
awsmadi Aug 26, 2026
7e472ce
Refuse a range check against a NaN instead of answering that it is ou…
awsmadi Aug 26, 2026
472a0a1
Refuse a multi-document stream instead of evaluating only its first d…
awsmadi Aug 26, 2026
d7a3a05
Count data-file lines and columns from one, as everything that reads …
awsmadi Aug 26, 2026
8d99c95
Resolve an empty YAML node to null rather than to the empty string
awsmadi Aug 26, 2026
9596a13
Read the YAML 1.2 core boolean set, not YAML 1.1's
awsmadi Aug 26, 2026
c4f9389
Resolve hex and 0o octal integers, and leave a leading-zero decimal a…
awsmadi Aug 26, 2026
0d96613
Keep the text of an integer too wide for i64 instead of making it a f…
awsmadi Aug 26, 2026
af5a4d8
Normalise the dotted !GetAtt short form to the list its other spellin…
awsmadi Aug 26, 2026
f4d000a
Bound how deeply a document may nest, so a deep one is refused rather…
awsmadi Aug 26, 2026
f651861
Name the key and the file when a non-string mapping key is refused
awsmadi Aug 26, 2026
184e1da
Resolve the YAML merge key instead of reading << as an ordinary key
awsmadi Aug 26, 2026
c16ed06
Keep an unrecognised ! tag instead of discarding it, and read the tag…
awsmadi Aug 26, 2026
7f37921
Pin that both document loaders read the same bytes as the same value
awsmadi Aug 26, 2026
a985063
Keep the text of a float literal that underflowed to zero
awsmadi Aug 26, 2026
335cb34
Stop reading a u64 as negative, and stop admitting a non-finite float…
awsmadi Aug 26, 2026
cdab492
Read a non-string scalar map key as the text CloudFormation would giv…
awsmadi Aug 26, 2026
813ca19
Say how many documents a refused multi-document stream holds
awsmadi Aug 26, 2026
51865e5
Report an unreadable data file as a broken input, not an internal fai…
awsmadi Aug 26, 2026
93a3caa
Regenerate the two operand-reporting fixtures the line-numbering comm…
awsmadi Aug 26, 2026
2883f67
Call an unreadable --payload the caller's input, in the test that ass…
awsmadi Aug 26, 2026
fffbacf
Read the template in rulegen with the loader that reads it in validate
awsmadi Aug 26, 2026
e2a08d6
Bound how deeply a rules file may nest, so a deep one is refused rath…
awsmadi Aug 26, 2026
78d983b
Say what a block clause's and a type block's bodies can actually hold…
awsmadi Aug 26, 2026
6689396
Hash a Char as the one-character string eq says it equals
awsmadi Aug 26, 2026
84d724f
Negate != against the left operand's unmatched values, not against th…
awsmadi Aug 26, 2026
7e8e0d3
Report the whole diff beside a refused pairing, not instead of it
awsmadi Aug 26, 2026
b52ad3a
Say that the NaN refusal in compare_eq is unreachable, and what would…
awsmadi Aug 26, 2026
05cf650
Record that the == swap made range-against-a-query-list decide elemen…
awsmadi Aug 26, 2026
4c03e50
Read a quoted `"<<"` as the ordinary key YAML says it is
awsmadi Aug 26, 2026
8b72638
Resolve a duplicate key inside a merge the way one in a plain mapping…
awsmadi Aug 26, 2026
394c9d8
Stop counting a trailing `---` as a second document
awsmadi Aug 26, 2026
1cb8594
Refuse an out-of-set payload under `!!bool` instead of returning a st…
awsmadi Aug 26, 2026
2668f37
Resolve the merge key and non-string scalar keys in the serde loader too
awsmadi Aug 26, 2026
83e5791
Record what the null resolution costs the `empty` operator
awsmadi Aug 26, 2026
cb5c05a
Settle what a non-string key renders as, in one comment instead of two
awsmadi Aug 26, 2026
493c5af
Take the amended stringify_scalar_key, which cannot hand back a default
awsmadi Aug 26, 2026
4ddb6ae
Parse a clause's query once rather than once per reading of it
Aug 26, 2026
3bf51b8
Record that the query filter does abort, and at what depth
Aug 26, 2026
afd4885
Correct the filter abort numbers, and drop a cause I had not checked
Aug 26, 2026
9ce1034
State the abort boundary as a band, since a single rung was wrong twice
Aug 26, 2026
3615a12
Bound the three nesting constructs the first depth bound left aborting
awsmadi Aug 26, 2026
9302e83
Treat an empty document as a separator at the leading end of a file too
awsmadi Aug 26, 2026
2097f16
Answer an unreadable test-case input with the test error code, not IN…
awsmadi Aug 26, 2026
af608df
Name the symbol, not the line, in every cross-file reference a commen…
awsmadi Aug 26, 2026
42368fd
Correct the filter abort threshold, and stop justifying 128 with a de…
awsmadi Aug 26, 2026
1937960
Record that the abort this bound prevents is thread-dependent, and th…
awsmadi Aug 26, 2026
f50172c
Bisect the filter abort boundary rung by rung, and name the one rung …
awsmadi Aug 26, 2026
c07b6c3
Make every row in the depth table renderable, and name the shape that…
awsmadi Aug 26, 2026
dbdaa24
Record both measurements of the unrepeatable rung, not just mine
awsmadi Aug 26, 2026
7593ae8
Restore the linear-time regression test dropped by the merge
awsmadi Aug 26, 2026
fe5c64e
Attribute the nine-rung disagreement over the filter's libtest ceilin…
awsmadi Aug 26, 2026
e323f71
State the guard's stack cost as 13, not "about twelve"
awsmadi Aug 26, 2026
cfcdaa6
Assert the filter's accepted side at the limit, now that it can be
awsmadi Aug 26, 2026
4b5b75d
docs(parser): spell a real function name in the depth table's call rows
awsmadi Aug 26, 2026
3a5f89d
docs(parser): correct the residual component to nineteen functions
awsmadi Aug 26, 2026
33944ca
docs(parser): give the nearest abort as 1107 in the argument for 128
awsmadi Aug 26, 2026
4735856
docs(parser): state the ten abort depths' actual range in the paired …
awsmadi Aug 26, 2026
47e86b6
fix(tests): parse near the depth bound on a stack sized for it, not l…
awsmadi Aug 26, 2026
685ea19
fix(functions): keep a member named "<<" through json_parse
awsmadi Aug 26, 2026
8c24258
Report a per-case test failure per case in the structured reporters too
awsmadi Aug 26, 2026
d362225
Correct what the structured reporter did with an unreadable input: 25…
awsmadi Aug 26, 2026
982aa4f
docs(parser): give the corpus maximum as 7 levels, and stop resting t…
awsmadi Aug 26, 2026
188fadb
fix(parser): stop the depth refusal quoting a corpus statistic it can…
awsmadi Aug 26, 2026
e2818fc
docs(parser): drop the 8.7x, which was still computed from the pre-bi…
awsmadi Aug 26, 2026
2bd2214
docs(parser): state the six guard sites without a call-graph count no…
awsmadi Aug 26, 2026
7feaa75
docs(loader): correct the document-depth corpus figures, and record h…
awsmadi Aug 26, 2026
655f061
fix(loader): stop the document depth refusal quoting a corpus statist…
awsmadi Aug 26, 2026
dff8455
fix(ffi): spell "incorrectly encoded" so typos stops failing typocheck
awsmadi Aug 26, 2026
3b9aeb7
fix(test): make the four output formats agree on the exit code and th…
awsmadi Aug 26, 2026
8e5144a
refactor(test): write the test-file parse-error sentence in one place
awsmadi Aug 26, 2026
8e2f5c1
fix(ci): decide the Windows registry integration tests on exit code
awsmadi Aug 26, 2026
2b5055d
ci: pin aws-guard-rules-registry to a SHA instead of main
awsmadi Aug 26, 2026
5dc5e46
fix(ci): print the skipped-rule count instead of the variable's name
awsmadi Aug 26, 2026
74ef958
docs(loader): label the conversion ceiling by profile, and stop stati…
awsmadi Aug 26, 2026
37459e6
docs(loader): the path-string cost is cubic in the depth, not quadratic
awsmadi Aug 26, 2026
a9a0ae2
fix(loader): re-evidence the serde agreement that justifies 128, and …
awsmadi Aug 26, 2026
8a0016f
docs(parser): take the file counts out of the corpus paragraph and ke…
awsmadi Aug 26, 2026
2de1283
docs(loader): take the file counts out of the depth table and keep th…
awsmadi Aug 26, 2026
159bcb5
docs(loader): correct the unoptimized per-level stack cost, and bisec…
awsmadi Aug 27, 2026
cd63bec
fix(loader): `Loader::load` needs at most 16 KB, not 1087 KB -- the o…
awsmadi Aug 27, 2026
f383608
fix(loader): the cubic time is repeated whole-tree Debug rendering, n…
awsmadi Aug 27, 2026
5144708
docs(loader): say that the re-derivation recipe needs the bound raised
awsmadi Aug 27, 2026
a2c159a
fix(loader): localize the cubic time to one discarded format!, and se…
awsmadi Aug 27, 2026
5b46d83
docs(loader): cut the measurement record to the argument, the figures…
awsmadi Aug 27, 2026
40460f8
fix(loader): correct two false instructions -- which build each figur…
awsmadi Aug 27, 2026
ea09021
fix(loader): a JSON test spec does print "recursion limit exceeded", …
awsmadi Sep 2, 2026
3d14af9
docs(loader): bound the cost at the depth limit instead of quoting a …
awsmadi Sep 2, 2026
2a91712
docs(loader): give the cost at the bound as a figure with its measuri…
awsmadi Sep 2, 2026
3477a02
fix: spell "unparsable" the way typos expects
awsmadi Sep 2, 2026
c734c33
docs(loader): the at-the-bound figure moves with core contention, not…
awsmadi Sep 2, 2026
2c1faa3
docs(loader): give load a curve instead of a point, and say which doc…
awsmadi Sep 2, 2026
0a4e8c5
docs(loader): cut the depth-bound comment to the guardrail
awsmadi Sep 2, 2026
559bdd2
docs(loader): restore the guardrails the last cut dropped
awsmadi Sep 2, 2026
b9ca2d7
fix(parse-tree, test): check a rules path before opening it, so the c…
awsmadi Sep 2, 2026
5a3bfd5
test(sarif, test-command): make two assertions able to fail
awsmadi Sep 2, 2026
d61ca14
fix(eval): three list-shaped comparisons that answered by accident
awsmadi Sep 2, 2026
3fd6367
fix(eval): decide each rhs element's reading per element, not per list
awsmadi Sep 2, 2026
5c73389
test(test): pin the precedence between an unchecked and an unmet expe…
awsmadi Sep 2, 2026
3986420
ci(registry): assert the corpus's known state, not that it passes
awsmadi Sep 2, 2026
4d76d25
Say why the one ignored test is ignored, and that it once passed
awsmadi Sep 2, 2026
33506a2
ci(registry): do not let an unusable TMPDIR kill the corpus check
awsmadi Sep 2, 2026
3572d9d
ci: stop asserting a pwsh exit-code mechanism nobody could source
awsmadi Sep 2, 2026
8eb8573
ci(registry): decide the temp root by trying it, not by testing it first
awsmadi Sep 2, 2026
258772a
ci(registry): the .exe note had it backwards, and the branch is proba…
awsmadi Sep 2, 2026
261c40d
fix(eval): a one-element list on the right of == answered differently…
awsmadi Sep 2, 2026
aae25d0
fix(eval): substring IN answered a query on the right differently fro…
awsmadi Sep 2, 2026
980a6be
fix(eval): a partly-contained list on the left of NOT IN admitted the…
awsmadi Sep 3, 2026
c09a0c9
ci(registry): locate the state files from where this script really is…
awsmadi Sep 3, 2026
432afac
ci(registry): report the bytes when two sides of a set comparison ren…
awsmadi Sep 3, 2026
f0fce73
fix(eval): a list of non-strings on the left of NOT IN admitted the v…
awsmadi Sep 3, 2026
193a11b
test(eval): state what a denylist holding a nested list admits, and w…
awsmadi Sep 3, 2026
d027d76
fix(eval): NOT IN denied every value whenever the denylist held a nes…
awsmadi Sep 3, 2026
d26fef1
fix(eval): a non-string value on the left of NOT IN admitted the hays…
awsmadi Sep 3, 2026
57f6767
fix(eval): == refused a one-element list written as a rule literal
awsmadi Sep 3, 2026
689b417
fix(eval): say what the unanswerable-containment suppression does, an…
awsmadi Sep 3, 2026
ec4b5f7
test(eval): state what a range in a list denylist admits, and watch i…
awsmadi Sep 3, 2026
0d2694f
fix(eval): a range in a list denylist admitted every list-valued prop…
awsmadi Sep 3, 2026
a0da535
test(eval): state what a queried denylist admits, and watch it fail
awsmadi Sep 3, 2026
05232a2
fix(eval): a queried denylist admitted a value whose element it names
awsmadi Sep 3, 2026
5613c46
docs(eval): NotAString's justification was false for a Map
awsmadi Sep 3, 2026
20ca56c
test(eval): pin which spelling of a queried denylist reaches which arm
awsmadi Sep 3, 2026
dc05c6f
docs(eval): the denylist rule is stated for a non-empty left-hand list
awsmadi Sep 3, 2026
bc86fe6
test(eval): assert which unanswerable-containment reason each value gets
awsmadi Sep 3, 2026
845634e
test(eval): cover a range and a regex inside a one-element list
awsmadi Sep 3, 2026
401a110
test(eval): state which clauses the NOT IN notice belongs to, and wat…
awsmadi Sep 3, 2026
f3eb258
fix(eval): the NOT IN deprecation notice said "passed" beside a failure
awsmadi Sep 3, 2026
b9a5435
docs(eval): say why aligning the notice predicate must wait, and bind…
awsmadi Sep 3, 2026
133846a
docs(eval): `is_one_of` swallows `NotComparable`, which arrives const…
awsmadi Sep 3, 2026
ea2fed5
docs(eval): name both cells the denylist iff excepts, and where the g…
awsmadi Sep 3, 2026
2631880
docs(eval): the whole-list membership swallow has no coverage at all
awsmadi Sep 3, 2026
b05f922
docs(eval): the notice predicate is wrong in both directions, with co…
awsmadi Sep 3, 2026
e331c6b
fix(eval): NOT IN admitted a value a right-expanded denylist named
awsmadi Sep 3, 2026
54bd23b
test(eval): pin that a successful list containment carries an empty diff
awsmadi Sep 3, 2026
8ed1b54
docs(eval): say why the right-expanded arm was missing, structurally
awsmadi Sep 3, 2026
5abf9c9
test(eval): ask what an empty left-hand list does beside a mixed deny…
awsmadi Sep 3, 2026
4609b60
fix(eval): a denylist that grew stopped denying an empty list
awsmadi Sep 3, 2026
7764521
docs(eval): record the open convention question at the line that deci…
awsmadi Sep 3, 2026
8636dd5
test(parser): state the line-ending invariant, and watch a bare-CR fi…
awsmadi Sep 3, 2026
bdcafac
fix(parser): count a line the way this parser reads one, not the way …
awsmadi Sep 3, 2026
26cda03
test(parser): watch a repeated map key decide a verdict by entry order
awsmadi Sep 3, 2026
f55c97f
fix(parser): refuse a map literal that names the same key twice
awsmadi Sep 3, 2026
08e16ae
fix(parser): count the eleventh position site the same way, unreachab…
awsmadi Sep 3, 2026
99c9038
test(eval): the NOT IN notice is suppressed on the clause it was writ…
awsmadi Sep 3, 2026
127ee19
fix(eval): the NOT IN notice skipped the silent-green case it was wri…
awsmadi Sep 3, 2026
8953126
docs(eval): two comment repairs around the membership notices
awsmadi Sep 3, 2026
16342c7
test(eval): a spent backtracking budget is read as an incomparable pair
awsmadi Sep 3, 2026
1584008
fix(eval): a regex that ran out of budget is not an incomparable pair
awsmadi Sep 3, 2026
2418fb5
test(utils): watch a bare-CR template render its whole self as line 1
awsmadi Sep 3, 2026
37e6f22
fix(utils): end a line where libyaml ends one, not where str::lines does
awsmadi Sep 3, 2026
9618fb9
docs(eval): the [*] bypass closed, so the note blocking alignment has…
awsmadi Sep 3, 2026
bae6b12
test(eval): the right-expanded nested entry has no repair inside its …
awsmadi Sep 3, 2026
a27b42e
test(eval): a list denylist admits a regex it could not evaluate
awsmadi Sep 3, 2026
158932b
fix(eval): a list denylist refuses a regex it could not evaluate
awsmadi Sep 3, 2026
27383c9
test(eval): sharpen the right-expanded argument from difficult to imp…
awsmadi Sep 3, 2026
9bcf205
fix(eval): an expanded denylist stopped denying an empty list
awsmadi Sep 3, 2026
74b90fb
test(eval): the over-denial is the same impossibility, and the pair p…
awsmadi Sep 3, 2026
53ace13
docs(eval): the right-expanded over-denial is this effort's, and the …
awsmadi Sep 3, 2026
c0a0a2c
Count all five libyaml line breaks in ReadCursor
awsmadi Sep 3, 2026
60400d1
Stop emit_messages panicking on a whitespace-only message
awsmadi Sep 3, 2026
f6639d5
fix(eval): the vacuous match read a string right operand as a set
awsmadi Sep 3, 2026
0d2f634
fix(eval): a collision the denylist named was reported as undecidable
awsmadi Sep 3, 2026
167bcf5
fix(test): abbreviate a commit reference so the spell check stops rea…
awsmadi Sep 3, 2026
8b3f949
test(path_value): pin both exits of a list-against-list comparison
awsmadi Sep 3, 2026
3e03ca9
docs(eval): a list-against-list comparison can refuse, so the whole-list
awsmadi Sep 3, 2026
d5e2bf3
docs(operators): date the probe counts and name the probe that reprod…
awsmadi Sep 3, 2026
d220925
fix(eval): the NOT IN notice warned about a change that does not move…
awsmadi Sep 3, 2026
7679a4b
fix(eval): a deprecation notice could not name which clause it was about
awsmadi Sep 3, 2026
291aac2
Point the repeated-map-key refusal at the literal
awsmadi Sep 3, 2026
afbd64f
Restore the displaced source index instead of clearing it
awsmadi Sep 3, 2026
266bb06
Correct why the map-key refusal does not reach documents
awsmadi Sep 3, 2026
c4ebd2c
fix(utils): guard NUL, and correct two false claims about the naive e…
awsmadi Sep 3, 2026
6aeb59b
fix(eval): one value's spent regex budget silenced another value's no…
awsmadi Sep 3, 2026
16d8e59
docs(eval): a Silent cell was justified by a report only its assertio…
awsmadi Sep 3, 2026
20c72f0
fix(eval): fail a gate closed when its comparison had no answer
awsmadi Sep 3, 2026
e8a03dd
style: rustfmt the two lines a merge pushed past the wrap threshold
awsmadi Sep 3, 2026
f1c26e9
fix(eval): the NOT IN notice asked about a comparison the clause neve…
awsmadi Sep 3, 2026
e7acaf8
Correct the surviving copy of two false claims about the naive extension
awsmadi Sep 3, 2026
4035f8c
Delete a false cross-format justification from emit_messages
awsmadi Sep 3, 2026
cf5b469
Pin the restore half of SourceScope's Drop
awsmadi Sep 3, 2026
3fe2c62
fix(eval): a decided failure decides a match_all gate, undecided sibl…
awsmadi Sep 3, 2026
9d6ecdb
fix: carry deprecation notices through the structured validate reporters
awsmadi Sep 3, 2026
a12ff5f
fix: locate a rules file by its path, not by its basename
awsmadi Sep 3, 2026
5f4dc5f
test(eval): cover the IN polarity the vacuous match had no cell for
awsmadi Sep 3, 2026
b8d3901
docs(operators): the vacuous match's IN polarity has no repair at its…
awsmadi Sep 3, 2026
4ac0f50
test: cover the three fix sites a one-line revert left green
awsmadi Sep 3, 2026
02c4855
docs(validate): the notice count was a symptom for the four structure…
awsmadi Sep 3, 2026
c67f877
fix(eval): the NOT IN notice asked about one granularity, and the ope…
awsmadi Sep 3, 2026
0777438
fix(eval): a spent regex budget in a map-key filter exited 255
awsmadi Sep 3, 2026
f54089b
docs(eval): the ninth comparison site owes nothing, and the zero is o…
awsmadi Sep 3, 2026
c6ccc57
fix(test): name a rules file that declares no rules by its path
awsmadi Sep 3, 2026
abbf73a
test: cover the no-rules locator, and narrow two cells to what discri…
awsmadi Sep 3, 2026
69628df
fix(operators): a skipped string pairing is not an unmatched value
awsmadi Sep 3, 2026
e26817a
fix(operators): the uncompared set has to leave `lhs`, not only `diff`
awsmadi Sep 3, 2026
ec2b9a5
fix(eval_context): a skip reason named a condition the rule did not c…
awsmadi Sep 3, 2026
f2f87b8
test(validate): IN and NOT IN report a comparison they never finished
awsmadi Sep 3, 2026
d63c171
fix(eval): IN and NOT IN carry the reason a comparison had no answer
awsmadi Sep 3, 2026
3957260
fix(test): name the rules file in an unchecked-expectation diagnostic
awsmadi Sep 3, 2026
a1e552f
docs: five stale comment claims, four of them invalidated by this branch
awsmadi Sep 3, 2026
82f7f29
docs: three stale comment claims, and re-derive every figure at this …
awsmadi Sep 3, 2026
88e2142
test(eval): IN and NOT IN drop the reason a map-key comparison had no…
awsmadi Sep 3, 2026
56c95a5
fix(reporters): a refused membership no longer claims a match happened
awsmadi Sep 3, 2026
ee60bc5
fix(eval): a skipped pairing earned a sibling value a false membershi…
awsmadi Sep 3, 2026
1fd235a
docs(test): name the case that exists
awsmadi Sep 3, 2026
dd24954
docs(eval): say that a skipped pairing beside a compared sibling does…
awsmadi Sep 3, 2026
5ed7948
test: a silence assertion needs the notice that IS emitted beside it
awsmadi Sep 3, 2026
c5b2b39
test: name both notice channels in the stay-quiet control
awsmadi Sep 3, 2026
3a0fcd8
test: the liveness control has to be able to fail
awsmadi Sep 3, 2026
bb5aee5
test: count the liveness control's call sites
awsmadi Sep 3, 2026
9a44931
docs(reporters): two live renderers carry this sentence, not three
awsmadi Sep 3, 2026
a4eaf4e
fix(eval_context): a skip reason read the referenced rule's gate as i…
awsmadi Sep 3, 2026
1a6b41a
docs: four stale suite absolutes, a quotation nobody wrote, and a lin…
awsmadi Sep 3, 2026
34d965b
test(eval): pin each loop table's length so a deleted cell cannot pas…
awsmadi Sep 3, 2026
b49818b
docs: three more claims this branch's messages got wrong
awsmadi Sep 3, 2026
8573f19
test(eval): make the loop-table length guard extend itself
awsmadi Sep 3, 2026
1e3503f
docs: the suite ledger's last five rows, and a SHA a spell-checker re…
awsmadi Sep 3, 2026
e083cd9
test(validate): the structured membership sentence had no cell assert…
awsmadi Sep 3, 2026
234c842
test(eval): cover the element loop no cell could observe in either di…
awsmadi Sep 3, 2026
d4286e6
docs(test): the covering cell cited a loop three other lines also match
awsmadi Sep 3, 2026
d8c2582
test(eval): bind the twelve inline loop tables so a deleted cell cann…
awsmadi Sep 3, 2026
e2cacdc
fix(eval): a rule that skipped on an empty selection gave no reason a…
awsmadi Sep 3, 2026
a9ad44e
fix(parser): a refusal inside a block pointed past the whole body
awsmadi Sep 3, 2026
b1ff384
test(eval): bind the twelve single-line loop tables; ten were silentl…
awsmadi Sep 3, 2026
1ba4648
fix(eval): the membership notice paired what the operator never built
awsmadi Sep 3, 2026
80ccdbb
docs(eval): the 143-rule cost is the gate path's, not the membership …
awsmadi Sep 3, 2026
81eed7d
docs(test): two suite totals and a population belonged to other commits
awsmadi Sep 3, 2026
1b81431
fix(eval): the empty-selection reason denied a refusal it could not see
awsmadi Sep 3, 2026
1862d24
fix(eval): the membership notice counted refusals from values that ma…
awsmadi Sep 3, 2026
13ee8ff
fix(eval): restore the operator's second gate on the whole-value pairing
awsmadi Sep 3, 2026
e1e3523
test(eval): bind the call site's both_queried, which no cell could ob…
awsmadi Sep 3, 2026
8515160
docs(test): a deletion attributed to the commit that made it not compile
awsmadi Sep 3, 2026
58b5826
docs(eval): the IN-path discard is three arms, and 19 prices only one…
awsmadi Sep 3, 2026
e9d1ba9
docs(eval): a false universal held up the case against reading the re…
awsmadi Sep 3, 2026
3309cf3
docs(ci): a designed corpus signal read as host noise
awsmadi Sep 3, 2026
a8b0734
docs(test): retire four stale figures and one impossible precedence c…
awsmadi Sep 3, 2026
f71e115
test(eval): pin the endpoint change by what the predicate answers, no…
awsmadi Sep 3, 2026
e036b32
fix(eval): the whole-value gate on a matched subset, and the argument…
awsmadi Sep 3, 2026
66eb35f
docs(eval): retire ten line citations and four figures the operators …
awsmadi Sep 3, 2026
f3b534d
test(eval): attribute the endpoint cells to what actually binds them,…
awsmadi Sep 3, 2026
8a4ffe1
docs(test): drop the loop-table absolute and supply the term its chai…
awsmadi Sep 3, 2026
1260011
fix(eval): count only the element pairings the membership walk actual…
awsmadi Sep 3, 2026
4f20417
fix(eval): close the third short-circuit site, and bind it at the pre…
awsmadi Sep 3, 2026
45d6022
docs(eval): the zero that proves nothing without its positive control
awsmadi Sep 3, 2026
41e2373
fix(eval): the whole-value walk stops where the operator's walk stops
awsmadi Sep 3, 2026
2917542
test(eval): the whole-value cell that looked discriminating and was not
awsmadi Sep 3, 2026
99a98af
fix(eval): the element loop runs only where the arm builds element pa…
awsmadi Sep 3, 2026
4215184
test(eval): the redundant early return, and what the cells can and ca…
awsmadi Sep 3, 2026
84c1dc2
fix(eval): element_pairings_built's contract needs its precondition
awsmadi Sep 3, 2026
46d98bd
docs(eval): scope the binding claim to the arms the cells actually reach
awsmadi Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/registry-corpus-state/orphaned-test-files.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Test files under a tests/ directory in the pinned aws-guard-rules-registry corpus that no .guard
# file claims, so nothing runs them. Paths are relative to the rules directory passed to
# `cfn-guard test -d`, with forward slashes so one list holds for the Windows job too.
#
# Listed here because an orphaned test file moves nothing: no verdict, no failure count, and no exit
# code. A rules file renamed without its tests, or a tests/ file whose stem stopped matching, reads
# as a clean run everywhere else. So this is the only thing that would notice a fourth one.
#
# A rules file is paired with a test file by longest matching filename stem, so these three are
# orphaned for two different reasons. The first two have no .guard file with a matching stem at all.
# The third begins with a literal space -- the file is named " cloudfront_accesslogs_enabled_tests.yml"
# -- which is why its stem matches nothing; leave the space alone, it is the defect being recorded,
# and renaming the file upstream is what removes this line.

aws/amazon_rds/tests/rds_instance_logging_enabled_tests.yml
aws/amazon_s3/tests/s3_bucket_default_lock_enable_tests.yml
aws/cloudfront/tests/ cloudfront_accesslogs_enabled_tests.yml
30 changes: 30 additions & 0 deletions .github/registry-corpus-state/unchecked-expectations.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Every expectation in the pinned aws-guard-rules-registry corpus that names a rule its .guard file
# never declares, so the expectation is never consulted and gets no verdict. `<rule-name> <count>`,
# where the count is how many test cases carry an expectation for that name.
#
# All 30 of them are in one file, rules/aws/aws_cloudformation/cfn_no_explicit_resource_names.guard.
# That file used to declare per-resource-type rules with these names and now has a single aggregate
# rule; the test file was never updated, so each of these asserts nothing.
#
# This list is what makes the integration jobs' green mean something. `cfn-guard test -d` over this
# corpus exits 1, and 1 is also what a run that genuinely broke would give, so the exit code alone
# cannot tell the two apart -- see .github/scripts/check-registry-corpus.sh. Read it as a lock on a
# known-bad state: a name appearing here is a new dead expectation and turns the job red, and a name
# disappearing means someone fixed one and this file has to say so.
#
# The count is here and not just the name so that a name losing some of its expectations, but not
# all, is still a change. The fix that empties this file is
# aws-cloudformation/aws-guard-rules-registry#288; when it merges, the pin in
# .github/workflows/pr.yml moves forward and these lines go with it.

no_static_names 2
static_alarmname 2
static_dbinstanceidentifier 2
static_deploymentconfigname 2
static_deploymentgroupname 2
static_domainname 2
static_groupname 4
static_managedpolicyname 2
static_name 7
static_repositoryname 2
static_rolename 3
487 changes: 487 additions & 0 deletions .github/scripts/check-registry-corpus.sh

Large diffs are not rendered by default.

137 changes: 112 additions & 25 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Shellcheck
run: shellcheck install-guard.sh
run: shellcheck install-guard.sh .github/scripts/check-registry-corpus.sh

formatting:
name: Formatting check (cargo fmt)
Expand Down Expand Up @@ -93,6 +93,37 @@ jobs:
components: clippy
- run: cargo clippy -- -D warnings

ffiHeader:
name: Compile the FFI header and example (C and C++)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Build the FFI dylib
run: cargo build --release -p cfn-guard-ffi
# The header shipped with two defects that only a compiler finds, so nothing had ever compiled
# it: `int32_t` with no `#include <stdint.h>`, which broke the example it ships next to, and a
# parameter named `template`, which is a C++ keyword and broke the `#include` itself. Both are
# one-line fixes, and both come back the moment someone edits the header unless something
# compiles it.
- name: Header alone, as C and as C++
run: |
set -e
cd guard-ffi/example
printf '#include "cfn_guard.h"\nint main(void){return 0;}\n' > /tmp/header_only.c
printf '#include "cfn_guard.h"\nint main(){return 0;}\n' > /tmp/header_only.cpp
gcc -std=c11 -Wall -Wextra -Werror -c /tmp/header_only.c -I. -o /dev/null
g++ -std=c++17 -Wall -Wextra -Werror -c /tmp/header_only.cpp -I. -o /dev/null
# Links as well as compiles, which is what checks the `extern "C"` guard: without it a C++
# caller gets C++ name mangling and fails at the link step rather than the compile step.
- name: Example, linked against the dylib and run
run: |
set -e
cd guard-ffi/example
gcc -std=c11 -Wall -Wextra -Werror cfn_guard_test.c -I. -o /tmp/cfn_guard_test \
-L../../target/release -lcfn_guard_ffi -Wl,-rpath,"$PWD/../../target/release"
/tmp/cfn_guard_test

typocheck:
name: Spell Check with Typos
runs-on: ubuntu-latest
Expand Down Expand Up @@ -146,15 +177,34 @@ jobs:
with:
repository: aws-cloudformation/aws-guard-rules-registry
path: aws-guard-rules-registry
ref: main
# Pinned to a SHA rather than tracking `main`. A moving ref means a commit in another
# repository can turn this repository's CI red with no change here, and by the time anyone
# looks the corpus has moved on again, so the failure is not reproducible. This SHA is the
# `main` commit these integration tests were last measured against.
#
# Read it as a lock on a known state, not on a green one. `cfn-guard test -d rules` exits
# 1 at this commit: 30 expectations across 11 distinct rule names in
# rules/aws/aws_cloudformation name a rule their file never defines, so those expectations
# get no verdict. Pinning makes that failure deterministic and reproducible; it does not
# fix it. The fix is aws-cloudformation/aws-guard-rules-registry#288 -- once that merges,
# move this SHA forward to a commit that contains it, here and in the Windows job below.
ref: 7f7340c26ae5d5e8874651dbffeb12e0e9f505b6
# A green here means the pinned corpus is in the known-bad state the comment above describes,
# not that the corpus passes. This step used to assert exit 0, which cannot hold at this SHA --
# `cfn-guard test -d` exits 1 over these rules and is right to. Asserting nothing more than
# "exit 1" would be worse than asserting the wrong thing, though: `get_exit_code` in
# guard/src/commands/test.rs makes the error code sticky, so a run where a rule ran and failed
# its expectation for real exits 1 too, and tolerating that number would hide it. So the check
# holds the report against checked-in lists in .github/registry-corpus-state/ instead -- which
# expectations go unchecked, which test files nothing claims, and that nothing else went wrong.
# Fixing the corpus turns this red on purpose: when
# aws-cloudformation/aws-guard-rules-registry#288 merges, the pin and those lists move
# together, and the lists become empty.
- name: Run integration tests using test command
run: |
if cloudformation-guard/target/release/cfn-guard test -d aws-guard-rules-registry/rules; then
echo "The integration tests for test command have passed."
else
echo "The integration tests for test command have failed."
exit 1
fi
bash cloudformation-guard/.github/scripts/check-registry-corpus.sh \
cloudformation-guard/target/release/cfn-guard \
aws-guard-rules-registry/rules

- name: Run integration tests using parse-tree command
run: |
Expand Down Expand Up @@ -214,16 +264,44 @@ jobs:
with:
repository: aws-cloudformation/aws-guard-rules-registry
path: aws-guard-rules-registry
ref: main
# The same pin as the linux/macos job above, and it has to stay in step with it: a SHA
# here and `main` there would have the two jobs testing different corpora, which is the
# one thing worse than both being wrong. See that job for why this commit is a known
# failing state, and for the PR that releases the pin
# (aws-cloudformation/aws-guard-rules-registry#288).
ref: 7f7340c26ae5d5e8874651dbffeb12e0e9f505b6
# The same script the linux/macos job runs, under `shell: bash` -- Git for Windows' bash, which
# this runner image ships -- rather than a pwsh transcription of it. See that job for what a
# green here means, and for why it is not "the corpus passes".
#
# One implementation and not two because the two had already drifted, on the one thing this
# check is about. This step was `cfn-guard test -d ...` followed by `if ($LASTEXITCODE -eq 0)`,
# and before that `if (<command>)` -- which does not test a command's exit code in PowerShell.
# The parenthesized expression captures the command's stdout and `if` coerces that to a
# boolean, where a collection of more than one element is always true. `cfn-guard test -d`
# prints thousands of lines of results against this ruleset, so the condition could never be
# false: the `else` was unreachable, and the step logged "have passed" for a run that had
# exited non-zero, while the linux/macos twin was testing exit status. The parse-tree step
# below is still pwsh and carries its own copy of that note, because its loop has nothing to
# share with a twin.
#
# Under `shell: bash` the step's status is the shell's, and the script is the whole body, so its
# exit status is the step's with nothing after it to overwrite the number. That follows from the
# invocation the workflow-syntax reference documents: `bash --noprofile --norc -eo pipefail {0}`,
# using the Git for Windows bash on this platform.
#
# How a pwsh step's exit code is derived is deliberately not asserted here. An earlier draft of
# this comment said Actions appends `exit $LASTEXITCODE` to every pwsh step; that is widely
# repeated and may well be true, but it could not be sourced -- the workflow-syntax reference
# truncates before any exit-code section and the add-scripts guide does not cover it. The
# observation above needs no such mechanism: the step logged "have passed" and the job was red,
# which is enough to establish that the condition was not testing what it appeared to.
- name: Run integration tests using test command
shell: bash
run: |
if (cloudformation-guard/target/release/cfn-guard test -d aws-guard-rules-registry/rules) {
echo "The integration tests for test command have passed."
}
else {
echo "The integration tests for test command have failed."
exit 1
}
bash cloudformation-guard/.github/scripts/check-registry-corpus.sh \
cloudformation-guard/target/release/cfn-guard \
aws-guard-rules-registry/rules

- name: Run integration tests using parse-tree command
run: |
Expand All @@ -234,21 +312,30 @@ jobs:

$rules = @(Get-ChildItem -Path .\ -Filter *.guard -Recurse -File)

# `elseif (<command>)` had the same defect as the test-command step above: it read the
# parse tree that `parse-tree` prints on stdout as the verdict, rather than the exit code.
# That happened to give the right answer for the two outcomes this command has today,
# because the success path always prints a non-empty tree and both failure paths print
# only to stderr -- but it silently passed any run that wrote partial output and then
# failed, so the step could print "All the rules have succeeded" and still exit non-zero.
# How the non-zero reached the step's own status is not asserted here; see the note on the
# test-command step above for why the pwsh exit-code mechanism is left unstated.
Foreach ($rule in $rules) {
$rule_files_without_comments = (Get-Content $rule.FullName) -replace '^[ \s]*#.*$', ''
if ([String]::IsNullOrWhiteSpace($rule_files_without_comments)){
$SKIPPED_RULES += "$rule"
}
elseif (../../cloudformation-guard/target/release/cfn-guard parse-tree --rules $rule.FullName) {
continue
} else {
$FAILED_RULES += "$rule"
}
if ([String]::IsNullOrWhiteSpace($rule_files_without_comments)) {
$SKIPPED_RULES += "$rule"
continue
}

../../cloudformation-guard/target/release/cfn-guard parse-tree --rules $rule.FullName
if ($LASTEXITCODE -ne 0) {
$FAILED_RULES += "$rule"
}
}

$SKIPPED_RULE_COUNT = $SKIPPED_RULES.Length
if ($SKIPPED_RULE_COUNT -gt 0) {
echo "The following `$SKIPPED_RULE_COUNT.Length` rule(s) were skipped because they contained only comments:"
echo "The following $SKIPPED_RULE_COUNT rule(s) were skipped because they contained only comments:"
echo $SKIPPED_RULES
}

Expand Down
17 changes: 17 additions & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,22 @@ ource = "ource"
[default.extend-identifiers]
# Ignore ID in evaluate_tests.rs
fooCounterTaskDef49BA9021 = "fooCounterTaskDef49BA9021"
# `typos` reads `ba` as `by`/`be`, so a short SHA is uncitable in prose when the PREFIX being
# written contains `ba`. Truncation is not a general remedy and not a general failure either: it
# removes a TRAILING `ba` and cannot remove an interior one. Measured, typos-cli 1.49.0, one SHA
# per file:
#
# 34d965ba rc=2 1ba4648d rc=2
# 34d965b rc=0 1ba4648 rc=2
# 34d96 rc=0 1ba rc=2
#
# So check the prefix you are about to write, and allowlist only when it flags. An earlier version
# of this comment said "every prefix length flags, because the token sits between digits" -- that
# is false of `34d965ba`, whose seven-character prefix passes, and true only of `1ba4648d`, which
# did not exist when it was written. Both entries below are needed because both SHAs are cited at
# full length: the suite ledger at the head of `eval_tests.rs` names the commit each figure was
# measured at, so the alternative to these lines is a row that cannot say what it measured.
34d965ba = "34d965ba"
1ba4648d = "1ba4648d"
[files]
extend-exclude = ["guard/ts-lib", "action"]
2 changes: 1 addition & 1 deletion docs/FUNCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ rule check when %security_group !EMPTY {

This function converts strings, and ints to their char equivalents

NOTE: this function will cause an error if the int is not 0 <= n <= 9, it will also error out if a string has a length > 1
NOTE: this function will cause an error if the int is not 0 <= n <= 9, it will also error out if a string is longer than one character. Length is counted in characters, not bytes, so a single character that is not ASCII converts like any other.

#### Argument(s)

Expand Down
10 changes: 10 additions & 0 deletions docs/KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ let api_gws = Resources.*[ Type == 'AWS::ApiGateway::RestApi' ]
2. When performing `!=` comparison, if the values are incompatible like comparing a `string` to `int`, an error is thrown internally but currently suppressed and converted to `false` to satisfy the requirements of Rust’s [PartialEq](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html). We are tracking to release a fix for this issue soon.

`integer` and `float` are no longer among the incompatible pairs: they compare against each other as numbers, so `Size > 10` holds for a `Size` of `50.5` and `Size == 50` holds for a `Size` of `50.0`. See [Guard: Clauses](CLAUSES.md) for the details, including why it mattered inside a `when` condition.

The non-finite spellings are strings, not numbers. `nan`, `inf` and `infinity` in a document are read as strings, which is what YAML resolves them to -- it spells the non-finite floats `.nan` and `.inf`, and those were always read as strings here. A clause comparing one of them against a number is therefore an incompatible pair as described above. A number in a document that is out of range for a 64 bit float, such as `1e999`, is read the same way and for the same reason -- it would otherwise become an infinity, which no comparison can decide against. A float literal in a *rule* that is out of range, or that rounds to zero, is a parse error rather than a silently different bound, because a rule is authored and its author can be told.
3. `exists` and `empty` checks do not display the JSON pointer path inside the document in the error messages. Both these clauses often have retrieval errors which does not maintain this traversal information today. We are tracking to resolve this issue.
4. <a name="function-limitation"></a> **No support for calling functions inline on the LHS of an operator**

Expand Down Expand Up @@ -109,3 +111,11 @@ let api_gws = Resources.*[ Type == 'AWS::ApiGateway::RestApi' ]
%root.Properties."some-key" == true
}
```

Key names that read as integers need the same quoting, for a different reason: unquoted, `.80` is an
array index, because that is how a list element is addressed without brackets. Quoted, `."80"` is a
key name. So an account id under `Mappings` is written

```
Mappings.AccountToEnv."123456789012".Env == "prod"
```
29 changes: 28 additions & 1 deletion guard-ffi/example/cfn_guard.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
#ifndef CFN_GUARD_H
#define CFN_GUARD_H

/* int32_t below. The header declared it without including anything, so it did not compile on its
* own: cfn_guard_test.c includes only stdio.h and stdlib.h, neither of which provides int32_t, and
* `gcc -std=c11 -c cfn_guard_test.c` failed with "unknown type name 'int32_t'". */
#include <stdint.h>

/* `bool` below, rather than the `_Bool` this used to declare. `_Bool` is a C keyword and is not a
* type in C++, so the header could not be included from C++ even after the parameter rename. In C99
* and later `bool` is a macro for `_Bool`, so the declared type and its width are unchanged. */
#ifndef __cplusplus
#include <stdbool.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
int32_t code;
char *message;
Expand All @@ -11,7 +27,18 @@ typedef struct {
char *file_name;
} validate_input_t;

char* cfn_guard_run_checks(validate_input_t template, validate_input_t rules, _Bool verbose, extern_err_t * err);
/* Every pointer in `data` and `rules` must be non-null and must hold valid UTF-8. A null one gives
* code 23 and a non-UTF-8 one gives code 24, both naming the field in `err.message`.
*
* The first parameter was named `template`, which is a reserved word in C++, so a C++ translation
* unit failed at the `#include` with "expected ',' or '...' before 'template'". It is now `data`,
* matching the Rust side. */
char *cfn_guard_run_checks(validate_input_t data, validate_input_t rules, bool verbose,
extern_err_t *err);
void cfn_guard_free_string(char *);

#ifdef __cplusplus
}
#endif

#endif
6 changes: 4 additions & 2 deletions guard-ffi/example/cfn_guard_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ int main() {
data.file_name = "data.json";
rules.content = "rule check_foo { foo.bar == true }";
rules.file_name = "check.rule";
char* result = cfn_guard_run_checks(data, rules, 0, &err);
char* result = cfn_guard_run_checks(data, rules, false, &err);
if (err.code == 0) {
printf(result);
/* `printf(result)` reads the returned JSON as a format string, which -Wformat-security
* rejects and which a `%` in the report would act on. */
printf("%s", result);
cfn_guard_free_string(result);
cfn_guard_free_string(err.message);
} else {
Expand Down
Loading
Loading