diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-09-01 20:10:20 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-09-01 20:10:20 +0900 |
| commit | 55e581bab9b600e7c070ec596d3109fc897d2d86 (patch) | |
| tree | 15ca196a1858696c216946dc9608fae613ad6e5f /doc/Security.md | |
feat: Firefox selection translation extension with local Ollama (initial version)
Implement the complete initial version of a Firefox WebExtension (Manifest V3)
that translates selected text on web pages using a local Ollama LLM and shows
the result in a small popover near the selection.
Specification alignment (Phase 0)
- Unified Specifications.md / Design.md / Security.md on the same approach:
Ollama REST API + Fetch API + NDJSON streaming (no ollama-js SDK dependency)
- Fixed host_permissions to http://localhost/* and http://127.0.0.1/* because
Firefox match patterns do not support port numbers (bug 1362809/1468162);
the actual connection target is restricted to localhost/127.0.0.1 + any port
by application-side validation (defense in depth, OLL-09)
- Clipboard: use navigator.clipboard.writeText() only inside the copy button
click (user activation); no deprecated execCommand fallback
Implementation (zero dependencies, vanilla JS, no npm/Node required)
- manifest.json: MV3, background.scripts + type:module (event page, no
service_worker), action popup, options_ui, content_scripts for http/https,
permissions: storage + localhost host permissions only
- src/shared: constants (ports, limits, presets, error codes), message
validation, settings schema (defaults / merge / sanitize / buildOptions /
toApiThinking), system prompt builder (fixed prompt first + user prompt)
- src/background: message-router (runtime.Port streaming + runtime.onMessage
for model list), translation-session (single active request, abort previous
on new request, stale chunk suppression), ollama-client (fetch against
/api/tags and /api/chat with buffering NDJSON line parser tolerant to chunk
boundaries inside JSON objects, AbortController, host re-validation before
every request, HTTP status to error-code mapping), settings-service
(storage.local load/save with validation and fallback to defaults)
- src/content/content.js: single classic script (Firefox does not support ES
modules in content scripts) with selection detection (selectionchange +
mouseup + keyup, debounced), Shadow DOM action button, Shadow DOM popover
with HIDDEN/LOADING/STREAMING/COMPLETED/ERROR state machine, port client
with requestId-based stale chunk filtering, enabled-state sync via
storage.onChanged; all dynamic text rendered via textContent only
- src/popup + src/options: full settings UI (enable toggle, Ollama host,
model list from Ollama with saved-model warning, target language, thinking
mode, temperature slider 0.0-2.0 step 0.1, context length presets
4k/8k/16k/32k/128k/256k, seed random/fixed with integer value, user system
prompt, advanced numeric options with steppers, stop sequences textarea)
- icons generated by tools/make-icons.ps1 (System.Drawing, no external tools)
- build.ps1 packages manifest.json + icons/ + src/ into dist zip, excluding
tests/, doc/, ollama-js/ and tools/
Tests (71 passed, 0 failed)
- In-browser test runner (tests/test.html) with custom describe/it/assert
framework; no npm, no Node.js
- Unit: constants sync between content mirror and shared module, Ollama host
validation (localhost/127.0.0.1 + any port only; rejects external hosts,
LAN IPs, https, credentials, paths), settings schema defaults and fallback,
temperature 0.1-step validation, context length presets, seed modes,
advanced option types, buildOptions omission of unset values and absence of
tfs_z/mirostat fields, system prompt composition order, message validation
with selection length boundary values
- Integration: NDJSON streaming across awkward byte boundaries, thinking
chunk forwarding, malformed line tolerance, error payload mapping, think/
options inclusion rules, model list, TranslationSession abort semantics,
settings round-trip with storage.onChanged cache refresh, full port flow
(started/chunk/completed), newer request aborts older with stale chunk
discard, malformed message rejection, OLLAMA_UNAVAILABLE and
INVALID_SETTINGS error paths, untrusted sender rejection
Security audit (Design.md section 24 checklist: all pass)
- No innerHTML/outerHTML/document.write/eval/new Function/localStorage
- No remote JS/CSS/fonts/CDN resources; no inline scripts in extension pages
- Ollama access limited to background; content scripts never fetch Ollama
- Background validates message type/length/requestId; single concurrent
translation enforced
- Clipboard API only (no execCommand fallback)
- Minimal permissions: storage + localhost host permissions
Docs
- README.md with build/load/usage instructions, API list, permission rationale
- doc/Testing-Results.md with automated test results, security audit results,
and a 14-item manual Firefox verification checklist (to be executed on a
real Firefox + Ollama environment)
Diffstat (limited to 'doc/Security.md')
| -rw-r--r-- | doc/Security.md | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/doc/Security.md b/doc/Security.md new file mode 100644 index 0000000..956f169 --- /dev/null +++ b/doc/Security.md @@ -0,0 +1,804 @@ +# Security.md + +## 1. Purpose + +This document defines the mandatory security constraints and implementation rules for the Firefox WebExtension project. + +The implementation agent MUST treat this file as a normative development policy together with `Specifications.md` and `Design.md`. + +When there is a conflict between convenience and this security policy, this policy takes precedence unless the project owner explicitly changes the requirement. + +--- + +## 2. Security Objectives + +| ID | Objective | +|---|---| +| SEC-001 | Minimize the extension's attack surface. | +| SEC-002 | Avoid third-party build-time and runtime dependency supply-chain risk. | +| SEC-003 | Treat all web page content as untrusted input. | +| SEC-004 | Prevent selected text and LLM output from being evaluated as HTML or JavaScript. | +| SEC-005 | Restrict privileged WebExtension operations to the minimum required components. | +| SEC-006 | Restrict network communication to the configured Ollama endpoint. | +| SEC-007 | Use current Firefox WebExtensions APIs and avoid deprecated or obsolete APIs. | +| SEC-008 | Keep the implementation auditable without a package manager or generated dependency tree. | + +--- + +## 3. Dependency Policy + +### 3.1 Mandatory Rules + +The project MUST be developed without Node.js or JavaScript package managers. + +The following are prohibited: + +- Node.js as a required development or runtime dependency +- npm +- yarn +- pnpm +- bun package management +- `node_modules` +- third-party JavaScript packages +- third-party CSS frameworks +- remote JavaScript +- remote CSS +- remote fonts +- CDN-hosted runtime resources + +The project SHOULD NOT contain `package.json`. + +All resources required at runtime MUST be included directly in the extension package or supplied by Firefox/Web Platform standard APIs. + +### 3.2 Allowed Technologies + +The implementation MAY use: + +- HTML +- locally stored CSS +- vanilla JavaScript +- Firefox WebExtensions APIs +- standard DOM APIs +- Fetch API +- Streams API +- `TextDecoder` / `TextDecoderStream` +- standard `JSON` APIs +- locally bundled PNG/SVG assets +- inline SVG created by the project +- operating-system fonts through CSS generic/system font families + +### 3.3 CSS and Asset Policy + +All application CSS MUST be owned by this project and stored locally in the repository. + +Do not download and vendor a CSS framework unless explicitly approved. + +Preferred approach: + +```text +content.css +popup.css +options.css +``` + +External resources such as the following MUST NOT be used: + +```text +Google Fonts +Font Awesome CDN +Material Icons CDN +Bootstrap CDN +Tailwind CDN +jsDelivr +unpkg +cdnjs +``` + +Icons SHOULD be: + +1. project-owned local SVG/PNG files, or +2. inline SVG defined directly by the application. + +System fonts SHOULD be used, for example: + +```css +font-family: system-ui, sans-serif; +``` + +--- + +## 4. Ollama Integration Policy + +### 4.1 No Ollama JavaScript SDK Dependency + +The project MUST NOT require `ollama-js`. + +The extension MUST communicate with Ollama directly using the official Ollama HTTP API and the browser Fetch API. + +The intent is to eliminate npm/package-manager supply-chain dependencies. + +### 4.2 Required API Scope + +The initial implementation SHOULD require only the minimum Ollama endpoints needed by the specification. + +Expected endpoints: + +| Endpoint | Purpose | +|---|---| +| `GET /api/tags` | Retrieve installed models. | +| `POST /api/chat` | Perform translation generation using streaming. | + +Do not add Ollama API endpoints unless required by a defined feature. + +### 4.3 Streaming + +Translation MUST use streaming responses. + +The Ollama client module MUST: + +- parse streamed response data safely; +- tolerate chunk boundaries that do not align with JSON object boundaries; +- cancel the current request when a newer translation request supersedes it; +- use `AbortController` or equivalent standard browser functionality; +- propagate only validated translation text to the content script. + +### 4.4 Ollama Endpoint + +Default endpoint: + +```text +http://localhost:11434 +``` + +The endpoint MAY be configurable according to the product specification. In the initial version, the configured endpoint MUST be limited to `http://localhost:<port>` or `http://127.0.0.1:<port>`. External hosts, LAN IPs, and HTTPS endpoints MUST be rejected by validation. + +The implementation MUST validate the configured value before use, and the Ollama client MUST re-verify the target host immediately before each request (defense in depth). + +The background component MUST be the only component that communicates directly with Ollama. + +Content scripts MUST NOT directly call Ollama. + +--- + +## 5. Firefox WebExtensions Policy + +### 5.1 Manifest + +The extension MUST use Manifest V3. + +Firefox-specific behavior MUST be verified against current MDN documentation before implementation. + +Do not blindly copy Chromium-specific Manifest V3 examples. + +### 5.2 Background Implementation + +For Firefox, use the supported background mechanism documented for Firefox. + +Do NOT assume Chromium's: + +```json +"background": { + "service_worker": "background.js" +} +``` + +is supported by Firefox. + +The implementation agent MUST verify the current Firefox support status before changing the background model. + +### 5.3 APIs to Prefer + +Use modern APIs where required: + +| Purpose | Preferred API | +|---|---| +| Toolbar action | `action` | +| Extension storage | `storage.local` | +| Long-lived extension communication | `runtime.connect()` / `runtime.Port` | +| One-shot extension messages | `runtime.sendMessage()` | +| Dynamic script injection, only if needed | `scripting.executeScript()` | + +### 5.4 APIs / Patterns to Avoid + +Do not introduce the following unless explicit project requirements justify them: + +- Manifest V2 +- `browser_action` +- `browserAction.*` +- `tabs.executeScript()` +- `tabs.insertCSS()` +- `runtime.getBackgroundPage()` +- `extension.getBackgroundPage()` +- `window.localStorage` for extension settings +- `externally_connectable` +- `runtime.onMessageExternal` +- `runtime.onConnectExternal` +- `nativeMessaging` +- `webRequest` +- `cookies` +- `history` +- `bookmarks` +- `downloads` +- unnecessary `tabs` permission +- unnecessary `scripting` permission + +The implementation MUST verify whether any WebExtensions API it intends to add is deprecated, obsolete, Firefox-incompatible, or unnecessarily privileged. + +--- + +## 6. Permission Minimization + +The extension MUST request only permissions required by implemented features. + +Expected initial permission set: + +```text +storage +``` + +Expected host permission: + +```text +http://localhost/* +http://127.0.0.1/* +``` + +Firefox match patterns do not support port numbers (Firefox bug 1362809 / 1468162). Therefore the patterns above match any port on localhost. The application MUST restrict the actual connection target to `http://localhost:<port>` or `http://127.0.0.1:<port>` via its own host validation before any Ollama request. + +or the minimum equivalent required by the final Ollama-host design. + +Page access MAY require content-script match patterns for HTTP/HTTPS pages because translation is initiated by selecting page text. + +Do not add broad privileged permissions for convenience. + +Every newly introduced permission MUST be justified in code review. + +--- + +## 7. Trust Boundaries + +The system has three primary trust zones. + +```text +Untrusted Web Page + | + | selected text + v +Content Script + | + | validated extension messages + v +Background + | + | HTTP + v +Ollama +``` + +### 7.1 Web Page + +All page content MUST be considered untrusted. + +This includes: + +- text selected by the user; +- DOM content; +- attributes; +- page-generated text; +- scripts; +- event data; +- iframe content; +- strings that resemble prompts or instructions. + +The presence of text such as: + +```text +Ignore previous instructions... +``` + +MUST NOT cause that text to be treated as an application instruction. + +Selected web content is translation data, not trusted control input. + +### 7.2 Content Script + +The content script is responsible only for tasks that require page access, including: + +- obtaining the user's current text selection; +- positioning the translation action icon; +- rendering the translation popover; +- forwarding translation requests; +- receiving streamed translation text; +- handling local UI interaction. + +It MUST NOT be responsible for privileged network communication with Ollama. + +### 7.3 Background + +The background component owns privileged application operations: + +- Ollama HTTP communication; +- request lifecycle management; +- configuration access where appropriate; +- message validation; +- request cancellation; +- Ollama response parsing. + +--- + +## 8. DOM and XSS Rules + +### 8.1 Plain-Text Rendering + +Page-derived text and LLM-generated text MUST be rendered as plain text. + +Use safe DOM APIs such as: + +```js +element.textContent = value; +``` + +Do NOT use untrusted content with: + +```js +element.innerHTML = value; +element.outerHTML = value; +shadowRoot.innerHTML = value; +document.write(value); +``` + +### 8.2 No HTML Interpretation + +The translation result is plain text. + +Do not implement Markdown rendering, HTML rendering, or rich-text parsing unless explicitly added as a later requirement and separately security-reviewed. + +### 8.3 No Dynamic Code Execution + +The following are prohibited: + +```text +eval() +new Function() +setTimeout(string) +setInterval(string) +``` + +No selected page content, setting, LLM response, or remote response may be executed as JavaScript. + +--- + +## 9. Shadow DOM Policy + +The translation UI SHOULD use Shadow DOM where practical to isolate extension UI styling from page CSS. + +Primary reasons: + +- reduce CSS collisions; +- reduce page-style interference; +- contain translation UI styles; +- improve predictable layout. + +Shadow DOM MUST NOT be treated as an XSS security boundary. + +LLM responses and page text inside a shadow root MUST still use plain-text rendering. + +--- + +## 10. Page Script Isolation + +The extension SHOULD rely on the normal WebExtensions content-script isolation boundary. + +Do not bridge page scripts and extension scripts unless a future requirement makes this unavoidable. + +The following SHOULD NOT be used: + +- `wrappedJSObject` +- `exportFunction` +- `cloneInto` +- custom page-script injection +- page-controlled `window.postMessage` communication + +If page-script communication becomes necessary later, it requires an explicit security design update. + +--- + +## 11. Message Validation + +All messages received by privileged extension components MUST be validated. + +Example translation request shape: + +```js +{ + type: "translate", + requestId: "...", + text: "..." +} +``` + +The receiver MUST validate at least: + +- message is an object; +- known `type`; +- expected fields are present; +- `text` is a string; +- `requestId` is a string; +- text length is within defined limits; +- unsupported fields do not alter privileged behavior. + +Do not dispatch privileged behavior using arbitrary method names supplied by messages. + +Avoid patterns such as: + +```js +handlers[message.method](message); +``` + +unless `message.method` is strictly allow-listed. + +--- + +## 12. Input Limits and Resource Abuse + +Selected text MUST have a maximum allowed size. + +The exact product limit MAY be defined in `Specifications.md`, but an implementation limit MUST exist. + +The purpose is to reduce: + +- accidental huge requests; +- malicious resource consumption; +- unnecessarily long model generation; +- UI hangs; +- memory pressure. + +If input exceeds the allowed limit, the extension MUST fail safely and provide a user-visible error. + +Repeated translation requests MUST cancel obsolete in-flight requests. + +--- + +## 13. LLM Prompt Boundary + +The application MUST maintain a separation between: + +1. application-controlled system instructions; +2. user-configurable system instructions; +3. selected page text. + +The application-controlled prompt MUST state that selected page content is translation material and must not be interpreted as instructions. + +Selected text MUST NOT be concatenated into trusted system instructions. + +Preferred conceptual request structure: + +```text +system: + application-controlled translation rules + + validated user translation preferences + +user: + selected page text +``` + +--- + +## 14. Settings Validation + +Every configurable Ollama generation value MUST be validated before being sent to Ollama. + +Validation MUST include: + +- expected type; +- finite numeric value where applicable; +- supported range; +- integer-only constraints where applicable; +- allowed enum values; +- context-length preset membership; +- string length limits. + +Invalid stored values MUST NOT be passed directly to Ollama. + +The implementation SHOULD fall back to safe defaults or request correction from the user. + +--- + +## 15. Seed Security / Correctness Rule + +Seed configuration MUST distinguish between: + +- variable/random behavior; +- fixed deterministic integer seed. + +When fixed seed mode is enabled, the value MUST be validated as an integer. + +When variable mode is enabled, the implementation MUST use the Ollama/API-supported behavior defined in the specification and MUST NOT generate executable or unsafe random data. + +--- + +## 16. Storage Rules + +Use: + +```text +browser.storage.local +``` + +for extension configuration. + +Do not use page `localStorage` for extension settings. + +The implementation MUST assume `storage.local` is not encrypted. + +Do not store: + +- passwords; +- arbitrary secrets; +- private authentication tokens; + +unless a later requirement explicitly adds such functionality and the security model is revised. + +The initial local-Ollama design SHOULD require no secret credentials. + +--- + +## 17. Content Security Policy + +Keep Firefox's extension CSP as restrictive as practical. + +Do not weaken CSP merely to make a library or framework work. + +Do not enable: + +- remote script execution; +- `unsafe-eval`; +- arbitrary inline executable JavaScript. + +JavaScript SHOULD be stored in local `.js` files where applicable. + +HTML event attributes such as the following SHOULD NOT be used: + +```html +<button onclick="..."> +``` + +Prefer JavaScript event listeners: + +```js +button.addEventListener("click", handler); +``` + +--- + +## 18. Remote Resource Ban + +At runtime, the extension MUST NOT load executable or styling resources from the Internet. + +Prohibited runtime resources include: + +- JavaScript; +- CSS; +- web fonts; +- icon libraries; +- UI frameworks; +- remotely hosted HTML fragments. + +The only intended network communication is application data communication with the configured Ollama service. + +A future feature that adds any other remote service requires explicit review and an update to this file. + +--- + +## 19. Build and Packaging + +The project SHOULD be directly loadable as a Firefox temporary extension without a build step. + +Expected development workflow: + +```text +edit source files + -> +about:debugging + -> +This Firefox + -> +Load Temporary Add-on + -> +select manifest.json +``` + +A build system MUST NOT be introduced unless an explicit requirement justifies it. + +Packaging SHOULD consist of the project files required by the WebExtension package, excluding tests and development-only files where appropriate. + +--- + +## 20. Testing Security Requirements + +Security-sensitive behavior MUST be tested. + +Minimum cases: + +| Test | Expected Result | +|---|---| +| Selected text contains `<script>` | Displayed as text, never executed. | +| LLM returns HTML tags | Displayed as text. | +| LLM returns `<img onerror=...>` | Displayed as text. | +| Selected text contains prompt-injection language | Treated only as translation input. | +| Oversized selected text | Rejected safely. | +| Invalid numeric option | Rejected or replaced with safe default. | +| Invalid context preset | Rejected. | +| Unknown message type | Ignored/rejected. | +| Rapid repeated translation requests | Previous request is cancelled. | +| Ollama unavailable | Safe error UI, no exception leak to page. | +| Malformed Ollama stream chunk | Parser fails safely without executing content. | +| Page has hostile CSS | Translation UI remains usable where practical. | +| Page script inspects DOM | No privileged extension object is exposed. | + +--- + +## 21. Logging + +Do not log full selected text or full translation results by default. + +Development logging SHOULD avoid unnecessary disclosure of page content. + +Do not log secrets if credential support is ever added. + +Error logs SHOULD contain technical diagnostics without unnecessarily reproducing user-selected text. + +--- + +## 22. Error Handling + +Errors MUST fail closed rather than enabling broader privileges or unsafe fallback behavior. + +Examples: + +- If Ollama host validation fails, do not attempt arbitrary network access. +- If JSON streaming parsing fails, stop or recover safely. +- If a setting cannot be validated, use a documented safe default or reject the operation. +- If content-script messaging fails, do not fall back to page-script execution. + +--- + +## 23. Source Review Policy + +Before adding any new external code, dependency, browser API, or permission, the implementation agent MUST answer: + +1. Is this required by `Specifications.md`? +2. Can the same requirement be implemented with a browser/WebExtensions standard API? +3. Does this introduce a package-manager dependency? +4. Does this introduce a runtime network dependency? +5. Does this require a new Firefox permission? +6. Is the API current and supported by Firefox? +7. Does it expand the trust boundary? +8. Can the implementation remain simpler without it? + +If the feature can reasonably be implemented without the new dependency or privilege, prefer the dependency-free / lower-privilege implementation. + +--- + +## 24. Explicitly Prohibited Design Changes + +Without project-owner approval, the implementation agent MUST NOT: + +- add npm dependencies; +- introduce Node.js tooling; +- add a JavaScript framework; +- add a CSS framework; +- load assets from a CDN; +- load Google Fonts or other remote fonts; +- add `ollama-js`; +- replace direct Ollama REST calls with a third-party client; +- request unrelated Firefox permissions; +- introduce Manifest V2 compatibility code; +- introduce Chromium-only service-worker assumptions into Firefox code; +- render LLM output using HTML; +- execute page or LLM content; +- expose privileged extension functions to page JavaScript; +- silently add telemetry or analytics; +- send selected text anywhere other than the explicitly configured Ollama endpoint. + +--- + +## 25. Privacy Requirements + +Selected text can contain private or sensitive page content. + +Therefore: + +- selected text MUST only be sent to the configured Ollama endpoint; +- the extension MUST NOT send telemetry; +- the extension MUST NOT send analytics; +- the extension MUST NOT contact external logging services; +- the extension MUST NOT automatically fall back to a cloud LLM; +- connection failures MUST NOT cause data to be sent to another provider; +- the UI SHOULD make the configured Ollama host visible to the user. + +If cloud Ollama support is added later, it must be an explicit user configuration and requires a privacy/security design update. + +--- + +## 26. Implementation Review Checklist + +Before declaring implementation complete, verify all of the following. + +### Dependencies + +- [ ] No `node_modules`. +- [ ] No npm/yarn/pnpm/bun dependency. +- [ ] No required Node.js build step. +- [ ] No third-party JS runtime dependency. +- [ ] No third-party CSS framework. +- [ ] No remote fonts. +- [ ] No CDN resources. +- [ ] No `ollama-js`. + +### Firefox + +- [ ] Manifest V3. +- [ ] Firefox-supported background implementation. +- [ ] No deprecated WebExtensions API used when a supported current alternative exists. +- [ ] Permissions are minimal. +- [ ] No unnecessary `tabs`, `scripting`, `webRequest`, `cookies`, or `nativeMessaging` permission. +- [ ] Firefox behavior manually verified in `about:debugging`. + +### DOM / Page Boundary + +- [ ] Page content treated as untrusted. +- [ ] LLM output treated as untrusted. +- [ ] `textContent` or equivalent safe APIs used. +- [ ] No untrusted `innerHTML`. +- [ ] No `eval()` / `new Function()`. +- [ ] No page-script privilege bridge. +- [ ] Shadow DOM does not contain unsafe HTML insertion. + +### Messaging + +- [ ] Message types are allow-listed. +- [ ] Payload types are validated. +- [ ] Selection length limit exists. +- [ ] Obsolete translation requests are cancelled. +- [ ] Unknown messages are rejected or ignored safely. + +### Network + +- [ ] Only background communicates with Ollama. +- [ ] Ollama host is validated. +- [ ] No silent fallback endpoint. +- [ ] Streaming parser handles malformed input safely. +- [ ] No telemetry or analytics endpoint. + +### Settings + +- [ ] Numeric values validated. +- [ ] Seed integer validated. +- [ ] Context length restricted to supported UI presets. +- [ ] Temperature validated. +- [ ] Stored invalid values are handled safely. + +### Testing + +- [ ] Script/HTML injection strings tested. +- [ ] Prompt-injection-like selected text tested. +- [ ] Oversized selection tested. +- [ ] Ollama offline case tested. +- [ ] Request cancellation tested. +- [ ] Malformed streaming data tested. +- [ ] Settings validation tested. + +--- + +## 27. Definition of Done + +Security work is complete only when: + +1. all implemented features comply with this document; +2. the extension runs without Node.js/npm; +3. all runtime resources are local except Ollama API traffic; +4. no untrusted page or LLM content is interpreted as executable markup/code; +5. Firefox permissions and APIs have been reviewed for necessity and current support; +6. security test cases pass; +7. deviations from this policy are explicitly documented and approved. |
