Is Movie maker MovieStudio video editor safe?
MovieStudio is high risk. On install, this extension generates a random 10-char ID stored permanently, attached to every request to redcoolmedia.net across sessions. Dynamic analysis confirmed ID mey7ptac6h in all 7 requests; a synced copy can follow other devices.…
Who publishes itRedcoolMedia - 15 other listings from the same operator, 10 of them carrying a finding
RedcoolMedia - 15 other listings from the same operator, 10 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
15 other listings published from this account, 362k+ users between them. 10 of them carry a finding.
Shared hosts - 2 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Persistent Tracking ID Generated at Install, Sent with Every Request
On install, this extension generates a random 10-char ID stored permanently, attached to every request to redcoolmedia.net across sessions.
Dynamic analysis confirmed ID mey7ptac6h in all 7 requests; a synced copy can follow other devices.
You install the Movie Maker MovieStudio extension.
The extension generates a random 10-character ID and saves it to permanent local storage, then attaches that ID to every subsequent request it makes.
The ID is never shown to you, never explained in the extension UI, and is not mentioned in the Chrome Web Store listing.
Read on every navigation/popup open. usernameredcool is the tracking ID; redcoolonline toggles URL tracking ('1'=on), defaulting to on.
chrome.storage.local key 'redcool_key'{
"redcoolonline": "1",
"usernameredcool": "mey7ptac6h"
}ID generation and storage logic from web.js (exx function) and apar.js (popup script).
// Runs on every navigation via exx().
// Reads the stored tracking object from local storage.
const stored = await chrome.storage.local.get(['redcool_key']);
let profile = stored['redcool_key'] || { usernameredcool: null, redcoolonline: null };
// If no ID exists yet, generate one and save it.
if (!profile.usernameredcool) {
profile.usernameredcool = randomString(10).toLowerCase(); // e.g. 'mey7ptac6h'
}
// Persist the (possibly new) ID back to storage.
await chrome.storage.local.set({ redcool_key: profile });
// The ID is then appended as u= on every fetch to redcoolmedia.net.// This runs when the service worker starts, separate from the local storage copy.
// The chrome.storage.sync API replicates data across all Chrome instances
// where the user is signed in — so the ID may follow the user to other devices.
const synced = await chrome.storage.sync.get('usernameredcool');
if (!synced.usernameredcool) {
const newId = randomString(10).toLowerCase();
await chrome.storage.sync.set({ usernameredcool: newId });
}
// Note: the sync and local IDs may differ (two separate generation paths).| Field | Value | Why it matters | |
|---|---|---|---|
Persistent tracking ID (u=) | mey7ptac6h | A 10-char random string set once at install, reused forever, linking navigation, popup, and file-manager activity to one record. | |
Opt-out flag (redcoolonline) | "1" | Stored with the tracking ID. '1' means URL tracking is on (default). A checkbox can disable it, but the ID still goes out in popup requests. |
- stream.redcoolmedia.net
Receives the ID as u= on URL-tracking (moviemakeru.php) and popup-load (moviemakerb.php) requests. Also handles file uploads/listing for the cloud storage feature.
- www.redcoolmedia.net
Receives the tracking ID as username= on file management operations (filemanager.php). Same developer network as stream.redcoolmedia.net.
Browsing History Sent to redcoolmedia.net on Every Navigation
Every navigation or tab switch, this extension sends the full URL to stream.redcoolmedia.net.
Dynamic analysis captured hex-encoded visited URLs (google.com, amazon.com) with a persistent 10-char ID, undisclosed in the Store listing.
You visit any web page or switch to a tab.
The extension immediately sends the page URL, hex-encoded, to stream.redcoolmedia.net along with a persistent tracking ID.
No user interaction with the extension is required. This fires on every navigation and every tab activation.
The URL is not transmitted in readable form; it is converted to hex before being placed in the l= query parameter. Decoding it reveals the exact page you visited.
https://www.google.com/
| Field | Value | Why it matters | |
|---|---|---|---|
The page URL you visited | l=68747470733a2f2f7777772e676f6f676c652e636f6d2f (decodes to https://www.google.com/) | The exact address of every page you load, hex-encoded in the l= parameter. Decodes directly to the full URL. | |
Persistent tracking ID | u=mey7ptac6h | A 10-char random ID generated on first install and reused on every request, linking all your visits to one profile. | |
Encoding flag | hex=1 | Tells the server that the URL is hex-encoded rather than plain text. |
The navigation listeners and the function that assembles and sends the request (web.js).
// Fires every time the user switches focus to a different tab
chrome.tabs.onActivated.addListener(function(activeInfo) {
xdii(activeInfo.tabId); // → fetch the URL and send it
});
// Fires every time a tab's URL changes or finishes loading
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
xdii(tabId); // → fetch the URL and send it
});function xdii(tabId) {
chrome.tabs.get(tabId, function(tab) {
// Skip if URL contains 'redco' (own domain) or is not http/https
// Skip if URL is the same as the last one seen on this tab
if (!tab.url.includes('redco') && tab.url.startsWith('http') && tab.url !== lastSeenUrl) {
exx(tab.url); // send the URL to remote server
lastSeenUrl = tab.url; // deduplicate same-URL events
}
});
}// Hex-encode the URL and POST it to the tracking endpoint
const response = await fetch(
'https://stream.redcoolmedia.net/api/moviemakeru.php'
+ '?l=' + bin2hex(pageUrl) // visited URL, hex-encoded
+ '&hex=1' // flag: URL is hex-encoded
+ '&u=' + trackingId // persistent 10-char user ID
);
// Side-effect: if server replies with a string containing '302',
// the extension silently redirects the active tab to redcoolmedia.net
if (response.status === 200) {
const body = await response.text();
if (body.includes('302')) {
chrome.tabs.update(activeTabId, {
url: 'https://www.redcoolmedia.net/api/app-moviemaker.php?url=' + bin2hex(pageUrl) + '&u=' + trackingId
});
}
}- stream.redcoolmedia.net
Primary tracking endpoint, run by redcoolmedia.net. Receives the hex-encoded URL and ID on every navigation. Also used for popup content (moviemakerb.php) and file management.
Decodes the hex-encoded URLs from the extension's outbound GET requests. Run this on any value captured from the l= query parameter to recover the plaintext URL.
// moviemaker-url-decode.js
// Decodes hex-encoded URLs from Movie Maker MovieStudio's tracking requests.
// Usage: node moviemaker-url-decode.js <hex-string>
// Example: node moviemaker-url-decode.js 68747470733a2f2f7777772e676f6f676c652e636f6d2f
const hex = process.argv[2];
if (!hex) {
console.error('Usage: node moviemaker-url-decode.js <hex-string>');
process.exit(1);
}
function hex2bin(hex) {
let result = '';
for (let i = 0; i < hex.length; i += 2) {
result += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return result;
}
console.log('Decoded URL:', hex2bin(hex));
- 1Copy the l= parameter from any request to moviemakeru.php (DevTools > Network).
- 2Run: node moviemaker-url-decode.js <hex-value>.
- 3The output is the exact URL visited when the request fired.
Uploaded videos are linked to a persistent RedCoolMedia ID
Choosing or dropping a video file in MovieStudio builds a form upload posted to stream.redcoolmedia.net.
The upload URL includes the same 10-char RedCoolMedia ID stored by the extension, tying uploads to that ID.
You choose or drop a video file in the extension popup.
The popup page labels this area as an MP4 upload control.
The extension posts the selected file to RedCoolMedia with a persistent identifier in the URL.
The same identifier is reused by the file list and download links for stored files.
| Field | Value | Why it matters | |
|---|---|---|---|
Your selected video | sample-video.mp4 (illustrative user-selected file) | The file you choose is the content sent to RedCoolMedia for online editing or storage. | |
Persistent RedCoolMedia ID | qwftbnbgxp | This value lets RedCoolMedia connect later file-management requests to the same extension installation. | |
Upload action | do=upload | The request tells the remote file manager to store the submitted file. | |
Folder path | / | The extension includes the current file-manager folder for where the upload should be placed. |
The popup code that stores the ID, accepts files, and posts uploads
var usernameredcool = "";
(async function(){
var redxda = { usernameredcool: null, redcoolonline: null };
const redcool_key = "redcool_key";
var redcoolonline = "";
let storageResult = await chrome.storage.local.get([redcool_key]);
if (redcool_key in storageResult) {
redxda = storageResult[redcool_key]
}
if ( redxda.usernameredcool ) {
usernameredcool = redxda.usernameredcool;
}
else {
usernameredcool = "" + randomString(10) + "".toLowerCase();
redxda.usernameredcool = usernameredcool;
}
if ( redxda.redcoolonline ) {
redcoolonline = redxda.redcoolonline;
}
else {
redcoolonline = "1";
redxda.redcoolonline = "1";
}
var stox = {};
stox[redcool_key] = redxda;
await chrome.storage.local.set(stox);
//console.log(usernameredcool);
//console.log(redcoolonline);
if ( redcoolonline == "1") {
document.getElementById("redcoolonline").checked = true;
}
else {
document.getElementById("redcoolonline").checked = false;
}
var xhrbb = new XMLHttpRequest();
xhrbb.open('GET', 'https://stream.redcoolmedia.net/api/moviemakerb.php?u=' + usernameredcool, true);
xhrbb.onload = function (e) {
if (xhrbb.readyState === 4) {
if (xhrbb.status === 200) {
//console.log(xhrbb.responseText);
var response1 = xhrbb.responseText;
internetx = document.getElementById('internetx');
internetx.innerHTML = "<tr><td>No mp4 videos in this webpage</td><td>-</td><td>-</td><td>-</td></tr>";
internetx.innerHTML = response1;
} else {
internetx = document.getElementById('internetx');
internetx.innerHTML = "<tr><td>No mp4 videos in this webpage</td><td>-</td><td>-</td><td>-</td></tr>";
}
}
};
xhrbb.onerror = function (e) {
internetx = document.getElementById('internetx');
internetx.innerHTML = "<tr><td>No mp4 videos in this webpage</td><td>-</td><td>-</td><td>-</td></tr>";
};
xhrbb.send();
$.fn.tablesorter = function() {
var $table = this;
this.find('th').click(function() {
var idx = $(this).index();
var direction = $(this).hasClass('sort_asc');
$table.tablesortby(idx,direction);
});
return this;
};
$.fn.tablesortby = function(idx,direction) {
var $rows = this.find('tbody tr');
function elementToVal(a) {
var $a_elem = $(a).find('td:nth-child('+(idx+1)+')');
var a_val = $a_elem.attr('data-sort') || $a_elem.text();
return (a_val == parseInt(a_val) ? parseInt(a_val) : a_val);
}
$rows.sort(function(a,b){
var a_val = elementToVal(a), b_val = elementToVal(b);
return (a_val > b_val ? 1 : (a_val == b_val ? 0 : -1)) * (direction ? 1 : -1);
})
this.find('th').removeClass('sort_asc sort_desc');
$(this).find('thead th:nth-child('+(idx+1)+')').addClass(direction ? 'sort_desc' : 'sort_asc');
for(var i =0;i<$rows.length;i++)
this.append($rows[i]);
this.settablesortmarkers();
return this;
}
$.fn.retablesort = function() {
var $e = this.find('thead th.sort_asc, thead th.sort_desc');
if($e.length)
this.tablesortby($e.index(), $e.hasClass('sort_desc') );
return this;
}
$.fn.settablesortmarkers = function() {
this.find('thead th span.indicator').remove();
this.find('thead th.sort_asc').append('<span class="indicator">↓<span>');
this.find('thead th.sort_desc').append('<span class="indicator">↑<span>');
return this;
}
$("#redcoolonline").click(async function() {
if (document.getElementById('redcoolonline').checked) {
redxda.usernameredcool = usernameredcool;
redxda.redcoolonline = "1";
var stox = {};
stox[redcool_key] = redxda;
await chrome.storage.local.set(stox);
}
else {
redxda.usernameredcool = usernameredcool;
redxda.redcoolonline = "0";
var stox = {};
stox[redcool_key] = redxda;
await chrome.storage.local.set(stox);
}
return false;
});
document.getElementById("newmovie").href = "https://www.redcoolmedia.net/PopcornEditor/moviemaker.html";
var XSRF = (document.cookie.match('(^|; )_sfm_xsrf=([^;]*)')||0)[2];
var MAX_UPLOAD_SIZE = 1100000000;
var $tbody = $('#listx');
$(window).on('hashchange',list).trigger('hashchange');
$('#table').tablesorter();
$('#table').on('click','.delete',function(data) {
$.post('https://www.redcoolmedia.net/onlineeditor/filemanager.php?service=&username='+ usernameredcool,{'do':'delete',file:$(this).attr('data-file'),xsrf:XSRF},function(response){
list();
},'json');
return false;
});
$('#mkdir').submit(function(e) {
var hashval = decodeURIComponent(window.location.hash.substr(1)),
$dir = $(this).find('[name=name]');
e.preventDefault();
$dir.val().length && $.post('?',{'do':'mkdir',name:$dir.val(),xsrf:XSRF,file:hashval},function(data){
list();
},'json');
$dir.val('');
return false;
});
$('#file_drop_target').on('dragover',function(){
$(this).addClass('drag_over');
return false;
}).on('dragend',function(){
$(this).removeClass('drag_over');
return false;
}).on('drop',function(e){
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
$.each(files,function(k,file) {
uploadMovieFile(file);
});
$(this).removeClass('drag_over');
});
$('input[type=file]').change(function(e) {
e.preventDefault();
$.each(this.files,function(k,file) {
uploadMovieFile(file);
});
});
function uploadMovieFile(file) {
//if (file.name.toLowerCase().indexOf(".mp4") < 0) {
// alert("This file is not a movie file");
// return;
//}
var folder = decodeURIComponent(window.location.hash.substr(1));
if(file.size > MAX_UPLOAD_SIZE) {
var $error_row = renderFileSizeErrorRow(file,folder);
$('#upload_progress').empty();
$('#upload_progress').append($error_row);
window.setTimeout(function(){$error_row.fadeOut();},5000);
return false;
}
var $row = renderFileUploadRow(file,folder);
$('#upload_progress').empty();
$('#upload_progress').append($row);
var fd = new FormData();
fd.append('file_data',file);
fd.append('file',folder);
fd.append('xsrf',XSRF);
fd.append('do','upload');
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://stream.redcoolmedia.net/onlineeditor/filemanager.php?service=&do=upload&username='+ usernameredcool);
xhr.onload = function() {
$row.remove();
list();
};
xhr.upload.onprogress = function(e){
if(e.lengthComputable) {
$row.find('.progress').css('width',(e.loaded/e.total*100 | 0)+'%' );
}
};
xhr.send(fd);
}
function renderFileUploadRow(file,folder) {
return $row = $('<div/>')
.append( $('<span class="fileuploadname" />').text( (folder ? folder+'/':'')+file.name))
.append( $('<div class="progress_track"><div class="progress"></div></div>') )
.append( $('<span class="size" />').text(formatFileSize(file.size)) )
};
function renderFileSizeErrorRow(file,folder) {
return $row = $('<div class="error" />')
.append( $('<span class="fileuploadname" />').text( 'Error: ' + (folder ? folder+'/':'')+file.name))
.append( $('<span/>').html(' file size - <b>' + formatFileSize(file.size) + '</b>'
+' exceeds max upload size of <b>' + formatFileSize(MAX_UPLOAD_SIZE) + '</b>') );
}
function list() {
$.get('https://www.redcoolmedia.net/onlineeditor/filemanager.php?service=&do=list&username='+ usernameredcool,function(data) {
$tbody.empty();
$('#breadcrumb').empty().html("");
if(data.success) {
var html = "";
var strxx = JSON.stringify(data.results);
if ( strxx.indexOf('mtime') < 0 )
{
$tbody.append('<tr><td class="empty" colspan=5>This folder is empty</td></tr>');
}
$('body').removeClass('no_write');
var loqueda = strxx;
while ( loqueda.indexOf('mtime') != -1 )
{
var part1 = loqueda.substring(
loqueda.lastIndexOf("\"mtime\""),
loqueda.lastIndexOf("}")
);
var pathx = part1.substring(
part1.lastIndexOf("\"path\":\""),
part1.lastIndexOf("\",\"is_dir\"")
);
pathx = pathx.replace('"path":"', "");
var namex = part1.substring(
part1.lastIndexOf("\"name\":\""),
part1.lastIndexOf("\",\"path\":\"")
);
namex = namex.replace('"name":"', "");
var sizex = part1.substring(
part1.lastIndexOf("\"size\":"),
part1.lastIndexOf(",\"name\":\"")
);
sizex = sizex.replace('"size":', "");
var is_dirx = part1.substring(
part1.lastIndexOf("\"is_dir\":"),
part1.lastIndexOf(",\"is_deleteable\"")
);
is_dirx = is_dirx.replace('"is_dir":', "");
var pathxtemp = pathx.replace("/var/www/html/weboffice/", "");
var filename = Math.floor(Math.random() * 10000) + Math.floor(Math.random() * 10000);
//var urly = "https://www.redcoolmedia.net/onlineeditor/preeditpdflocal.php?service=" + service + "&username="+ username +"&filename="+filename+"&filepath=" + pathxtemp;
var urix = "https://www.redcoolmedia.net/" + pathxtemp + "?service=";
var mediaurl = encodeURIComponent(urix);
var urly = "javascript:void(0)";
var urlxx = "https://www.redcoolmedia.net/PopcornEditor/moviemaker.html?mediaurl=" + encodeURIComponent(urix);
//alert(urlxx);
var $urlymp4 = $('<a/>').attr('target','_blank').attr('href',urlxx)
.addClass('name').text('Edit Movie');;
var $link = $('<a class="name" />')
.attr('href', urly)
.attr('target', "_blank")
.text(namex);
var allow_direct_link = false;
//$link.css('pointer-events','none');
var $dl_link = $('<a/>').attr('href','https://www.redcoolmedia.net/onlineeditor/filemanager.php?service=&do=download&username='+ usernameredcool + '&file='+ pathx)
.addClass('download').text('download');
var $delete_link = $('<a href="#" />').attr('data-file',pathx).addClass('delete').text('delete');
var perms = [];
perms.push('read');
perms.push('write');
var $html = $('<tr />')
.append( $('<td class="first" />').append($link) )
.append( $('<td/>').attr('data-sort', is_dirx ? -1 : sizex)
.html($('<span class="size" />').text(formatFileSize(sizex))) )
//.append( $('<td/>').attr('data-sort',data.mtime).text(formatTimestamp(data.mtime)) )
.append( $('<td/>').text(perms.join('+')) )
.append( $('<td/>').append( $delete_link )
.append( $urlymp4 ));
$tbody.append($html);
loqueda = loqueda.replace(part1, "");
}
} else {
console.warn(data.error.msg);
}
//$('#table').retablesort();
},'json');
}
function renderBreadcrumbs(path) {
var base = "",
$html = $('<div/>').append( $('<a href=#>Home</a></div>') );
$.each(path.split('%2F'),function(k,v){
if(v) {
var v_as_text = decodeURIComponent(v);
$html.append( $('<span/>').text(' ?~V? ') )
.append( $('<a/>').attr('href','#'+base+v).text(v_as_text) );
base += v + '%2F';
}
});
return $html;
}
function formatTimestamp(unix_timestamp) {
var m = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var d = new Date(unix_timestamp*1000);
return [m[d.getMonth()],' ',d.getDate(),', ',d.getFullYear()," ",
(d.getHours() % 12 || 12),":",(d.getMinutes() < 10 ? '0' : '')+d.getMinutes(),
" ",d.getHours() >= 12 ? 'PM' : 'AM'].join('');
}
function formatFileSize(bytes) {
var s = ['bytes', 'KB','MB','GB','TB','PB','EB'];
for(var pos = 0;bytes >= 1000; pos++,bytes /= 1024);
var d = Math.round(bytes*10);
return pos ? [parseInt(d/10),".",d%10," ",s[pos]].join('') : bytes + ' bytes';
}
})(jQuery);
function randomS(len, charSet) {
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz,randomPoz+1);
}
return randomString;
}
function randomString(len, charSet) {
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz,randomPoz+1);
}
return randomString;
}
- stream.redcoolmedia.net
Receives the POST upload request for the selected video file with the persistent username value in the URL.
- www.redcoolmedia.net
Handles related file-manager list, delete, download, and editor links that also use the same username value.
+1 more finding not shown
What it can do
Permissions this extension asks for, as declared in version 1.4.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Store data in your browser
storage
See the address and title of every tab you have open
tabs