Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 130 additions & 38 deletions .mise/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3186,63 +3186,155 @@ flag "--device <device>" help="The machine the rows name, such as its make and m
flag "--submit" help="Drive Chrome through chromedriver and submit every probe set, rather than open it for a person"
"""

[tasks.gpu-probe-firefox]
[tasks.gpu-probe-browser]
depends = ["build-gpu-probe-page"]
description = "Probe Firefox on the GPU probe page from a fresh profile, optionally with fingerprinting protection on"
# Serve the probe page and open Firefox on it, for a person to submit from or, with --submit, to submit by itself.
description = "Probe a browser on the GPU probe page from a fresh profile: Firefox, or a Flatpak one such as Brave"
# Serve the probe page and open a browser on it, for a person to submit from or, with --submit, to submit by itself.
#
# The page is served at localhost:8099 for the length of the task, unless a `gpu-probe-page` already serves it, and
# every job the task starts is stopped when it ends. Each run gets a fresh profile under target/, so the person's own
# Firefox is never touched, and every run is a session of its own: Firefox keys its canvas PNG encoding to the
# session, so two runs are what tells a stable signature from a randomised one.
# The task serves the page itself, on a port of its own, and every job it starts is stopped when it ends. Each run
# gets a fresh profile under target/, so the person's own profile is never touched, and every run is a session of its
# own: Firefox keys its canvas PNG encoding to the session, so two runs are what tells a stable signature from a
# randomised one. firefox is the host's, and the others are the system Flatpaks from Flathub, given the profile's
# directory: the probes measure the browser the person has, as it ships, its own defences named as the rows' defence.
#
# There is no WebDriver for Firefox among the tools, so with --submit the page is opened with `submit` and read from
# Firefox's own output instead, where the page writes its status lines once every set is submitted. firefox is the
# host's: the probes measure the browser the person has.
# There is no WebDriver for most of these, and the one GNOME Web ships cannot reach the browser from inside its
# sandbox, so with --submit the page is opened with `submit` and read from the server: the page posts its status
# lines to `/status` once every set is submitted, and the server logs them.
run = """
trap 'coreutils kill $(jobs -p) 2>/dev/null || true' EXIT
if ! curl -sf -o /dev/null http://localhost:8099/; then
{{ vars.gpu_probe_server }} {{ vars.gpu_probe_server_args }} --http --port 8099 &
server=$!
for _ in $(coreutils seq 600); do
if curl -sf -o /dev/null http://localhost:8099/; then break; fi
if ! kill -0 "$server" 2>/dev/null; then
printf 'the gpu-probe server exited before it served the page\\n' >&2
exit 1
fi
coreutils sleep 1
done
fi
run=target/gpu-probe-firefox
browser="$usage_browser"
window=normal
defence=""
case "$browser" in
firefox) app="" ;;
librewolf)
app=io.gitlab.librewolf-community
defence="privacy.resistFingerprinting, on as LibreWolf ships"
;;
mullvad-browser)
app=net.mullvad.MullvadBrowser
window=private
defence="privacy.resistFingerprinting, on as Mullvad Browser ships"
;;
brave)
app=com.brave.Browser
defence="Shields farbling, on as Brave ships"
;;
chromium) app=org.chromium.Chromium ;;
edge) app=com.microsoft.Edge ;;
gnome-web) app=org.gnome.Epiphany ;;
tor-browser)
app=org.torproject.torbrowser-launcher
window=private
defence="privacy.resistFingerprinting, on as Tor Browser ships"
;;
*)
known="firefox, librewolf, mullvad-browser, tor-browser, brave, chromium, edge or gnome-web"
printf 'no way to run %s: name %s\\n' "$browser" "$known" >&2
exit 2
;;
esac
run="target/gpu-probe-browser/$browser"
profile="$PWD/$run/profile"
coreutils rm -rf "$profile"
coreutils rm -rf "$run"
coreutils mkdir -p "$profile"
coreutils cp .mise/gpu-probe-firefox.user.js "$profile/user.js"
query="device=$(device="${usage_device:-}" yq -n 'strenv(device) | @uri')"
if [ "${usage_fingerprinting_protection:-false}" = true ]; then
printf 'user_pref("privacy.fingerprintingProtection", true);\\n' >>"$profile/user.js"
query="$query&defence=privacy.fingerprintingProtection%20on"
port="${usage_port:-8097}"
log="$run/server.log"
NO_COLOR=1 {{ vars.gpu_probe_server }} {{ vars.gpu_probe_server_args }} --http --port "$port" >"$log" 2>&1 &
server=$!
for _ in $(coreutils seq 600); do
if curl -sf -o /dev/null "http://localhost:$port/"; then break; fi
if ! kill -0 "$server" 2>/dev/null; then
printf 'the gpu-probe server exited before it served the page; its log is %s\\n' "$log" >&2
exit 1
fi
coreutils sleep 1
done
if [ "$browser" = tor-browser ]; then
# The launcher's Flatpak downloads Tor Browser on its first run; this runs that copy's own start script.
tbb="$HOME/.var/app/$app/data/torbrowser/tbb/$(coreutils uname -m)/tor-browser/Browser"
if [ ! -x "$tbb/start-tor-browser" ]; then
printf 'Tor Browser is not downloaded yet: run "flatpak run --system %s" once first\\n' "$app" >&2
exit 1
fi
# The browser runs with no Tor, which cannot reach the page on localhost anyway.
# With Tor it waits on its connection before it opens the page. Its defences are the browser's own, as it ships them.
launch=(flatpak run --system "--filesystem=$profile" --env=TOR_PROVIDER=none)
launch+=("--command=$tbb/start-tor-browser" "$app")
version="$(jaq -r .version "$tbb/tbb_version.json")"
elif [ -n "$app" ]; then
launch=(flatpak run --system "--filesystem=$profile" "$app")
version="$(flatpak info --system "$app" | rg -o 'Version: (\\S+)' -r '$1')"
else
launch=(firefox)
version=""
fi
query="device=$(device="${usage_device:-}" yq -n 'strenv(device) | @uri')&browser=$browser&window=$window"
if [ -n "$version" ]; then
query="$query&browser_version=$version"
fi
case "$browser" in
firefox | librewolf | mullvad-browser | tor-browser)
coreutils cp .mise/gpu-probe-browser.user.js "$profile/user.js"
if [ "$browser" = tor-browser ]; then
# Tor Browser sends even localhost through Tor, which cannot reach the page; these let it reach it directly.
# Neither is a fingerprinting defence. Quick start connects to Tor without waiting on its connect screen.
{
printf 'user_pref("network.proxy.allow_hijacking_localhost", false);\\n'
printf 'user_pref("network.proxy.no_proxies_on", "localhost");\\n'
printf 'user_pref("torbrowser.settings.quickstart.enabled", true);\\n'
} >>"$profile/user.js"
fi
if [ "$browser" = mullvad-browser ] || [ "$browser" = tor-browser ]; then
# Both leave out NoScript, which freezes the page inside the Flatpak sandbox.
# Its log says "Too many SyncMessage loops". Both ship it as a distribution add-on, which a profile that
# records it as installed already never installs.
noscript='{73a6fe31-595d-460b-a920-fcc0f8843232}'
printf 'user_pref("extensions.installedDistroAddon.%s", true);\\n' "$noscript" >>"$profile/user.js"
defence="$defence, NoScript left out"
fi
if [ "${usage_fingerprinting_protection:-false}" = true ]; then
printf 'user_pref("privacy.fingerprintingProtection", true);\\n' >>"$profile/user.js"
defence="privacy.fingerprintingProtection on"
fi
args=(--no-remote --profile "$profile" --new-window)
# Mullvad Browser and Tor Browser start through a script that sends the browser's output nowhere unless asked.
if [ "$browser" = mullvad-browser ] || [ "$browser" = tor-browser ]; then
args=(--verbose "${args[@]}")
fi
;;
gnome-web) args=(--private-instance) ;;
*) args=("--user-data-dir=$profile" --no-first-run --no-default-browser-check) ;;
esac
if [ -n "$defence" ]; then
query="$query&defence=$(defence="$defence" yq -n 'strenv(defence) | @uri')"
fi
if [ "${usage_submit:-false}" != true ]; then
firefox --no-remote --profile "$profile" --new-window "http://localhost:8099/?$query"
"${launch[@]}" "${args[@]}" "http://localhost:$port/?$query"
exit
fi
firefox --no-remote --profile "$profile" --new-window "http://localhost:8099/?submit&$query" >"$run/firefox.log" 2>&1 &
browser=$!
"${launch[@]}" "${args[@]}" "http://localhost:$port/?submit&$query" >"$run/browser.log" 2>&1 &
opened=$!
# The page says "Submitted." only when every set went in or was on record already, and "Not submitted." otherwise.
verdict='gpu-probe: (Submitted|Not submitted|The probes could not run)'
verdict='page status: (Submitted|Not submitted|The probes could not run)'
for _ in $(coreutils seq 300); do
if rg -q "$verdict" "$run/firefox.log" || ! kill -0 "$browser" 2>/dev/null; then break; fi
if rg -q "$verdict" "$log"; then break; fi
coreutils sleep 2
done
# Firefox writes each console line as the call's arguments, quoted; the status lines are what follows the prefix.
rg -o 'gpu-probe: [^"]*' "$run/firefox.log" || printf 'the page said nothing within 600s\\n'
rg -q 'gpu-probe: Submitted' "$run/firefox.log"
if [ -n "$app" ]; then
flatpak kill "$app" 2>/dev/null || true
else
coreutils kill "$opened" 2>/dev/null || true
fi
rg -o 'page status: (.*)' -r '$1' "$log" || printf 'the page said nothing within 600s; its server log is %s\\n' "$log"
rg -q 'page status: Submitted' "$log"
"""
shell = "{{ vars.task_shell }}"
usage = """
arg "<browser>" help="firefox, librewolf, mullvad-browser, tor-browser, brave, chromium, edge or gnome-web"
flag "--device <device>" help="The machine the rows name, such as its make and model"
flag "--fingerprinting-protection" help="Turn on privacy.fingerprintingProtection, and name it as the rows' defence"
flag "--fingerprinting-protection" help="Turn on privacy.fingerprintingProtection, for one built on Firefox"
flag "--port <port>" help="The port to serve the page on for the run, 8097 if not given"
flag "--submit" help="Submit every probe set from the page and print how each went, rather than open it for a person"
"""

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// The prefs `mise run gpu-probe-firefox` starts each fresh Firefox profile with.
// The prefs `mise run gpu-probe-browser` starts each fresh profile of a browser built on Firefox with.
// They skip what a new profile opens with -- the onboarding and pre-onboarding screens, the terms-of-use and
// data-reporting notices, the welcome page and the default-browser question -- so the probe page is the only thing
// shown, and send the page's console to stdout, where the task reads how the run went.
// shown, let it be served over plain HTTP from localhost, which Mullvad Browser's HTTPS-Only mode would refuse, and
// write the page's console to the browser's output, which the run keeps beside the server's log.
// None of them is a fingerprinting defence, so each browser's own defences stay as it ships them.
// Firefox defines `user_pref` itself, as it reads this file into the profile.
/* global user_pref */
user_pref("browser.aboutwelcome.enabled", false);
Expand All @@ -13,6 +15,8 @@ user_pref("browser.uitour.enabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
user_pref("devtools.console.stdout.content", true);
user_pref("dom.security.https_only_mode", false);
user_pref("dom.security.https_only_mode_pbm", false);
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("termsofuse.bypassNotification", true);
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Changelog 1.1.0.
The same feature builds for `wasm32`, opening the browser's WebGPU, and every `native::Gpu` call is async. The
server serves a probe page that runs the probe sets as WASM, through WebGL 2 and WebGPU, and submits each signature;
`mise run gpu-probe-page` builds and serves it on plain HTTP at localhost, which `--http` allows on a loopback only.
- The probe page also puts the probes to WebGL 1, which some browsers offer while withholding WebGL 2 and WebGPU, and
takes both WebGL interfaces in each mode the research page does: a shader a probe, filed as `webgl1-split`, and
every probe in one shader, filed as `webgl1-combined` and `webgl2-combined`. `et-gpu-probe`'s new `batched` writes
those shaders, in GLSL ES 3.00 or 1.00 by `Glsl`, and halves a combined shader the browser refuses until the probe
it will not take is alone. `mise run gpu-probe-browser <browser>` probes Firefox or a Flatpak browser from a fresh
profile, Mullvad Browser and Tor Browser with NoScript left out.
- A WASI module can read a lidar. `et-ws-wasi-runner`'s new `lidar` feature, on by default, hosts
`et:ws-wasi/serial`, lending a guest the serial ports its `SERIAL_PORTS_<NAME>` variables name: a device, or
`tcp://<host>:<port>` for one a bridge such as ser2net serves. The interface's own `serial-host` world, which the
Expand Down
6 changes: 5 additions & 1 deletion CHECKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- **Tools:** 49
- **Shell-script checks:** 11
- **Checks with a repair pass:** 22
- **Rules:** 401 custom local rules, or non-default strict linter settings
- **Rules:** 402 custom local rules, or non-default strict linter settings
- **Meta rules:** 22 of those rules are rules about rules

Every check is listed across every `MISE_ENV`, filed under the tool it runs. Each entry names the env whose config
Expand Down Expand Up @@ -286,6 +286,10 @@ Rules, from `config/ast-grep/rules`:
`match`, or `.unwrap_or_else(...)` when the fallback is real work -- clippy's `unnecessary_lazy_evaluations` objects
to a closure around a plain constructor. This mirrors `DeepSource RS-W1031`. clippy's `or_fun_call` lets an enum or
tuple-struct constructor such as `Ok(None)` through as cheap, so a constructor here gets past clippy alone.
- `no-wildcard-in-or-pattern` -- As `DeepSource RS-W1037` says, a `_` among the alternatives of an or-pattern matches
everything, so the alternatives beside it say nothing: the arm reads as though it names the cases it takes, while it
takes every one. Write the arm as `_` alone, or, where the named cases matter, give them an arm of their own above it.
Clippy's `wildcard_in_or_patterns` is the same check, but it stays quiet when the enum is `#[non_exhaustive]`.
- `org-identity-lives-in-et-org` -- This constant spells out the organisation name or the crate prefix. Both belong to
`et-org`, which is the one place either literal is written: take the value from `et_org::ORG`, `et_org::CRATE_PREFIX`,
`et_org::NPM_SCOPE`, `et_org::REPOSITORY_URL` or `et_org::IMAGE_REGISTRY`, or build a longer string from them, rather
Expand Down
14 changes: 14 additions & 0 deletions config/ast-grep/rules/no-wildcard-in-or-pattern.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
id: no-wildcard-in-or-pattern
language: Rust
severity: error
message: |
As `DeepSource RS-W1037` says, a `_` among the alternatives of an or-pattern matches everything, so the alternatives
beside it say nothing: the arm reads as though it names the cases it takes, while it takes every one. Write the arm
as `_` alone, or, where the named cases matter, give them an arm of their own above it. Clippy's
`wildcard_in_or_patterns` is the same check, but it stays quiet when the enum is `#[non_exhaustive]`.
# Matched on the pattern's own text: the wildcard is an anonymous node that a structural `has` does not reach.
# The fix replaces the whole or-pattern with `_`, which matches exactly what it matched before.
rule:
kind: or_pattern
regex: '(^|\|)\s*_\s*(\||$)'
fix: _
33 changes: 33 additions & 0 deletions config/ast-grep/tests/no-wildcard-in-or-pattern.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
id: no-wildcard-in-or-pattern
valid:
- |
match glsl {
Glsl::Es300 => 1,
_ => 2,
}
- |
match glsl {
Glsl::Es300 | Glsl::Es100Float => 1,
_ => 2,
}
- |
let add = values.iter().map(|_| 1);
- |
match pair {
(_, Some(value)) | (Some(value), _) => value,
_ => 0,
}
invalid:
- |
match glsl {
Glsl::Es300 => 1,
Glsl::Es100Packed | _ => 2,
}
- |
match glsl {
_ | Glsl::Es300 => 1,
}
- |
match glsl {
Glsl::Es300 | _ | Glsl::Es100Float => 1,
}
2 changes: 1 addition & 1 deletion config/oxlintrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"overrides": [
{
// A Firefox profile's prefs file, which Firefox reads with `user_pref` defined.
"files": ["**/.mise/gpu-probe-firefox.user.js"],
"files": ["**/.mise/gpu-probe-browser.user.js"],
"globals": { "user_pref": "readonly" },
},
{
Expand Down
Loading
Loading