# Security brainclaw is designed to be safe by default. ## Security model ### No network access by default The CLI does not need to call external services to function. The optional supply-chain gate (see below) is the one feature that talks to a remote service, and it is off until explicitly enabled. ### No telemetry brainclaw does not collect or send usage data. ### No secret management brainclaw is not a vault and should not be treated like one. ### Plain-text visibility The storage model is intentionally inspectable. That makes review easier, but it also means users must be careful about what they write and commit. ## Built-in safety behaviors ### Content scanning (scanText) Whenever content is added to `.brainclaw/` brainclaw scans the text in three layers: 1. **User-configured regex patterns** from `redaction.patterns`. The defaults catch words like `api_key`, `secret`, `token`, `password`. 2. **Structural detectors** for well-known token shapes — GitHub PATs (`ghp_`, `gho_`, `github_pat_…`), AWS access keys (`AKIA…`/`ASIA…`), Google API keys (`AIza…`), Slack tokens and webhooks, Stripe `sk_live_/sk_test_` keys, JWTs, PEM-encoded private keys, and URLs with embedded `user:password@host` credentials. 3. **Entropy detector** that flags high-Shannon-entropy substrings near a secret keyword (api_key/token/secret/password/auth/...). This catches custom-shaped keys the structural list does not enumerate. In `security.mode: warn` matches surface as warnings; in `security.strict_redaction: true` (or `security.mode: strict`) the same matches block the write. ### Sensitive paths When `security.block_sensitive_paths: true` (default), content that references paths like `.env`, `secrets/`, `.git/`, or `node_modules/` emits a warning. ### Configuration ```yaml security: mode: warn # 'warn' or 'strict' — affects redaction scanning strict_redaction: false # if true, blocks entries with sensitive content block_sensitive_paths: true token_detection: enabled: true # turn off all structural + entropy detection entropy: enabled: true min_length: 32 # minimum substring length to consider min_entropy: 4.0 # minimum Shannon entropy (bits/char) detectors: # per-detector override map; explicit false disables a detector aws_access_key: true jwt: true redaction: enabled: true patterns: - '(?i)api[_-]?key' - '(?i)secret' - '(?i)token' - '(?i)password' ``` ## Supply-chain pre-install gate `brainclaw setup-security` enables a preinstall gate that intercepts `npm`/`pnpm`/`yarn`/`pip`/`pip3` install commands and asks Socket.dev's scoring service whether the requested packages are risky. ``` brainclaw setup-security --mode advisory # default — warn-only brainclaw setup-security --mode enforced # block risky installs ``` ### Advisory vs enforced The mode determines what happens when the gate decides a package is risky: - **advisory** — verdicts are surfaced (printed warning, security trap created) but the install is *not* aborted. - **enforced** — a `block` verdict aborts the install with a non-zero exit code. The mode is read from `security.preinstall.mode` and can be overridden per call with `brainclaw check-security --mode `. ### Exit codes `brainclaw check-security` emits exit codes that already encode the mode decision, so wrapper scripts are mode-agnostic: | Exit | Meaning | |------|------------------------------------------------------------| | 0 | pass — no risky packages | | 1 | warn — advisory-mode block, or warn-threshold verdict | | 2 | block — enforced-mode block; the wrapper aborts the install| The wrapper scripts (`<.brainclaw/security/bin>/npm`, `pnpm`, `yarn`, `pip`, `pip3` and their `.ps1` counterparts) call the CLI and react purely to the exit code, so flipping advisory↔enforced is a config change — no regeneration required. ### Composite scoring Each package gets five sub-scores from Socket (supply-chain, vulnerability, quality, maintenance, license) on a 0–100 scale. The gate combines them into a single composite using configurable weights: ```yaml security: preinstall: weights: supply_chain: 0.35 vulnerability: 0.30 quality: 0.15 maintenance: 0.15 license: 0.05 thresholds: composite_pass: 70 # composite >= 70 → pass composite_warn: 50 # composite >= 50 but < 70 → warn; below → block supply_chain_block: 30 # hard block when supply_chain < 30 vulnerability_block: 20 # hard block when vulnerability < 20 ``` Weights are normalized to sum to 1.0 automatically — a config like `{ supply_chain: 1, vulnerability: 1 }` is rescaled rather than producing composites above 100. Thresholds are clamped to `[0,100]` and `composite_warn` is capped at `composite_pass` so the verdict function stays monotonic. ### Allowlist / denylist ```yaml security: preinstall: allowlist: - npm:internal-pkg # ecosystem-scoped - npm:axios@1.14.0 # exact version pin - lodash # bare name — any ecosystem, any version denylist: - npm:axios@1.14.1 # known-compromised version - pypi:bad-pkg ``` Matching is exact on each component. Bare names match by package name only; an `ecosystem:` prefix scopes the match to that ecosystem; appending `@version` (or `==version` for pip-style) requires an exact version. The wildcard `@*` matches any version (useful for explicit "any version" entries). This is a hard tightening from the MVP: previously `denylist: ['lodash']` matched any package whose purl contained the substring `lodash`, so `react-lodash` was silently blocked. The new matcher requires exact package-name equality. ### Package extraction sources `check-security` can pull the list of packages from three places, which may be combined: - `--packages axios,express@1.2.3` — comma-separated specs - `--requirements requirements.txt` — pip-style requirements file; recursive `-r` includes, env markers, and extras are handled - `--lockfile package-lock.json` — npm package-lock (v1, v2, v3); scans top-level direct deps and devDeps The wrappers auto-translate `npm install -r foo.txt` → `--requirements`, skip filesystem paths and URL specs that aren't registry packages (`./local-pkg`, `git+https://…`, `*.tgz`, `*.whl`), and handle the common npm/pip flag conventions. ### Offline / fetch-error fallback When the Socket call fails (network down, service unreachable), the gate uses `security.preinstall.fallback_on_error`: - `block` — fail closed regardless of mode - `warn` — surface a warning, continue with whatever cached scores exist; if no cache exists the gate exits with code 1 - `pass` — silent fall-through (the install proceeds) Cached scores have a TTL of `cache_ttl_hours` (default 24) and live in `.brainclaw/security/cache.json`. ## Recommended stance - do not store secrets — even with detection, the safer move is to never write them - review what gets committed - keep machine-local observations machine-local when appropriate - use stricter redaction settings in sensitive environments - run `setup-security --mode advisory` first; flip to `enforced` once the team has gotten used to the verdicts ## Important nuance brainclaw reduces hidden behavior, but it does not remove the need for operational discipline. It warns; the team still decides what belongs in shared memory.