Repository navigation
ergo.rego 0.3.0
·
69 commits
to main
since this release
Changed
- This version follows spec 0.3, so a policy says
ergo_spec: v0.3. One that saysv0.2is written wrong, withunsupported version v0.2, expected v0.3, and still works with 0.2.0. The report'sergochanges with it, from"ergo": "v0.2"to"ergo": "v0.3". min_subjectsis a whole number from 0 to 1,000,000. A requirement with a larger one, like1000001or1e308, is written wrong, with its$well_formedrow failing and no other row, where beforeergo.regoread any whole number up to what a 64-bit float holds, and rejected some big ones like1e308by accident. The message for every wrongmin_subjects,-1,1.5,"2"and1000001too, is nownot a whole number from 0 to 1000000, where it wasnot a whole number of 0 or more. A number past a float, like1e400, still saysnumber out of range. Reports change for such policies: the$well_formedrow shows the new message.- A subject with an
identified_bywhosefromline usesnamed by each ofis written wrong, withnot allowed with named by each of: the key is the idunderidentified_byin the$well_formedrow, and no other row for its requirements. Before,ergo.regoignored theidentified_byand used the key as the id.
Fixed
- A
sum ofor acount ofwith a path doesn't read the path of an item itswhosecan't decide, so the side fails with thewhose's own cause, as tally.whose says. It read the path and gave the cause of what it found. Withsum of items.x whose k is "a" is 1on{"things": [{"id": "t", "items": [{"x": "ab", "k": null}]}]}, the check now fails asnull, where it failed asunusable. Withkmissing it now givesabsent, and a field that is missing withknullgivesnull, where it gaveabsent. The same goes for a path that goes on, likeitems.x.y, and forcount of items.x whose k is "a": on[{"k": null}]it went fromabsenttonull, andcount of items.x.y whose k is "a" is 1on[{"x": "s", "k": null}]fromunusabletonull. Only the cause changes. - A
forline whose item path goes on into a list item that isn't an object,nullincluded, fails that item asnot_an_object, aseveryandsomedo. It failed asunusable, or asabsentfor anullitem. Withtset to["a"]or[null],for: every t.k as xnow failsitem 1 of tasnot_an_object.count ofandsum offail anullitem asnot_an_objecttoo, where they saidabsent. The same checks fail as before, and only the cause changes, in the row, infailed_itemsand in the requirement's causes. - A
foritem that picks with brackets, liket[0] as i, is written wrong, as the spec says. Before,ergo.regoread the brackets as a position, sot[0] as imeantfirst of t as i. It fails$well_formedwithfor: an item starts with "every", "some" or a position, like "last of", and the requirement gets no other row. The same goes for an item after the first, likeevery t as x, t[0] as i, which used to be read.first of t as iand brackets aftereveryorsome, likeevery t[0].u as i, are read as before. Reports change for a policy that has such an item: with{"things": [{"id": "a", "t": [{"u": [1, 2]}]}]},for: t[0] as iwithassert: i existsused to be satisfied, with the inputitem 1 of t, and is now not met. - A child subject under a parent that isn't an object fails
$min_subjectsasnot_an_object, as the spec now says (subjects.chain.not_an_object). Before, a parent that was a string, a number, a list or a boolean gaveunusable, and anullparent gaveabsent. Reports change for such inputs, like{"trail": {"artifacts": ["x"]}}with a pull request subject fromartifact.pull_requests. The verdict doesn't change, and a parent that is an object without the field still givesabsent. - A
fromline that is another subject's name and thennamed by each of, likeartifact named by each of $params.keys, reads the keys inside each parent subject and puts the parent underin, as the spec says. Before,ergo.regoread a top-level input field with the parent's name, so a check could pass on data the policy never pointed at. A parent that isn't an object fails$min_subjectsasnot_an_object. A first key with a dot, like.artifact named by each of $params.keysor.artifact.p, reads the input field and not the parent, and a position, likeartifact[0] named by each of $params.keys,first of artifact named by each of $params.keysorartifact.x[0] named by each of $params.keys, is no longer dropped: it reads the parent as a list and fails$min_subjectsasunusable. The report prints.artifact.pand.artifact named by each of $params.keyswith their dot, insubjects.<name>.fromand in the$min_subjectsand$unique_idsexpressions, where it printedartifact.p, which reads the parent. A policy with such afromline could pass on data it never pointed at. Reports change for such a policy: with{"artifacts": [{"id": "a1"}], "artifact": {"x": {"ok": true}}}and keys["x"],ok is trueused to pass on/artifact/x/okand now fails asabsentforxina1. - A
sum ofor apluswhose numbers can't all be held as whole numbers below 10^15 at one scale fails asunusable, with its entrynull. The scale is the most decimal places any number added has, counted on the value, and the result counts too. More than 15 decimal places fails too. Before, such a sum fell back to the runtime's own addition, which gave different answers in different runtimes. Reports change for such inputs:x plus y is at least 0on{"x": 0.123456789012345, "y": 100}was satisfied and now fails asunusable, and so does1e20plus1. Sums of ordinary numbers, and numbers with trailing zeros like1.500000000000000000, are added exactly as before. - A
withincheck works out the gap exactly on the decimals, where it used OPA's arithmetic, which isn't exact. That's promised for numbers with at most 15 significant digits, the ones the spec covers. Reports change for such inputs:x equals y within 0.2on{"x": 0.3, "y": 0.1}failed asvalueand is now satisfied, and so is0.1and0.3, whilewithin 0.19999still fails asvalue. A check that passed before still passes, except one whose two sides andwithinnumber have exponents more than 4096 apart, like0and1e-5000within1, which passed and now fails asunusable. - A
fromline withnamed by each offails$min_subjectswhen the object it reads the keys from is missing,nullor not an object, asabsent,nullorunusable, so the requirement isn't met. Each listed key is still a subject and gets its rows, as before. A key that isn't a string makes no subjects and fails$min_subjectsasunusable. Before,from: scans named by each of $params.requiredon an input withoutscansgave one subject per key, so a check likex does not existpassed for each and the policy was compliant. The same holds when a ref step before it can't be used, likescans named by $params.env named by each of $params.requiredwithenvset to5, which used to be met too. A child subject of such a parent gets one$min_subjectsrow with noin, failing with the parent's cause, and one$unique_idsrow with noin, instead of a row per key. - When the input is a list,
from: $input,from: every $inputand aneverywalk that starts at the input, likefrom: every commits, make each item a subject. They failed$min_subjectsasunusable. Each item gets its own rows, with locations like/0/x, and an empty list fails$min_subjectsasvalue. A position on a list input, likefrom: $input[0]orfrom: first of $input, reads the item, and endsabsentpast the end or on[], where it failedunusable. The same goes for a child subject whosefromstarts with a position on a parent that is a list, likeartifact[0].c: it reads the item of each parent, and a parent that is[]ornullfails$min_subjectsasabsent. A field, like$input.commits, staysunusableon a list. The$min_subjectsand$unique_idsexpressions offrom: every $inputandfrom: every $input whose ...now keepevery, likecount(matching(every $input)) >= 1, on any input and not only a list. A report for a policy whose subjects come from a list input changes, and so does the expression text of every policy withfrom: every $input. subjectsat the top of a report is written the way the spec says for a policy written wrong. A chain of more than three subjects lists every subject in it, not only the last. Afromline that reads but has a problem, likevalues of mixed types, shows as<invalid from>instead of the line. A subject whosefromstarts at another subject lists that subject even when the line is written wrong, as withevery parent.items, and a subject that narrows another has that subject's id then, even with anidentified_byof its own. Afromthat doesn't parse, or isn't a string, lists no other subject, as before.- A check written wrong with
values of mixed typesand nothing else is printed as written, like every other check written wrong, and its$well_formedinput is named after the check. Before, itsexpressionwas printed from the parse and its input was named by the place inside the check. Reports change for such checks:x is one of 1.0, 'a'had theexpressionx is one of 1, "a"and now hasx is one of 1.0, 'a', and its input, which was named by the place inside the check, is now named after the check:checks.f.checks.0under aforline is nowchecks.f,checks.g.checks.1in a list of assertions is nowchecks.g, withline 2:in front of the problem, andchecks.c.whose.0orchecks.c.left.whose.0, for mixed values in thewhoseofevery xs whose …orcount of xs whose …, is nowchecks.c. A problem in thewhoseof aforitem waschecks.i.items.0.whose.0and readsfor: values of mixed typesunderchecks.i. Every problem with theforline now starts withfor:, so a form itswhosecan't take, like"starts with" can't take a count of, a sum of or a plus, anunknown aliasin aforitem, and a walk over more than ten steps on theforline, which had no prefix, now have one. A check now listsvalues of mixed typeswith every other problem it has: on another line, likeline 2: doesn't parse, on the same line, likey is compared with itself, after aforline written wrong, likefor: 5withinvalid for, or after one that doesn't parse, likefor: every xs. Before, the other problem hid it. - A
whosecheck that compares two fields names the one it couldn't read infailed_items, or the left side when neither could be read or both were. Before,ergo.regonamed the side that isn't acount of, or the left side when there was no count, so withevery items.status whose lo is at least hi is "ok"and an item{"lo": 1}it nameditems[0].lowith1, and now namesitems[0].hiwithnull. A field that'snullcounts as not read too, and so does the list of acount oforsum ofthat isn't a list, sowhose count of kids is at least minon{"kids": 5, "min": 1}nameskidswith5. Aplusside is named when it's the one not read, likehiforwhose lo is at least hi plus 1on{"lo": 1}. A side that reads outside the item is named by its own path with its own location: a$paramsside that isn't given is$params.min, at$params:, where it was nameditems[0]."$$params".min, a part is its alias, likew, and a parent's field is likeart.q. Reports change for such items: only thepathandvalueof an undecided item infailed_items. Which checks pass, their causes and the statuses don't change. - A subject name with a
$is quoted where Ergo writes it into a report, as the spec says (name.keys). With a subject$xand a child subject read from"$x".items, the$min_subjectsand$unique_idsexpressions saycount(matching("$x".items)) >= 1andcount(repeated(ids("$x".items))) == 0, where they said$x.items, which can't be pasted back into a sentence. Thefromof a subject named$xthat has nofromis"\"$x\""inreport.subjects, where it was"$x". Which checks pass doesn't change. - A
forline whose second item has a position, likeevery xs as p, first of ys as q, lists a failed list once infailed_items, as one without a position does. Withysset to"s", missing ornull, the row failed asunusable,absentornullwith"failed_items": []. It now has{"item": "ys", "path": "ys", "cause": "unusable", "value": "s", "failed_assertions": [], "locations": ["/things/0/ys"]}. The same goes forsome xs as pas the first item and forlast of ys sorted by n as q. An emptyysstill fails asabsentwith no entry. Onlyfailed_itemschanges. - A
fromline witheveryand a first key with a dot that is named like a subject, likeevery .artifact.porevery .artifact, reads the top-level input field, as a dot means. It was written wrong, withevery can't go with a from line that starts at another subject yet, so$well_formedfailed and the requirement got no other row. Now it makes one subject per value the walk ends at, and the report prints it with its dot. Afrom: .artifactwith noeveryno longer narrows the subjectartifact: it reads the top-level fieldartifact, where it used to take the subject's items and their id. Without the dot,every artifact.pis still written wrong. Reports change for a policy with such afromline. - Inside a
whose, a part's alias on the right side was read as a field of the item when it was a term of aplusor the path of an elapsed time, so a check could pass on the item's own data. With the partpaset tometa,every items.status whose lo is at least pa.w plus hi is "ok"on{"kids": [{"id": "K", "meta": {}, "items": [{"lo": 5, "hi": 1, "pa": {"w": 1}, "status": "ok"}]}]}was satisfied, because it read the item's ownpa.w, andevery items.status whose lo is at least pa.w is "ok"failed asabsent. Both now read the part, so both fail asabsent, and withmetaset to{"w": 1}and nopaon the item, the first now passes where it failed. The same went forsome, aforline's item anddays elapsed from pa.t to hi, with or withoutplus. A parent's name did the same in thewhoseof acount of, likecount of items whose lo is at least days elapsed from fam.t to hi is 1. Reports change for such checks. - A second
foritem that picks, likefirst of ys whose k is p.k as q, can read the first item's alias in itswhose.ergo.regoworked out the pick before the first alias was bound, so such a check always failed asabsent. It now picks for each item of the first list, so the check passes or fails on the data. The same checks without the first alias in thewhoseare read as before. Reports change for such policies: with{"s": [{"xs": [{"k": 1}], "ys": [{"k": 1, "n": 1}]}]},for: every xs as p, first of ys whose k is p.k as qwithassert: q.n is 1used to fail asabsentand is now satisfied, and itsinputslist the pickeditem 1 of ysinstead of the list. A first item that picks nothing, becauseysis empty or no item has itsk, fails the check asabsentand isn't listed infailed_items. Aysthat is missing,nullor not a list is listed once infailed_items, not under each first item, and an empty first list still showsys[]ininputs. - The
inputsof a failed sentence on aforline follow for.failure_inputs when a walk reads a name that sorts before$, like awhosethat reads$inputor a parent, or a walked path like"#rows": the entries that start at an alias first, sorted by name, then the rest. Before, what thewhoseread came first. Withfor: every xs as xandevery rows.n whose z is at least $input.q is at least x.v, the names were$input.q,x.v,rows[].nand are nowx.v,$input.q,rows[].n, infailed_assertionsand initems. Whether a check passes and its cause don't change. - On a
forline whose first item has awhoseand whose second item starts at the first item's alias, the row'sinputsshowed the second list's values under every first item, including the oneswhoseleft out or couldn't decide. They're shown only under the first items thatwhosekept now, as for.inputs says, and the first item's own entry still lists every item. Reports change for such checks. The verdict, the cause andfailed_itemsdon't. ergo.regoreads the grammar of a sentence, aforline or afromline before it looks at its names, so an unknown$<word>no longer hides the rest of the line. A line with an unknown name and a syntax error, likeparent.items whose zz is $foo foo bar, saysdoesn't parseinstead ofunknown name $foo, and one that reads, likeparent.items whose zz is $foo, saysunknown name $foonext to its other problems, a comparison of a path with itself included, asx is x and y is $foodoes. A subject whosefromhas an unknown name lists the subject it starts at inreport.subjects, and one that narrows another has that subject's id and parts.- A child subject whose
fromis another subject's name followed only by a position, likep[1],p[0],first of poritem 2 of p, reads that position inside each parent, as subjects.chain.not_an_object says. Before,ergo.regodropped the position and narrowed the parent, so a requirement could be met when the item wasn't there. Withpsset to[{"x": 1}],kwithfrom: p[1]now fails$min_subjectsasunusable, and it used to be met. A parent that is a list gives the item at that position, or fails asabsentwhen the list is too short, whichergo.regocouldn't read before. Reports change for such policies. - A subject whose
fromhaseveryand anamed bythat reads a missing,nullor wrong-typed param no longer takes its requirement out of the report.$min_subjectsfails asabsent,nullorunusable, as it does withoutevery. Withfrom: every x named by $params.kand params{},requirementsandresultsused to be empty andergo.violationsgave nothing. Reports change for such policies. last of pafter a parent's name sayslast of isn't supported yet hereagain and still listspinsubjects.- A
count ofor asum ofinput carries a part'sparttag only when the list it counts or sums starts at the part, as parts.tag says.ergo.regoalso tagged one whosewhoseonly read the part. With a partpa: "pa", the checkcount of items whose w is pa.w is 1and{"things": [{"id": "a", "items": [{"w": 1}, {"w": 2}], "pa": {"w": 2}}]}, the entry{"name": "count of items whose w equals pa.w", "value": 1, ...}had"part": {"id": null, "name": "pa"}and now has none. Thepa.wentry keeps its tag, andcount of pa.items whose w is pa.wkeeps it too. No verdict or cause changes. - On the right of a
whose, acount oforsum ofthat starts at a part's alias counts the part, not the item's field of the same name, as parts.where says. With a partx: "p", the checkevery xs.v whose n is count of x.z is 1and{"things": [{"id": "a", "p": {"z": [1]}, "xs": [{"n": 1, "v": 1, "x": {"z": [1, 1, 1]}}, {"n": 3, "v": 5, "x": {"z": [1, 1, 1]}}]}]}, it counted each item'sx.z(3), kept the item withn3 and failed asvalue. It now counts the part'sz(1), keeps the item withn1 and is satisfied, and the row listsx.zwith the part's tag. The same goes forsome,count of … whose,sum of … whoseand aforitem'swhose. The left side of awhoseand a name with a dot,.x.z, still read the item's field. The ten-step limit now counts such a part's own path, so a part of ten names there is written wrong, as it is afterevery. - A parent subject's name on the right of a
whoseinside acount oforsum of, likefam.zincount of items whose n is count of fam.z is 1, read each item's field of that name. It now reads the parent, as it does inevery,someand aforitem'swhose. The left side and.fam.zstill read the item's field, and a parent's part counts its path toward the ten-step limit there. - A walk over ten steps on a
forline, in itswhosetoo, givesfor: every and some over more than ten steps aren't supported yet, with thefor:that sentence.for.line puts on every problem with theforline. It had nofor:before. - A
fromline witheveryon anullinput, likeevery commitsorevery $input[0], fails$min_subjectsasnull, as itsfailed_itemsalready said, where it failed asabsent. Reports change for such policies.