Skip to content

Releases

Ergo spec 0.5

Choose a tag to compare

@github-actions github-actions released this 05 Nov 23:33
spec-v0.5
5fa8f24

New

  • Every failed row has a message, a line of plain text that says what Ergo found, never what the check expected: approver is "ana", and author is "ana", item 2 of tests: ok is false or signed: signature is missing; reviewed: reviewer is missing. message.row and the rules after it say how each cause, value, list, for line and any_of option is written, and how a for item is named, with or without its alias (message.alias). A row that passed has none.
  • A violation carries the message of its row.
  • A path on the right side of is one of, is not one of, contains, does not contain, contains all of, contains none of, contains some of, does not contain all of, matches and does not match walks lists the way a path after every does (walk.right): pin is in $input.sbom.components.name compares pin with the names of the components. The right side is listed as $input.sbom.components[].name, and 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 field that is on both sides of a phrase is said once in the message.
  • A message that repeats an earlier one in its row is left out, a for line over a part whose whose leaves no item prints the part's list, a for item that isn't an object reads item 2 of commits is null, which isn't an object, within an item, the items of a list under it come before a part that couldn't be read, a sentence on a for line over two lists that walks another list gives each of that list's items its own part, and a for line whose sentence reads a param that isn't a list prints the list it read, or the check followed by is false (message.join, message.empty, message.items).
  • A count of, a sum of or a whose that reads an item that isn't an object names it, item 2 of commits is null, which isn't an object, and an alias or a part is named without a $ (message.items, message.alias). A value or a key that spells an alias, like "$z: x", prints as the input had it.
  • A $min_subjects row that failed because a from line couldn't be read names where the read stopped, item 2 of things is missing or ws is missing for a child subject, without the parent's name (message.from).
  • that picks items by the value itself, where whose picks them by a field: from: every ws that starts with "b" (sentence.condition.word). It works wherever whose does, on a from line, in a check and on a for line. A field called that is written .that. On a from line the $applies row names the item by the subject's plain name, in its inputs and its message, and its expression writes the name in quotes when it has a space or is a word of the grammar, and in a check a that names a list it walks as a whose does (sentence.that.name).
  • distinct after every, some, count of or sum of drops an item whose value equals an earlier one's, by deep JSON equality, so 1 and 1.0 are the same (sentence.distinct). Its scope is the value its path points at, so an every or some phrase with distinct that reads another field of the item is written wrong (sentence.distinct.place). A failed item lists the location of every copy when the read goes through the subject, and a direct $input path lists its own location.
  • A path that reads inside an every, some or for alias that isn't an object and names its key by a field, like w.k named by zz with w a list, ends not_an_object whatever zz holds, and w named by zz with w holding 3 ends as the field says: absent, null or unusable when zz is missing, null or not text, and not_an_object when it is text. A list alias read by a position, like w[0] named by zz, is read as a list (path.field_step). A tie in the second list of a for line names the second item's key (message.cause). Cases cover all three.
  • A from line whose named by each of list starts anywhere but $params or $input, like t named by each of t.keys with t a parent subject, keys or .keys, is written wrong, and $well_formed lists named by each of takes a list from $params or $input, so it can't start at a field or a subject under subjects.<name>.from (subjects.keyed.list). Nothing is read from it, so the subject has no other rows.
  • A list in a list that no step separates from the list around it, like x[0] or x[0][0], is named it in a message: item 1 of x: item 1 of it is 1, which isn't an object (message.items). A count of a key read from a field names the value once, x.k is 1, which isn't a list or an object.
  • A named by field that can't be read, missing, null or not a string, never lets a check pass, does not exist included, whatever the path before it holds, the path being missing among them: x[0] named by zz does not exist with no x and no zz fails as absent (path.field_step).
  • A check whose path into params that aren't an object stops on them names them in its message: $params is 3, which isn't an object for $params.a is 1, zz is in $params.a and $params.a named by zz is 1, $params is 3, which isn't a list when the path starts with a position, like every $params[0].a is 1, and item 1 of $params is 3, which isn't an object when the position is there and the key after it isn't. It never says the check is false. A walk or a count that reads null params, like every $params.a is 1 or count of $params.a is 1, fails as absent with $params is missing, as a plain path does (path.start). A report that changes for a policy that reads such params has a new message, and for those walks and counts a new cause. The same goes for a for line over them, like for: every $params as p, a sentence of a for line, a named by key and a whose that read them, and a sum of or a count of ... whose over params that are a list, and $params exists and $params does not exist with null params fail as absent. A subject's from line that reads them, like things named by each of $params.ks, makes no subjects, and its $min_subjects row fails as unusable and names them in the same way, where it failed as absent and said $params.ks is missing. A check that reads them whole, like x is in $params, or reads a value that is there, says what it says with any params. Cases cover each.
  • A path from $input or $params on the right of contains or is in is named after the left side when both can't be read, zz is "v", which isn't a list, and $input.y is 3, which isn't an object, and a field of the subject there is named first, y is 3, which isn't an object, and zz is "v", which isn't a list. A count of through a position names the item once, item 1 of y: v is 1, which isn't a list or an object, and id is in $input[0].id with the input [{"id": "a"}] says $input[0].id is "a", which isn't a list (message.cause). Cases cover each.

Changed

  • A bare field called distinct is written wrong, and .distinct reads it. Reports of a policy with such a field now fail $well_formed.
  • Reports change: the report's ergo says v0.5, every failed row has a new message field, and so does every violation. The report schema allows it. Nothing else in a report changes, except that a path on the right side of those phrases that goes through a list of objects used to fail every row as unusable with no message that said why, and now compares with the list it reads or fails with the cause and message of the item that can't be read, and that a $input or $params path that reads into text, like $input.a.b with a text, now says $input.a is "x", which isn't an object.
  • Every condition in a whose chain starts with its own whose or that, after each and: deployments whose environment is "prod" and whose change_type is "emergency". A line written the old way, whose a is 1 and b is 2, is written wrong. Reports of policies that use whose with and change that way, since they now fail $well_formed until the policy is changed. Their problem reads a condition after and starts with whose or that. Other reports change in a few places: whose a and whose b used to fail with doesn't parse and ...
Read more

Ergo CLI and Rust engine 0.3.0

Choose a tag to compare

@github-actions github-actions released this 05 Nov 23:35
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 ...
Read more

ergo.rego 0.5.0

Choose a tag to compare

@github-actions github-actions released this 05 Nov 23:32
rego-v0.5.0
5fa8f24

New

  • Every failed row has a message, a line of plain text that says what Ergo found, as spec 0.5 says: approver is "ana", and author is "ana", item 2 of tests: ok is false or signed: signature is missing; reviewed: reviewer is missing. A row that passed has none. Each violation carries the message of its row.

  • A path on the right side of is one of, contains, matches and the other phrases that take a list walks lists, as spec 0.5 says: pin is in $input.sbom.components.name compares pin with the names of the components. It works from $input, $params, the subject's fields, a parent, a part or an alias, and inside whose, count of, sum of and for lines. A list it can't read from every item fails every row, with the cause and words every uses, and the right side is in $refs or the row's inputs as $input.sbom.components[].name. A leading dot reads the subject's field. The message and the input name keep the dot only when a part, a parent subject or an alias has the same first name, so x is in .lock.pkgs.name says .lock.pkgs.name is missing then, and lock.pkgs.name is missing otherwise, also for an item that isn't an object and in the names of a count of and a sum of. On a for line the message names the item with its alias, and the inputs are sorted by name. A field that is on both sides, like tags contains none of tags, is said once, tags is ["a"], where a message that repeats a clause used to say it twice. No policy that worked changes, except that last one, because a right side that walks a list failed as unusable before.

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

  • distinct after every, some, count of or sum of drops exact copies, as spec 0.5 says: every distinct sbom.components makes one subject of components that are the same, count of distinct commits.author is at least 2 counts each author once, and sum of distinct stages.usd adds each different amount once. 1 and 1.0 are the same value. A failed item lists the location of every copy when it was read through the subject, and null is kept as a value. On a from line it goes after every. See REFERENCE.md.

Changed

  • A bare field called distinct is written wrong, as spec 0.5 says: write .distinct. Reports of a policy with such a field change, because they now fail $well_formed.
  • This version 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.5.0, and each failed row and each violation has a message. Besides that, a path on the right side of those phrases that goes through a list of objects used to fail every row as unusable with it is in $input.sbom.components.name is false, and now compares or says what couldn't be read, and a $input or $params path that reads into text says which value can't hold the next step.
  • Every condition after an and in a whose chain starts with its own whose or that, as spec 0.5 says, so whose a is 1 and b is 2 is written wrong and becomes whose a is 1 and whose b is 2. 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. Other reports change in a few places: whose a and whose b used to fail with doesn't parse and now reads, a is a word of the grammar: write .a when a is a word of the grammar, a field called that must be written .that, a whose or that in the wrong place names the word that was written, and a for line with a whose and a that gets the same cause in either order. On a from line, the $applies row of a that names the item by the subject's plain name, so a subject called item gives item is "apple" where it gave .item is "apple", and a subject called my w gives my w is "apple" where it gave "my w" is "apple".
  • it is gone, and the subject's name takes its place, as spec 0.5 says. With subject region, region is one of $params.allowed_regions checks each region string, and every tags is a string checks a subject that is a list. The name reads the subject on the right side of a phrase too, so some $input.deps whose name is lib exists compares each dependency's name with the subject lib. Nothing can follow the name: lib.name is 1 is written wrong, with lib.name: nothing can follow lib, which means the subject: for a field called lib, write .lib.name, and named by lib with named by lib: lib is the whole subject, so it can't name a key. A field called like the subject is written with a dot, .lib. A name with a space, or one that is a word of the grammar like item, goes in double quotes, as in "production deployment" is an object. it is an ordinary word, so a subject, a field, an alias or a part can be called it. Reports change for every policy that wrote it: it now reads a field called it, or fails with a missing field, until it names the subject. They change for plain names too, because a row that read a value that isn't an object names the subject by its name, deployment is "str", which isn't an object, where it said it is "str", which isn't an object, and because a row's inputs name a subject that is a list or a value by its name, and a field named like the subject with a dot, .lib. See REFERENCE.md.
  • ergo.as_sentences no longer writes a check with an empty path, [], as it is "x". It leaves the check as it was, and it is written wrong: write the check with the subject's 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. Follows spec 0.5.
  • On the left of a whose and after sorted by, the subject's name is the item's field, like any bare word there, in a check, a for line and a from line, as spec 0.5 says. So from: every ws whose w starts with "b", for a subject called w, reads each item's w field, where it read the item itself, and a quoted word of the grammar there, like "item", is a field too. On the right of a whose the name reads the subject and .s the item's field, so count of $input.ys whose k is s is 1 compares each k with the subject, where it compared it with the item's own s. A row that reads a field named like the subject or a part prints it with its dot, like .kid is missing and item 1 of .cov.xs is 1. count of "my rel"[0] is 1 is written wrong with write ."my rel"[0], as count of rel[0] is. Reports change for policies that write the subject's name in a whose or after sorted by, or read a field named like the subject or a part.

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 spec 0.5 says, where it read <invalid ref> is missing whatever the field held. 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, and the subject has no other rows.
  • A for item that isn't an object no longer stops the report with eval_conflict_error when a path reads inside it and names its key by a field, like w.k named by zz with w a list, or w[1] named by zz and w[0].a named by zz with w a list: the check fails with the cause the spec gives. Reports change for checks that read such a path and used to fail as not_an_object because the key field was bad. With for: every ws as w, assert: w named by zz is 1 and input {"things": [{"id": "a", "ws": [3]}]} (no zz), the row said item 1 of ws is 3, which isn't an object with cause not_an_object, and says item 1 of ws: zz is missing with cause absent. A zz that is null or not text gives null or unusable the same way. With assert: w[0] named by zz is 1 and {"ws": [[{"a": 1}]]}, not_an_object became absent, and with "zz": 3 it is unusable. w[0] named by zz does not exist with {"ws": [[1]], "zz": "a"} failed as not_an_object before and now passes, as w[0].a does not exist already did.
  • A path that starts at a for alias holding a list and reads a position first, like w[0].a does not exist or w[0] named by zz is 1 with for: every ws as w, now reads the alias as a list, as spec 0.5 says. w[0].a does not exist fails as unusable when the item at that position is a number or text, where it passed. w[0] named by zz is 1 and w[0] named by zz does not exist fail as unusable when the alias is a number (they said not_an_object) or when the item at that position is a number (they said absent or passed). `w[0] named b...
Read more

Ergo spec 0.4

Choose a tag to compare

@github-actions github-actions released this 02 Nov 06:58
spec-v0.4
29e790b

New

  • Every phrase that lacked an opposite has one: does not start with and does not end with (sentence.phrase.starts), is not a <type> and is not a non-empty string (sentence.phrase.type), is not between (sentence.phrase.between), is not <value> within <number> and does not equal <field> within <number> (sentence.phrase.within), and contains some of and does not contain all of (sentence.phrase.contains_all). is alphabetically on or before and is alphabetically on or after join the alphabetical phrases (sentence.phrase.alphabetical). Each one fails as the phrase it is the opposite of does when a field is missing, null or of the wrong type, so it never passes by reading nothing. They read into lacks_type, not_non_empty_string, outside_range, includes_some and not_includes_all, and into the new not_starts_with, not_ends_with, alphabetically_lte and alphabetically_gte comparisons, and compare with ne takes a within (compare.within). A sentence that was written wrong before, like x is not 3 within 1 or count of x is not 3 within 1, now reads, and so does is not ... within after a count of, a sum of, a plus or an elapsed time. Only sentences that did not read before change: a policy that was written wrong because of one now reads and gets rows.
  • Text can be joined on the right side with followed by, like rego.version is tag.version followed by "-dev" (sentence.operand.followed_by, compare.join). It takes written strings and fields, any number of them, and goes after is, is not, starts with, ends with, their negatives, contains, does not contain and the alphabetical phrases. Every piece must be a string: a written one that isn't is written wrong, and a field that isn't one fails as unusable. The first piece that can't be read decides the cause, before the left side does. It is written wrong after within, the order and time phrases, a count and a plus, and where plus isn't run yet: after every or some, in a whose and in a from line. A sentence with followed by was not readable before, so no report changes.

Changed

  • In a comparison of plain paths and written values, and in the pieces of followed by, a path that names its key by a field, like m named by p, lists the field in the row's inputs right after the entry of the path, so m named by p is q shows m named by p, p, q (path.field_step). ergo.rego listed the field after every other entry, so it showed m named by p, q, p. The Rust engine already listed it this way. The same goes for a count of, a sum of, an elapsed time, a plus, an every and a some, and the check of a for line lists the field among its entries sorted by name. Reports change for such checks that read another path after the one with the field step.
  • The steps of a path before a field step are read first, and when they can't be read the path ends as they do, whatever the field holds (path.field_step): $params.m named by p is q with no m in the params is absent when p is null. ergo.rego read the field first, so it was null, and both engines did so for an every or some. So every zz.x named by p is 1 with zz missing or a string is absent or unusable when p is null, where ergo.rego and the Rust engine said null. A list before the field in an every, some, count of, sum of or a for line is walked, not read, so there the field still decides, as before: every rows.w named by k is "xy" is null when k is. A null before the value the field picks from ends such a walk null whatever the field holds, as the walk does without the field: with rows null, every rows.w named by k is "xy" is null when k is missing or a number, where both engines said absent or unusable, and its failed_items names rows, where it was empty when k couldn't be read. Where nothing walks it, a list before the field ends the path unusable: rows.w named by p is "xy" with rows a list is unusable whatever p holds, where ergo.rego gave the cause of p. The Rust engine already said unusable.
  • A report's engine name is ergo- and the engine, like ergo-rego or ergo-rust, so a name and version match a release tag like rego-v0.3.0 (report.engine). The Rego engine said ergo.rego and the Rust engine ergo-policy-engine. A policy says ergo_spec: v0.4, and the report's ergo changes to match. The schemas are under https://ergo.kosli.com/spec/0.4/. Reports change in engine only, and a report's hash leaves engine out, so hashes stay the same.
  • added.text now says that a from that starts at the input with a named by step, like every $input named by $params.k, is written as it is, whatever follows it, in the $min_subjects and $unique_ids expressions. subjects.every_narrowed says that a subject narrowing a subject read with every takes that subject's flat list and filters it. name.dot says that an input that reads the item's own field named like a parent subject keeps its ., like .art.k, in a check, a for line and the whose of a from line, in inputs, in failed_items and in the from the report gives, with a position after the dot, like item 1 of .art.xs. Reports change for such policies: the expressions were count(matching(every )) and count(matching(artifacts.releases)) in ergo.rego, a subject narrowing an every subject had none of its subjects, and the input was named art.k.
  • A policy with no requirement to hold the $well_formed row, because requirements is missing, null, not an object or empty, gets a requirement of Ergo's own, $policy, with one failing $well_formed row that says what is wrong, like requirements with ["missing"] or policy ergo_spec with ["unsupported version v9.9, expected v0.4"] (policy.no_requirements). Its subject is {"type": "$policy", "id": "$policy"}. The violations list that row, where they were empty for such a policy while compliant was false. Reports change for such policies: they had no requirement and no row. The report schema says $policy can be a subject type and id.
  • A name that starts with $ is written wrong, whether it names a subject, a requirement, a check, an any_of option or a part, with a name can't start with $ (names.dollar). It replaces name reserved, which only a check named like one of Ergo's own rows got, a subject can't be named $input and $params, invalid alias for a part called $dollar, and the $policy rule above. A $ after the first character stays fine, and so do $input and $params at the start of a path. A check with such a name has no entry in requirements. Reports change for such policies: a name like $x was read before, and showed quoted in the report. The Markdown compiler already refused these names with the same words.
  • A name that starts or ends with a space or an invisible character is written wrong too, with a name can't start or end with a space or an invisible character (names.edge): any name names.dollar refuses, when its first or last character is Unicode White_Space or has the general category Cf, like " $well_formed", "approved " or U+200B followed by $x. Spaces inside a name and a full-width $x stay fine. Reports change for such policies: a name like "approved " was read before.
  • names.empty says that an empty name, "", is written wrong too, whether it names a requirement, a check, an any_of option or a part, with a name can't be empty. Reports change for such policies: a "" was read before.
  • written_wrong.row now says what "sorted by name" means for a name that needs quoting. The $well_formed row lists the problems of fields, parts and checks sorted by name as the report writes it, quotes and escapes included, by code point. Reports change for such names: the Rego engine sorted parts by the raw name and the Rust engine ignored quotes, and the order of a part like "x y" changes. A require that is written wrong is listed second, where the Rego engine listed it among the other fields. The requirement's own fields come first, then the subject's, then the checks, each group sorted, as the Rego engine had it.
  • [name.dot](https://github.com/kosli-dev/ergo/blob/spec-v0.4/spec/syntax.md#namin...
Read more

Ergo CLI and Rust engine 0.2.0

Choose a tag to compare

@github-actions github-actions released this 02 Nov 07:03
rust-v0.2.0
29e790b

New

  • Every phrase that lacked an opposite has one, as spec 0.4 says: does not start with, does not end with, is not a <type>, is not a non-empty string, is not between, is not <value> within <number>, does not equal <field> within <number>, contains some of and does not contain all of, plus is alphabetically on or before and is alphabetically on or after. The sentence reader's Phrase has the variants NotStartsWith, NotEndsWith, NotType, NotNonEmptyString, ContainsSome and NotContainsAll, Between has a not field and Alphabetically an or_equal field. A count, a sum, a plus or an elapsed time can now be followed by is not ... within too, which the engine refused before.
  • Text can be joined on the right side with followed by, as spec 0.4 says: rego.version is tag.version followed by "-dev". The sentence reader's Operand has a new variant Joined, with the pieces in order, and compare and contains read it.

Changed

  • A path that names its key by a field lists the field in a row's inputs for a count of, a sum of, an every and a some too, right after the list's entry, and the check of a for line keeps its whole path, m named by p and then p, where it showed only m, as spec 0.4 says. When the path before the field can't be read, an every or some fails with that path's cause, not the field's, so every zz.x named by p is 1 with zz missing or a string is absent or unusable when p is null, where it was null. A list it walks before the field doesn't count, so every rows.w named by k is "xy" still fails as null when k is null. With rows null it fails as null whatever k holds, where a missing or number k gave absent or unusable, and failed_items names rows, where it was empty when k couldn't be read. A for line whose own path has such a field, like every rows.w named by p as w, lists p too, sorted with the rest by name, as ergo.rego does. A plus or an elapsed time that reads such a path no longer has the field's location in its own entry, as spec 0.4 says. Reports change for such policies.
  • The report's engine name is ergo-rust, where it was ergo-policy-engine, so it matches the Rego engine's ergo-rego and the rust-v* tag. Every report changes in that field only, and the hash leaves engine out, so hashes stay the same. The version is the same.
  • The engine follows spec 0.4, so a policy says ergo_spec: v0.4.
  • An empty name, "", is written wrong too, whether it names a requirement, a check, an any_of option or a part, with a name can't be empty.
  • The CLI has its own page, CLI.md: install, how to run it, its exit codes and how it reads YAML. They were in the README and in this folder's README.
  • The $well_formed row lists the problems of fields, parts and checks sorted by name as the report writes it, quotes and escapes included, by code point, where the quotes were left out, and lists the requirement's own fields first, then the subject's, then the checks. Reports change for names that need quoting and for a requirement with both a field and a subject problem.
  • An own field named like a part of the subject or like a for alias keeps its dot in the name of an input, like .cov.pct, and so does the list a count of, sum of or a walk reads, like .art.xs[]. A policy written wrong keeps the dots it was written with in the expression and in from: a check whose for isn't a string, and a part named like one of its parent's.
  • An input that reads the item's own field named like a parent subject keeps its dot, like .art.k, as spec 0.4 says. A from that starts at the input with a named by step is written as it is in the $min_subjects and $unique_ids expressions, so every $input named by $params.k is no longer count(matching(every [$params.k])). Reports change for such policies. The from a subject gets in subjects keeps every dot the policy wrote on the left of a whose, like .art.k, .art.xs[0] or a part's .att.id, where it dropped them.
  • A policy with no requirement, because requirements is missing, null, not an object or empty, gets Ergo's own requirement $policy with a failing $well_formed row that says what is wrong, as spec 0.4 says, where the report had no row and violations was empty while compliant was false. A requirement called $policy is written wrong. Reports change for such policies.
  • A name that starts with $ is written wrong, whether it names a subject, a requirement, a check, an any_of option or a part, with a name can't start with $, as spec 0.4 says, in place of name reserved, a subject can't be named $input and invalid alias for a part called $dollar. A check with such a name has no entry in requirements. Reports change for such policies.
  • A name that starts or ends with a space or an invisible character (Unicode White_Space or general category Cf, like U+200B or U+FEFF) is written wrong too, with a name can't start or end with a space or an invisible character, as spec 0.4 says, for a subject, a requirement, a check, an any_of option and a part. Reports change for such policies.
  • A key read from $params or $input, after named by or in brackets, has to be a string, as spec 0.4 says. A whole number used to read that item of a list, so m named by $input.k is 1 with k holding 1 and m holding [5, 1] passed by reading 1, and commits[$params.n] read a commit. It fails as unusable now, and a from line with such a key gives no subjects. Reports change for such policies.
  • A from line like scans named by $params.env named by each of $params.k gave $min_subjects the worst of the causes of env and k, so a null env with k missing gave absent. The ref before the list decides now, as subjects.sentence says, so that one gives null. Reports change for such policies, in the cause only.

Fixed

  • A check could pass when it should fail. A path named by a for alias that holds a number read the number as a list position, so m named by k is 1 under every ks as k passed on {"ks": [1], "m": [5, 1]}, by reading m[1]. So did a later item, every ks as k, every m named by k as x, and a one-line every or some under the for line. A for alias that holds anything but a string now fails as unusable, as for.named_by says, and as ergo.rego does. When the path before it is an object, the failed item is named m named by k, where it was item 4 of m for a 3. Reports change for such policies: some that passed now fail.

ergo.rego 0.4.0

Choose a tag to compare

@github-actions github-actions released this 02 Nov 06:55
rego-v0.4.0
29e790b

New

  • Every phrase that lacked an opposite has one, as spec 0.4 says: does not start with, does not end with, is not a <type>, is not a non-empty string, is not between, is not <value> within <number>, does not equal <field> within <number>, contains some of and does not contain all of, plus is alphabetically on or before and is alphabetically on or after. Each fails as its opposite does when a field is missing, null or of the wrong type. A sentence like x is not 3 within 1 was written wrong before and reads now, after a count of, a sum of, a plus or an elapsed time too (count of x is not 3 within 1), and a compare with ne can have a within. In a comparison of plain paths and written values, and in the pieces of followed by, a path that names its key by a field now lists the field right after the path in a row's inputs, as spec 0.4 says: m named by p is q shows m named by p, p, q, where it showed m named by p, q, p. So does a count of, a sum of, an elapsed time and a plus: count of m named by p is q shows count of m named by p, m named by p[], p, q, where p came last. When the path before the field can't be read, the cause comes from that path and not from the field, so $params.m named by p is q with no m in the params fails as absent when p is null, where it failed as null, and rows.w named by p is q with rows a list fails as unusable. An every, some, count of or sum of walks a list before the field, so every rows.w named by p is q fails as the field says when rows is a list. When rows is null, it now fails as null whatever p holds, a missing p included, where a missing p failed as absent, and failed_items names rows, where it was empty when p couldn't be read, as every rows.w.k is q does.
  • Text can be joined on the right side with followed by, as spec 0.4 says: rego.version is tag.version followed by "-dev", rego.spec is "v" followed by tag.version. It takes written strings and fields. Nothing that read before changes.

Changed

  • This version follows spec 0.4, so a policy says ergo_spec: v0.4, and the report's ergo follows. One that says v0.3 is written wrong, with unsupported version v0.3, expected v0.4.
  • The report's engine name is ergo-rego, where it was ergo.rego. Every report changes in that field only, and the hash leaves engine out, so hashes stay the same. A compiled policy made by an older copy says ergo.rego, so ergo.report reads it as one from another engine, and the message says expected ergo-rego. Compile the policy again.
  • A from that starts at the input with a named by step is written as it is, whatever follows it, in the $min_subjects and $unique_ids expressions, so every $input named by $params.k is no longer count(matching(every )). A subject that narrows a subject read with every takes its flat list of subjects and filters it, where ergo.rego dropped the every and found none. An input that reads the item's own field named like a parent subject keeps its dot, like .art.k. Reports change for such policies.
  • A policy with no requirement, because requirements is missing, null, not an object or empty, gets Ergo's own requirement $policy with a failing $well_formed row that says what is wrong, where its report had no row and ergo.violations returned [] while compliant was false. A requirement called $policy is written wrong. Reports change for such policies.
  • A name that starts with $ is written wrong, whether it names a subject, a requirement, a check, an any_of option or a part, with a name can't start with $, where only a check named like one of Ergo's own rows was (name reserved), $input and $params as subject names (a subject can't be named $input), and a part called $dollar (invalid alias). Reports change for such policies. A check with such a name has no entry in requirements any more.
  • A name that starts or ends with a space or an invisible character (Unicode White_Space or general category Cf, like U+200B or U+FEFF) is written wrong too, with a name can't start or end with a space or an invisible character, for a subject, a requirement, a check, an any_of option and a part. Reports change for such policies.
  • An empty name, "", is written wrong too, whether it names a requirement, a check, an any_of option or a part, with a name can't be empty. Reports change for such policies: a "" was read before.
  • The docs are split by engine. README.md is short and the same for both engines, OPA.md holds how to install and run ergo.rego and the parts of REFERENCE.md that only it has (OPA, Wasm, numbers, ergo.compile), and REFERENCE.md describes only the language.
  • The $well_formed row lists the problems of fields, parts and checks sorted by name as the report writes it, quotes and escapes included, by code point, where parts were sorted by the raw name. Reports change for parts with names that need quoting, and a require that is written wrong is listed second in the $well_formed row.
  • An own field named like a part of the subject or like a for alias keeps its dot in the name of an input, like .cov.pct, and so does the list a count of, sum of or a walk reads, like .art.xs[]. Reports change for such policies: they were named cov.pct and art.xs[]. The failed_items of a walk or a for line keep the dot too.
  • A key read from $params or $input, after named by or in brackets, has to be a string, as spec 0.4 says. A number used to read that item of a list, so m named by $input.k is 1 with k holding 1 and m holding [5, 1] passed by reading 1, and commits[$params.n] read a commit. It fails as unusable now, and so does a boolean. An object, a list or null failed as absent in a followed by piece, an every, a some, a count of, a sum of, a for line and a field after sorted by on a for line, and fails as unusable, or null for null, now, as it did in a plain comparison. Reports change for such policies.
  • A from line like scans named by $params.env named by each of $params.k gave $min_subjects the list's cause when both env and k couldn't be read. The ref before the list decides now, as subjects.sentence says: env holding 1 or an object gives unusable whatever k holds. Reports change for such policies, in the cause only.

Fixed

  • A for line whose check reads a path with a named by field, like row.w named by p is "xy" under every rows as row, stopped the whole report with eval_conflict_error when an item wasn't an object and p was null or a number. The row fails as not_an_object now, with the item in failed_items, as in the Rust engine.
  • A later for item whose path is named by an alias of an earlier one, like every ks as k, every m named by k as x, failed as absent when the alias held an object, a list, a number or null. It fails as unusable now, and as null when the alias holds null, as for.named_by says and as the Rust engine does. So does every rows as r, every r.xs named by r.k as x, where the key after named by is the one word r, the whole row. Reports change for such policies, in the cause only: none of them passed.
  • some vals is m named by k under every ks as k stopped the whole report with eval_conflict_error when k held anything but a string, and so did every, is in and the other comparisons where an every or some reads a path named by a for alias on the right. The row fails as unusable now, or as null when the alias holds null, as in the Rust engine. Reports change for such policies: they have rows where there was an error.

ergo.rego 0.3.1

Choose a tag to compare

@github-actions github-actions released this 31 Oct 12:05
rego-v0.3.1
a870e75

ergo.rego is the same as in 0.3.0 apart from its version, and follows spec 0.3. This release brings the docs up to date with the first release of the Rust engine: README.md says how to install the ergo CLI from crates.io or download it, and calls it the ergo CLI where it meant the command. It also says again how to update ergo.rego.

Ergo spec 0.3

Choose a tag to compare

@github-actions github-actions released this 30 Oct 21:45
spec-v0.3
9151aa7

Changed

  • A policy says ergo_spec: v0.3, and the report's ergo changes from "ergo": "v0.2" to "ergo": "v0.3". A policy that says v0.2 is written wrong, with unsupported version v0.2, expected v0.3. The schemas are under https://ergo.kosli.com/spec/0.3/.
  • An implementation may fail a check as unusable when it can't read or compare a number exactly, like one whose exponent is too long to hold (Values). It used to have to compare every number it was given. ergo.rego compares such numbers as before, so its reports don't change.
  • Whitespace of any kind at either end of a sentence, a for line or a from line is dropped before it's read, not only spaces: every character with Unicode's White_Space property, like a tab, a newline or U+3000 (sentence.ends). So a YAML block like assert: |, which ends in a newline, reads as the sentence it holds. ergo.rego already read these lines, so no report changes. A byte order mark or a zero-width space at either end still makes a line not read. The same goes for the path of an identified_by or a part, which ergo.rego also trimmed already.
  • min_subjects is now a whole number from 0 to 1,000,000. It used to say a whole number of 0 or more, so engines disagreed on big values. A requirement with a larger min_subjects is written wrong, and the message for every wrong min_subjects is not a whole number from 0 to 1000000, where it was not a whole number of 0 or more. A number a 64-bit float can't hold still says number out of range. The policy schema has "maximum": 1000000 on min_subjects. Reports change for such policies: a requirement with a min_subjects over 1,000,000 fails $well_formed and gets no other row, and the $well_formed row of any wrong min_subjects shows the new message.
  • subjects.keyed.id says that a subject listed by named by each of takes its key as its id, so an identified_by on it is written wrong, with not allowed with named by each of: the key is the id. Before, subjects.id said identified_by gave the id, with no exception, and ergo.rego ignored it and used the key. A report changes only for a policy that has both.
  • subjects.sentence says a position after another subject's name, like p[1] or first of p, isn't narrowing: it is a child subject that reads the position inside each parent, and subjects.from says a from line with every whose named by can't be read keeps its requirement and fails $min_subjects like one without every. New cases cover both. Reports change for a policy that relied on ergo.rego dropping the position or losing the requirement.

Fixed

  • for.flaws now says that an item of a for line that isn't an object, null included, fails as not_an_object when the item's path goes on into it, with some and a position too, as every does. It used to say so only for a second item that reads into the first, and the spec didn't say what the other items fail as. for.walk says it too. Reports change for such inputs: with t set to ["a"], for: every t.k as x failed item 1 of t as unusable and now fails it as not_an_object. ergo.rego also failed null items as absent, and count of and sum of on a null item as absent where tally.walk says not_an_object. Only the cause changes, not whether a check fails.
  • tally.whose now says that the path of an item the whose can't decide isn't read, so only its cause counts: null for a null k and absent for a missing one. Cases pin it for sum of and count of. ergo.rego read the path anyway, so such a side failed as unusable when the value wasn't a number, and as absent where k was null and the path missing. Reports change for such inputs, in the cause only.
  • sentence.for.pick now says that an index in brackets doesn't pick, so a for item like t[0] as i is written wrong, as the grammar already had it. ergo.rego read it as a position, so t[0] as i meant first of t as i, and a second item was read the same way. Reports change for such a policy: the check fails $well_formed and the requirement gets no other row.
  • A child subject whose parent isn't an object, null included, fails $min_subjects as not_an_object, as subjects.chain.not_an_object now says. The spec didn't say, and ergo.rego gave unusable for a string, number, list or boolean parent and absent for a null one. Reports change for such inputs. A parent that is an object without the field still gives absent.
  • A from line that is a subject's name and then named by each of, like artifact named by each of $params.keys, makes a child subject, as subjects.chain now says. It reads the keys inside each parent, with in, and a parent that isn't an object fails $min_subjects as not_an_object. A first key with a dot is the input field and never the parent, and the report prints it with its dot when it is named like another subject (sentence.print.canonical), and a position after the parent's name is read as a list and not dropped, so $min_subjects fails as unusable. ergo.rego read a top-level input field of the parent's name, and dropped such a position. Reports change for such a policy.
  • The spec now says these things that ergo.rego already did, so no report changes:
  • tally.sum now says when a sum_of or an add fails as unusable: when a number added, or the result, isn't a whole number below 10^15 once scaled by 10^p, where p is the most decimal places any of them has, counted on the value, or when p is above 15. It used to say such sums are added as the runtime adds them, which gave different answers in different runtimes. Reports change for such inputs, like 0.123456789012345 plus 100, which now fails as unusable with a null entry.
  • sentence.phrase.within now says the gap is worked out exactly on the decimals, for the numbers Values covers, and that an implementation that can't compare the numbers exactly fails the check as unusable, as Values says. ergo.rego and the Rust engine do when the exponents of the two sides and the within number are more than 4096 apart. It used to say nothing, and ergo.rego used OPA's arithmetic, which isn't exact. Reports may change for such inputs: x equals y within 0.2 on 0.3 and 0.1 failed as value and now passes.
  • A from line with named by each of fails $min_subjects when the path before it doesn't lead to an object, as subjects.sentence now says: as absent or null when the object is missing or null, and as unusable when it's a list, a string or a number. Each listed key is still a subject, as before, so its checks still show what's missing, but the requirement is never met. A list of keys that holds anything but strings makes no subjects and fails $min_subjects as unusable. The spec didn't say, and ergo.rego met the requirement whenever every check passed, so with from: scans named by each of $params.required and no scans in the input, x does not exist passed for every key and the policy was compliant. The same holds when a ref step before named by each of can't be used, like named by $params.env with env set to 5, which was met too. Reports change for such inputs: $min_subjects fails and the requirement is not_met, a key list with a non-string key gives no subject rows, and a child subject of such a paren...
Read more

Ergo CLI and Rust engine 0.1.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 22:00
rust-v0.1.0
9151aa7

New

  • The first version of the Rust engine. It follows spec 0.3, so a policy says ergo_spec: v0.3, and it's experimental: anything in it may still change.
  • ergo-policy-sentences reads check sentences, for lines, from lines and paths, without the engine.
  • ergo-policy-engine builds the report and the violations from a policy, an input and params, as JSON.
  • ergo-policy-cli is the ergo command, which prints the report or the violations from policy, input and params files in JSON or YAML.
  • ergo-policy-wasm builds the report in a browser, from JSON text. It isn't published: it's built from the repository for the editor.
  • ergo-policy-sentences-wasm reads check sentences in a browser, from and to JSON text. It isn't published either: it's built from the repository for the editor.
  • A for item whose path goes on into a list item that isn't an object, null included, fails that item as not_an_object, as every and some do, where it failed as unusable.

ergo.rego 0.3.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 21:46
rego-v0.3.0
9151aa7

Changed

  • This version follows spec 0.3, so a policy says ergo_spec: v0.3. One that says v0.2 is written wrong, with unsupported version v0.2, expected v0.3, and still works with 0.2.0. The report's ergo changes with it, from "ergo": "v0.2" to "ergo": "v0.3".
  • min_subjects is a whole number from 0 to 1,000,000. A requirement with a larger one, like 1000001 or 1e308, is written wrong, with its $well_formed row failing and no other row, where before ergo.rego read any whole number up to what a 64-bit float holds, and rejected some big ones like 1e308 by accident. The message for every wrong min_subjects, -1, 1.5, "2" and 1000001 too, is now not a whole number from 0 to 1000000, where it was not a whole number of 0 or more. A number past a float, like 1e400, still says number out of range. Reports change for such policies: the $well_formed row shows the new message.
  • A subject with an identified_by whose from line uses named by each of is written wrong, with not allowed with named by each of: the key is the id under identified_by in the $well_formed row, and no other row for its requirements. Before, ergo.rego ignored the identified_by and used the key as the id.

Fixed

  • A sum of or a count of with a path doesn't read the path of an item its whose can't decide, so the side fails with the whose's own cause, as tally.whose says. It read the path and gave the cause of what it found. With sum of items.x whose k is "a" is 1 on {"things": [{"id": "t", "items": [{"x": "ab", "k": null}]}]}, the check now fails as null, where it failed as unusable. With k missing it now gives absent, and a field that is missing with k null gives null, where it gave absent. The same goes for a path that goes on, like items.x.y, and for count of items.x whose k is "a": on [{"k": null}] it went from absent to null, and count of items.x.y whose k is "a" is 1 on [{"x": "s", "k": null}] from unusable to null. Only the cause changes.
  • A for line whose item path goes on into a list item that isn't an object, null included, fails that item as not_an_object, as every and some do. It failed as unusable, or as absent for a null item. With t set to ["a"] or [null], for: every t.k as x now fails item 1 of t as not_an_object. count of and sum of fail a null item as not_an_object too, where they said absent. The same checks fail as before, and only the cause changes, in the row, in failed_items and in the requirement's causes.
  • A for item that picks with brackets, like t[0] as i, is written wrong, as the spec says. Before, ergo.rego read the brackets as a position, so t[0] as i meant first of t as i. It fails $well_formed with for: 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, like every t as x, t[0] as i, which used to be read. first of t as i and brackets after every or some, like every 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 i with assert: i exists used to be satisfied, with the input item 1 of t, and is now not met.
  • A child subject under a parent that isn't an object fails $min_subjects as not_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 gave unusable, and a null parent gave absent. Reports change for such inputs, like {"trail": {"artifacts": ["x"]}} with a pull request subject from artifact.pull_requests. The verdict doesn't change, and a parent that is an object without the field still gives absent.
  • A from line that is another subject's name and then named by each of, like artifact named by each of $params.keys, reads the keys inside each parent subject and puts the parent under in, as the spec says. Before, ergo.rego read 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_subjects as not_an_object. A first key with a dot, like .artifact named by each of $params.keys or .artifact.p, reads the input field and not the parent, and a position, like artifact[0] named by each of $params.keys, first of artifact named by each of $params.keys or artifact.x[0] named by each of $params.keys, is no longer dropped: it reads the parent as a list and fails $min_subjects as unusable. The report prints .artifact.p and .artifact named by each of $params.keys with their dot, in subjects.<name>.from and in the $min_subjects and $unique_ids expressions, where it printed artifact.p, which reads the parent. A policy with such a from line 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 true used to pass on /artifact/x/ok and now fails as absent for x in a1.
  • A sum of or a plus whose numbers can't all be held as whole numbers below 10^15 at one scale fails as unusable, with its entry null. 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 0 on {"x": 0.123456789012345, "y": 100} was satisfied and now fails as unusable, and so does 1e20 plus 1. Sums of ordinary numbers, and numbers with trailing zeros like 1.500000000000000000, are added exactly as before.
  • A within check 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.2 on {"x": 0.3, "y": 0.1} failed as value and is now satisfied, and so is 0.1 and 0.3, while within 0.19999 still fails as value. A check that passed before still passes, except one whose two sides and within number have exponents more than 4096 apart, like 0 and 1e-5000 within 1, which passed and now fails as unusable.
  • A from line with named by each of fails $min_subjects when the object it reads the keys from is missing, null or not an object, as absent, null or unusable, 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_subjects as unusable. Before, from: scans named by each of $params.required on an input without scans gave one subject per key, so a check like x does not exist passed for each and the policy was compliant. The same holds when a ref step before it can't be used, like scans named by $params.env named by each of $params.required with env set to 5, which used to be met too. A child subject of such a parent gets one $min_subjects row with no in, failing with the parent's cause, and one $unique_ids row with no in, instead of a row per key.
  • When the input is a list, from: $input, from: every $input and an every walk that starts at the input, like from: every commits, make each item a subject. They failed $min_subjects as unusable. Each item gets its own rows, with locations like /0/x, and an empty list fails $min_subjects as value. A position on a list input, like from: $input[0] or from: first of $input, reads the item, and ends absent past the end or on [], where it failed unusable. The same goes for a child subject whose from starts with a position on a parent that is a list, like artifact[0].c: it reads the item of each parent, and a parent that is [] or null fails $min_subjects as absent. A field, like $input.commits, stays unusable on a list. The $min_subjects and $unique_ids expressions of from: every $input and from: every $input whose ... now keep every, like count(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 with from: every $input.
  • subjects at 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. A from line that reads but has a problem, like values of mixed types, shows as <invalid from> instead of the line. A subject whose from starts at another subject lists that subject even when the line is written wrong, as with every parent.items, and a subject that narrows another has that subject's id then, even with an identified_by of its own. A from that doesn't parse, or isn't a string, lists no other subject, as before.
  • A check written wrong with values of mixed types and nothing else is printed as written, like every other check written wrong, and its $well_formed input is named after the check. Before, its expression was 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 the expression x is one of 1, "a" and now has x 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.0 under a for line is now checks.f, checks.g.checks.1 in a list of assertions is now checks.g, with line 2: in front of the problem, and checks.c.whose.0 or checks.c.left.whose.0, for...
Read more