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,
|
extensions. TrustLab and Velvet Hammer are the proven reference implementation,
|
||||||
not the final delivery environment.
|
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:
|
The next milestone should establish the smallest real Firefox integration:
|
||||||
|
|
||||||
1. Map NSS/PSM validation and certificate data into immutable `TlsFacts`.
|
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.
|
4. Define the browser-produced manual security-concern capture record.
|
||||||
5. Connect Velvet Hammer and demonstrate failed navigation, informed decision,
|
5. Connect Velvet Hammer and demonstrate failed navigation, informed decision,
|
||||||
durable scoped policy, and a fresh enforced retry.
|
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