Skip to content

Security: mrsae123/Image-Preview-

SECURITY.md

Security Policy

Image Preview is a free, open-source plugin for Tavo. It has no server, no account and no analytics, and it never sends your chats, characters or personas anywhere.

Reporting a problem

Please report security issues privately: open the repository's Security tab and choose Report a vulnerability. Please don't post details in public issues or Discord before it's fixed. A short description and the steps to reproduce are enough.

Only the latest release is supported. If you installed v2.21.1 (including from the Tavo Hub), please update: it was taken down from GitHub and Discord so its security hardening could be finished. Versions before v2.26.1 could also be frozen for a long time by crafted text and had gaps in the link and download checks (see the changelog); please update. Versions before v2.27.1 could also be frozen by one crafted heading line (a # line followed by thousands of spaces) on the Character page or in Page view; please update.

What the plugin does with your data

What Where it goes
Saved images, pins, Recycle Bin, settings, game data, the list of recent pages in Page view (for each of the last 20 pages: its address, title, the first 1,500 characters of its text and up to 500 picture addresses), and the names and sizes of files you import from your device (only used to avoid saving the same file twice) Stays on your device, inside Tavo
Quick Lookup text (only when you tap "Look up", up to 1,000 characters) HTTPS to Google Translate, Lingva, MyMemory, dictionaryapi.dev, Wiktionary, Wikipedia, DuckDuckGo, Brave Search or Tavily. Privacy Mode blocks all of it
Brave Search API key (optional) Only to api.search.brave.com, as a request header. Never in a URL, never to another host
Tavily API key (optional) Only to api.tavily.com, as an Authorization request header. Never in a URL, never to another host. When you ask Tavily to read a page, that page's link is sent with it
Images and pages you ask it to preview or save (including in Page view) Downloaded from the address you gave, after a headers-only check sent to that same address (no cookies, no Referer, the body is never read), and checked again once they arrive. A page is read as inert text and its temporary file is deleted. That site sees your IP address, but no Referer
The open chat's character card, persona, lorebooks and messages Read on your device only when you tap "Scan this chat for images", to find image links. Never sent anywhere and never changed
The open chat's character card (for the Character page) Read on your device only when you open the Character page. Never sent anywhere and never changed. Its pictures load from the addresses the card lists, only while the page is open
Your agreement to the Terms of Use (a version number and a time) Stays on your device, inside Tavo
Everything else in Tavo (other chats, your library, other settings) Never read

Guardrails built in

  • Links: only public http(s) addresses are opened or downloaded. localhost, private and link-local ranges, *.local and other intranet-style names, names that spell out a private address (for example 192-168-1-1.sslip.io), well-known wildcard-DNS domains (nip.io, sslip.io, localtest.me and similar), IPv6 literals, addresses with a username/password and non-web schemes (javascript:, intent:, file:, data: other than images) are refused.
  • No unsafe HTML: every piece of stored, downloaded or typed text is escaped before it is shown. Automated tests feed hostile names, links and saved data through every screen.
  • Added games: run in a sandbox with only allow-scripts (no same-origin access, popups, forms, downloads or navigation) and a Content-Security-Policy that blocks all network access. Messages from a game are accepted only from its own frame and reduced to a number or a small settings object.
  • Bounded work: pasted text is capped and scanned in one linear pass (so is every other check run over card, lorebook, message or page text: a crafted 300,000-character text is handled in under a second in tests), Extract shows at most 200 links, lookups are capped at 1,000 characters, image downloads time out after 45 seconds and web page previews after 30, at most 30 images can be pinned at once, and the Saved Previews shelf draws 200 at a time.
  • No outside code: the package loads no scripts, fonts or styles from the internet and has no dependencies.
  • Chat scan: manual only (never runs by itself), read-only, capped (only the newest 5,000 messages are requested from Tavo, 6 MB of text, 200 tiles), and its results follow the same link rules as everything else.
  • What may be downloaded: only images, GIFs and web pages. Videos, music, archives, apps, documents, fonts and model files are refused by their file ending before anything is fetched. For other links, and for links that end like an image, a headers-only request asks the link's own server what it is and how big it is (no body, no cookies, no Referer): answers that are not an image or a web page, and oversized ones (images over 50 MB, pages over 6 MB), are refused. If the server won't say, an oversized web page is discarded unread and an oversized image is deleted after it arrives. Every saved image is then checked again once it has arrived: a link without a file ending must start like a picture, for every other link the web engine has to accept the saved file as a picture (if it gives no answer in time the file is kept), and a data: image must start like a picture. Anything else is deleted at once, even if the server called it an image.
  • Character page: read-only and manual. Text is turned into a short allow-list of plain elements and never inserted as HTML; scripts, frames, forms, event handlers and links are dropped, and links become pills that open Page view. A card's own look is carried over only as plain numbers and keywords that are rebuilt, never copied as written: text colours, backgrounds and gradients, borders, rounded corners, spacing, side-by-side rows, centered text, text sizes, bold, italic, underline, letter spacing, line height, upper/lower case, dimming (never below 45%) and soft text shadows. A picture address (url()), positioning, z-index, transforms, animation, filters, clipping and display: none are never carried; text a card tries to hide (zero size, see-through, a box too narrow to hold words) is shown anyway; and the text colour is checked against the background it lands on (a colour that can't be read is replaced, and a gradient no text can be read on becomes its average colour). Rows always wrap, the sideways spacing of nested boxes is capped and at most 600 boxes are decorated. Pictures must pass the link rules (or be an embedded PNG, JPEG, GIF, WebP, AVIF or BMP, never SVG). Limits: 300,000 characters per field and 100 pictures, and a card made of tens of thousands of tags, or nested hundreds deep, is shown as plain words. Automated tests feed hostile cards, and thousands of random hostile style strings, through it and check that only allow-listed values come out.
  • Page view: manual only. The page is downloaded to a temporary file, read with an inert parser (nothing in it runs, loads or is inserted as HTML), shown as plain elements, and the temporary file is deleted. Ads, trackers and one-pixel images are counted, never kept; its pictures follow the same link rules. Pages over 6 MB are refused or discarded unread, and only the first 3,000,000 characters are read. For Image Chest links the picture list inside the page's data is read with a JSON parser (nothing in it runs), every address in it goes through the same link rules, at most 3,000 entries are looked at and 500 pictures kept, and anything unexpected is ignored. Pictures are shown 80 at a time.
  • Storage manager: manual only, and behind the Terms gate like every other screen. It looks only for Image Preview's own files and settings (recognised by name; at most 8,000 files per place are looked at), shows exactly what a clean-up will remove before it runs, and asks for an extra tick before the Recycle Bin is emptied, because that can't be undone. Leftover temporary files are removed only when they are provably old (by the time in their name and by when they were last touched); anything touched lately is kept. It sends nothing anywhere.
  • Terms of Use gate: nothing opens, scans, restores or tidies up until the Terms are agreed to (a version number and a time, stored on your device). The plugin's internal events are checked against it too, and older copies of the plugin's panel that are still alive in the page cannot react to them. A copy of the panel whose page is gone ignores the events too, and a menu action that nobody answers tells you the panel isn't ready.
  • Least privilege: the plugin's permissions are file, network, variable, input and message (read-only use). It doesn't create, edit or delete characters, personas, chats or presets.

Known limits (honest list)

  • Names that spell a private address (like 192-168-1-1.sslip.io) are refused, but a public name whose own DNS record points at a private address, and a redirect from a public address to a private one, can't be detected by the plugin itself.
  • The lookup services above can see the text you send them. Use Privacy Mode if that matters.
  • Scripts running on the same chat page (for example inside a character card) can trigger the plugin's seven internal events (open the Recycle Bin, reset the floating button, show the Extract picker, start a chat scan, open the Character page, open Page view for a link, open the Storage manager). Until the Terms are agreed to they only ask for the agreement. None of them can make the plugin save, delete or send anything on their own; a scan or an Extract only opens the picker for you to look at, the Character page only shows the open card, Page view only downloads and shows a public page (the site sees your IP address) until you tap Save, and the Storage manager only opens its screen: nothing is removed until you confirm a clean-up.
  • The plugin's saved data lives in Tavo's variable storage. That includes the web addresses of saved images, the names of files you import and the optional Brave and Tavily keys (as plain text). Whether other scripts can read it depends on Tavo. If you use a key, use a free one you can revoke.
  • The games' network block relies on the app's built-in web engine honouring Content-Security-Policy, which current Android WebView does.
  • The headers-only check goes to the link's own server, and some servers don't answer it. The plugin can't cancel a download once Tavo has started it, so a server that hides its size could still use some bandwidth before an oversized file is discarded, and a file that arrives and isn't a picture is deleted at once but has still been downloaded.
  • Thumbnails found by a scan or an extract, and the pictures on the Character page, load from the web, but only the ones you can see, and without a Referer. The site hosting an image can still see your IP address, and a character card can list images on a server its author controls.
  • Because a card's look is now carried over, a card can draw boxes that look like buttons, warnings or system messages. Nothing a card draws can be tapped (only link pills and pictures can), but a drawing can still mislead, as in any chat. The text stays readable, but how a card looks is the card's own choice.
  • Page view reads what a page sends. A site that draws its pictures with its own scripts shows only its preview picture (Page view says so). Image Chest is read from the picture list inside its page, which depends on that site's current page format and may need an update if the site changes it.

For anyone installing

Only install .tpg files from this repository's Releases page and compare the SHA-256 shown beside the file. A copy from anywhere else could have been modified. After updating, close Tavo completely and open it again once.

There aren't any published security advisories