Skip to content

Commit f2a7f64

Browse files
committed
feat(ai-firewall): wire local AI clients to the gateway and manage its policy
`vulnetix ai-firewall` points the AI clients on a machine at the hosted policy-enforcing gateway, proves they are pointed there, and manages the policy it enforces. Previously only `config set ai-firewall` existed — three RPC wrappers — and everything else was manual clicking in the dashboard. install shell, project env, Claude Code, Codex, Continue, aider status what is wired, and every way it conflicts with org policy policy provider / model / guardrail rules key BYOK provider keys, write-only apply reconcile org policy from .vulnetix/ai-firewall.yaml snippet ready-to-run code for the SDKs env vars cannot reach Three facts drive the design and must not be "tidied up": Only OPENAI_BASE_URL, OPENAI_API_BASE and ANTHROPIC_BASE_URL are read by real SDKs. MISTRAL_BASE_URL and its friends do not exist. Writing a variable that looks like an SDK setting and is read by nothing is worse than writing nothing: the user believes they are protected while their traffic goes straight to the provider. Those providers get a code snippet instead, and install says so. Anthropic needs ANTHROPIC_AUTH_TOKEN, not ANTHROPIC_API_KEY — the latter is sent as x-api-key and the gateway authenticates with Bearer, so the obvious variable produces a 401 that looks exactly like a bad key. And its base URL carries no /v1: the Anthropic SDK appends /v1/messages itself, so a trailing /v1 yields /v1/v1/messages. Codex and Claude Code speak non-chat wires. Both are gated on the gateway's advertised capabilities and skipped with a reason when it cannot serve them, rather than written a config that 404s at request time. status exists for the finding nothing else surfaces: a client whose base URL points somewhere other than the gateway never errors — it simply is not screened. It also compiles every guardrail pattern, because the gateway silently skips one that does not compile, leaving a rule that looks enforced and enforces nothing. Config files are edited surgically and reversibly: managed blocks for env files, non-destructive merges for JSON/TOML/YAML that preserve comments and unrelated keys byte-for-byte, and a backup before any whole-file write. Cursor and Windsurf keep their base URL in application state, so they are detected and explained, never written — a file we invented there would be ignored by the app and would make status lie.
1 parent 0c1fbc6 commit f2a7f64

43 files changed

Lines changed: 8265 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ The `cbom` command discovers cryptographic usage in code and config and emits a
3636

3737
Detection is driven by the catalog in `internal/cbom/catalog/*.json` (`algorithms.json` with aliases/primitive/`pqc_status`/`standards`/`source_patterns`/`config_patterns` + top-level `call_extractors`; `libraries.json`) — the single source of truth, validated (regex + CycloneDX enum ranges) by `Compile()`. After editing, run `just gen-cbom` to regenerate docs under `website/content/docs/cbom/`. The `CryptoDetections` types + `BuildCBOM`/`ParseCBOM` builder live in the shared `vdb-cyclonedx` module (parity with `BuildAIBOM`); the CLI calls `cyclonedx.BuildCBOM`. Authenticated runs upload to `/v2/cli.cbom` (best-effort; backend endpoint is a follow-up).
3838

39+
### AI Firewall Subcommand
40+
41+
The `ai-firewall` command wires local AI clients to the hosted OpenAI-compatible gateway at `https://guardrails.vulnetix.com/{providerSlug}/{orgUuid}/v1` (org is a **URL path component**, unlike the Package Firewall which carries it as the HTTP Basic username), and manages the org policy the gateway enforces. Two keys: the client sends `Authorization: Bearer $VULNETIX_API_KEY`; the org's *provider* key is held server-side (BYOK, KMS-encrypted, write-only) and swapped in by the gateway.
42+
43+
Command tree: `install [client...]` / `uninstall [--all|--except]` / `status [--strict]` / `policy {provider,model,guardrail}` / `key {set,remove}` / `settings` / `get` / `apply` / `export` / `baseline` / `snippet`. `config set|get ai-firewall` still works — the same constructors in `cmd/config_ai_firewall.go` are registered under both parents (delegate, not alias: a `*cobra.Command` cannot live in two trees).
44+
45+
Clients (`pkg/aifirewall/clients.go`): `shell`, `env` (project `.env`/`.envrc`/`Makefile`, existing files only), `claude-code` (JSON merge into `settings.json` `env`), `codex` (textual TOML block — never a `toml.Marshal` round-trip, which drops comments), `continue` (YAML Node API + the unavoidable literal key in `~/.continue/.env`), `aider`, and `cursor`/`windsurf` (**detect-and-instruct only** — their base URL lives in app state, so writing a file would be a lie).
46+
47+
Three facts drive the design and must not be "fixed": (1) only `OPENAI_BASE_URL`, `OPENAI_API_BASE`, `ANTHROPIC_BASE_URL` (and probably `GROQ_BASE_URL`) are read by real SDKs — `MISTRAL_BASE_URL` etc. **do not exist**, so those providers get a snippet, not an invented env var; (2) Anthropic needs `ANTHROPIC_AUTH_TOKEN`, not `ANTHROPIC_API_KEY` (the latter is sent as `x-api-key`); (3) Codex requires `wire_api = "responses"` and Claude Code speaks `/v1/messages`, so both are gated on the server's `gateway.wireApis` advertisement and **skipped with a reason** when it is absent.
48+
49+
File writing goes through `internal/managedfile` (extracted from `cmd/package_firewall.go`, shared by both firewalls): `Markers` are a **parameter**, so `package-firewall uninstall` cannot strip the AI Firewall's block from the same `~/.zshrc`. Whole-file writes (Structured/Merge) always back up to `<path>.vulnetix.bak`; managed blocks are reverted surgically and never restore a stale backup.
50+
51+
Declarative policy lives in `.vulnetix/ai-firewall.yaml` (`apiVersion: vulnetix.com/v1`, `kind: AiFirewallPolicy`); `apply` plans in the order **guardrails → models → providers → keys → settings** so tightening never opens a window, reports unmanaged server objects as drift (deletes only with `--prune`), and composes in the server's recommended guardrail baseline (`cli.ai-firewall-baseline`, soft-fails on 404 unless `--baseline-required`). Guardrails reconcile by **name**, so names must be unique. Patterns are Go RE2 — no lookaround — and an uncompilable pattern is *skipped by the gateway*, so `status` compiles every one and warns.
52+
53+
Backend follow-ups (not yet implemented): `cli.ai-firewall-get` must return `hasKey`/`keyUpdatedAt`/`logsEnabled`/`gateway.wireApis`; new endpoints `cli.ai-firewall-key`, `cli.ai-firewall-settings`, `cli.ai-firewall-baseline`. The CLI ships first and tolerates their absence. Docs: `website/content/docs/ai-firewall/` (hand-written, not generated).
54+
3955
### Malscan Subcommand
4056

4157
The `malscan` command runs the `malscan-engine` (module `github.com/vulnetix/malscan-engine`, consumed like `vdb-cyclonedx`) **in-process** against the project's locally-installed dependencies — unlike `--block-malware` on the `sca` path, which defers to the backend's periodic pipelines. It runs the full engine over each resolved ecosystem target: `iocscan.Scan` (STIX IOC filesystem scan — known-bad domains/IPs/URLs in text + extracted binary strings, with file+line+context), `detect.Detect` (manifest/install-script pattern + shell-obfuscation detectors), `ioc.ExtractIOCs`, and `badhash` (known-bad artifact-hash blocklist over declared/candidate hashes).

0 commit comments

Comments
 (0)