Skip to content

Releases: hamr0/bareguard

v0.4.7 — harden shared-budget locking

24 May 08:09

Choose a tag to compare

Hardens shared-budget cross-process locking (follow-up to 0.4.6), validated by a /code-review pass that empirically tested crash modes.

Changed

  • budget.record() fails loud on a corrupt/unreadable budget file. It previously fell back to writing stale local state over the committed total (silent spend loss → overspend risk); it now throws BudgetUnavailableError, per PRD §13. Only fires on genuine corruption — a missing file is re-seeded before the lock, and init() keeps its rebuild-from-audit recovery. Migration: a corrupt file now propagates out of gate.record() / gate.run(); wrap them to degrade rather than halt.
  • proper-lockfile stale 10s → 20s for the budget file — shrinks the lock-steal window that could put two writers in the sub-millisecond critical section and silently lose an update. The ~2.25s retry budget stays well under stale, so a waiter on a live holder always errors before it would steal. Fail-safe under a SIGKILLed-mid-lock holder (halts rather than overspends).

Tests

Suite 106 → 107 (corrupt-file fail-loud), green on ubuntu/macos/windows × Node 20/22. Also folds in 0.4.6's flaky concurrent-budget test fix.

Full notes in CHANGELOG.

v0.4.6 — fs trailing-slash fail-open + redaction/net fixes

23 May 19:56

Choose a tag to compare

Fixes from a /code-review pass over the v0.4.4/v0.4.5 security changes — it caught a fail-open in the fs primitive those releases were meant to harden.

Security

  • fs deny/scope entries with a trailing slash no longer mishandle the directory node. deny: ["/etc/secret/"] previously did not deny read /etc/secret (fail-open; children were denied), and writeScope: ["/app/data/"] wrongly denied a write to /app/data (fail-closed). within() now strips a trailing slash before both the exact-match and prefix checks, and special-cases root.
  • secrets redaction masks every occurrence on a line. redact() used String.replace, which only masks the first match for a non-global pattern (/sk-[a-z0-9]+/) — leaking the 2nd+ secret. Since v0.4.5 routes every audit line through redact(), this undercut the audit-safety guarantee. Patterns are now forced global.
  • net.denyPrivateIps defense-in-depth: IPv4-compatible IPv6 (::a.b.c.d) is decoded like the mapped form, and the local range now covers deprecated site-local fec0::/10. Public addresses unaffected.

Tests

Suite 100 → 106 (test/security-review-followup.test.js), green on ubuntu/macos/windows × Node 20/22.

Full notes in CHANGELOG.

v0.4.5 — security hardening (bash.allow, audit redaction, cap validation)

23 May 18:56

Choose a tag to compare

Hardening follow-up to v0.4.4's audit. Three of the four remaining findings fixed; the fourth (allows()/askHuman) is documented rather than changed (it's correct for its pre-filter purpose).

Security

  • bash.allow fails closed on shell metacharacters. When allow is set, commands containing ; | & $ ` ( ) < > newline are denied (bash.allow.shellMeta) — a prefix allowlist can't bound what runs after a chain/pipe/substitution. Denies legit git log | head by design; use content.denyPatterns for chaining-aware screening.
  • Audit auto-redacts when secrets is configured. The gate redacts action/result/reason on every audit line at write time, so raw secrets never hit the JSONL. Eval runs on the unredacted action — matching is never weakened (and this is more correct than the old pre-check() redact pattern). reason is included because diagnostics can echo action data (e.g. net.invalidUrl embeds the URL).
  • raiseCap/topup reject negative & non-finite caps. A negative cap silently wedged the run in permanent halt; lowering a positive cap stays allowed.

Tests

Suite 93 → 100 (test/security-hardening.test.js), green on ubuntu/macos/windows × Node 20/22.

Full notes in CHANGELOG.

v0.4.4 — fs/net containment security fixes

23 May 18:22

Choose a tag to compare

Security patch. Two opt-in containment controls were bypassable by the agent they bound.

Security

  • fs — paths are now lexically normalized (./.. collapsed) and matched with segment boundaries, so traversal can no longer escape readScope/writeScope/deny (e.g. /app/data/../../etc/passwd), and a scope no longer leaks to a prefix sibling (/app/data/app/data-secrets). Symlinks are not resolved (lexical only) — canonicalize upstream if needed.
  • netdenyPrivateIps now actually blocks IPv6 ([::1], ULA, link-local — brackets were dead-coding the whole branch), IPv4-mapped IPv6, IPv4 link-local 169.254.0.0/16 (cloud-metadata IMDS 169.254.169.254), and 0.0.0.0. Hostname-based — no DNS-rebinding defense (resolve-then-check upstream).

Tests

Suite 88 → 93 (test/security-regression.test.js), green on the full ubuntu/macos/windows × Node 20/22 matrix.

Also bundles the previously-unreleased docs restructure + docs/identity-and-the-gate.md. Full notes in CHANGELOG.

v0.1.1 — pre-publish review fixes

30 Apr 14:49

Choose a tag to compare

Patch release addressing the v0.1.0 pre-publish review.

Added

  • gate.allows(string) shorthand — pass a tool-name string instead of { type: name }.
  • _truncated: true boolean at audit line root when truncation happens.
  • One-time stderr WARN when humanChannel is unset and an ask/halt fires.
  • README "Common gotchas" section (allowlist-doesn't-silence-asks, glob * over-matching /, humanChannel required for safe defaults, soft caps, serial gate calls).

Removed

  • Gate.fromConfig(config)new Gate(config) is the only canonical form.

Docs

  • bareguard.context.md version line bumped to 0.1.1.

Tests

  • 30 → 33 across Linux + macOS + Windows × Node 20 + 22 (CI matrix). All green.

See CHANGELOG for full notes.

v0.1.0 — first release

30 Apr 14:41

Choose a tag to compare

First release of bareguard. Action-side runtime policy library for autonomous agents — bounds what the agent does, not what it says.

What's in v0.1.0

  • Single Gate class with three call sites (redact, check, record) plus run / allows / haltContext / terminate.
  • Ten primitives (twelve minus rate limits): bash, budget, fs, net, limits, tools, secrets, audit, content, approval. Each in its own file, src/primitives/*.js.
  • Severity-graded decisions (action vs halt) — halt-severity events (budget exhaustion, maxTurns, terminate) MUST escalate to a human, never bubble to the LLM.
  • One humanChannel callback consolidates ALL ask/halt/topup/terminate. Runner branches on terminal allow/deny only.
  • Single audit file via POSIX O_APPEND atomicity (no lock on Linux/macOS); Windows uses proper-lockfile fallback. Phases: gate, record, approval, halt, topup, terminate.
  • Shared budget across processes via proper-lockfile (the one allowed dep). Versioned format. Rebuilds from audit log on cold start.
  • Six-step eval order (deny > ask > scope > default). Allowlist is scope-only — does NOT silence asks.
  • Safe defaults shipped — DROP TABLE, rm -rf /, destructive verbs.
  • 30/30 tests passing on Linux, macOS, Windows × Node 20, Node 22 (matrix CI).
  • 931 LOC source (under PRD §21's 1000 target).

Install

npm install bareguard

See README for usage, bareguard.context.md for the LLM integration guide, CHANGELOG for full release notes.

Sibling of bareagent in the bare suite.