
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
uname -sanduname -m. Supported: Linuxx86_64/aarch64, macOSarm64(Apple silicon). If it's Windows, or Intel macOS, stop: there is no published build — tell the user plainly.command -v curl(orwget). If neither, stop and say so.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(anyclass:nix,chezmoi,git-dotfiles,unknown) orclass=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 fixupwrites its one-line hook with an atomicrename()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=nixornix-generated): add the hook to the shell-init attribute, e.g.programs.zsh.initContent(newer) orinitExtra/programs.bash.initExtra/bashrcExtra; on NixOSprograms.zsh.interactiveShellInit. If they instead manage the file wholesale (home.file.".zshrc".text = ''…''), append the hook inside that string. A bare$HOMEis 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 ~/.zshrcopens it, or edit the matchingdot_zshrcin~/.local/share/chezmoi; append the hook, thenchezmoi apply. - GNU stow / dotfiles git repo (
class=git-dotfiles): theresolved=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/resolvedpath, 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
AskUserQuestionwhether 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-certsthemselves 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
- Run
mfw doctor(use~/.local/bin/mfw doctorif 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 ✗. - Tell them to restart their shell —
exec "$SHELL" -lor a new terminal — then re-runmfw 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 — runmfw 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-runningmfw fixupwon't fix it. - Summarize what you did, where things live (
~/.semgrep/mfw/, shims on PATH, the managed daemon), and thatmfw uninstallreverses everything (and deletes the mfw binary). mfw keeps itself updated via the supervisor (auto-update on by default);mfw upgradeforces it now andmfw restartbounces the daemon.
Notes
- Prefer the pre-approved commands above so the user isn't re-prompted. The
curl … | install.sh | shline 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'sApply to <path>? [Y]es / [n]o, I'll do it myself / [s]kip. The default is yes (Enter applies the edit);nimmediately prints the source line to paste yourself,sskips. Because your Bash-tool calls have no TTY,mfw fixupruns non-interactively: it still writesrc-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.
More from Semgrep
View publishercode-security
write and review secure code
skills
Apr 6Code ReviewComplianceSecurityllm-security
apply LLM security guidelines
skills
Apr 6ComplianceLLMRisk AssessmentSecuritysemgrep
run Semgrep scans and create rules
skills
Apr 6Code AnalysisSecuritySemgrepStatic Analysissetup-semgrep-plugin
set up the Semgrep plugin
cursor-plugin
Apr 6Local DevelopmentSecuritySemgrep