Automate the browser trust step so nobody types certutil - #5
Merged
Conversation
The adoption barrier was never that people distrust a locally-generated root — it is that the README ended with certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "Moshpit Local CA" ... and a hope. Certificates cannot leave the stock-browser path (no extension API can validate a registry pin, which is the whole reason the CA exists), but the ceremony can. Adds `moshpit-trust` and an install.sh. The word "certificate" appears nowhere a user reads. There is still exactly one consent moment, in plain language — installing a root silently is what malware does, and "they didn't have to think about it" is not a reason to skip asking. What doing it by hand tends to get wrong, and this does not: - Firefox keeps its own NSS database per profile on every platform, including macOS, so the keychain command covers nothing for it. - On current Ubuntu the default Firefox is the snap, whose profiles live under ~/snap/... — the documented path silently covers zero browsers. - certutil is not installed by default on Debian or Ubuntu, so the documented command fails on a machine that has the store it points at. Detected up front with the exact per-distro package, before the consent prompt rather than after it. - certutil can exit zero having written where the browser will not read, so every install is read back and verified rather than assumed. Idempotent: status is checked before writing, and running twice is a no-op. 51 tests pass (16 new), tsc --noEmit clean under strict. Every path and command runner is injected, so the suite asserts what certutil would have been asked to do without asking it — a test here must never modify the trust store of whoever runs it. The one real-certutil test skips cleanly when the tool is absent, which is the common case; it did not execute on this machine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Right — and the fix isn't removing certificates (impossible for stock browsers: no extension API can validate a registry pin, which is the entire reason the local CA exists). It's removing the step. The word "certificate" now appears nowhere a user reads.
Before
The README ended with this, and a hope:
After
curl -fsSL https://raw.githubusercontent.com/profullstack/moshpit-proxy/main/install.sh | shThere is still exactly one consent moment. Installing a root into someone's trust store silently is what malware does, and "they didn't have to think about it" is not a reason to skip asking. What's removed is the jargon, not the disclosure.
What doing it by hand gets wrong, and this doesn't
Each of these is a real way the documented command half-works and looks like it succeeded:
security add-trusted-certkeychain command covers precisely nothing for it.~/snap/firefox/common/.mozilla/firefox/. The documented path covers zero browsers there. Flatpak too.certutilisn't installed by default on Debian or Ubuntu. The documented command fails on a machine that does have the store it points at. Now detected up front with the exact per-distro package — and checked before the consent prompt, not after, since asking someone to agree and then failing wastes the one moment of attention this gets.certutilcan exit zero having written somewhere the browser won't read (a profile never launched, a locked db). Every install is read back and verified rather than assumed — reporting success while the browser still shows a warning is worse than a clean failure.cert9.dbare skipped — they've never been launched, so writing there does nothing.C,,— server certificates only. NotCT,c,c, which would also trust the root for mail and code signing.Idempotent: status is checked before writing, so running twice is a no-op rather than a duplicate nickname.
Also added
moshpit-trust --status(reports, changes nothing),--uninstall, andinstall.sh --uninstallwhich undoes the browser setup before removing the code.install.shchecks Node by capability rather than version string, for the same reasonmoshpit-transportdoes.Testing
51 pass (16 new),
tsc --noEmitclean understrict.Every path and command runner is injected, so the suite asserts what
certutilwould have been asked to do without asking it. That matters more than usual here — a test that got this wrong would silently modify the trust store of whoever ran it, and a test suite must never be something you have to undo afterwards.The one real-
certutiltest creates a throwaway NSS database and round-trips add/verify/remove. It skips whencertutilis absent, which is the common case — and it did not execute on my machine. Worth running somewhere withlibnss3-toolsinstalled before this merges.Not fixed by this
Worth stating plainly: this does not fix
https://scrambled.eggs/giving an invalid certificate today. That failure is a name mismatch —pit.moshcode.sh:443answers SNIscrambled.eggswith Railway'sCN=*.up.railway.appcert, because thessl_prereadgateway innginx/moshpit-gateway.confisn't deployed and can't be deployed on Railway. No trust store fixes a name mismatch. Separate problem.🤖 Generated with Claude Code