Aguara v0.27.0 ships two things: a native fuzz harness over every parser that reads attacker-controlled files, and a terminal that finally behaves like a good citizen in pipes, CI logs, and interactive sessions alike.
The fuzz harness is the security story. Aguara's core job is parsing files an attacker wrote: lockfiles in a repo you just cloned, skill markdown, agent configs, CI workflows. If one of those parsers panics on a crafted input, the security gate itself becomes the target. Crash the scanner, and the check never happens.
The input is hostile by definition
The whole point of aguara check . on a fresh clone is that you do not trust the repo yet. That means every byte the parsers consume is chosen by whoever you do not trust: the package-lock.json, the pnpm-lock.yaml, the bun.lock, the .claude/settings.json, the SKILL.md with its encoded payloads. A scanner that can be crashed by its own input fails in the worst possible direction. Depending on how the pipeline treats the exit, it either blocks every build or, worse, gets wrapped in a retry-and-skip and fails open.
v0.27.0 adds 22 Go fuzz targets covering every parser that touches that input:
- The ten lockfile parsers behind
aguara check: npmpackage-lock.json,pnpm-lock.yamlwith its alias resolution, both yarn generations,bun.lock, and the Go, Rust, PHP, Ruby, Java, and .NET formats. - The pattern engine, including the NFKC normalization pass and the 8-decoder rescan (base64, hex, URL encoding, Unicode escapes, and friends) that exist precisely to catch encoded evasion - which makes them parsers of deliberately weird input.
- The NLP extractor that walks markdown ASTs and pulls strings out of JSON and YAML.
- The language scanners for JavaScript, Python, and Rust build scripts, and the cross-file capability correlator.
- The policy analyzers that read
package.json,.npmrc,pnpm-workspace.yaml,.claude/settings.json, and GitHub workflow YAML. - The custom-rule loader reached via
--rules, because a rules file from a shared repo is untrusted input too.
What each target asserts
Every target seeds from real grammar - actual lockfile fragments, actual workflow YAML - and holds two invariants no matter what the fuzzer mutates: the parser never panics, and it never emits a finding without a rule ID. The second one matters because a finding with no rule behind it cannot be explained, suppressed, or trusted.
The harness runs at three speeds. Seed corpora execute as plain tests inside make test, so every PR exercises the known-tricky inputs. A nightly workflow gives each target a 60-second budget on the runner and uploads any crasher as a reproducible artifact. And make fuzz runs the same loop locally with a polite default budget:
$ make fuzz
── fuzz .../internal/packagecheck FuzzParsePNPMLock (10s)
fuzz: elapsed: 10s, execs: 392184 (39218/sec), new interesting: 0
PASS
── fuzz .../internal/engine/pattern FuzzAnalyze (10s)
...
The first shakeout ran about 37 million executions across all targets. Zero crashes. That is the boring result you want from code that has been ground-truthed against real package managers for months - and now there is a harness making sure it stays boring.
The terminal grew up
The second half of the release is everything you see. Until now each command hand-rolled its own ANSI codes; v0.27.0 moves scan, check, audit, explain, and clean onto one shared style layer: the same palette, the same severity icons, the same framed verdicts.
More importantly, output now adapts to where it is going:
$ aguara scan . | less # no ANSI noise, no spinner frames
$ aguara scan . -o report.txt # clean text in the file
$ NO_COLOR=1 aguara explain AGENTCFG_HOOK_FETCH_EXEC_001
Color and the progress spinner turn themselves off when stdout is not an interactive terminal, when --output writes to a file, or when NO_COLOR is set - and NO_COLOR now covers every command, including explain and clean, which previously ignored it. Separators and the spinner size themselves to the real terminal width instead of assuming 80 columns.
One change is about honesty rather than polish: aguara audit renders the FINDINGS verdict in yellow instead of green. Exit code 0 with unresolved findings is not a clean pass, and the color now says so. The audit view also gained aligned finding columns under PACKAGE CHECK and CONTENT SCAN sections, a --verbose flag that lists every content finding instead of capping at 10, and a closing Next: aguara explain <rule> hint pointing at the most severe finding.
Nothing changed for machines
JSON, SARIF, and markdown outputs are byte-for-byte governed by the same contracts as before. Rule IDs, severities, and the numeric Severity encoding are unchanged. If a pipeline parses Aguara output, v0.27.0 is a drop-in upgrade; only humans get the new view. 250 cataloged detections, same as v0.26.0.
Full details in the release notes and CHANGELOG. For the coverage expansion this release builds on, see v0.26.0: 228,000 known-malicious packages, offline.
One binary that audits the repo before you trust it
aguara audit . runs the package check and the content scan offline and gives one verdict. Now with output that respects your pipes.