The extension is shipped from main. Only the latest commit on main receives security fixes.
Please do not open a public GitHub issue for security problems.
Email kristjan.pikhof@gmail.com with:
- A description of the issue and its impact.
- Steps or a proof-of-concept that demonstrates it.
- The commit SHA (or release) you tested against.
- Whether you'd like credit in the changelog.
You should expect an acknowledgement within a few days. Reasonable disclosure timelines are appreciated; once a fix is in main, the report can be discussed publicly.
The extension is local-first. The interesting surfaces are:
- Provider API key storage. Stored in
chrome.storageand never sent anywhere except the configured base URL. - Local helper service. Binds to
127.0.0.1:19720, requires a configured Chrome extension ID on every request, and validatesOriginplus an authorization header. CORS preflight rejects unknown origins. - Native messaging bridge. Used only to install, start, stop, and inspect the helper. The allowed extension ID is written by
helper:setupand enforced by the host launcher. - Content script. Reads watch-page metadata and caption availability. It does not exfiltrate page contents.
If you find a way around any of these guarantees, that's a vulnerability.
YouTube captions are user-generated text and must be treated as untrusted input. Every prompt that embeds caption text — chunk summarization, the final structured-analysis pass, and Ask AI — wraps the transcript inside <<<TRANSCRIPT_BEGIN>>> / <<<TRANSCRIPT_END>>> fence markers, and the system prompt explicitly instructs the model to treat anything between those markers as data and never as instructions, role changes, or tool requests.
This mitigates, but does not eliminate, prompt injection. A determined adversary can still try to bias the summary; what the fences and system instruction prevent is the model executing transcript-supplied directives such as "ignore prior rules", "exfiltrate the API key", or "switch to a different output format". If you find a video where embedded text causes the model to actually follow injected instructions in a way that escapes the summary container, that is a vulnerability.
The provider config form accepts any OpenAI-compatible base URL the user enters, but it does not blindly trust them. The URL is classified into three buckets:
https://...— accepted without prompts.http://on the loopback allowlist (127.0.0.1,localhost,::1) — accepted without prompts so legitimate local providers like Ollama and LM Studio keep working.http://to any other host — flagged as untrusted. The form refuses to save the provider, and the connection-test refuses to send the API key, until the user explicitly toggles a confirmation switch acknowledging that the key will travel in clear text. The acknowledgement is bound to the specific host and is invalidated whenever the URL is edited or the saved provider is switched, so a green light cannot be inherited across hosts.
This is the trust boundary between the extension's stored API key and the network. The extension makes no other outbound requests to user-supplied URLs.
A malicious web page that controls a hostname resolving to 127.0.0.1 can
direct the browser to send requests that look locally sourced. To close this
attack, the helper now rejects any request whose Host header is not in the
loopback allowlist (127.0.0.1:19720, localhost:19720, [::1]:19720). A
missing or non-loopback Host header gets a 403 before the Origin or
authorization checks run. This means a page that DNS-rebinds to the helper's IP
cannot smuggle a request through — its own hostname will appear in Host and
be rejected immediately.
Verbose console output is off by default. The Diagnostic logging toggle in Options gates the helper-settings native-messaging trace logs ([HelperSettings] Native messaging connected, status: and [HelperSettings] Native messaging failed:); when the flag is off these calls do not run and nothing about the helper status is written to the devtools console. Render-time errors from the helper-settings React error boundary are still logged unconditionally — those represent unexpected failures we want surfaced regardless of the flag.