Skip to content

Fixing what Bulwark finds ​

Bulwark fixes the problems that are safe to fix automatically, and hands you the exact command for the ones that aren't. That split is the whole design: a scanner that edits your sshd_config or sudoers and gets it wrong has locked you out of your own machine — a strictly worse outcome than the finding it was trying to resolve. So fixes divide by blast radius. The reversible, low-risk ones Bulwark automates — always dry-run first, always with a backup. The genuinely dangerous ones it shows you and gets out of the way.

What Bulwark can fix for you ​

The fix command is the front door for the safe autofixes. Everything previews by default and only changes anything with --apply:

bash
bulwarkctl fix list             # preview every available fix, changes nothing
bulwarkctl fix ssh-perms --apply    # tighten ~/.ssh (dir 700, keys/config 600) — no privilege needed
sudo bulwarkctl fix all --apply     # the safe set: ~/.ssh + /etc permissions + non-lockout sshd hardening

Each fixer is reversible and never widens access:

  • fix ssh-perms tightens over-permissive files in ~/.ssh (directory to 700, private keys / config / authorized_keys to 600). Only ever tightens, never follows a symlink, records the prior mode.
  • fix etc-perms pins sensitive /etc files (shadow 640, sudoers 440, sshd_config600) when they're world- or group-writable. Needs root.
  • fix sshd hardens /etc/ssh/sshd_config to clear the SSH findings (X11/TCP forwarding off, MaxAuthTries, …) by inserting one managed block at the top of the file, backing up the original and validating with sshd -t before keeping it. The two directives that can lock you out — PasswordAuthentication no and PermitRootLogin no — are opt-in behind --include-auth and excluded from fix all.
  • ssh protect adds one passphrase to every unencrypted key in ~/.ssh at once, backing each up first — far better than leaving plaintext keys on disk.

In the desktop app these live in Settings → SSH hardening (tighten permissions, protect keys).

Leaked secrets: redaction ​

Secret redaction is fully reversible in the same way: the secret is either there or it isn't, and the original is always backed up.

bash
# Preview — shows exactly which files and how many secrets. Changes nothing.
bulwarkctl ai redact

# Apply. Each file is backed up 0600 first, its permissions preserved, and every high-confidence
# secret replaced with an inert placeholder.
bulwarkctl ai redact --apply

In the desktop app this is the Redact button on the Agent Security tab.

Only the credential's own bytes are replaced. Everything around it — the KEY= name, the quotes around the value, the line ending that ends it — survives byte-for-byte, so a redacted .env still parses and a redacted file has exactly as many lines as it started with. This matters more than it sounds: the detection patterns match a terminator after the secret (usually the newline itself), and a redactor that rewrote the whole matched span would delete it, welding the next line onto the placeholder.

Two things it deliberately will not do:

  • It won't rotate the key for you. Redaction removes the secret from disk; it cannot un-leak it. Anything that reached a transcript should be assumed compromised — go rotate it at the provider.
  • It won't touch a low-confidence (generic-*) match. Those are reported but never rewritten, because blindly editing a value that merely looked like a secret could corrupt a real config.

What you should fix by hand ​

Everything below changes how an agent — or your shell, or SSH — behaves. A wrong edit is expensive, so Bulwark shows you the command rather than running it. Each finding in a scan already carries its own one-line fix; this is the same guidance, organised by category.

Auto-execution on repo open (Critical) ​

These let a repository you merely opened run code before you reviewed it. Fix the repo, and be wary of any repo you didn't author.

FindingFix
BLWK-AI-002 Claude Code hooksRemove the hooks block from the repo's .claude/settings.json. Keep hooks only in your own trusted user-level settings.
BLWK-AI-009 VS Code "YOLO mode"Remove "chat.tools.autoApprove": true from settings.json.
BLWK-AI-011 auto-run taskRemove runOn: "folderOpen", or set "task.allowAutomaticTasks": "off".
BLWK-AI-010 Workspace Trust offSet "security.workspace.trust.enabled": true.
BLWK-AI-008 auto-enable MCPRemove enableAllProjectMcpServers / enabledMcpjsonServers from committed settings.

MCP servers — third-party code with your permissions (High) ​

An MCP server is a program running as you. Treat it like one.

FindingFix
BLWK-AI-003 unpinned packagePin to an exact version (@scope/pkg@1.2.3, not -y latest) and vet the publisher.
BLWK-AI-004 vulnerable mcp-remoteUpgrade mcp-remote to ≥ 0.1.16 and pin it (CVE-2025-6514).
BLWK-AI-005 shell-wrapped serverReplace bash -c/sh -c with a direct executable + args.

Over-broad permissions (High) ​

FindingFix
BLWK-AI-006 wildcard allowlistScope it: replace Bash(*) / Bash(curl:*) / a bare "*" with the specific, read-only commands the agent actually needs.
BLWK-AI-007 bypass modeRemove defaultMode: "bypassPermissions", and don't run with --dangerously-skip-permissions outside a throwaway container.
BLWK-AI-017 Codex danger configIn ~/.codex/config.toml, move approval_policy away from "never" or sandbox_mode away from "danger-full-access".

Exfiltration and injection surface (High → Medium) ​

FindingFix
BLWK-AI-014 base-URL overrideRemove the ANTHROPIC_BASE_URL / OPENAI_BASE_URL override unless it points at a proxy you trust — pointed at an attacker host it ships your API key in the auth header.
BLWK-AI-012 hidden UnicodeStrip the zero-width / bidirectional control characters from the instruction file. They're invisible to you and read by the model (the "Rules File Backdoor").
BLWK-AI-013 injection phrasesRead the flagged line. This is a low-confidence heuristic — confirm before trusting the file.

Secret hygiene (High → Medium) ​

The secrets themselves are redactable (above); these are about their blast radius.

FindingFix
BLWK-AI-016 unignored in gitecho '<file>' >> .gitignore, then git rm --cached <file> if it was already committed, and rotate the credential.
BLWK-AI-015 world-readable credschmod 600 <file>.

The one-line version ​

Redact secrets with Bulwark; rotate them at the provider. Everything else, apply the command the finding gives you — by hand, because the cost of getting it wrong is yours, not the scanner's.

Released under the Apache License 2.0.