Skip to content

v0.6.3

Choose a tag to compare

@aliasfoxkde aliasfoxkde released this 24 Sep 17:05
· 14 commits to main since this release
Immutable release. Only release title and notes can be modified.
a4e33a8

Added

  • aegis-mcp now speaks the Model Context Protocol lifecycle:
    initialize (version negotiation across 2024-11-05, 2025-03-26, and
    2025-06-18), notifications/initialized, tools/list (all seven
    scanning tools with JSON-Schema input schemas), tools/call (results
    as text content; tool failures as isError: true results), and
    ping. Generic MCP clients such as Claude Desktop can now use the
    server without a translation shim; the original custom JSON-RPC
    method set is unchanged and dispatches to the same implementations.
  • Wire-conformance fixture suites replayed against the real binaries
    pin both integration surfaces: aegis-mcp's stdio handshake,
    discovery, sandbox rejection, unknown-tool, and parse-error semantics
    (crates/aegis-mcp/tests/fixtures/), and aegis-daemon's
    Unix-socket JSON-lines protocol — error envelopes, sandbox refusal
    without content leakage, blank-line framing, and scan receipts
    (crates/aegis-daemon/tests/fixtures/).
  • The corpus harness now measures per rule, not just in aggregate: any
    rule with enough corpus observations must hold a per-rule precision
    floor (0.80 at ≥ 2 observations), and hand-assigned confidence
    labels are calibrated against measured precision demote-only
    (high ≥ 0.95, medium ≥ 0.80 at ≥ 3 observations — the gate can
    force a label down or a regex fix, never a promotion). A new
    tests/corpus/negative/ section pins the 2026-09-22 false-positive
    audit: each fixed rule (hipaa-phi, mesa-optimization,
    k8s-run-as-non-root, code-injection-request, executable-file-upload)
    keeps a regression fixture where it must stay silent, preserving the
    shape that made the old regex fire.
  • aegis-core::clone now detects Type-3 (near-miss) clones —
    copy-paste with reordered or inserted statements — instead of only
    labelling them. Similarity is a longest-common-subsequence ratio over
    the two blocks' token sequences (identifiers and literals compared by
    role), scored on the best-aligned of a block stride's window phases,
    so order and multiplicity count; the previous bag-of-tokens score
    ignored both and rated unrelated same-shape functions as Type-1 at
    1.0. Blocks grew from 20 to 40 tokens because a near-miss claim
    cannot rest on a statement and a half of role-normalized code, so
    regions shorter than 40 tokens are no longer compared, overlapping
    windows are no longer compared with each other, a file yielding more
    than 256 windows is sampled rather than paired in full, and reported
    clone locations carry real line numbers (they were always line 1).
  • Clone detection is wired into the CLI: aegis scan --detect-clones
    runs the detector on every analyzed file and reports the pairs in a
    Code clones section of the human output and under stats.clones in
    JSON output (kind, description, similarity, token count, and the two
    locations with real line numbers). Clones are a separate output
    channel, not findings: they never enter the finding list, risk score,
    SARIF document, or exit code, and default scans serialize
    byte-identical JSON to previous releases. A clone_detection
    criterion bench feeds the weekly trend.

Security

  • Both wire servers now cap request frames at 10 MiB. Previously a
    single authorized peer could exhaust server memory with one
    arbitrarily long line (read_line/next_line grow without limit).
    aegis-mcp answers an oversized line with JSON-RPC -32600 and ends
    the session; aegis-daemon answers once with a size error and drops
    the connection while continuing to serve other clients.

Changed

  • The workspace denies clippy::unwrap_used, clippy::expect_used, and
    clippy::panic in production code: errors must be values. The 29
    unwrap()/expect() call sites the gate surfaced were removed — 27
    by converting internal lock state to parking_lot, which does not
    poison and so needs no unwrap-on-lock idiom, and 2 by propagating the
    error properly — and the 3 deliberate exceptions (a compile-time
    constant regex, epoch arithmetic, a documented panicking convenience
    constructor) carry an inline #[allow] naming the invariant it
    protects. Test code is exempt through clippy.toml's
    allow-…-in-tests flags.
  • A new [lints.rustdoc] table denies broken and private intra-doc
    links, and CI gains a Rustdoc job that runs cargo doc with
    RUSTDOCFLAGS=-D warnings so the gate is actually exercised.
  • The bounded newline framing shared by both wire servers lives in one
    place: aegis-core::transport (read_bounded_line, FrameRead,
    MAX_FRAME_BYTES) is now the single implementation behind the MCP
    stdio server and the daemon socket server, which previously carried
    byte-identical copies plus duplicate test suites.
  • -c/--config resolution is deterministic: a value with a path
    separator or a .json extension is a file path (a typo'd path now
    fails with the real I/O error instead of the preset list); a bare
    name is always a preset, even if the working directory happens to
    contain a file with that name.
  • .aegis.yml loading probes the scan root once: callers that already
    located the file use load_user_pattern_definitions_from_path
    instead of re-running the directory probe inside
    load_user_pattern_definitions.
  • The daemon's list_patterns no longer builds a JSON description of
    every pattern only to throw it away; it reports the registry count,
    which is what the daemon protocol documents. Full pattern metadata
    remains the MCP server's list_patterns response.
  • The CLI's internal scan dispatcher refuses --stdin with an
    explicit error instead of silently scanning a decoy empty string;
    real stdin content flows through the async entry point, which is
    unchanged.
  • A coverage floor now actually gates: the Codecov upload has been
    failing on every run for lack of a token, so codecov.yml's targets
    were never evaluated. The Coverage job computes a native floor from
    the lcov report with scripts/coverage-floor.sh — 97.0% lines
    against the CI-measured 97.61% over the counted set (2026-09-24),
    ignoring crates/aegis-wasm and the root shim binary exactly like
    codecov.yml, whose project target is synced. The gate logs a
    per-crate breakdown of what it counted; the floor ratchets up, never
    down to pass.

Removed

  • Dead Scanner state and config surface that no shipped binary could
    observe: the never-read suppression_manager field (the scan path
    already builds its own), the Config fields strict_mode,
    performance_mode, exit_on_findings, max_file_size_mb,
    binary_file_detection, gitignore_respect, aegisignore_respect,
    and timeout_seconds, and the StrictMode/PerformanceMode enums
    only those fields carried. Three of the fields were applied in
    exactly one place, Scanner::from_config, which no binary in this
    workspace calls — the CLI builds scanners from flags instead — so a
    gitignore_respect: false in a profile never actually disabled
    ignore handling anywhere; keeping parse-but-ignore fields was the
    lie. Existing user profiles still load because serde skips unknown
    keys by default. The dead is_path_dangerous / get_sandbox_root
    sandbox helpers are gone too.
  • The DEFAULT_CATEGORIES constant, Pattern::regex_matches helper,
    and the unused parse_source AST wrapper — all unreferenced.

Fixed

  • Pattern-finding columns are now 1-indexed within the match's own line.
    They previously carried the byte offset within the whole file, so
    file.rs:42:18513-style locations could not be opened in an editor and
    SARIF startColumn values were meaningless on anything but tiny files.
    fingerprint (the baseline key) never included the column, so existing
    baselines still match; stable_id values change because the column is
    part of their material.
  • env-file-in-git matched a .env path at a line start by consuming the
    preceding newline character, which attributed the finding to the previous
    line — where an aegis:ignore:env-file-in-git directive does not sit —
    so a deliberately committed-and-suppressed .env mention in
    docker/.dockerignore still failed the scan. The rule now uses ^ in
    multiline mode for the line-boundary case, so the match starts at the
    .env itself and inline suppression works.
  • An unrecognized --severity-threshold value (or the severity_threshold
    key of a -c profile) silently disabled the severity filter — a typo
    like hihg reported more findings than requested at exit code 1,
    with no diagnostic. The value is now validated the way --categories
    and the anomaly allow-list always were: the CLI rejects it before any
    scan mode runs (including --env/--stdin/--diff), and the
    aegis-core scan entry points return
    ScanError::InvalidOptions; ScanOptions::validate offers the same
    check to library callers.
  • Clone reports are bounded: pairing stops at 256 clone pairs per file
    (MAX_REPORTED_CLONES), so a minified or generated file where nearly
    every window pair qualifies can neither flood the report with a
    multi-gigabyte stats.clones list nor pay for the rest of the
    quadratic pairing pass behind it. Previously the per-file pair count
    was unbounded (up to 32,640 with a full block grid).
  • A clone-detector failure on a file is now recorded in the inspection
    ledger (<path>#clones, status Failed) instead of only being
    logged, so a failed pass cannot masquerade as "no clones found" in
    SARIF run properties and receipts.
  • Anchored ignore rules (.aegisignore / .gitignore) now match when
    the scan root is absolute: rules are evaluated against the path
    relative to the configured root, so "docs/files/js/" fires on
    walker paths like /workspace/docs/files/js/mock.js where it
    previously silently never matched. Path separators are normalized to
    / before matching, which also makes these rules work on Windows
    (strip_prefix yields backslash relatives there).
  • The CLI now honors RUST_LOG. The container images set
    RUST_LOG=info and documented it as the log-level control, but the
    binary never read it — it hard-coded aegis=info (aegis=debug
    under -v). RUST_LOG now wins when set; -v remains the
    verbosity switch otherwise.

Binary provenance notes

  • checksums.txt lets you verify the assets against each other, but it
    is itself unsigned on this mirror. The signed provenance anchor for
    every release is the GitForge release (minisign-signed
    attestation.json); the mirror is a distribution copy.
  • macOS binaries are built on Linux with a zig cross-linker (recorded in
    attestation.json) and are not Apple-signed or notarized.
    Gatekeeper will quarantine them on first launch: run
    xattr -d com.apple.quarantine aegis (or right-click → Open) after
    extracting.