Skip to content

fix(launcher): trust Moshpit certificates in the store Chromium reads - #90

Merged
ralyodio merged 1 commit into
mainfrom
worktree-moshpit-nss-trust
Sep 1, 2026
Merged

fix(launcher): trust Moshpit certificates in the store Chromium reads#90
ralyodio merged 1 commit into
mainfrom
worktree-moshpit-nss-trust

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

The bug

curl https://chovy.hacker returns the page. The same URL in TronBrowser fails with ERR_CERT_AUTHORITY_INVALID.

Not a Moshpit regression and not a resolver problem — the two read different trust stores:

store who writes it
curl / OpenSSL /etc/ssl/certs Moshpit (/usr/local/share/ca-certificates/moshpit-*.crt + update-ca-certificates)
Chromium on Linux ~/.pki/nssdb nobody

That NSS path is keyed off $HOME, not off --user-data-dir, so no TronBrowser profile could ever have carried it and reinstalling never helped. From inside the browser there is nothing the user can do about it, which is why it reads as a TronBrowser bug.

The fix

Launcher mirrors whatever Moshpit installed into ~/.pki/nssdb. On every start, not once at install: names get pointed and trusted long after the browser is installed. An entry already present is skipped by nickname, so the steady state costs one certutil -L. TRONBROWSER_NO_MOSHPIT_TRUST=1 opts out.

The trust flag is the part that is easy to get wrong:

  • Moshpit Local CACA:TRUE, an anchor, C,,. What a machine behind the pinned-TLS proxy is served.
  • Per-name origin leafCA:FALSE, self-signed, SAN = the name, P,, (trusted peer).

Importing a leaf as C,, fails: Chromium will not anchor a CA:FALSE certificate — the same wall the old CA:TRUE origin certificates hit from the other side. basicConstraints is read rather than guessed from the filename, because getting it backwards fails exactly like doing nothing. The Local CA keeps the nickname moshcode dns enable uses, so a database it already wrote is recognised as done rather than written twice.

install.sh installs certutil (libnss3-tools / nss-tools / nss / mozilla-nss-tools) on install and upgrade — that is the one part of the job needing root, and without it the launcher can only print a command for the user to run. Gated on the machine actually having Moshpit certificates: a package nobody needs is not ours to install. macOS returns early; Chromium there reads the system keychain, which moshcode dns enable already writes.

Drive-by, and worth a look

Both ensure_ calls are now || true. They return 1 when they could not install their tool, and the script runs under set -eu — so on a machine with no supported package manager, ensure_tor was aborting an install that had already put the browser on disk. Pre-existing; it sits on the lines this PR touches.

Verified

On Linux, against the real Moshpit certificates on the dev box:

  • a Chromium whose NSS database was built only by this code loads https://chovy.hacker and https://seo.rank at 200
  • the same Chromium with an empty database fails ERR_CERT_AUTHORITY_INVALID (the control)
  • flags land correctly: three leaves P,,, the Local CA C,,
  • second run imports nothing (idempotent)
  • certutil-missing path prints the hint, writes no database, returns 0
  • TRONBROWSER_NO_MOSHPIT_TRUST=1 returns early
  • full launcher still reaches the browser, rc=0
  • sh -n clean on both files, plus the extracted tron CLI heredoc; shellcheck reports nothing new

🤖 Generated with Claude Code

https://claude.ai/code/session_01P2zwGvXnFseRtrDWH9khxn

`curl https://chovy.hacker` returns the page while the same URL in
TronBrowser fails with ERR_CERT_AUTHORITY_INVALID. Not a Moshpit
regression and not a resolver problem: the two read different trust
stores. curl/OpenSSL reads /etc/ssl/certs, which is the only store
Moshpit writes. Chromium on Linux takes user-added trust from the
per-user NSS database at ~/.pki/nssdb and nowhere else — a path keyed
off $HOME, not off --user-data-dir, so no TronBrowser profile could
ever have carried it and reinstalling never helped.

The launcher now mirrors whatever Moshpit installed into ~/.pki/nssdb
on every start. Every start rather than once at install, because names
get pointed and trusted long after the browser is installed; an entry
already present is skipped by nickname, so the steady state costs one
`certutil -L`.

The trust flag is the part that is easy to get wrong. The Moshpit Local
CA is CA:TRUE and goes in as an anchor ("C,,"); a per-name origin leaf
is CA:FALSE, self-signed, and must go in as a trusted peer ("P,,").
Importing a leaf as "C,," fails, because Chromium will not anchor a
CA:FALSE certificate — the same wall the old CA:TRUE origin certificates
hit from the other side. basicConstraints is read rather than guessed
from the filename, since getting it backwards fails exactly like doing
nothing. The Local CA keeps the nickname `moshcode dns enable` uses, so
a database it already wrote is recognised as done.

install.sh installs certutil (libnss3-tools / nss-tools / nss /
mozilla-nss-tools) on install and upgrade, but only on a machine that
actually has Moshpit certificates — a package nobody needs is not ours
to install. That is the one part of the job needing root, and without
it the launcher can only print a command for the user to run.

Also guards both ensure_ calls with `|| true`. They return 1 when they
could not install their tool and the script runs under `set -eu`, so on
a machine with no supported package manager `ensure_tor` was aborting an
install that had already put the browser on disk.

Verified on Linux against real Moshpit certificates: a Chromium whose
NSS database was built only by this code loads https://chovy.hacker and
https://seo.rank at 200, where the same Chromium with an empty database
fails ERR_CERT_AUTHORITY_INVALID. Import is idempotent, the
certutil-missing path prints the hint and writes no database, the
opt-out (TRONBROWSER_NO_MOSHPIT_TRUST=1) returns early, and the full
launcher still reaches the browser with rc=0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2zwGvXnFseRtrDWH9khxn
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

49 finding(s)

MEDIUM: 31 | LOW: 18

Severity Rule Location
MEDIUM js-open-redirect apps/desktop/extensions/ai-sidebar/install-helper.js:156
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/media.js:34
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/media.js:57
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/newtab.js:237
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/newtab.js:266
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/newtab.js:336
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/options.js:305
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/sidepanel.js:77
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/sidepanel.js:165
MEDIUM sh-remote-script-execution apps/desktop/launcher/tronbrowser:84
MEDIUM sh-remote-script-execution apps/desktop/launcher/tronbrowser:394
MEDIUM js-unescaped-html-sink apps/extensions/public/store.js:77
MEDIUM js-unescaped-html-sink apps/extensions/public/store.js:227
MEDIUM js-unescaped-html-sink apps/extensions/public/store.js:569
MEDIUM js-unescaped-html-sink apps/web/public/app.js:29
MEDIUM js-unescaped-html-sink apps/web/public/dns.js:54
MEDIUM sh-remote-script-execution apps/web/public/install.sh:138
MEDIUM sh-remote-script-execution apps/web/public/install.sh:143
MEDIUM sh-remote-script-execution apps/web/public/install.sh:242
MEDIUM sh-remote-script-execution apps/web/public/install.sh:260
MEDIUM sh-remote-script-execution apps/web/public/install.sh:870
MEDIUM js-unescaped-html-sink apps/web/public/settings.js:96
MEDIUM js-unescaped-html-sink apps/web/public/settings.js:168
MEDIUM js-dynamic-code-execution packages/sdk/src/mcp/tools.ts:80
MEDIUM js-dynamic-code-execution packages/sdk/src/mcp/tools.ts:92
MEDIUM js-dynamic-code-execution packages/sdk/src/mcp/tools.ts:103
MEDIUM js-dynamic-code-execution packages/sdk/src/page.ts:65
MEDIUM js-dynamic-code-execution packages/sdk/src/page.ts:70
MEDIUM sql-template-interpolation services/api/src/store/db.ts:116
MEDIUM js-dynamic-code-execution services/api/src/store/scanner.ts:44
MEDIUM sh-predictable-temp-path start.sh:25
LOW js-dynamic-code-execution packages/agent-runtime/src/analyze/form-script.test.ts:6
LOW js-unescaped-html-sink packages/agent-runtime/src/analyze/form-script.test.ts:26
LOW js-unescaped-html-sink packages/agent-runtime/src/analyze/form-script.test.ts:49
LOW js-dynamic-code-execution packages/browser-core/src/automation/extract-script.test.ts:6
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:35
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:47
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:70
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:89
LOW js-dynamic-code-execution packages/browser-core/src/automation/snapshot-script.test.ts:11
LOW js-unescaped-html-sink packages/browser-core/src/automation/snapshot-script.test.ts:24
LOW js-unescaped-html-sink packages/browser-core/src/automation/snapshot-script.test.ts:63
LOW js-unescaped-html-sink packages/provenance/src/scan.test.ts:47
LOW js-unescaped-html-sink packages/provenance/src/scan.test.ts:170
LOW js-unescaped-html-sink packages/provenance/src/scan.test.ts:216
LOW secret-generic-api-key packages/storage/src/config.ts:51
LOW secret-generic-credential packages/storage/src/config.ts:51
LOW js-dynamic-code-execution services/api/src/store/scanner.test.ts:32
LOW js-hardcoded-crypto-key services/api/src/store/signing.test.ts:24

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 9a745eb into main Sep 1, 2026
8 checks passed
@ralyodio
ralyodio deleted the worktree-moshpit-nss-trust branch September 1, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant