Caption.Ed

ID: aokjakfeailcmenjjgmbcepkfkcfgpie

Could be malicious

Supported Languages

🇺🇸English

Extension Info & Metadata

Status
Removed
Version
1.7.6
Size
0.26 MB
Rating
1.9/5
Reviews
18
Users
10,000
Type
Extension
Updated
Mar 9, 2025
Category
Make_chrome_yours Accessibility
Price
Free
Featured
No
Visibility
Listed
Mature
No
By Google
No
Trusted
No

Publisher Contextual Analysis

Author
CareScribeView Profile
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
Total Extensions
1
Active
0
Obsolete
1
Listed
1
Unlisted
0
Total Users
10,000

Email Change History

1 change
Jan 1, 2025
Domain changed

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.

Item
Type
Severity
Description
webRequest
Permission
Critical
This permission enables the extension to monitor and analyze all web requests made by the browser. Rated Critical because it can observe all network traffic including sensitive data, track browsing behavior, and gather authentication tokens.
tabCapture
Permission
High
This permission captures content and audio from browser tabs. Rated High because it can record sensitive web content, capture form input, and monitor user interactions.
cookies
Permission
High
This permission provides full access to read and modify browser cookies. Rated High because it can steal session tokens, modify authentication cookies, and compromise accounts across websites.
tabs
Permission
Medium
This permission enables tab management and monitoring. Rated Medium because it can track open tabs, access tab metadata, and monitor user browsing patterns.
activeTab
Permission
Medium
This permission grants temporary access to the current tab. Rated Medium because it can access current page content when invoked, though limited to user-initiated actions.
storage
Permission
Medium
This permission allows storing data locally in the browser. Rated Medium because it can persist sensitive user data, track user activities over time, and potentially store malicious payloads.
*://*.echo360.org.uk/
Host
Medium
Host permission — access limited to this URL pattern.
*://*/*.m3u8*
Host
Medium
Host permission — access limited to this URL pattern.
*://*/*.mp4*
Host
Medium
Host permission — access limited to this URL pattern.
*://*.sentry.io/
Host
Medium
Host permission — access limited to this URL pattern.
https://captioned.talk-type.com/
Host
Medium
Host permission — access limited to this URL pattern.
*://*.carescribe.io/
Host
Medium
Host permission — access limited to this URL pattern.

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.

background.bundle.js (Line 179)
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.

background.bundle.js (Line 131)
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.

background.bundle.js (Line 131)
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.

autoLoginScript.bundle.js (Line 23)
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.

background.bundle.js (Line 1)
/* * 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.

background.bundle.js (Line 131)
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.

66547f3131c7524c0a66.js (Line 22)
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

Critical0
High1
Medium5
Low1

Versions scanned

Showing 1 of 20 scanned versions with more than one unique finding. Counts are unique findings that include each version.

Extension VersionCode Review Findings
1.7.67

Files with findings

3 distinct paths — top paths by unique finding count:

  • background.bundle.js5
  • 66547f3131c7524c0a66.js1
  • autoLoginScript.bundle.js1
S.No.
Category
Severity
File
Summary
Found in Version
1Data Exfiltration
high
background.bundle.js (line 179)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 l…
2Code Injection
medium
background.bundle.js (line 131)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 remo…
3Credential Theft
medium
autoLoginScript.bundle.js (line 23)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.lo…
4Network Interception
medium
background.bundle.js (line 131)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; th…
5Obfuscation
medium
background.bundle.js (line 1)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 sour…
6Unauthorized Data Collection
medium
background.bundle.js (line 131)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. Vi…
7Other
low
66547f3131c7524c0a66.js (line 22)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 on…
URLs
98
IPv4
0
IPv6
0

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.

www.w3.org/2000/svghttp://www.w3.org/2000/svg
www.w3.org/1999/xlinkhttp://www.w3.org/1999/xlink
webpack.js.org/configuration/devtool/https://webpack.js.org/configuration/devtool/
webpack.js.org/configuration/mode/https://webpack.js.org/configuration/mode/
fonts.googleapis.com/css2https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&display=swap
captioned.talk-type.com/users/password/newhttp://captioned.talk-type.com/users/password/new
captioned.talk-type.com-http://captioned.talk-type.com/
captioned.talk-type.com-https://captioned.talk-type.com\
o427409.ingest.sentry.io/5679968https://[email protected]/5679968
feross.org-https://feross.org
Showing 1 to 10 of 100 rows
Rows per page:

Gain full insight into all external connections.

Upgrade for full visibility.

No IP addresses found
Showing 1 to 10 of 20 rows
Rows per page:

Code Diff

Compare extension code between any two versions.

0 changed files (scanned top 25 shared text files)

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.