add hardening, zoom and useragent options

This commit is contained in:
2026-10-17 10:35:14 +02:00
parent ead23646b2
commit 495a0696ba
15 changed files with 724 additions and 70 deletions
+72 -4
View File
@@ -69,7 +69,7 @@ is hidden while a page is fullscreen.
| `H` / `L` | back / forward in history |
| `r` / `R` | reload / reload bypassing cache |
| `y` | yank the current URL to the clipboard |
| `+` `-` `=` | zoom in / out / reset |
| `+` `-` `=` | zoom in / out a level, back to default |
The command line keeps a history (`Up`/`Down`) and aborts on `Escape`, `Ctrl-c`,
or if you delete the leading `:`.
@@ -77,7 +77,8 @@ or if you delete the leading `:`.
While typing `:open` (or `:o`), a completion list of previously visited pages
appears above it, most recent first. Every word you type must appear in the URL
or the title. `Tab` / `Shift-Tab` cycle through the list and put the selected URL
on the command line; `Enter` opens it.
on the command line; `Enter` opens it. `:useragent` completes preset names the
same way.
## Hint mode
@@ -134,17 +135,82 @@ the page. Like qutebrowser, att:
`GM_setClipboard`, `GM_log`, `GM_notification` and the promise-based `GM.*`
versions.
`@require` must be `https://`; see *Security* for pinning with `#sha256=`.
Limits: stored values live in the site's `localStorage`, so they are per site
rather than shared across sites. `GM_xmlhttpRequest` is a plain XHR, so it can't
make cross-origin requests the server doesn't allow (CORS). `@resource` isn't
supported. `GM_openInTab` does nothing until new-window requests are handled.
## Security
On top of Chromium's own protections, att:
- blocks third-party cookies, which stops most cross-site tracking. A few embedded
logins (e.g. some comment widgets) may stop working.
- doesn't let local HTML files (`file://`) read other local files. QtWebEngine
allows that by default, so opening a malicious downloaded page could leak e.g.
`~/.ssh`.
- keeps WebRTC from revealing your LAN address, and doesn't let scripts open
pop-up windows.
- pins the safe defaults for mixed content, clipboard access and plugins.
- shows plain `http://` URLs in orange in the status bar.
- only accepts Greasemonkey `@require` files over HTTPS. A `@require` URL may pin
its content as in Tampermonkey (`…#sha256=<hex or base64>` or `…#sha256-<base64>`),
and the script is skipped if the file doesn't match.
- warns at startup if Chromium's sandbox has been disabled
(`QTWEBENGINE_DISABLE_SANDBOX`, `--no-sandbox`).
The engine's security fixes come from QtWebEngine, so keep it current with
`nix flake update`. `:version` shows the Chromium version and the security patch
level it carries.
## User agent
`:useragent <preset>` (alias `:ua`) changes the browser identity that every
window sends; with no name it shows the current one. Requests use the new one at
once, and `navigator.userAgent` from each page's next load. The client hints
(`Sec-CH-UA*`, `navigator.userAgentData`) are set to match the preset's brand
and platform.
| Preset | Claims to be |
| ----------------- | ------------------------------------------------- |
| `chrome-linux` | Chrome on Linux (the default) |
| `chrome-windows` | Chrome on Windows |
| `chrome-mac` | Chrome on macOS |
| `chrome-android` | Chrome on Android (sites may serve mobile pages) |
| `edge-windows` | Edge on Windows |
| `firefox-linux`, `firefox-windows`, `firefox-mac` | Firefox |
| `safari-mac`, `safari-iphone` | Safari |
| `qtwebengine` | QtWebEngine's own string, which names it |
Chrome and Edge claim the engine's real Chromium major version (as qutebrowser
does), so sites don't use features the engine lacks. The Firefox and Safari
versions are pinned in `src/useragent.cpp`. Those presets only change what att
says, not how it behaves: Chromium's low-entropy client hints still name
Chromium, and feature detection still sees Chromium.
## Settings
att has no config file yet. The settings live in `src/config.h`, named after
their qutebrowser equivalents; change them there and rebuild:
| Setting | Default | Meaning |
| -------------- | ------------------------------- | ---------------------------------------- |
| `zoom.default` | 125% | zoom of new windows; `=` returns to it |
| `zoom.levels` | 25%, 33%, 50%, … 125%, 150%, … 500% | the steps `+` / `-` move through |
| `content.cookies.accept` | no third-party | `kBlockThirdPartyCookies` |
| `content.pdfjs` | on | `kPdfViewer`: Chromium's PDF viewer |
| `content.headers.user_agent` | `chrome-linux` | `kUserAgent`: the preset att starts with |
## Commands
`:open [-w] <url|query>` (alias `:o`; `-w`, or qutebrowser's `-t`, opens a new
window), `:close` (alias `:tab-close`, closes this window), `:quit` (alias `:q`,
closes all windows), `:reload`, `:back`, `:forward`, `:stop`, `:history-clear`,
`:greasemonkey-reload`, `:adblock-update`, `:adblock-toggle`.
`:greasemonkey-reload`, `:adblock-update`, `:adblock-toggle`, `:zoom [level]`
(`150` or `150%`; no level means the default), `:zoom-in`, `:zoom-out`,
`:useragent [preset]` (alias `:ua`), `:version`.
`:open` accepts a full URL, a bare host (`example.com` → `https://example.com`),
an existing local path, or free text, which goes to DuckDuckGo.
@@ -159,13 +225,15 @@ an existing local path, or free text, which goes to DuckDuckGo.
| `src/browserwindow` | the window, mode state machine, key dispatch, commands |
| `src/commandline` | the `:` prompt |
| `src/greasemonkey` | userscript loading and the GM_* API wrapper |
| `src/hardening` | profile security settings, sandbox check |
| `src/history` | SQLite browsing history |
| `src/completionview`| the `:open` completion list |
| `src/completionview`| the `:open` / `:useragent` completion list |
| `src/statusbar` | mode / URL / progress line |
| `src/adblock` | request interceptor around the adblock engine |
| `adblock/` | Rust crate: C API over adblock-rust, built by Corrosion |
| `src/hints` | the injected hint-mode script |
| `src/urlutil` | "what the user typed" → `QUrl` |
| `src/useragent` | user-agent presets and matching client hints |
Key events are intercepted with an **application-wide event filter**
(`BrowserWindow::eventFilter`) rather than `QWidget::keyPressEvent`, because