Skip to content

Guardana Control 0.14.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 08:57
· 73 commits to main since this release
Immutable release. Only release title and notes can be modified.

supervise no longer passes a run on evidence that may hide a call. A request
whose trail is in doubt leaves not checked every rule that would read as
checked. A span or observation an import refused while it claimed a run leaves
the rules that read a source's reports not checked, and makes a finding citing
that run's observations INDETERMINATE. Either way the command now exits 1,
where it exited 0
(ADR-0058).
An import report carrying such a doubt is schema 0.2, which a reader of 0.1
only refuses, and the doubt lasts until the log is rotated by hand. A gateway
configuration with a tool classification no envelope could carry is now refused
when it loads
(ADR-0059).
policy lint, policy test, policy sign and guardana-gateway dev refuse
an obligation parameter its type does not declare, a missing required one or a
malformed value, while a bundle already signed still loads
(ADR-0061).
In OBSERVE the enforcement API now blocks every call whose envelope the
contract refuses, as the MCP adapter does. Both of its clients refuse an admit
answer whose decision carries no schema version, or one on another major.
pkg/contract exports what each effect class requires
(ADR-0060).
The experimental hook profile is qualified on a second release of its runtime
and is still labelled "decided, with failure gaps". Among the pre-release
audit's fixes: a source's doubt is read from that source's own reports only,
a --source of another tenant or project is refused, and cap_amount's max
is held to the integer range the canonical form writes.
The project is still alpha, and the 0.x series makes no compatibility promise
between minor versions.

Security

  • mcp.New refuses an override whose ReturnsTrust or ReturnsSensitivity
    is a number the contract does not declare. Such an override loaded through
    the Go API and its calls ran with a result classification the run's flow
    state under-reported; the configuration file could not spell one, and
    enforce.New refused it already
    (ADR-0059).
  • Built with Go 1.27.3, which fixes GO-2026-6809 in crypto/x509;
    govulncheck found it reachable from the evidence reader under 1.27.2.
  • The MCP client the filesystem server's guide is checked with locks
    certifi 2026.10.19, which fixes PYSEC-2026-4320 (GHSA-6q48-6c7w-9gw2).
    Neither binary nor the Python client of the enforcement API uses it.
  • The filesystem server the same guide installs locks body-parser 2.4.0,
    which fixes GHSA-27r3-jxmh-p828, and fast-uri 3.1.9, which fixes
    GHSA-333x-w4r6-6crp, GHSA-w74x-v6f9-j3xg, GHSA-wjr9-j795-jxcr and
    GHSA-xhq7-xmqj-769h. Both are packages below the server, at the version its
    fingerprints were taken from; nothing this project ships uses them.

Added

  • pkg/contract exports EffectRequires, what Validate requires of each
    effect class
    (ADR-0060).

Fixed

  • scripts/github-bootstrap.sh's preflight runs the docscheck test that
    holds .github/CODEOWNERS on every pull request, so it judges each file by
    its last matching rule, and needs go on PATH. Its own prefix match refused
    .golangci.yml, which /.golangci.* gives to the security maintainers, so
    the declared settings could not be applied. That test now refuses **,
    [ ] and a glob in a directory pattern, which it read as matching nothing,
    and reads a glob that gives a file to anyone else as also taking what lies
    under a directory it names.
  • guardana-gateway doctor exits 1 when standard output does not take its
    whole report, as on a full device, and says so on standard error; it exited
    0 with the report cut short.
  • guardana-gateway scenario run and dev fail a scenario whose stateful
    session does not close on the transport, naming the close; the error was
    dropped and the scenario passed.
  • The enforcement API answers a call whose rewrite moved the decided resource
    with BLOCK and the abort's code, OBLIGATION_NOT_UNDERSTOOD, after the
    decision's own; it carried the kernel's allow alone, so the hook bridge and
    the Python client reported a block with only OBLIGATIONS_ATTACHED.
  • The Go and Python clients of the enforcement API refuse an admit answer
    whose decision has no schema version or one on another major as malformed,
    and run no tool on it; the answer vectors and client cases carry the
    version.
  • A resource pointer's array index is decimal digits only, as RFC 6901 reads
    it: +1, -0 and +01 no longer name an element.
  • An enforcement API plane in OBSERVE handed out a call whose envelope the
    contract refused for more than an unnamed tool, such as a transaction
    naming no resource; it now aborts it and answers BLOCK, as the MCP
    adapter does. A tools table that classes a tool SPAWN_OR_DELEGATE,
    which this API can never decide, is refused when the plane starts and by
    doctor.
  • supervise leaves not checked, and exits 1, every rule that would read as
    checked while a request's trail is in doubt, and the rules that read a
    source's reports while an observation id is read with two contents; the
    why names the record, quoted and cut at 64 bytes. A broken chain or a
    conflicting copy only lowered a finding's verdict, so a run with no finding
    passed on evidence that may hide a failure, a denial or a call.
  • supervise holds in doubt a run an import refused a copy of: a span the
    importer refused, or an observation the log refused for an id it held with
    other content. While a run it judges is in doubt, the rules that read a
    source's reports are not checked, naming the source, and a finding citing
    an observation of that run is INDETERMINATE. The log kept the first
    content of an id and only counted the other, and a refused span was only
    counted, so an agent that sent an allowed call and then a forbidden one,
    under the same trace and span id or in a span the import refuses, passed
    with exit 0. An import report names the runs those copies claimed, both
    copies' of a conflict, in doubted_run_ids, at most 64, or sets
    every_run_doubted for a refused copy that claims none, a line refused
    whole or a 65th run; a report carrying either is schema 0.2, which a
    0.1-only reader refuses, and a 0.1 report that counts a refusal or a
    conflict doubts every run. Such a doubt lasts until the log is rotated by
    hand. A rule cut for several reasons names each.
  • supervise --view draws a call that came through the enforcement API,
    protocol enforce, as decided, not enforced, in every mode, as coverage
    already holds it; it was drawn enforced under ENFORCE.
  • The OTLP exporter refuses a collector's answer whose partial success
    carries an errorMessage that is not a string of UTF-8, or escapes half of
    a surrogate pair, and retries the batch; an object, an array, a number or a
    boolean was read as an acknowledgement.
  • observe export ends at the observation log's last import report, as the
    log's reader does; the lines after it are the tail while a writer holds the
    log and a partial_tail gap when none does. It handed out observations of a
    write a crash left, which the next writer cuts. A cursor into a write still
    open is refused as past the end until a report commits that write.
  • coverage reads a source descriptor's sampling: a path observed only by
    sources that do not sample completely, sampling absent included, prints
    observed, <trust>, sampled, and so does each such source's line. Nothing
    read the member before.
  • The dependency scan in make security fails when osv-scanner prints no
    "Scanned" line for a go.mod, uv.lock or package-lock.json the
    repository holds, reads any file as holding no package, or finds no
    lockfile at all, instead of passing over a lockfile it could not read;
    make scan-repo-files-probe, now part of make quality, is its negative
    control.
  • make test fails on a package with no test files outside api/gen/.
  • make test-race reads its results through the test reporter, as
    make test does, so a package that runs no test under the race detector
    fails it.
  • make check-shell also checks a file whose first line names sh, bash,
    dash or ksh in /bin, /usr/bin or /usr/local/bin, directly or
    through env, not only *.sh.
  • make fuzz-smoke fails, naming the file, on a test file it cannot read
    instead of passing over it.
  • CI's quality gate checks every pinned action SHA against the commit its
    version comment's tag names in that action's repository, and fails when
    GitHub cannot answer.
  • The format versions page names v1alpha1 as the enforcement API version
    this tree writes and reads, and says which formats carry no version of
    their own: the signed envelopes and the spool's segment framing.
  • policy sign and policy lint refuse a cap_amount max below
    -9007199254740991, the smallest integer canon writes. The gateway capped an
    amount to such a limit and then could not canonicalize the arguments, so
    every call the rule matched was denied with OBLIGATION_NOT_UNDERSTOOD; a
    bundle already signed with one still loads and is denied as before.
  • supervise reads a source's doubt from that source's own import reports,
    those naming its source, tenant and project, as it reads its liveness. A
    line another source's import refused, in the same log directory, put the
    supervised source's runs in doubt and left STEP_OUTSIDE_PROCEDURE and
    DENIED_ACTION_RETRIED_AROUND not checked. supervise also refuses a
    --source whose descriptor names another tenant than the run's, or another
    project than its events, and an export in which an event of the run names
    no project. The run's observations were read under such a descriptor while
    its liveness and its doubt were another project's.
  • The OTLP exporter writes a log record's time from the event's seconds and
    nanoseconds as an unsigned count. An event before 1678 or after 2262 could
    get another time or none: 1600-01-01 was sent as a time in the future, and
    2300-01-01 without one. A time before the epoch, or past what 64 unsigned
    bits count, carries no time, as an event without one does.
  • The Makefile refuses, whatever the goal, a MAKECMDGOALS or a
    .DEFAULT_GOAL set on the command line, through MAKEFLAGS or from the
    environment, and a makefile named in MAKEFILES. Its make quality guard
    read the goals from MAKECMDGOALS, so make quality MAKECMDGOALS= MAKE=/usr/bin/true and make .DEFAULT_GOAL=quality MAKE=/usr/bin/true
    passed every guard, ran no check and printed quality: green, and a
    MAKEFILES makefile could undefine MAKECMDGOALS to the same end. CI's
    gate step also unsets MAKEFILES, since such a makefile can undefine
    MAKEFILES too. make quality also refuses SHELLOPTS, BASHOPTS,
    CDPATH, GLOBIGNORE and an exported bash function, which change what its
    recipes' shell runs: SHELLOPTS=noexec ran nothing and exited 0. These
    guards, and those on BASH_ENV, ENV, GOFLAGS and MAKEFILES, read the
    value as the shell or go gets it, not as make expands it, so a spelling make
    expands to nothing is refused too.
  • scripts/bootstrap.sh compares a tool's whole version word with its pin,
    suffix included, so a prerelease or development build such as
    2.14.0-rc.1, 2.14.0-dev or 2.14.0+dirty fails a pin of 2.14.0; it
    read only the leading x.y.z and passed them, in --check too, which the
    release commit's cold gate runs.
  • The hook profile's qualification passes the secret-read case only when the
    runtime's stream holds its error answer to that Read; a runtime that
    stopped after the hook, with no answer recorded, passed it. Every case is
    unknown when a line of the runtime's stream or of the trail does not parse,
    a hook event cannot be read or its directory cannot be listed, and the two
    rewrite cases are unknown when
    the plane's count of halted runs was not read; such a line was skipped and
    the rest judged. A results line that does
    not parse fails the report. The 2.1.310 observations, judged again, still
    pass all 20 cases.
  • guardana-gateway doctor reports unknown, not fail, when a decision
    point's published metadata gives its identifier or evaluation endpoint as
    JSON null, as it does for any other value that is not a string.

Changed

  • The built-in hook profile is
    qualified on a newer release of its runtime too: all 20 cases passed on the
    first run, in 22 invocations, and the profile names that release among its
    tested versions.
  • make check-sizes holds *.py and *.sh files to the size budget too,
    tests included: above 350 non-blank lines warns, above 500 fails. The
    files that were over it are split, with nothing changed for a caller.
  • Statements on the public pages that the code contradicted are corrected:
    what OBSERVE still blocks, a held trail's closing record after hand-out,
    shutdown of an enforcement API plane, the collector's 415, the hook's
    deadlines, coverage's join and others. The status page, roadmap and backlog
    stop describing the Python client and the hook bridge as planned, and
    ADR-0050 is marked accepted, as it was on merging. The pre-release audit
    corrected fifteen more: among them the trail of a call blocked after it
    started, the digest OBSERVE records for a call whose input the kernel
    refused, when an evidence record is on disk, which collector answers
    quarantine a record, and which stale decisions carry POLICY_STALE.
  • A gateway configuration refuses a tool classification no envelope could
    carry, naming the key, where before it loaded and every call to the tool was
    refused: a WRITE, DELETE, CONFIGURE, TRANSACT or
    IDENTITY_OR_ACCESS override or tool without resource_from, a
    COMMUNICATE one without trust_zone, a resource_type, tool name or
    provider the contract's identifier rule refuses (invalid UTF-8, white
    space at either end, or anywhere a space other than U+0020, a control or
    format character, a line or paragraph separator or a default-ignorable code
    point), an override's resource_type past 1024 bytes, and, in coverage,
    an override's resource_from that is not an RFC 6901 pointer within its
    bounds. An upstream's name, every MCP call's action.provider, is held to
    the same rule and to 1024 bytes. In every mode, OBSERVE included, each call to such a tool was
    blocked before it was sent. mcp.New and enforce.New refuse the same, and
    the wording of these refusals now names the configuration key
    (ADR-0059).
  • policy lint, policy test, policy sign and guardana-gateway dev
    refuse an obligation parameter its type does not declare, a missing
    required one, or a malformed value; a loaded bundle is unaffected, and
    policy explain still explains such a document. The five types nothing
    applies, cap_rate among them, take no parameters, and
    restrict_resources needs ids or prefix. The catalogue states each
    type's parameters and the reference page renders them from it
    (ADR-0061).

Image: ghcr.io/guardana/control-gateway@sha256:c0859dd5e1a3a49f292bde8be0ac90ac4bd9c6c74f6238ffac75685a5891dcc8