Document Firefox integration seam and first prototype
This commit is contained in:
parent
dedd80d79d
commit
c35e405248
268
FIREFOX_INTEGRATION.md
Normal file
268
FIREFOX_INTEGRATION.md
Normal file
@ -0,0 +1,268 @@
|
||||
# Firefox integration spike
|
||||
|
||||
Status: active design and source reconnaissance
|
||||
Date: 2026-08-17
|
||||
|
||||
## First running prototype
|
||||
|
||||
The adjacent Firefox checkout now has branch `codex/browsec-spike` with commit
|
||||
`5ec348b0f1` (*Add Velvet Hammer certificate investigation prototype*).
|
||||
|
||||
The prototype adds a visibly distinct, browser-owned Velvet Hammer frame to
|
||||
`about:certerror`. Its investigation action reads Firefox's protected
|
||||
`FailedCertSecurityInfo`, displays the requested host, original NSS error,
|
||||
failure category, conventional override eligibility, HSTS state, and the number
|
||||
of handshake certificates, then computes and displays SHA-256 fingerprints from
|
||||
the original DER certificate strings.
|
||||
|
||||
It deliberately remains read-only and displays `NOT TRUSTED`; no prototype UI
|
||||
action silently changes Firefox trust. The dedicated browser test
|
||||
`browser_aboutCertError_velvetHammer.js` passes all seven assertions, including
|
||||
the complete DER-derived fingerprint and unchanged terminal trust state.
|
||||
|
||||
This first visible surface uses data already exposed to Firefox's trusted
|
||||
certificate-error document. It does not yet provide the richer immutable
|
||||
`TlsFacts`, append-only journal, plugin chain, policy broker, or fresh retry
|
||||
required by the architecture below.
|
||||
|
||||
## Pinned reconnaissance baseline
|
||||
|
||||
The first source inspection uses an adjacent, shallow Firefox checkout:
|
||||
|
||||
```text
|
||||
Checkout: /home/sergeych/dev/browsec-firefox
|
||||
Upstream: https://github.com/mozilla-firefox/firefox.git
|
||||
Branch: main
|
||||
Depth: 1
|
||||
Commit: b462c13f11417e13461f1202d71b14e2784f5db0
|
||||
Commit date: 2026-08-17T09:27:35Z
|
||||
Version: 156.0a1
|
||||
```
|
||||
|
||||
This is a reconnaissance baseline, not the release baseline. Browsec will
|
||||
select an ESR only after the integration seam has been proven. The exact
|
||||
commit, rather than the moving branch name, defines all source references in
|
||||
this document.
|
||||
|
||||
The Firefox checkout is deliberately not nested in this repository. Git
|
||||
history can be added only when needed:
|
||||
|
||||
```sh
|
||||
git fetch --deepen=50 origin main
|
||||
# Or, only if full history becomes necessary:
|
||||
git fetch --unshallow origin
|
||||
```
|
||||
|
||||
## Architectural boundary
|
||||
|
||||
An ordinary WebExtension cannot enforce Browsec trust semantics. Firefox core
|
||||
must remain the reference monitor. It alone may:
|
||||
|
||||
- receive authoritative results and certificate bytes from PSM/NSS;
|
||||
- pause or terminate a TLS connection;
|
||||
- validate a plugin verdict against identity, capabilities, scope, lifetime,
|
||||
policy version, connection identity, and overridable error classes;
|
||||
- make a stored policy visible to later certificate verification;
|
||||
- require a genuinely fresh connection after a new decision; and
|
||||
- host browser chrome that content and ordinary extensions cannot imitate.
|
||||
|
||||
Velvet Hammer and other security extensions remain JavaScript/TypeScript. They
|
||||
receive immutable facts, append attributed journal entries, and return the
|
||||
strict API's Boolean verdicts. They never receive an NSS handle, arbitrary
|
||||
XPCOM access, or direct access to another extension's data.
|
||||
|
||||
## Confirmed certificate-verification path
|
||||
|
||||
At the pinned Firefox revision, the ordinary TLS path is:
|
||||
|
||||
1. `AuthCertificateHookInternal` in
|
||||
`security/manager/ssl/SSLServerCertVerification.cpp` captures the peer DER
|
||||
chain and dispatches an `SSLServerCertVerificationJob`.
|
||||
2. `SSLServerCertVerificationJob::Run()` executes on a certificate-verification
|
||||
thread in the parent process.
|
||||
3. `AuthCertificate()` calls `CertVerifier::VerifySSLServerCert()` and obtains
|
||||
the constructed DER chain, validation result, EV state, CT information,
|
||||
issuer-source information, and related facts.
|
||||
4. `AuthCertificateParseResults()` categorizes a failed result and consults
|
||||
Firefox's existing `nsICertOverrideService` for eligible exact-certificate
|
||||
overrides.
|
||||
5. Both successful and failed results are moved through
|
||||
`BaseSSLServerCertVerificationResult::Dispatch()`.
|
||||
6. `SSLServerCertVerificationResult::Run()` executes on the socket transport
|
||||
thread, populates `CommonSocketControl`, and finally calls
|
||||
`SetCertVerificationResult(mFinalError)`.
|
||||
|
||||
The result object already carries both the constructed chain and peer-presented
|
||||
chain as original DER byte arrays. This makes it the narrowest promising seam
|
||||
for producing `TlsFacts` without reparsing lossy display objects.
|
||||
|
||||
HTTP/3 has an additional path in `netwerk/protocol/http/Http3Session.cpp`, which
|
||||
uses `AuthCertificateHookWithInfo()`. It reaches the same verification
|
||||
machinery, but must receive explicit integration tests; shared implementation
|
||||
does not by itself prove equivalent pause, retry, and connection-reuse
|
||||
semantics.
|
||||
|
||||
## Proposed native seam
|
||||
|
||||
Add a browser-internal `BrowsecTrustHost` continuation between receipt of the
|
||||
PSM result and the final call to `SetCertVerificationResult`:
|
||||
|
||||
```text
|
||||
NSS/PSM verification
|
||||
|
|
||||
v
|
||||
SSLServerCertVerificationResult::Run
|
||||
|
|
||||
v
|
||||
freeze Browsec TLS observation
|
||||
|
|
||||
+---- conventional success ---> bounded pre-accept chain
|
||||
|
|
||||
`---- conventional failure ---> normal failure + investigation record
|
||||
|
|
||||
v
|
||||
protected Velvet Hammer UI
|
||||
```
|
||||
|
||||
This should be an asynchronous native state machine, not a thread-blocking call
|
||||
from C++ into JavaScript. On conventional success, the socket remains in its
|
||||
existing certificate-authentication wait state while the request is dispatched
|
||||
to the privileged extension host. Each JavaScript `onBeforeTlsAccept` callback
|
||||
must return synchronously once invoked, but delivery and continuation across
|
||||
Firefox threads are naturally asynchronous. A short browser-enforced deadline
|
||||
fails closed.
|
||||
|
||||
Heavy work, network access, and interactive UI are forbidden in that hook.
|
||||
Extensions perform such work in the background and consult prepared local state
|
||||
when the hook arrives.
|
||||
|
||||
On conventional failure, the first slice need not retain the failed socket.
|
||||
Core freezes an investigation record, completes the failure normally, and
|
||||
correlates the resulting navigation with a protected investigation page. If the
|
||||
user creates a valid exception, Browsec stores the policy and starts a fresh
|
||||
navigation on a fresh connection.
|
||||
|
||||
## Why the existing override service is only a bootstrap adapter
|
||||
|
||||
`nsICertOverrideService` already stores host, port, origin attributes, exact
|
||||
certificate, and session-or-persistent lifetime. PSM consults it only for
|
||||
errors Firefox categorizes as overridable and rejects overrides where policy
|
||||
such as HSTS forbids them. This is useful for the first exact-leaf prototype.
|
||||
|
||||
It cannot represent Browsec's complete policy model:
|
||||
|
||||
- exact DER authority trust restricted to a namespace;
|
||||
- explicit distrust of otherwise accepted authorities;
|
||||
- arbitrary timed lifetime;
|
||||
- plugin identity and decision provenance;
|
||||
- journal references and the exact set of errors authorized by a verdict;
|
||||
- independent extension evidence storage; or
|
||||
- user-inspectable policy history.
|
||||
|
||||
Therefore the adapter must be named and documented as provisional. Browsec's
|
||||
durable policy overlay remains the source of truth even while an exact-leaf
|
||||
rule is mirrored into the Firefox service for the first demonstration.
|
||||
|
||||
## Privileged extension host
|
||||
|
||||
The initial host should be a built-in Firefox WebExtension API namespace, not a
|
||||
legacy external Experiment loaded from an arbitrary path. Current Firefox
|
||||
implements parent-process extension APIs under
|
||||
`toolkit/components/extensions/parent/`, with schemas and explicit manifest
|
||||
permissions controlling exposure.
|
||||
|
||||
The Browsec API is available only to packages that pass all of these checks:
|
||||
|
||||
1. Browsec security-extension package type;
|
||||
2. verified package identity;
|
||||
3. requested capability in the package manifest;
|
||||
4. local capability grant;
|
||||
5. permitted execution mode for the current profile/context; and
|
||||
6. API-version compatibility.
|
||||
|
||||
The built-in Velvet Hammer package is compiled into the product, always enabled,
|
||||
and always ordered last. Its irremovability is enforced by the host, not by an
|
||||
ordinary extension preference.
|
||||
|
||||
## First vertical slice
|
||||
|
||||
The spike is complete when an automated browser test demonstrates:
|
||||
|
||||
1. Navigate a clean Browsec profile to a local HTTPS server with an unknown
|
||||
self-signed certificate.
|
||||
2. Capture original DER, host, port, origin attributes, Firefox error, and both
|
||||
available chain representations.
|
||||
3. Display them in a browser-owned Velvet Hammer investigation page.
|
||||
4. Obtain an explicit exact-certificate, exact-host, exact-port, session-scoped
|
||||
user decision.
|
||||
5. Validate and append the verdict to the audit journal.
|
||||
6. Persist the Browsec policy rule and provisionally mirror it through
|
||||
`nsICertOverrideService`.
|
||||
7. Abandon the failed connection and start a fresh navigation.
|
||||
8. Load the site and display a persistent Browsec-controlled indication that
|
||||
conventional validation failed and named local policy permitted it.
|
||||
9. Revoke the rule and prove that another fresh navigation fails again.
|
||||
|
||||
No page content, cookie, credential, authorization header, private key, or TLS
|
||||
session secret is included in the investigation record.
|
||||
|
||||
## Patch sequence
|
||||
|
||||
### Patch 1: built-in UI and exact-leaf prototype
|
||||
|
||||
- add the built-in Velvet Hammer package and protected `about:` surface;
|
||||
- expose immutable failed-connection facts through a minimal internal API;
|
||||
- implement the browser-owned **Record security concern** chrome action;
|
||||
- execute the existing TrustLab failure chain;
|
||||
- use the provisional exact-leaf override adapter; and
|
||||
- add browser and xpcshell tests.
|
||||
|
||||
This patch can begin in artifact-build mode if it changes only packaged
|
||||
JavaScript, HTML, CSS, and existing scriptable interfaces.
|
||||
|
||||
### Patch 2: native pre-accept continuation
|
||||
|
||||
- add the `BrowsecTrustHost` continuation before
|
||||
`SetCertVerificationResult(0)`;
|
||||
- bind every observation and verdict to a unique connection attempt;
|
||||
- enforce deadline, cancellation, and fail-closed behavior;
|
||||
- ensure that plugin failure never becomes implicit trust; and
|
||||
- test ordinary TLS and HTTP/3 paths.
|
||||
|
||||
This requires a full compiled Firefox build.
|
||||
|
||||
### Patch 3: native Browsec policy overlay
|
||||
|
||||
- stop depending on Firefox's override store as the source of truth;
|
||||
- enforce certificate and authority rules with precise error substitution;
|
||||
- implement explicit distrust on conventional-success paths;
|
||||
- add timed and persistent policy lifetimes; and
|
||||
- make policy snapshots auditable and atomically replaceable.
|
||||
|
||||
### Patch 4: network-path audit
|
||||
|
||||
Cover top-level navigation, redirects, subresources, WebSocket, workers,
|
||||
service workers, speculative connections, proxy `CONNECT`, TLS resumption,
|
||||
HTTP/2 and HTTP/3 coalescing, private browsing, containers, and client
|
||||
certificate interactions. A path is not considered covered merely because it
|
||||
eventually calls a shared verifier; its connection reuse and enforcement
|
||||
behavior must be tested.
|
||||
|
||||
## Immediate implementation questions
|
||||
|
||||
The spike should resolve these from code and tests rather than policy intuition:
|
||||
|
||||
- which identifier safely binds the UI investigation to one network attempt;
|
||||
- where a fresh-retry flag can prohibit reuse and coalescing;
|
||||
- how much constructed-chain material remains available for every failure;
|
||||
- which process owns the immutable investigation registry;
|
||||
- whether the first built-in API can remain JS-only or requires new WebIDL;
|
||||
- how the protected page proves to the user that it is browser chrome; and
|
||||
- which Firefox error classes can safely use the provisional override adapter.
|
||||
|
||||
## Maintenance rule
|
||||
|
||||
Every Firefox rebase must rerun the integration tests and re-audit the call path
|
||||
from `VerifySSLServerCert` through `SetCertVerificationResult`, including the
|
||||
HTTP/3 entry point. Silent upstream movement of this boundary is a release
|
||||
blocker, not an implementation detail.
|
||||
@ -74,6 +74,10 @@ The primary product is a Firefox downstream that hosts full-fledged security
|
||||
extensions. TrustLab and Velvet Hammer are the proven reference implementation,
|
||||
not the final delivery environment.
|
||||
|
||||
The first reconnaissance baseline and proposed patch sequence are recorded in
|
||||
[`FIREFOX_INTEGRATION.md`](FIREFOX_INTEGRATION.md). It pins the inspected
|
||||
Firefox commit and must be updated whenever that baseline changes.
|
||||
|
||||
The next milestone should establish the smallest real Firefox integration:
|
||||
|
||||
1. Map NSS/PSM validation and certificate data into immutable `TlsFacts`.
|
||||
|
||||
@ -348,3 +348,29 @@ Do not implement distributed trust next. Continue from the Firefox host seam:
|
||||
4. Define the browser-produced manual security-concern capture record.
|
||||
5. Connect Velvet Hammer and demonstrate failed navigation, informed decision,
|
||||
durable scoped policy, and a fresh enforced retry.
|
||||
|
||||
### Firefox integration reconnaissance (2026-08-17)
|
||||
|
||||
- A depth-1 Firefox checkout now exists beside this repository at
|
||||
`/home/sergeych/dev/browsec-firefox`.
|
||||
- It is pinned for reconnaissance to Firefox `156.0a1`, commit
|
||||
`b462c13f11417e13461f1202d71b14e2784f5db0` from
|
||||
`https://github.com/mozilla-firefox/firefox.git`.
|
||||
- [`FIREFOX_INTEGRATION.md`](FIREFOX_INTEGRATION.md) records the confirmed PSM
|
||||
path, proposed native continuation seam, limitations of
|
||||
`nsICertOverrideService`, patch sequence, and first end-to-end acceptance
|
||||
test.
|
||||
- The promising seam is `SSLServerCertVerificationResult::Run()` immediately
|
||||
before `CommonSocketControl::SetCertVerificationResult()`: both constructed
|
||||
and peer-presented DER chains are still present there.
|
||||
- The JavaScript success hook remains synchronously returning, but its native
|
||||
cross-thread bridge must be continuation-based and bounded rather than
|
||||
blocking a verifier or socket thread while calling JavaScript.
|
||||
- Firefox artifact mode was bootstrapped without system changes and the pinned
|
||||
baseline built successfully. The absent optional `watchman` and repository-
|
||||
wide `cargo-audit` setup do not affect the frontend artifact build.
|
||||
- Branch `codex/browsec-spike`, commit `5ec348b0f1`, contains the first running
|
||||
browser-owned Velvet Hammer certificate investigation surface and its browser
|
||||
test. The test passes 7/7 assertions. The surface exposes protected Firefox
|
||||
failure facts and DER-derived SHA-256 fingerprints but intentionally cannot
|
||||
alter trust yet.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user