Repository navigation
Guardana Control 0.14.0
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.Newrefuses an override whoseReturnsTrustorReturnsSensitivity
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.Newrefused it already
(ADR-0059).- Built with Go 1.27.3, which fixes GO-2026-6809 in
crypto/x509;
govulncheckfound it reachable from the evidence reader under 1.27.2. - The MCP client the filesystem server's guide is checked with locks
certifi2026.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-parser2.4.0,
which fixes GHSA-27r3-jxmh-p828, andfast-uri3.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/contractexportsEffectRequires, whatValidaterequires of each
effect class
(ADR-0060).
Fixed
scripts/github-bootstrap.sh's preflight runs the docscheck test that
holds.github/CODEOWNERSon every pull request, so it judges each file by
its last matching rule, and needsgoon 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 doctorexits 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 runanddevfail 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
withBLOCKand 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 onlyOBLIGATIONS_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,-0and+01no longer name an element. - An enforcement API plane in
OBSERVEhanded 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 answersBLOCK, as the MCP
adapter does. Atoolstable that classes a toolSPAWN_OR_DELEGATE,
which this API can never decide, is refused when the plane starts and by
doctor. superviseleaves 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.superviseholds 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 isINDETERMINATE. 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, indoubted_run_ids, at most 64, or sets
every_run_doubtedfor a refused copy that claims none, a line refused
whole or a 65th run; a report carrying either is schema0.2, which a
0.1-only reader refuses, and a0.1report 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 --viewdraws a call that came through the enforcement API,
protocolenforce, as decided, not enforced, in every mode, ascoverage
already holds it; it was drawn enforced underENFORCE.- The OTLP exporter refuses a collector's answer whose partial success
carries anerrorMessagethat 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 exportends 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 apartial_tailgap 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.coveragereads a source descriptor'ssampling: a path observed only by
sources that do not sample completely,samplingabsent included, prints
observed, <trust>, sampled, and so does each such source's line. Nothing
read the member before.- The dependency scan in
make securityfails whenosv-scannerprints no
"Scanned" line for ago.mod,uv.lockorpackage-lock.jsonthe
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 ofmake quality, is its negative
control. make testfails on a package with no test files outsideapi/gen/.make test-racereads its results through the test reporter, as
make testdoes, so a package that runs no test under the race detector
fails it.make check-shellalso checks a file whose first line namessh,bash,
dashorkshin/bin,/usr/binor/usr/local/bin, directly or
throughenv, not only*.sh.make fuzz-smokefails, 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
v1alpha1as 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 signandpolicy lintrefuse acap_amountmaxbelow
-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 withOBLIGATION_NOT_UNDERSTOOD; a
bundle already signed with one still loads and is denied as before.supervisereads 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 leftSTEP_OUTSIDE_PROCEDUREand
DENIED_ACTION_RETRIED_AROUNDnot checked.supervisealso refuses a
--sourcewhose 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
MAKECMDGOALSor a
.DEFAULT_GOALset on the command line, throughMAKEFLAGSor from the
environment, and a makefile named inMAKEFILES. Itsmake qualityguard
read the goals fromMAKECMDGOALS, somake quality MAKECMDGOALS= MAKE=/usr/bin/trueandmake .DEFAULT_GOAL=quality MAKE=/usr/bin/true
passed every guard, ran no check and printedquality: green, and a
MAKEFILESmakefile could undefineMAKECMDGOALSto the same end. CI's
gate step also unsetsMAKEFILES, since such a makefile can undefine
MAKEFILEStoo.make qualityalso refusesSHELLOPTS,BASHOPTS,
CDPATH,GLOBIGNOREand an exported bash function, which change what its
recipes' shell runs:SHELLOPTS=noexecran nothing and exited 0. These
guards, and those onBASH_ENV,ENV,GOFLAGSandMAKEFILES, 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.shcompares 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-devor2.14.0+dirtyfails a pin of2.14.0; it
read only the leadingx.y.zand passed them, in--checktoo, 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 doctorreportsunknown, notfail, when a decision
point's published metadata gives its identifier or evaluation endpoint as
JSONnull, 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-sizesholds*.pyand*.shfiles 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:
whatOBSERVEstill 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 digestOBSERVErecords 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 carryPOLICY_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: aWRITE,DELETE,CONFIGURE,TRANSACTor
IDENTITY_OR_ACCESSoverride or tool withoutresource_from, a
COMMUNICATEone withouttrust_zone, aresource_type, toolnameor
providerthe 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'sresource_typepast 1024 bytes, and, incoverage,
an override'sresource_fromthat is not an RFC 6901 pointer within its
bounds. An upstream'sname, every MCP call'saction.provider, is held to
the same rule and to 1024 bytes. In every mode,OBSERVEincluded, each call to such a tool was
blocked before it was sent.mcp.Newandenforce.Newrefuse the same, and
the wording of these refusals now names the configuration key
(ADR-0059). policy lint,policy test,policy signandguardana-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 explainstill explains such a document. The five types nothing
applies,cap_rateamong them, take no parameters, and
restrict_resourcesneedsidsorprefix. 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