Skip to content

Ergo CLI and Rust engine 0.3.0

Choose a tag to compare

@github-actions github-actions released this 05 Nov 23:35
· 5 commits to main since this release
rust-v0.3.0
5fa8f24

New

  • Every failed row has a message, a line that says in plain words what Ergo found, as the spec says: approver is "ana", and author is "ana", item 2 of tests: ok is missing or signed: signature is missing; reviewed: reviewer is missing. A row that passed has none. Each violation carries the message of its row. The Rego engine writes the same text for every case in the spec.

  • A path on the right side of is one of, contains, matches and the other phrases that take a list walks lists, as the spec says, from the input, the params, the subject, a parent, a part or an alias and inside whose, count of, sum of and for lines: pin is in $input.sbom.components.name compares pin with the names of the components. A list it can't read from every item fails every row, with the cause and words every uses. A leading dot on that path reads the subject's field, and a message and an input name keep the dot only when a part, a parent subject or an alias has the same first name. A $input path or a field on the right that reads into text now says which value can't hold the next step, where it said the check is false. A position after a key, like id is in $input.l[0], fails every row as unusable when an item holds a value that isn't a list there, and the message names it, item 2 of $input: l is "ab", which isn't a list, where it read the position as null and a row could pass on the other items.

  • that picks items by the value itself, where whose picks them by a field, as the spec says: from: every ws that starts with "b". It works on a from line, in a check and on a for line, and a field called that is written .that.

  • distinct drops exact copies after every, some, count of and sum of, as the spec says, and a bare field called distinct is written .distinct.

Changed

  • ergo reads YAML the way OPA 1.21 and later do, and no longer the way OPA 1.20 does. ergo.rego is tested with OPA 1.22.0, which reads YAML the same way as 1.21. Reports change for YAML inputs, policies and params that use any of these:
    • Values that read differently: yes, no, on, off, y and n (in any of their spellings) are strings, as values and as keys, where they were booleans. With approved: yes, approved is true used to pass and now fails as unusable with approved is "yes", which isn't true or false, as in ergo.rego. A key written in a mapping wins over a merged one wherever the << is, and the first of several merges wins, where the later one did. A date tagged !!timestamp is written out in full, like 2001-12-14T00:00:00Z, where it kept its text.
    • Files that were refused and are now read: a tag YAML doesn't define on a value, like !custom 5, which keeps the text, "5"; a bare !, which reads a plain value as if it had no tag, so ! 5 is 5 and ! .inf: 1 is {".inf": 1}; a verbatim tag like !<tag:yaml.org,2002:int> 5; !!merge; a key from 9223372036854775808 to 18446744073709551615; and %YAML 01.1 or %YAML 1.01.
    • Files that were read and are now refused: a later document that isn't valid YAML, has an alias with no anchor, nests more than 10,000 levels or says %YAML 1.2 after ...; a directive other than %YAML and %TAG, like %FOO bar; the same alias twice as a key of one mapping, compared by the anchor's name; a !!timestamp key; a !!timestamp whose time zone is 24 hours or more; and !!bool yes and !!bool on. These are refused too, where OPA reads them: a directive inside a document with no ... before it, like a: 1, %YAML 1.1, --- and b: 2 on four lines, which OPA reads as {"a": 1}; a byte order mark right after --- at the start of a later document; and flow collections nested more than 255 levels in a later document. CLI.md lists them.
  • The message of a $min_subjects row that failed because a from line couldn't be read is the same as ergo.rego's for every shape in spec/cases/messages: item 2 of things is missing, and for a child subject the path as it walks its parent's value, without the parent's name, like ws is missing, item 1 of ws is missing or meta.owner is missing.
  • A whose in a from line that compares a field of the item with itself, like things whose x is x or t.ws whose t.lo is at most .t.lo, is written wrong, whose: x is compared with itself, as in ergo.rego. A left side that names a parent, like t.ws whose t.lo is at least t.lo, reads the item's own field, and the right side reads the parent only when it has no dot, so that one is fine.
  • When a child's parent has a from line that fails on a named by key or on last of, a whose of the child that compares or tests membership against a subject further up, like w.kids whose lo is at least t.cov.x, adds the problem subjects.g.from: unknown alias $t, listed after the other problems, as ergo.rego does. The same goes for a parent written wrong by a whose that compares a field with itself, where a contains on the right is reported too. It isn't reported when the parent's from has every or named by each of, when the right side is a count of, a sum of, a plus, contains, matches or contains all of, or when the name starts with a dot. The child's from is <invalid from> in the report's subjects, as in Rego. For a parent written wrong by a self-comparison, matches on the right isn't reported, and Rego does report it. The policy fails $well_formed either way. named by one, where one is a word of the grammar, gives only one can't be an alias.
  • A comparison whose right side can't be read through something that isn't an object, like lo is at least t.cov.x with t.cov 5 and lo true, names the left side too when it isn't a number: t.cov is 5, which isn't an object, and lo is true, which isn't a number.
  • In an any_of, an input that reads the same path as a part of the check carries the part's part tag, like ergo.rego: has: {cv: "name"} with name exists in one option and cv is 1 in another.
  • On the left of a whose and after sorted by, the subject's name is the item's field, in a check, a for line and a from line, as the spec says, so from: every ws whose w starts with "b", for a subject called w, reads each item's w field. On the right of a whose the name reads the subject and .s the item's field. A field named like the subject or a part, read on the item, is printed with its dot, like item 3 of ys: .s is missing and item 1 of .cov.xs is 1, as ergo.rego prints it.
  • The engine follows spec 0.5, so a policy says ergo_spec: v0.5. Reports change for every policy: the report's ergo says v0.5, its engine version says 0.3.0, and each failed row and each violation has a message. Every case in spec/cases passes, so known-failures.txt is empty. The text still differs from the Rego engine's in a few cases that no spec case covers. named by each of t.keys, a list that is a field of a parent, is an open question: the spec doesn't define it and the grammar allows it. Rego never reads it, and says <invalid ref> is missing whatever the parent holds, and the Rust engine reads the parent's field. A sum of over a number of more than 15 digits next to a missing value is absent with item 2 of ws: lo is missing in Rego and unusable with sum of ws.lo needs more than 15 digits in the Rust engine. For a child subject whose own value isn't an object, Rego says item 1 of items: it is 3, ... and the Rust engine item 1 of items is 3, ..., and Rego looks wrong there. When both sides of a child's whose name an ancestor, like w is .w within 1 and t.lo is more than t.lo, Rego gives two problems and the Rust engine only the first. The from printed in the report's subjects can differ in a dot, .t.lo against t.lo, when another subject is broken, as it also does on main for valid policies like w.kids whose t contains all of .t.lo. The pass or fail result is the same in all of these. In a from line, a whose that is a quantified condition isn't checked for comparing a field with itself. unknown alias $t is still missing for w.kids whose .w.lo contains t.lo under a parent with named by zz. For an $applies row of is in or is not in with a path on the right, like w.kids whose .w.lo is in .w.lo, the message and sometimes the cause differ. The Rust engine adds problems that Rego doesn't in some policies with more than one: unknown alias zz and last of isn't supported yet here next to an exists problem, and can't build on a subject read from another subject yet next to Rego's last of problem. An $applies message can name one side more than Rego's, like t.ws whose x is before t.cov.y with t.cov [1] and x null. A grandchild of t.ws named by each of $params.keys with ws a list, null or a scalar, or with the keys missing or [], gets one row without the key in Rego and a row with it in the Rust engine. On a for line over a path through two lists, like kids.kids, Rego repeats the outer item and the Rust engine prints item 1 of kids[0].kids: .... Rego has bugs the Rust engine doesn't share: w: t.ws whose meta.owner contains all of t with meta 5 and whose sum of lo is not t.zz.x and lo is not in t end in an eval_conflict_error, and on a for line over two lists, a whose or sorted by that meets an item of the second list that isn't an object can leave the message empty, a key sort that can't be used is named by its value (item 1 of items is missing for a missing lo), and when two sentences of a for line read the same, like i.k is in $params.l and i.k is one of $params.l, Rego lists each failed item in items once for each of them.
  • Every condition after an and in a whose chain starts with its own whose or that, as ergo.rego reads it, so whose a is 1 and b is 2 is written wrong. Reports of policies written the old way change, because they now fail $well_formed. Their problem reads a condition after and starts with whose or that, and a whose or that in the wrong place names the word that was written. The expression of a that filter writes the subject's name in quotes when it is a word of the grammar, as ergo.rego does, like "item" starts with "b".
  • it is gone. A subject's name means the subject, as the spec says, so a check on a subject called thing writes thing is not empty, and thing.x is written wrong with nothing can follow thing, which means the subject: for a field called thing, write .thing.x. On the right side the name reads the subject too, a leading dot reaches a field named like the subject, and first of thing and item 2 of thing read an item of a list subject. named by thing is written wrong, thing is the whole subject, so it can't name a key. A subject, field, alias or part called it is an ordinary name, and an alias named like its subject is written wrong. A message about a subject that isn't an object names the subject, like thing is null, which isn't an object. Reports change for every policy that wrote it, and the messages of the rows that read the subject change from it to its name.
  • A message no longer says the subject is ..., and names the subject or where a read starts instead. A for line on a subject that isn't an object names it, thing is 3, which isn't an object. A for line's whose that meets an item that isn't an object names the item without a colon, item 1 of xs is 1, which isn't an object, where it said item 1 of xs: it is 1, which isn't an object. An $applies row on an item that isn't an object names the subject, t is 3, which isn't an object, and a $min_subjects row whose from stops before its first step names where it starts, $input is null, or the parent subject, artifact is "x", which isn't a list or an object. A child subject read from an item of its parent names the parent, item 1 of p is missing, where it said item 1 of $input is missing. Reports of plain policies change in those messages.
  • The subject's name followed by brackets, s[0], is written wrong like any other step, and the problem says write .s[0] for a field called s. first of s and item 2 of s read an item of a subject that is a list. Same as ergo.rego.

Fixed

  • A from line whose named by each of list starts at a field or a subject, like t named by each of t.keys, is written wrong, as the spec says, where the engine read the parent's field. Its $well_formed row lists named by each of takes a list from $params or $input, so it can't start at a field or a subject, like ergo.rego.
  • A tie in the second list of a for line names the second item's key, like item 1 and item 2 of p.items tie on hi, where it named the first item's.
  • A for line whose first item is the subject's own name, like every thing as k, fails as not_an_object on a subject that is a list, text, a number or null, as ergo.rego does, where it read a list subject as the list.
  • The from the report gives for a subject keeps the . of a whose that reads a field named like the subject, like every kids whose .kid is 1 for the subject kid, and so does the assertion of a failed for item, like .thing is 1 for the subject thing.
  • A check count of .kid is 1 on a subject kid says kid is 1, which isn't a list or an object once, as ergo.rego does, where it said it twice, once with a dot. The input is named count of .kid.
  • A whose in a check that reads a field named like the subject, like count of ys whose thing is p for the subject thing, names that field with its dot in a message, .thing is 1, which isn't an object, as ergo.rego does.
  • A whose on a from line reads the item's field when its left side starts with a subject's name, like kid.x in rel.kids whose kid.x is 1 or rel.w in rels whose rel.w is 1, where the engine wrote the policy wrong with nothing can follow kid.
  • first of xs sorted by rel sorts by the item's field rel when rel is the subject's name or a parent's or grandparent's name, as ergo.rego does, where it sorted by the subject or the parent and failed as unusable. "item" is ."item" is no longer said to compare a field with itself when item is the subject's name.
  • A count of ys whose thing is p on a subject that isn't an object lists the part p in inputs, as ergo.rego does, where it left it out.
  • A failed count of whose path ends in a key read from a field, like count of x named by zz is 1 with x holding {"k": 1} and zz set to "k", names the value once, as ergo.rego does: x.k is 1, which isn't a list or an object, where the message said it twice.
  • does not exist on a path with a named by field that can't be read, like x[0] named by zz does not exist with x missing and zz missing, null or not text, now fails as absent, as spec 0.5 says and ergo.rego does, where it passed. Reports change for such a check: passed goes from true to false.
  • A check that reads the params by a position, like every $params[0].a is 1 or some $params[0].a is 1, with params that aren't a list or an object, now says $params is 3, which isn't a list as ergo.rego does, where it said every $params[0].a is 1 is false, and a check that reads them by a key, like $params.a is 1 or zz is in $params.a, says $params is 3, which isn't an object, where it said $params.a is 1 is false. count of $params[0] is 1 with params [] says $params is [], which isn't an object, where it said needs more than 15 digits. Messages change for such checks. Params that are a list, text, a number or a boolean are read for inputs, so an entry that reads a position of them has the value and the location the spec says, $params:/0, where it had no value and $params:.
  • A for line over params that aren't an object, a sentence of a for line, a named by key and a whose that read them, like for: every $params as p, x named by $params.v is 1 or every ws whose x is $params.v is 1, say $params is 3, which isn't an object as ergo.rego does, where they said the check was false, and so does count of $params whose v is 1 is 1 with params [3], where it said needs more than 15 digits. x is in $params[0].v with params [[1]] says item 1 of $params is [1], which isn't an object. Messages change for such checks.
  • A subject's from line that reads params that aren't an object, like things named by each of $params.ks or things named by $params.env, says so in its $min_subjects message, $params is 3, which isn't an object, as ergo.rego does, where it said $params.ks is missing. A position says what it found, like item 1 of $params is 3, which isn't a list of text for $params[0] with params [3]. It still makes no subjects, also when the list of keys can be read, like $params[0] with params [["k"]]. Messages change for such policies.
  • When both sides of contains or is in can't be read and the right side is a path from $input or $params, the message names the left side first, as ergo.rego does: zz is "v", which isn't a list, and $input.y is 3, which isn't an object. A count of through a position, like count of y[0].v is 1 with y holding [{"v": 1}], names the item once, item 1 of y: v is 1, which isn't a list or an object, where it also said y[0].v is 1, which isn't a list or an object. Messages change for such checks.