Skip to content

Ergo spec 0.4

Choose a tag to compare

@github-actions github-actions released this 02 Nov 06:58
· 32 commits to main since this release
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 now also covers an own field named like a part of the subject or like an alias of a for line, like .cov.pct, and the name of the list a count of, sum of or a walk reads keeps the dot, like .art.xs[]. Reports change for such policies: the input was named cov.pct and art.xs[]. name.dot also says that a part's name refused because a parent has the same one still counts, and that a check whose for isn't a string is printed as it was written, so a written dot stays.
  • A key read from $params or $input, after named by or in brackets, has to be a string, as for a field and a for alias, so named by always names a key and never a position (path.ref_step, sentence.path.brackets). A number, a boolean, a list or an object fails the check as unusable, null as null and a missing one as absent. 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. Reports change for such policies: those checks fail as unusable now, and a from line with such a key gives no subjects.
  • A from line with named by each of reads its path before the list, so a ref in the path that is missing, null or not a string decides $min_subjects, as absent, null or unusable, whatever the list holds (subjects.sentence). Only when every ref in the path reads a string does the list decide. So scans named by $params.env named by each of $params.k, with env holding 1 and k missing, fails as unusable, not absent. Reports change for such policies, in the cause only.