Skip to content

Security: JoseRFJuniorLLMs/NeuralIA

SECURITY.md

Security Policy

Supported versions

Version Supported
2.x Yes
1.x Yes
< 1.0 No

Security fixes target the current main branch and the latest stable release.

Reporting

Please do not publish credentials, cookies, tokens, private browsing data, or working exploit details in a public issue.

For a suspected vulnerability, contact the repository owner through GitHub and provide:

  • affected commit/version;
  • impact;
  • minimal reproduction;
  • whether Reader mode or Full Web mode is involved.

Security boundaries

Reader mode treats remote HTML as untrusted text and does not execute site JavaScript. Redirects are followed manually; one navigation-wide deadline covers the complete redirect chain. Public Reader DNS resolution filters loopback, private, link-local and reserved IP ranges before connection, reducing DNS-rebinding/client-side-SSRF exposure.

Explicit direct navigation to obvious local HTTP(S) targets such as localhost remains a user action and uses a separate local Reader path. A public Reader URL is not permitted to pivot into an obvious local target through redirects.

TLS uses the operating-system certificate verifier.

Reader output is generated from escaped text blocks, not copied raw markup. The Reader Content Security Policy has script-src 'none', connect-src 'none', frame-src 'none' and object-src 'none'.

Read-aloud (Ctrl+Shift+U) in the PDF viewer and in Reader mode uses the WebView2 speechSynthesis and opens no connection of its own; the PDF viewer's connect-src stays 'self'. By default it only picks voices the runtime reports as local (localService === true, the Windows voices), and with no local voice it asks instead of falling back. A voice the runtime reports as online is used only when the user picks it in the voice picker, which says that such a voice needs internet and that the text goes to that voice's provider.

Full Web mode intentionally delegates web execution and sandboxing to the installed system WebView2 runtime on Windows. Permission requests are denied by default. On user-visible Full Web, comparator and Split surfaces, only microphone, camera and display capture continue through the native WebView2 consent flow; NeuralIA never silently grants them. Agent-driven WebViews and unrelated permissions stay denied. Top-level navigation on remote web surfaces is restricted to HTTP(S) plus view-source:; neuralia: navigation is denied there and remains only for internal Reader links.

NeuralIA exposes a bounded WebView2 page-to-native message channel, but no ambient native API. Each message is a JSON envelope capped at 8 KiB, must match a closed action/argument schema, and must carry the per-WebView capability generated by the operating-system CSPRNG and compared in constant time. Injected scripts capture window.chrome.webview.postMessage and JSON.stringify at document-created time. Capability-bearing scripts abort in child frames before declaring the token, and the top-frame capability remains in the closure and is never placed in a navigation URL, so Chromium's Navigation API cannot reveal it through destination.url. Unsigned, malformed, oversized or wrongly signed messages are rejected, and injected user-event handlers require isTrusted, so synthesized clicks and key presses are ignored. No injected handler acts on a page message instead of a gesture; Ctrl+wheel over a child frame does not zoom. Split URLs supplied by pages are restricted to HTTP(S) and obvious local/private/special targets are rejected before DNS. The WebView2 surface does not perform the Reader's pre-connect DNS-resolution filtering, so hostname-to-private-address rebinding is not claimed as blocked by this boundary. The floating omnibox is a native Windows control: a page may request that it open, but can never read or submit its contents.

NeuralIA does not claim that Full Web mode is safer than WebView2 itself.

Ad blocking is off until the user chooses «Ativar bloqueio de anúncios» in the right-click menu of a comparator column, of the page opened beside a column or of the full Web page. No list is bundled: that click downloads Peter Lowe's domain list from https://pgl.yoyo.org/adservers/serverlist.php?hostformat=nohtml&showintro=0&mimetype=plaintext (unless a copy saved less than a week earlier is on disk), and while blocking stays on the list is refreshed at most once a week, when a web page finishes loading -- never on the Home. The download uses the AI client's network policy: HTTPS to that fixed host only, resolved to public addresses only, no proxy from the environment, no redirect followed, a 20-second deadline and a 2 MiB cap on the decoded body; the answer is accepted only if it is not HTML and names at least 1,000 domains. The request is a plain GET with no key, cookie or page address. Blocking answers a matching subresource request with a 403 on the columns, the non-private page beside a column and the full Web page; it never blocks a document (a navigation or a frame), a request to the loopback or to NeuralIA's own origins, a request from the page's own site, anything on an AI provider or sign-in page (the hosts of the provider registry), or a site the user unchecked. The choice is kept in <data_dir>/adblock-settings.json and the list in <data_dir>/adblock-list.json.

When a Google session already exists in the WebView2 profile, NeuralIA may keep a hidden WebView on mail.google.com to announce new mail. It relies on the session cookies already present in the profile only to detect that a session exists; it never requests a password and never exports those cookies. While that session exists, this means background traffic to mail.google.com even when the app is sitting on the native home screen. Set NEURALIA_NO_GMAIL=1 to disable the monitor.

Gemini Live (the eye button in the comparator bar) is off until the user turns it on. While its panel is open, and only after NeuralIA tells the panel to start a session, the panel opens a WebSocket to wss://generativelanguage.googleapis.com (Google's Gemini Live API, model models/gemini-2.5-flash-native-audio-preview-12-2025). Over it go the microphone audio (16 kHz PCM), about one JPEG frame per second composed from the shared screen or window and the camera, and the API key, which the API requires as the key query parameter of that WebSocket URL. Screen, camera and microphone each go through the WebView2 consent prompt; NeuralIA never grants them silently, and each can be switched off in the panel. The eye is filled red from the moment NeuralIA starts a session until the page reports that it ended or the panel closes. Turning the eye off, going Home, opening another comparator, leaving the comparator for any other surface, or a native error screen destroys the panel's WebView; the panel's own «Desligar» button first stops every capture track, the socket and the audio context. If the connection drops, the panel resumes the same session on a new connection with the handle the server gave it, and otherwise stops everything and offers «Conectar de novo». The panel is a page NeuralIA serves itself from its own origin (http://neuralia-live.localhost, four fixed files, with a Content Security Policy whose only connect-src is that WebSocket host), and it navigates nowhere else. Its message channel is separate from the comparator's: it accepts only messages posted by that page, from a closed list of five actions (ready, save_key, forget_key, stopped, close) capped at 1 KiB, and carries no capability token.

The Gemini API key is stored in <data_dir>/gemini-live.key, encrypted with Windows DPAPI for the current user and an application-specific entropy value; a missing, corrupt or foreign file counts as no key. «Trocar chave» in the panel deletes that file and asks for a new key. The key reaches the page only inside the script that starts a session, never in a navigation URL, and the debug log replaces key-shaped text with a marker. Transient in-memory copies of the key (the WebView2 message, the start script, the renderer process) are not wiped, so a memory dump of a running NeuralIA can contain it.

EPUB books (Livros) are untrusted input. The archive reader accepts only stored and deflate entries, refuses names with .., absolute paths, drive letters, backslashes, NUL or control characters and ZIP-level encryption, and enforces entry-count, per-entry, total-size and compression-ratio limits before inflating a byte; each entry's CRC-32 is checked. Package XML (container, OPF, NCX, nav) is parsed without entity expansion, and a DOCTYPE that declares entities is refused. Values copied once per spine item are capped (media type 255 characters; manifest id, href and properties 4 KiB), and so are the spine (10,000 documents) and the parse warnings (64). Metadata is read field by field: a field nested inside another is part of the outer one and is not read again, each field's text is cut while it is read (1,024 characters for titles, names and subjects), the lists of authors, contributors and subjects keep at most 64 entries and the EPUB 3 refinements at most 512, so a crafted OPF cannot multiply memory on import or push a book's entry in the library index past a fixed size. Books with DRM (rights.xml, or encryption.xml encrypting anything but an obfuscated font) are refused. Book resources are served from NeuralIA's own origin http://neuralia-epub.localhost, only by the exact normalised path of an entry of a book in the library, with X-Content-Type-Options: nosniff, a content type from a closed list (a book's JavaScript is never served with a script type) and a Content Security Policy with script-src 'none' that allows images, styles, fonts and media only from that origin or data:, so a book cannot load anything from the network. Chapters render in an <iframe sandbox="allow-same-origin"> without allow-scripts, forms or popups. The library and reader pages are NeuralIA's own files with script-src 'self' and no inline code, and they reach the native side only through their own WebView's message handler (never the remote-page channel): a closed list of eight messages of at most 4 KiB, accepted only from those two pages. A book's http(s) links open in the normal web view after the omnibox's URL validation, with local-network targets rejected; other schemes are ignored.

The omnibox rejects local filesystem paths and unsupported schemes rather than forwarding them to remote AI search.

Local history is bounded and can be cleared with Ctrl+Shift+Delete. NeuralIA has no remote history synchronization or telemetry endpoint.

The repository MUST NOT contain secrets, cookies, tokens, private certificates, or user history.

There aren't any published security advisories