# @bellona/remote-extension

Minimal **Manifest V3** browser extension for the Bellona remote-control surface
(`180.C.20.06`–`.08`).

It exists to provide the three things the 180.C.20.06 evaluation found that
direct CDP cannot cleanly deliver:

1. a **tamper-resistant, page-visible automation indicator** that the page
   cannot remove (shadow-DOM-isolated, re-mounted on tamper),
2. a **native-ish consent/approval popup** (the extension action popup), and
3. a **per-origin profile-grant surface** (an explicit allow-list of origins the
   extension will operate on).

## Security model (all decisions are pure, unit-tested functions)

| Concern | Function | Module |
| --- | --- | --- |
| Origin allow-list (closed-by-default, `<all_urls>` forbidden) | `isExtensionOriginAllowed`, `compileExtensionOriginAllowList` | `origin-policy.ts` |
| Extension-origin authentication (handshake, per-session token) | `verifyExtensionMessageOrigin`, `verifyExtensionMessageSender`, `mintSessionToken` | `extension-auth.ts` |
| Sensitive-action guards (clipboard / upload / form) | `evaluateClipboardAccess`, `evaluateFileUpload`, `evaluateFormSubmission` | `action-guards.ts` |
| Visible automation indicator (mount / re-mount on tamper) | `mountAutomationIndicator`, `indicatorNeedsRemount` | `automation-indicator.ts` |
| Disable/uninstall teardown (revoke token, drop grants, remove indicator) | `runExtensionTeardown`, `ExtensionSessionRegistry` | `session-lifecycle.ts` |
| Manifest domain-scoping invariants | `assertManifestDomainScoped` | `manifest-policy.ts` |

The Chrome-runtime wiring (`background.ts`, `content-script.ts`) is a thin shell
around these functions; it cannot be loaded in CI, so the LOGIC is tested
directly via the pure functions (the established dependency-boundary approach).

## host_permissions are an explicit allow-list — NOT `<all_urls>`

`manifest.json` declares `host_permissions` and `content_scripts.matches` as an
explicit list of `*.bellona.internal` origins (placeholders an operator replaces
at packaging time). `<all_urls>`, `*://*/*`, `http://*/*`, and `https://*/*` are
**forbidden** and rejected fail-loud by `compileExtensionOriginAllowList` /
`assertManifestDomainScoped`. `externally_connectable.matches` names a single
trusted gateway origin, not a wildcard.

`permissions` is the justified minimal set `["scripting", "activeTab",
"storage"]`; `clipboardRead`/`clipboardWrite` are **optional** (requested at
runtime, gated by `evaluateClipboardAccess`).

## Build / test

```bash
# type-check the source
tsc --noEmit -p tsconfig.app.json
# run the adversarial unit tests
vitest run
```
