148 lines
7.0 KiB
Markdown
148 lines
7.0 KiB
Markdown
# LinkLog Firefox Extension Privacy Validation
|
|
|
|
Date: 2026-09-15
|
|
|
|
## Scope
|
|
|
|
This report validates the privacy-relevant properties of the Firefox extension in `webextension/` only. It does not treat the LinkLog backend as part of the extension, except where the extension explicitly sends data to the user-configured backend.
|
|
|
|
## Validation Method
|
|
|
|
- Reviewed the extension manifest, popup and options scripts, localized privacy text, and packaged-page HTML.
|
|
- Searched the extension source for storage, permission, tab-capture, cookie, history, clipboard, and network APIs.
|
|
- Ran `python3 scripts/release/validate_release.py` to confirm the checked-in signed XPI and Firefox disclosure metadata are consistent.
|
|
|
|
## Summary
|
|
|
|
The extension's implemented privacy behavior is mostly consistent with its declared purpose:
|
|
|
|
- It does not include telemetry, analytics, adtech, crash reporting, or third-party beaconing.
|
|
- It captures website activity only from the active tab when the user opens the popup.
|
|
- It stores long-lived account metadata locally, but keeps bearer session credentials in Firefox session storage rather than persistent extension storage.
|
|
- It sends captured link data only to the user-selected backend, plus standard extension-update traffic to the configured self-update URL.
|
|
- Firefox privacy disclosure metadata is present and validated for the signed XPI.
|
|
|
|
The main residual privacy risk is that the extension accepts both `http` and `https` backend URLs. That is useful for local development, but a non-local HTTP backend would expose credentials and saved-link data in transit.
|
|
|
|
## Verified Properties
|
|
|
|
### 1. Declared Firefox permissions are narrow
|
|
|
|
- Required permissions are limited to `activeTab` and `storage`.
|
|
- Host access is not pre-granted broadly at install time. Instead, the extension declares optional HTTP and HTTPS host permissions and requests access only for the specific backend origin entered by the user.
|
|
- The signed-release validator enforces the Firefox `data_collection_permissions` declaration of required `websiteActivity` and no optional data-collection categories.
|
|
|
|
Assessment: consistent with a least-privilege model for a link-saving extension.
|
|
|
|
### 2. Website activity access is limited to the active tab
|
|
|
|
- The popup reads the current page title and URL via `browser.tabs.query({ active: true, currentWindow: true })`.
|
|
- No content scripts are injected into visited pages.
|
|
- No background/service-worker logic observes browsing continuously.
|
|
|
|
Assessment: the extension collects the minimum page context needed for its stated function when the user explicitly opens the popup.
|
|
|
|
### 3. Local storage use is bounded and understandable
|
|
|
|
Persistent local storage:
|
|
|
|
- `backendUrl`
|
|
- `email`
|
|
- `username`
|
|
|
|
Session-scoped storage:
|
|
|
|
- `accessToken`
|
|
- `refreshToken`
|
|
- `tokenExpiresAt`
|
|
- `deviceId`
|
|
|
|
Observed behavior:
|
|
|
|
- Access and refresh credentials are stored in `browser.storage.session`.
|
|
- Sign-out and token invalidation clear those session values and also remove older legacy token keys from `browser.storage.local`.
|
|
- Account-identifying metadata remains in persistent storage to preserve configuration across browser restarts.
|
|
|
|
Assessment: token persistence is minimized appropriately, but the extension still retains backend/account metadata locally until changed or removed.
|
|
|
|
### 4. Outbound network traffic is limited and attributable
|
|
|
|
User-configured backend traffic:
|
|
|
|
- `POST /api/auth/login`
|
|
- `POST /api/auth/refresh`
|
|
- `POST /api/auth/logout`
|
|
- `GET /api/auth/me`
|
|
- `GET /api/tags`
|
|
- `GET /api/public/config`
|
|
- `GET /api/user/plugins/mastodon`
|
|
- `GET /api/links/check`
|
|
- `GET /api/scrape`
|
|
- `POST /api/links`
|
|
|
|
What the extension sends to the backend:
|
|
|
|
- Sign-in credentials: email, password, optional OTP.
|
|
- Session tokens in `Authorization` headers after login.
|
|
- Link payloads: title, cleaned URL, comment, timestamp, and tags.
|
|
- Lookup values for duplicate detection and title scraping.
|
|
|
|
Other network destinations:
|
|
|
|
- Firefox self-update metadata and signed XPI download are configured via `webextension/updates.json` and the manifest `update_url` at `git.kolkman.org`.
|
|
- Repository links in the popup and options page go to `git.kolkman.org` only when the user clicks them.
|
|
|
|
Assessment: no hidden third-party data sinks were found in the extension code.
|
|
|
|
### 5. The extension avoids higher-risk browser APIs
|
|
|
|
No use was found of:
|
|
|
|
- cookies APIs
|
|
- browsing history APIs
|
|
- bookmarks APIs
|
|
- clipboard APIs
|
|
- native messaging
|
|
- remote script loading
|
|
- `XMLHttpRequest` or `sendBeacon`
|
|
|
|
Assessment: this reduces both privacy scope and accidental data leakage paths.
|
|
|
|
### 6. Packaged extension pages use a restrictive CSP
|
|
|
|
- Extension pages declare `script-src 'self'; object-src 'none'`.
|
|
- Popup and options pages load only bundled local scripts and assets.
|
|
|
|
Assessment: this materially lowers the risk of third-party script injection into extension pages.
|
|
|
|
### 7. The privacy notice is broadly accurate, with one caveat
|
|
|
|
The options page says the extension does not collect personal data beyond what is necessary to log links to the LinkLog server. That statement is broadly supported by the code, because the extension only stores and transmits data needed for authentication and link submission.
|
|
|
|
Caveat: the notice would be more precise if it explicitly mentioned that the extension stores the configured backend URL, email address, and username locally, and stores session credentials for the current browser session.
|
|
|
|
## Residual Privacy Risks
|
|
|
|
### R1. Non-TLS backends are allowed
|
|
|
|
The extension accepts `http` and `https` backend URLs. For local development this is reasonable, but for non-local use it means login credentials, tokens, titles, URLs, comments, timestamps, and tags can be transmitted without transport encryption.
|
|
|
|
Recommendation: enforce `https` for non-localhost backends, or at minimum warn before allowing a non-TLS backend.
|
|
|
|
### R2. Backend publication and retention are outside extension control
|
|
|
|
The extension's privacy behavior ends once data is sent to the configured LinkLog server. The backend may publish links publicly, combine them with user profile data, retain them, or forward them through enabled plugins such as Mastodon.
|
|
|
|
Recommendation: keep extension-facing privacy text explicit that saved link data becomes subject to the chosen backend's policies and configuration.
|
|
|
|
### R3. Persistent local account metadata is not cleared on sign-out
|
|
|
|
Sign-out clears session credentials, but leaves `backendUrl`, `email`, and `username` in persistent extension storage for convenience.
|
|
|
|
Recommendation: this is a reasonable default, but the UI could offer a separate "forget this account on this browser" action.
|
|
|
|
## Overall Conclusion
|
|
|
|
The Firefox extension has a relatively narrow privacy footprint and largely follows least-privilege principles. Its privacy disclosure about website activity is implemented and release-validated, session tokens are kept in session storage, and no unexpected telemetry or third-party exfiltration paths were found in the extension code.
|
|
|
|
The most important remaining privacy improvement is to prevent or strongly discourage non-HTTPS backends outside local development. |