Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
67cef8e
Build world-class privacy and governance foundation
mdheller Jun 18, 2026
d6498d4
Add source build packaging and fix macOS build tooling
mdheller Jun 18, 2026
51599f7
Add BearBlocker — native adblock-rust content classifier and cosmetic…
mdheller Jun 18, 2026
af6bd07
Add BearBlocker build wiring to bearbrowser-patches.py
mdheller Jun 18, 2026
2b6a7bb
Add nightly DMG build workflow for developer use
mdheller Jun 18, 2026
ed9b87b
Add hold-queue status bar app and wire agent-runtime profile
mdheller Jun 18, 2026
2ec3ba0
Close governance loop: auto-launch, receipt bridge, LaunchAgent
mdheller Jun 18, 2026
fed531b
Seal extension ecosystem, add registry and unseal mechanism, add Linu…
mdheller Jun 18, 2026
81ffe50
Native SponsorBlock + keyboard nav + Fedora CI; seal extension surface
mdheller Jun 18, 2026
642cbbd
Fix crash on macOS 26 when opening Gmail or any SSO/OAuth popup
mdheller Jun 18, 2026
0129f8e
Remove gesture-timing popup gate — eliminates fingerprinting vector
mdheller Jun 18, 2026
cce1971
Expand fingerprinting shield: 10 new vectors closed
mdheller Jun 18, 2026
d553548
Close final fingerprinting gaps: WebGPU, font enum, rAF timing, devic…
mdheller Jun 18, 2026
558e80b
Engine-level font enumeration and timer precision hardening
mdheller Jun 18, 2026
f8df5df
Add BearCapture, BearClip, BearVault actors; update BearSponsor and r…
mdheller Jun 18, 2026
d28bcf5
Defeat JS shield detection + close locale, resource timing, identity …
mdheller Jun 18, 2026
34e7d3d
Normalize Accept-Language header, complete navigator identity, networ…
mdheller Jun 18, 2026
4de625f
Achieve 36/36 fingerprint regression — fix four shield crashes and si…
mdheller Jun 18, 2026
4f50c3d
Close remaining fingerprinting gaps — 47/47 regression coverage
mdheller Jun 18, 2026
400d846
Extend fingerprint shield to 59/59 — WebGL params, Range BCR, perf en…
mdheller Jun 18, 2026
32239b3
Advance fingerprint shield to 72/72 — AudioBuffer, plugins PDF set, S…
mdheller Jun 18, 2026
0e2b105
Advance fingerprint shield to 86/86 — Math precision JSC→V8 override
mdheller Jun 18, 2026
231c5c3
Advance fingerprint shield to 91/91 — timeOrigin, Intl.supportedValue…
mdheller Jun 18, 2026
3825d16
Advance fingerprint shield to 92/92 — canvas noise, pdfViewerEnabled
mdheller Jun 18, 2026
7068fc9
Advance fingerprint shield to 96/96 — WebKit-only API removal, perfor…
mdheller Jun 18, 2026
4d37ac9
Advance fingerprint shield to 108/108 — Chromium API presence stubs
mdheller Jun 18, 2026
0548c3c
Advance fingerprint shield to 111/111 — window.chrome stub with prope…
mdheller Jun 18, 2026
48a547d
Fix navigator.vendor: 'Apple Computer, Inc.' → 'Google Inc.'
mdheller Jun 18, 2026
ca23712
Restore Safari-consistent identity — revert Chrome impersonation dive…
mdheller Jun 18, 2026
6173aa2
Advance fingerprint shield to 101/101 — Math native concealment, _nat…
mdheller Jun 18, 2026
71b256f
Plug _nat registration gaps — getShaderPrecisionFormat, FontFaceSet.c…
mdheller Jun 18, 2026
9dc6dfe
Plug remaining _nat registration gaps — WebGL2 getShaderPrecisionForm…
mdheller Jun 18, 2026
77167d2
Force WKWebView Aqua appearance — CSS system color consistency
mdheller Jun 18, 2026
f98e47a
RFP audit: remove WebKit-shield contamination and dead/contradictory …
mdheller Jun 18, 2026
1b8160f
Add Gecko RFP regression harness; fix cross-profile drift it caught
mdheller Jun 18, 2026
f3bfad6
Wire Gecko RFP harness into package.json verify targets
mdheller Jun 18, 2026
5cebbdf
Harden QUIC/TLS cross-connection supercookies: disable 0-RTT early data
mdheller Jun 18, 2026
71f03e4
Switch DoH provider Cloudflare -> Quad9; harden DNS/HTTPS-RR/ECH layer
mdheller Jun 18, 2026
6676641
Harness: validate policies.json strips to valid strict JSON
mdheller Jun 18, 2026
5ea0cad
Harmonize network/TLS privacy cohort across both profiles
mdheller Jun 18, 2026
25e7156
Agent policy: block enterprise-root import + EME; harness locks anti-…
mdheller Jun 18, 2026
9731da3
T0: empirical fingerprint measurement harness + first real baseline
mdheller Jun 18, 2026
4d4ea0a
Measurement: add WebRTC IP-leak vector (confirmed clean both profiles)
mdheller Jun 18, 2026
726db8c
T3 spec: font + text-metric uniformity; track text-metric readback leak
mdheller Jun 18, 2026
b8f73d9
T3 implementation plan (no-holes runbook) + track both metric paths
mdheller Jun 18, 2026
d114f63
T3 runbook: add Section 0 — build-environment on-ramp (turnkey)
mdheller Jun 18, 2026
05a23f4
T3 plan: correct architecture to nsRFPService helper + DOM call sites
mdheller Jun 18, 2026
431a7ca
T3 W4 (canvas): real Gecko patch for text-metric quantization — appli…
mdheller Jun 18, 2026
4636ff9
CI: anti-fingerprint workflow — yes, this runs in GitHub Actions
mdheller Jun 18, 2026
e30b75a
Fix CI: timezone cohort assertion + patch-apply path bug
mdheller Jun 18, 2026
cb7f926
T3 W4: close OffscreenCanvas hole in the text-metric patch
mdheller Jun 18, 2026
d24d16d
W1+W2: bundle metric-compatible fonts + font.system.whitelist allowlist
mdheller Jun 18, 2026
e611f5c
W6: audio farble patch — closes the one vector Brave beats us on
mdheller Jun 18, 2026
9d7a000
Tier-3: real-binary measurement via geckodriver + fix timezone false-…
mdheller Jun 18, 2026
b6cdc1b
Wire anti-fp patches into the build (mirror-safe) + verify full sequence
mdheller Jun 18, 2026
60f9f56
Trigger the real compile: build anti-fp branch on nightly-linux + mea…
mdheller Jun 19, 2026
cabd3c8
Fix nightly-linux startup failure (broken block scalar) — unblocks th…
mdheller Jun 19, 2026
cd8f31c
Fix Linux build: convert BSD `sed -i ''` to Python in bearbrowser-pat…
mdheller Jun 19, 2026
3a20107
Build: skip missing pref-pane assets gracefully (unblocks past patch-…
mdheller Jun 19, 2026
feefdc8
Build: strip stale --with-l10n-base from mozconfig (3rd infra fix)
mdheller Jun 19, 2026
c813419
Tor mode Phase 1: SOCKS routing + cohort alignment profile + design doc
mdheller Jun 19, 2026
a7641e2
tor-mode: spoof normality instead of disabling tech
mdheller Jun 19, 2026
d5c515b
tor-mode: ride the Firefox 140 cohort line, not 150
mdheller Jun 19, 2026
d219cad
tor-mode: pull current ESR source (140.12.0esr) without mirror change
mdheller Jun 19, 2026
7bdbc32
ci: verify the full stack applies to current ESR source (Tor mode)
mdheller Jun 19, 2026
cd06875
ci: fix false-pass in ESR patch check (discover real extracted dir)
mdheller Jun 19, 2026
f2a3705
ci: drop Windows-only msix patch from ESR check; fix nightly-dmg YAML
mdheller Jun 19, 2026
d4f49fb
tor-mode: omit bearbrowser anti-fp patches (disabled to match cohort)
mdheller Jun 19, 2026
dad43ff
tor-mode: fix Makefile esr dirname mismatch (unblocks real build)
mdheller Jun 19, 2026
97104ce
ci: sync native-shell + sidecar validators to refactored contract
mdheller Jun 19, 2026
8c54cf6
fix: restore canonical cross-component contract strings (not mask them)
mdheller Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
188 changes: 188 additions & 0 deletions .github/workflows/anti-fingerprint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: Anti-fingerprint

# Three tiers of verification for the anti-fingerprint track:
# 1. harness-and-measure — config harness + empirical fingerprint baseline
# (real Gecko via Playwright). Fast; the regression gate.
# 2. patch-apply — proves the gecko-patches/ apply to the pinned Firefox
# source (the `check-patchfail` equivalent). No compile.
# 3. full-build — manual: real `make build` + geckodriver measurement of
# the actual binary. Heavy; needs a large/self-hosted
# runner (a full Firefox build OOMs/overflows the stock
# 14 GB GitHub runner — the project's Forgejo/Woodpecker
# pipeline is the natural home; this is the GH scaffold).

on:
pull_request:
push:
branches: [main, anti-fingerprint-verify]
schedule:
- cron: '23 7 * * 1'
workflow_dispatch:

env:
# Keep in sync with the build repo's `version` file.
FIREFOX_VERSION: '150.0.1'

jobs:
# ── Tier 1: config posture + measured fingerprint baseline ─────────────────
harness-and-measure:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install deps + Gecko engine
run: |
npm ci || npm install
npx playwright install --with-deps firefox
- name: Config harness (static + runtime RFP) — HARD GATE
run: node scripts/verify-gecko-rfp.mjs
- name: Fingerprint measurement (both profiles)
run: |
node scripts/measure-fingerprint.mjs --profile human-secure --json > fp-human-secure.json
node scripts/measure-fingerprint.mjs --profile agent-runtime --json > fp-agent-runtime.json
echo "human-secure: $(jq -r '.neutralized' fp-human-secure.json)/$(jq -r '.total' fp-human-secure.json) neutralized"
echo "agent-runtime: $(jq -r '.neutralized' fp-agent-runtime.json)/$(jq -r '.total' fp-agent-runtime.json) neutralized"
- name: Upload fingerprint scorecards
uses: actions/upload-artifact@v4
with:
name: fingerprint-scorecards
path: fp-*.json

# ── Tier 2: do the gecko-patches still apply to pinned Firefox source? ──────
patch-apply:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Fetch pinned Firefox source
run: |
url="https://archive.mozilla.org/pub/firefox/releases/${FIREFOX_VERSION}/source/firefox-${FIREFOX_VERSION}.source.tar.xz"
echo "fetching $url"
wget -q "$url" -O firefox-src.tar.xz
tar xf firefox-src.tar.xz
- name: Dry-run apply every anti-fp patch (fail on reject)
run: |
patches="$(pwd)/gecko-patches"
cd "firefox-${FIREFOX_VERSION}"
fail=0
while IFS= read -r p; do
echo "==> $p"
if ! patch -p1 --dry-run -i "$p"; then
echo "::error file=$p::patch does not apply to firefox-${FIREFOX_VERSION}"
fail=1
fi
done < <(find "$patches" -name '*.patch' | sort)
exit $fail

# ── Tier 2.5: does the FULL stack apply to current ESR source? (Tor mode) ──
# Tor mode rides the Firefox 140 cohort but pulls the current ESR point release
# (140.12.0esr) for security — see docs/tor-mode.md §version. The open risk is
# that LibreWolf's 140.0.4-era patch stack (32 patches) + our anti-fp patches
# may not apply cleanly to the ESR tree (backports shift hunks). This proves it
# with GNU patch on Linux — the authoritative answer (local macOS BSD patch is
# only indicative). No compile, so it runs on a free runner.
tor-esr-patch-apply:
runs-on: ubuntu-latest
env:
TOR_COHORT_TAG: '140.0.4-1' # LibreWolf build-scripts tag (140 line)
TOR_ESR_VERSION: '140.12.0esr' # current ESR point release to retarget to
MIRROR: 'https://github.com/SourceOS-Linux/librewolf-source-mirror.git'
steps:
- uses: actions/checkout@v4
with:
path: overlay
- name: Clone LibreWolf build scripts at the 140 cohort tag
run: git clone --depth 1 --branch "$TOR_COHORT_TAG" "$MIRROR" lw
- name: Retarget source to current ESR (Tor-mode stack)
run: |
cd lw
echo "$TOR_ESR_VERSION" > version
# Tor mode omits the bearbrowser anti-fp patches (CanvasTextMetrics +
# WebAudioFarble) — they are DISABLED to match the Tor Browser cohort, so
# compiling them in is pointless, and these 150-authored patches reject on
# the 140 ESR tree anyway (the default 150 build keeps them). So the
# Tor-mode ESR stack is the LibreWolf stack only, matching what
# apply-sourceos-overlays.sh --profile tor-mode injects (i.e. nothing).
echo "tor-mode stack: $(grep -c . assets/patches.txt) LibreWolf patches against firefox-$(cat version) (anti-fp patches omitted by design)"
- name: Fetch ESR source
run: |
cd lw
url="https://archive.mozilla.org/pub/firefox/releases/${TOR_ESR_VERSION}/source/firefox-${TOR_ESR_VERSION}.source.tar.xz"
echo "fetching $url"
wget -q "$url" -O "firefox-${TOR_ESR_VERSION}.source.tar.xz"
- name: check-patchfail against ESR (GNU patch — authoritative, robust dir)
run: |
cd lw
rm -rf t && mkdir t && cd t
tar xf ../firefox-${TOR_ESR_VERSION}.source.tar.xz
srcdir="$(ls -d firefox-* | head -1)"
echo "::notice::ESR tarball extracts to '$srcdir' (version file says $TOR_ESR_VERSION)"
cd "$srcdir"
fail=0
while IFS= read -r p; do
[ -z "$p" ] && continue
# msix.patch is Windows Store (MSIX) packaging — irrelevant to a
# Linux/macOS Tor browser, and it's the one patch that drifts on ESR.
# Excluded from the Tor-mode stack (see apply-sourceos-overlays.sh).
case "$p" in *msix*) echo "-- SKIP $p (Windows-only, dropped in tor-mode)"; continue ;; esac
echo "==> $p"
if ! patch -p1 --dry-run -i "../../$p" > /tmp/po 2>&1; then
echo "::error::REJECT $p"; cat /tmp/po; fail=1
fi
done < ../../assets/patches.txt
[ "$fail" = 0 ] && echo "✓ all $(grep -c . ../../assets/patches.txt) patches apply to $srcdir"
exit $fail

# ── Tier 3: real build + measure the actual binary (manual, large runner) ──
full-build:
if: github.event_name == 'workflow_dispatch'
# A full Firefox build needs ~40 GB disk + lots of RAM/time. Override with a
# large or self-hosted runner: set repo variable BUILD_RUNNER (e.g. a
# self-hosted label) — falls back to ubuntu-latest, which will likely run out
# of space (kept here so the wiring is visible; the Forgejo pipeline is the
# production path).
runs-on: ${{ vars.BUILD_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
with:
path: overlay
- name: Checkout build repo
uses: actions/checkout@v4
with:
# Set repo variable BUILD_REPO to the LibreWolf-style build repo
# (the one with Makefile + scripts/bearbrowser-patches.py + assets/patches.txt).
repository: ${{ vars.BUILD_REPO }}
path: build-repo
- name: Register anti-fp patches into the build
run: |
cp overlay/gecko-patches/anti-fingerprint/*.patch build-repo/patches/
for p in overlay/gecko-patches/anti-fingerprint/*.patch; do
echo "patches/$(basename "$p")" >> build-repo/assets/patches.txt
done
- name: Validate patches apply (fast gate before the slow build)
run: cd build-repo && make check-patchfail
- name: Build BearBrowser
run: cd build-repo && make bootstrap && make build
- name: Set up measurement deps
run: cd overlay && npm ci || (cd overlay && npm install)
- name: Measure the REAL binary (geckodriver — authoritative)
run: |
# Locate the built binary (Linux: dist/bin/{firefox,bearbrowser};
# macOS: dist/*.app/Contents/MacOS/*). measure-fingerprint.mjs --bin
# drives it via geckodriver — Playwright (Juggler) can't drive a stock
# build. This is the authoritative measurement: it sees what Playwright
# masks (e.g. RFP timezone, real letterboxing).
bin="$(find build-repo -type f \( -name firefox -o -name bearbrowser \) -path '*dist*' 2>/dev/null | grep -vE '\.dSYM' | head -1)"
if [ -z "$bin" ]; then echo "::error::built binary not found under build-repo dist"; exit 1; fi
echo "measuring: $bin"
export PATH="$PWD/overlay/node_modules/.bin:$PATH"
node overlay/scripts/measure-fingerprint.mjs --profile human-secure --bin "$bin" --json > fp-real-human-secure.json
node overlay/scripts/measure-fingerprint.mjs --profile agent-runtime --bin "$bin" --json > fp-real-agent-runtime.json
echo "human-secure (real binary): $(jq -r '.neutralized' fp-real-human-secure.json)/$(jq -r '.total' fp-real-human-secure.json)"
jq -r '.rows[] | select(.status=="LEAKING") | " LEAK: \(.vector)"' fp-real-human-secure.json
- name: Upload real-binary scorecards
uses: actions/upload-artifact@v4
with:
name: real-binary-scorecards
path: fp-real-*.json
8 changes: 7 additions & 1 deletion .github/workflows/feature-plane.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ on:
- "scripts/verify-interactive-sidecar.sh"
- "scripts/verify-comparison-plane.sh"
- "scripts/verify-agent-sidecar-contract.py"
- "scripts/bearbrowser-create-receipt.py"
- "scripts/bearbrowser-update-receipt.py"
- ".github/workflows/feature-plane.yml"
push:
branches: [main]
Expand Down Expand Up @@ -53,6 +55,8 @@ on:
- "scripts/verify-interactive-sidecar.sh"
- "scripts/verify-comparison-plane.sh"
- "scripts/verify-agent-sidecar-contract.py"
- "scripts/bearbrowser-create-receipt.py"
- "scripts/bearbrowser-update-receipt.py"
- ".github/workflows/feature-plane.yml"
workflow_dispatch:

Expand Down Expand Up @@ -84,7 +88,9 @@ jobs:
scripts/bearbrowser-governance-queue.py \
scripts/bearbrowser-sidecar-server.py \
scripts/bearbrowser-sidecar-status.py \
scripts/verify-agent-sidecar-contract.py
scripts/verify-agent-sidecar-contract.py \
scripts/bearbrowser-create-receipt.py \
scripts/bearbrowser-update-receipt.py

- name: Shell syntax
run: |
Expand Down
126 changes: 126 additions & 0 deletions .github/workflows/nightly-dmg.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: BearBrowser Nightly DMG

on:
schedule:
- cron: '0 4 * * *'
workflow_dispatch:
push:
branches: [main]
paths:
- 'settings/**'
- 'scripts/bearbrowser-patches.py'
- 'branding/**'

jobs:
nightly-dmg:
name: Build and package (macOS)
runs-on: macos-15
timeout-minutes: 360

env:
MOZBUILD_STATE_PATH: ${{ github.workspace }}/.mozbuild
VERSION: 150.0.1
RELEASE: 1
PROFILE: human-secure

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Cache Mozilla toolchain
uses: actions/cache@v4
with:
path: .mozbuild
key: mozbuild-${{ env.VERSION }}-macos15-v1
restore-keys: mozbuild-${{ env.VERSION }}-macos15-

- name: Cache Firefox source tarball
uses: actions/cache@v4
with:
path: build/workspaces/${{ env.PROFILE }}-${{ env.VERSION }}-${{ env.RELEASE }}/source/firefox-${{ env.VERSION }}.source.tar.xz
key: ff-tarball-${{ env.VERSION }}

- name: Cache Cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
key: cargo-${{ env.VERSION }}-macos15-v1

- name: Set up workspace from LibreWolf mirror
run: |
bash scripts/apply-sourceos-overlays.sh \
--profile ${{ env.PROFILE }} \
--ref latest \
--workspace-root build/workspaces

- name: Fetch Firefox source tarball
working-directory: build/workspaces/${{ env.PROFILE }}-${{ env.VERSION }}-${{ env.RELEASE }}/source
run: make fetch

- name: Apply BearBrowser patches
working-directory: build/workspaces/${{ env.PROFILE }}-${{ env.VERSION }}-${{ env.RELEASE }}/source
run: make dir

- name: Bootstrap Gecko build environment
working-directory: build/workspaces/${{ env.PROFILE }}-${{ env.VERSION }}-${{ env.RELEASE }}/source/bearbrowser-${{ env.VERSION }}-${{ env.RELEASE }}
run: |
MOZBUILD_STATE_PATH=${{ github.workspace }}/.mozbuild \
./mach --no-interactive bootstrap --application-choice=browser

- name: Build BearBrowser
working-directory: build/workspaces/${{ env.PROFILE }}-${{ env.VERSION }}-${{ env.RELEASE }}/source/bearbrowser-${{ env.VERSION }}-${{ env.RELEASE }}
run: MOZBUILD_STATE_PATH=${{ github.workspace }}/.mozbuild ./mach build

- name: Package BearBrowser.app
run: |
bash scripts/bearbrowser-package-source-build.sh \
--workspace build/workspaces/${{ env.PROFILE }}-${{ env.VERSION }}-${{ env.RELEASE }}/source/bearbrowser-${{ env.VERSION }}-${{ env.RELEASE }} \
--profile ${{ env.PROFILE }} \
--out-dir build/nightly \
--version ${{ env.VERSION }} \
--skip-verify

- name: Create DMG
id: dmg
run: |
DATE=$(date +%Y%m%d)
DMG="BearBrowser-${{ env.VERSION }}-${DATE}-dev.dmg"
hdiutil create \
-volname "BearBrowser" \
-srcfolder "build/nightly/BearBrowser.app" \
-ov -format UDZO \
"build/nightly/${DMG}"
echo "name=${DMG}" >> $GITHUB_OUTPUT
echo "path=build/nightly/${DMG}" >> $GITHUB_OUTPUT

- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.dmg.outputs.name }}
path: ${{ steps.dmg.outputs.path }}
retention-days: 30

- name: Publish nightly release
if: github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
DATE=$(date +%Y-%m-%d)
TAG="nightly-${DATE}"
# Replace any same-day release rather than fail on duplicate tag
gh release delete "$TAG" --yes 2>/dev/null || true
NOTES="$(printf '%s\n' \
'Automated nightly build from `main`.' \
'' \
'**Not notarized — developer use only.**' \
'To open: right-click BearBrowser.app → Open (bypasses Gatekeeper).' \
'' \
"Commit: \`${{ github.sha }}\`" \
"Firefox base: ${{ env.VERSION }}")"
gh release create "$TAG" \
"${{ steps.dmg.outputs.path }}" \
--title "BearBrowser Nightly ${DATE}" \
--notes "$NOTES" \
--prerelease
Loading
Loading