Semgrep logo

Skill

install-mfw

install and configure Semgrep Malware Firewall

Published by Semgrep Updated Jul 21
Covers Security Onboarding CLI Deployment

Description

Install the mfw (Semgrep Malware Firewall) CLI via the curl|sh installer and walk the user through setup — trust store, PATH shims, shell integration, and the background daemon — asking questions at real decision points. Detects when the user's shell rc files are *managed* (symlinked into nix / home-manager / chezmoi / GNU stow / a dotfiles git repo) and, instead of clobbering them, adds mfw's source line to the right source-of-truth. Use when the user wants to install, set up, or onboard mfw on this machine.

SKILL.md

Install mfw

Install and set up the mfw client — Semgrep's Malware Firewall, a local MITM proxy that gates package-manager downloads (pip / npm / pnpm / uv / cargo / maven / gem / nuget / composer / go) on Semgrep's backend before bytes hit disk.

$ARGUMENTS may carry a backend/dist URL override (e.g. https://example.semgrep.dev) or --store system. Default backend is https://semgrep.dev (mfw is distributed off prod). If empty, use the defaults.

Your job: drive the install end to end, ask the user only at genuine decision points (via AskUserQuestion), and — the part that needs care — never edit a managed (symlinked) shell rc file in place. Work through the steps in order.

What fixup will do (state this to the user up front)

The installer downloads + checksum-verifies the binary to ~/.local/bin/mfw, then mfw fixup: ① generates a local CA and adds it to the OS trust store (plus, on macOS with Homebrew present, into Homebrew's openssl trust dir), ② drops package-manager shim symlinks in ~/.semgrep/mfw/shims, ③ writes ~/.semgrep/mfw/rc-setup.sh (PATH + proxy/CA env) and makes your shell source it, ④ installs a background daemon (launchd/systemd/openrc — skipped inside containers with no service manager) that runs the proxy and, via mfw supervise, auto-updates mfw on a schedule (on by default). Undo anytime with mfw uninstall (which also deletes the mfw binary). It is a real, system-modifying install — make sure the user is on board before you run it (they invoked this skill, so a brief summary + proceeding is fine; only stop for the choices below).

Step 1 — Preconditions

  1. uname -s and uname -m. Supported: Linux x86_64/aarch64, macOSarm64 (Apple silicon). If it's Windows, or Intel macOS, stop: there is no published build — tell the user plainly.
  2. command -v curl (or wget). If neither, stop and say so.
  3. command -v mfw — if already installed, this is an idempotent re-run / upgrade; carry on (fixup self-heals).

Step 2 — Inspect the shell config BEFORE installing

Run the inspector and parse it (do not dump it on the user):

sh ${CLAUDE_SKILL_DIR}/scripts/inspect-shell-config.sh

For the user's login_shell, look at its rc file(s): zsh → ~/.zshrc; bash → ~/.bashrc plus ~/.bash_profile or ~/.profile. Decide managed vs plain:

  • Managed = symlink=yes (any class: nix, chezmoi, git-dotfiles, unknown) or class=nix-generated (a real file with a "Generated by Home Manager/Nix" header). These are owned by a tool; editing the deployed file is wrong — it's read-only (nix) or gets reverted on the next rebuild/apply.
  • Plain = a regular, writable file (or absent). Safe to edit directly.

Why this gate exists: mfw fixup writes its one-line hook with an atomic rename() over the rc file, which replaces a symlink with a regular file — clobbering a nix/home-manager-managed rc. So managed rc files must never be edited in place; mfw's one-line hook goes into the user's source-of-truth instead.

Remember the result; it decides Step 3 and Step 4.

Step 3 — Install

If the shell config is PLAIN (and you don't otherwise need the managed path), use the standard installer — it downloads, verifies, and runs mfw fixup, correctly editing the real rc file:

curl -fsSL https://semgrep.dev/dist/mfw/install.sh | sh

(Swap the host if $ARGUMENTS/env gave an override: …/<host>/dist/mfw/install.sh.)

install.sh reconnects a TTY (mfw fixup </dev/tty) so a human running it in a terminal gets the interactive prompts, and falls back to mfw fixup --yes when there's no controlling terminal. When you run this line through the Bash tool there's no TTY, so it takes the --yes path: fixup auto-accepts and edits the real rc file at mfw's best-guess paths — exactly what we want for a plain config.

If the shell config is MANAGED, do not run the bundled fixup (it would clobber the symlink). Install the binary only, then drive fixup with shell edits turned off:

sh ${CLAUDE_SKILL_DIR}/scripts/install-binary.sh
~/.local/bin/mfw fixup --skip-login

mfw fixup here runs non-interactively (no TTY), so it writes ~/.semgrep/mfw/rc-setup.sh, installs certs/shims/daemon, and prints the shell lines instead of editing your rc — exactly what we want. We handle login in Step 6 and the shell hook in Step 4. (Pass an override URL with --backend-url <url> if one was given.)

Use ~/.local/bin/mfw for subsequent calls in case ~/.local/bin isn't on PATH in this session yet.

Step 4 — Shell integration for managed configs

Skip this whole step for plain configs (the installer already did it). For managed configs, the only thing the user's shell needs is this exact hook, which sources mfw's self-managed env file:

[ -r "$HOME/.semgrep/mfw/rc-setup.sh" ] && . "$HOME/.semgrep/mfw/rc-setup.sh"

This single line is exactly what mfw doctor looks for (a trimmed line match — no comment markers). Now find where it belongs and ask the user with AskUserQuestion, offering the candidate= paths the inspector found plus an "Other" path:

  • nix / home-manager (class=nix or nix-generated): add the hook to the shell-init attribute, e.g. programs.zsh.initContent (newer) or initExtra/programs.bash.initExtra/bashrcExtra; on NixOS programs.zsh.interactiveShellInit. If they instead manage the file wholesale (home.file.".zshrc".text = ''…''), append the hook inside that string. A bare $HOME is literal in nix strings (interpolation is ${…}), so the line is safe verbatim; if you're inside a ''…'' block and the linter complains about ${, there is none here, so no escaping is needed. After editing, tell the user to rebuild: home-manager switch / darwin-rebuild switch / sudo nixos-rebuild switch.
  • chezmoi (class=chezmoi): edit the source, not the deployed file — chezmoi edit ~/.zshrc opens it, or edit the matching dot_zshrc in ~/.local/share/chezmoi; append the hook, then chezmoi apply.
  • GNU stow / dotfiles git repo (class=git-dotfiles): the resolved= path IS the real file in the repo (the symlink points straight at it). Append the hook there with Edit, then remind the user to commit. No relink needed.
  • unknown: show the link_target/resolved path, ask the user where the source-of-truth is, and edit that.

Read the target file first, then make a minimal Edit that inserts the hook in the right place (inside the right nix attribute / at the end of the rc source). Confirm the destination with the user before writing if there's any ambiguity.

Step 5 — Finish the trust store

  • Linux: the per-user pass usually can't establish trust (no per-user store). Ask with AskUserQuestion whether to install the CA into the system store (needs sudo): sudo mfw install-certs --store system. If they decline, note that gated downloads will fail TLS verification until it's trusted.
  • macOS: adding the CA to the login keychain can pop a GUI "allow" / password prompt that you can't click. If the certs step didn't complete, tell the user to run mfw install-certs themselves and approve the prompt. (Homebrew's openssl trust is handled by fixup automatically when brew is present.)

Step 6 — Login (optional, browser)

mfw runs without login — unauthenticated requests just follow the configured fail-open/closed policy — but it can't actually scan until authenticated. Check the login line in mfw doctor (Step 7). If not authenticated, ask whether to log in now. mfw login opens a browser OAuth flow you can't complete for them, so have them run it in this session: suggest they type ! mfw login (or set SEMGREP_APP_TOKEN). Don't block the rest of fixup on it.

Step 7 — Verify and hand off

  1. Run mfw doctor (use ~/.local/bin/mfw doctor if needed). Walk the user through anything still ✗. For managed configs the shell line stays ✗ until they restart their shell (and, for nix, rebuild first). Doctor also prints a few informational lines that aren't failures: a version/update check (up to date / update available … run mfw upgrade), whether auto-update is disabled, and whether the supervisor is running — don't treat those as ✗.
  2. Tell them to restart their shellexec "$SHELL" -l or a new terminal — then re-run mfw doctor; it should print the "Semgrep mfw is protecting this machine." banner (a green ○○○ logo + a little watching face whose eyes are randomized each run). If they're not logged in the headline is instead "mfw can't scan anything until you authenticate — run mfw login" (see Step 6); other steps can still be ✓. If they're signed in but their Semgrep deployment doesn't have the Semgrep malware firewall enabled, the login line is ✗ and the headline is "the Semgrep malware firewall isn't enabled for your Semgrep deployment. Enable it in Semgrep or ask your org admin." — that's a backend entitlement issue, not a fixup step, so re-running mfw fixup won't fix it.
  3. Summarize what you did, where things live (~/.semgrep/mfw/, shims on PATH, the managed daemon), and that mfw uninstall reverses everything (and deletes the mfw binary). mfw keeps itself updated via the supervisor (auto-update on by default); mfw upgrade forces it now and mfw restart bounces the daemon.

Notes

  • Prefer the pre-approved commands above so the user isn't re-prompted. The curl … | install.sh | sh line modifies the system — it's expected to need one confirmation; let it.
  • mfw's own rc-edit prompt (only shown with a TTY — not when you drive it via the Bash tool): for the shell step it batches into one question, Edit them all? [Y]es / [n]o, show me the line to add myself / [s]kip, and per-file it's Apply to <path>? [Y]es / [n]o, I'll do it myself / [s]kip. The default is yes (Enter applies the edit); n immediately prints the source line to paste yourself, s skips. Because your Bash-tool calls have no TTY, mfw fixup runs non-interactively: it still writes rc-setup.sh, installs certs/shims/daemon, and prints (never edits) the rc lines — which is exactly why the managed-config path in Step 3 works.
  • Never put secrets/tokens in anything you write. The hook only sources a file.
  • If a step fails, report the actual error and what it means for protection (e.g. "certs not trusted → gated downloads fail TLS"); don't paper over it.

© 2026 YourAI.tools. Every skill from an identity-verified publisher.

Independent catalog. Not affiliated with, endorsed by, or sponsored by Anthropic or any listed publisher. All trademarks belong to their respective owners.