brave-real-browser-mcp-server 2.27.28 → 2.27.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/handlers/advanced-tools.js +530 -1
- package/package.json +2 -2
|
@@ -1048,6 +1048,319 @@ function decodeRot13(input) {
|
|
|
1048
1048
|
function reverseString(input) {
|
|
1049
1049
|
return input.split('').reverse().join('');
|
|
1050
1050
|
}
|
|
1051
|
+
// ============================================================
|
|
1052
|
+
// ULTRA ADVANCED DECODE FUNCTIONS
|
|
1053
|
+
// ============================================================
|
|
1054
|
+
/**
|
|
1055
|
+
* Common streaming site encryption keys database
|
|
1056
|
+
* Used for tryCommonKeys feature
|
|
1057
|
+
*/
|
|
1058
|
+
const COMMON_STREAMING_KEYS = {
|
|
1059
|
+
hubstream: {
|
|
1060
|
+
keys: ['kiemtienmua911ca', '0123456789abcdef'],
|
|
1061
|
+
ivs: ['1234567890oiuytr', '0123456789abcdef'],
|
|
1062
|
+
},
|
|
1063
|
+
vidstack: {
|
|
1064
|
+
keys: ['streamkey12345ab', 'vidstack1234567'],
|
|
1065
|
+
ivs: ['abcdef0123456789', '1234567890abcdef'],
|
|
1066
|
+
},
|
|
1067
|
+
player: {
|
|
1068
|
+
keys: ['playerkey123456', 'defaultkey12345'],
|
|
1069
|
+
ivs: ['playeriv12345678', 'defaultiv1234567'],
|
|
1070
|
+
},
|
|
1071
|
+
generic: {
|
|
1072
|
+
keys: ['aaaaaaaaaaaaaaaa', '0000000000000000', '1111111111111111'],
|
|
1073
|
+
ivs: ['0000000000000000', '1111111111111111', 'aaaaaaaaaaaaaaaa'],
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
/**
|
|
1077
|
+
* XOR Cipher Decryption
|
|
1078
|
+
* XOR each byte with key (repeating if key is shorter)
|
|
1079
|
+
*/
|
|
1080
|
+
function decryptXOR(input, key, inputFormat = 'hex') {
|
|
1081
|
+
try {
|
|
1082
|
+
let inputBytes;
|
|
1083
|
+
// Parse input based on format
|
|
1084
|
+
if (inputFormat === 'hex') {
|
|
1085
|
+
inputBytes = Buffer.from(input.replace(/\s/g, ''), 'hex');
|
|
1086
|
+
}
|
|
1087
|
+
else if (inputFormat === 'base64') {
|
|
1088
|
+
inputBytes = Buffer.from(input, 'base64');
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
inputBytes = Buffer.from(input, 'utf-8');
|
|
1092
|
+
}
|
|
1093
|
+
// Parse key
|
|
1094
|
+
let keyBytes;
|
|
1095
|
+
if (typeof key === 'number') {
|
|
1096
|
+
keyBytes = Buffer.from([key & 0xFF]);
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
keyBytes = Buffer.from(key, 'utf-8');
|
|
1100
|
+
}
|
|
1101
|
+
// XOR decrypt
|
|
1102
|
+
const result = Buffer.alloc(inputBytes.length);
|
|
1103
|
+
for (let i = 0; i < inputBytes.length; i++) {
|
|
1104
|
+
result[i] = inputBytes[i] ^ keyBytes[i % keyBytes.length];
|
|
1105
|
+
}
|
|
1106
|
+
return result.toString('utf-8');
|
|
1107
|
+
}
|
|
1108
|
+
catch (e) {
|
|
1109
|
+
return input;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* RC4 Stream Cipher Decryption
|
|
1114
|
+
* Used by some streaming sites for lightweight encryption
|
|
1115
|
+
*/
|
|
1116
|
+
function decryptRC4(inputHex, key) {
|
|
1117
|
+
try {
|
|
1118
|
+
const input = Buffer.from(inputHex.replace(/\s/g, ''), 'hex');
|
|
1119
|
+
const keyBytes = Buffer.from(key, 'utf-8');
|
|
1120
|
+
// RC4 Key Scheduling Algorithm (KSA)
|
|
1121
|
+
const S = new Uint8Array(256);
|
|
1122
|
+
for (let i = 0; i < 256; i++)
|
|
1123
|
+
S[i] = i;
|
|
1124
|
+
let j = 0;
|
|
1125
|
+
for (let i = 0; i < 256; i++) {
|
|
1126
|
+
j = (j + S[i] + keyBytes[i % keyBytes.length]) & 0xFF;
|
|
1127
|
+
[S[i], S[j]] = [S[j], S[i]]; // Swap
|
|
1128
|
+
}
|
|
1129
|
+
// RC4 Pseudo-Random Generation Algorithm (PRGA)
|
|
1130
|
+
const result = Buffer.alloc(input.length);
|
|
1131
|
+
let i = 0;
|
|
1132
|
+
j = 0;
|
|
1133
|
+
for (let k = 0; k < input.length; k++) {
|
|
1134
|
+
i = (i + 1) & 0xFF;
|
|
1135
|
+
j = (j + S[i]) & 0xFF;
|
|
1136
|
+
[S[i], S[j]] = [S[j], S[i]]; // Swap
|
|
1137
|
+
const keyByte = S[(S[i] + S[j]) & 0xFF];
|
|
1138
|
+
result[k] = input[k] ^ keyByte;
|
|
1139
|
+
}
|
|
1140
|
+
return result.toString('utf-8');
|
|
1141
|
+
}
|
|
1142
|
+
catch (e) {
|
|
1143
|
+
return inputHex;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* AES-256-CBC Decryption
|
|
1148
|
+
* For sites using stronger 256-bit encryption
|
|
1149
|
+
*/
|
|
1150
|
+
function decryptAES256(inputHex, key, iv) {
|
|
1151
|
+
try {
|
|
1152
|
+
const crypto = require('crypto');
|
|
1153
|
+
const inputBuffer = Buffer.from(inputHex, 'hex');
|
|
1154
|
+
const keyBuffer = Buffer.from(key, 'utf-8'); // 32 bytes for AES-256
|
|
1155
|
+
const ivBuffer = Buffer.from(iv, 'utf-8'); // 16 bytes
|
|
1156
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuffer, ivBuffer);
|
|
1157
|
+
decipher.setAutoPadding(true);
|
|
1158
|
+
let decrypted = decipher.update(inputBuffer);
|
|
1159
|
+
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
1160
|
+
return decrypted.toString('utf-8');
|
|
1161
|
+
}
|
|
1162
|
+
catch (e) {
|
|
1163
|
+
throw new Error(`AES-256 decryption failed: ${e}`);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* AES-GCM Decryption (Authenticated Encryption)
|
|
1168
|
+
* More secure mode used by modern sites
|
|
1169
|
+
*/
|
|
1170
|
+
function decryptAESGCM(inputHex, key, iv, authTag) {
|
|
1171
|
+
try {
|
|
1172
|
+
const crypto = require('crypto');
|
|
1173
|
+
const inputBuffer = Buffer.from(inputHex, 'hex');
|
|
1174
|
+
const keyBuffer = Buffer.from(key, 'utf-8');
|
|
1175
|
+
const ivBuffer = Buffer.from(iv, 'utf-8');
|
|
1176
|
+
const decipher = crypto.createDecipheriv('aes-128-gcm', keyBuffer, ivBuffer);
|
|
1177
|
+
if (authTag) {
|
|
1178
|
+
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
|
|
1179
|
+
}
|
|
1180
|
+
let decrypted = decipher.update(inputBuffer);
|
|
1181
|
+
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
1182
|
+
return decrypted.toString('utf-8');
|
|
1183
|
+
}
|
|
1184
|
+
catch (e) {
|
|
1185
|
+
throw new Error(`AES-GCM decryption failed: ${e}`);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Auto-Detect Encryption Type
|
|
1190
|
+
* Analyzes input and detects the most likely encryption/encoding
|
|
1191
|
+
*/
|
|
1192
|
+
function autoDetectEncryption(input) {
|
|
1193
|
+
// Check for JSFuck (all symbols)
|
|
1194
|
+
if (/^[\[\]\(\)\+\!\s]+$/.test(input) && input.length > 50) {
|
|
1195
|
+
return { type: 'jsfuck', confidence: 95, details: 'JSFuck obfuscated code' };
|
|
1196
|
+
}
|
|
1197
|
+
// Check for Packed JS
|
|
1198
|
+
if (input.includes('function(p,a,c,k,e,d)') || input.includes("eval(function(p,a,c,k,e")) {
|
|
1199
|
+
return { type: 'packedJs', confidence: 98, details: 'P.A.C.K.E.R. packed JavaScript' };
|
|
1200
|
+
}
|
|
1201
|
+
// Check for Hex (only hex chars and even length)
|
|
1202
|
+
if (/^[0-9a-fA-F]+$/.test(input) && input.length % 2 === 0 && input.length >= 32) {
|
|
1203
|
+
return { type: 'hex', confidence: 85, details: 'Hex-encoded data' };
|
|
1204
|
+
}
|
|
1205
|
+
// Check for Base64
|
|
1206
|
+
if (/^[A-Za-z0-9+/=_-]+$/.test(input) && input.length >= 4) {
|
|
1207
|
+
try {
|
|
1208
|
+
const decoded = decodeBase64(input);
|
|
1209
|
+
if (decoded && /^[\x20-\x7E\s]+$/.test(decoded)) {
|
|
1210
|
+
return { type: 'base64', confidence: 90, details: 'Base64 encoded' };
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
catch { }
|
|
1214
|
+
}
|
|
1215
|
+
// Check for ROT13 (common ROT13 patterns)
|
|
1216
|
+
const rot13Decoded = decodeRot13(input);
|
|
1217
|
+
if (rot13Decoded.startsWith('http') || rot13Decoded.includes('://')) {
|
|
1218
|
+
return { type: 'rot13', confidence: 85, details: 'ROT13 encoded URL' };
|
|
1219
|
+
}
|
|
1220
|
+
// Check for AES (hex string of typical AES block sizes)
|
|
1221
|
+
if (/^[0-9a-fA-F]+$/.test(input) && (input.length === 32 || input.length === 64 || input.length % 32 === 0)) {
|
|
1222
|
+
return { type: 'aes', confidence: 70, details: 'Possibly AES encrypted' };
|
|
1223
|
+
}
|
|
1224
|
+
return { type: 'unknown', confidence: 0, details: 'Could not detect encryption type' };
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Extract URLs from decoded content
|
|
1228
|
+
* Finds http/https URLs in any text
|
|
1229
|
+
*/
|
|
1230
|
+
function extractUrlsFromText(text) {
|
|
1231
|
+
const urlPatterns = [
|
|
1232
|
+
/https?:\/\/[^\s"'<>\\]+\.m3u8[^\s"'<>\\]*/gi,
|
|
1233
|
+
/https?:\/\/[^\s"'<>\\]+\.mp4[^\s"'<>\\]*/gi,
|
|
1234
|
+
/https?:\/\/[^\s"'<>\\]+\.mp3[^\s"'<>\\]*/gi,
|
|
1235
|
+
/https?:\/\/[^\s"'<>\\]+/gi,
|
|
1236
|
+
];
|
|
1237
|
+
const urls = [];
|
|
1238
|
+
for (const pattern of urlPatterns) {
|
|
1239
|
+
let match;
|
|
1240
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
1241
|
+
const url = match[0].replace(/['",;}\]]+$/, ''); // Clean trailing chars
|
|
1242
|
+
if (!urls.includes(url) && url.length > 10) {
|
|
1243
|
+
urls.push(url);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return [...new Set(urls)]; // Remove duplicates
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Recursive Decode - Keep decoding until URL found or max depth reached
|
|
1251
|
+
*/
|
|
1252
|
+
function recursiveDecode(input, maxDepth = 10) {
|
|
1253
|
+
let current = input;
|
|
1254
|
+
const layers = [];
|
|
1255
|
+
let depth = 0;
|
|
1256
|
+
while (depth < maxDepth) {
|
|
1257
|
+
const detection = autoDetectEncryption(current);
|
|
1258
|
+
if (detection.type === 'unknown' || detection.confidence < 50) {
|
|
1259
|
+
break;
|
|
1260
|
+
}
|
|
1261
|
+
let decoded;
|
|
1262
|
+
try {
|
|
1263
|
+
switch (detection.type) {
|
|
1264
|
+
case 'base64':
|
|
1265
|
+
decoded = decodeBase64(current);
|
|
1266
|
+
break;
|
|
1267
|
+
case 'hex':
|
|
1268
|
+
decoded = decodeHex(current);
|
|
1269
|
+
break;
|
|
1270
|
+
case 'rot13':
|
|
1271
|
+
decoded = decodeRot13(current);
|
|
1272
|
+
break;
|
|
1273
|
+
case 'packedJs':
|
|
1274
|
+
decoded = unpackJs(current);
|
|
1275
|
+
break;
|
|
1276
|
+
default:
|
|
1277
|
+
decoded = current;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
catch {
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
if (decoded === current || !decoded) {
|
|
1284
|
+
break;
|
|
1285
|
+
}
|
|
1286
|
+
layers.push(detection.type);
|
|
1287
|
+
current = decoded;
|
|
1288
|
+
depth++;
|
|
1289
|
+
// Check if we found URLs
|
|
1290
|
+
const urls = extractUrlsFromText(current);
|
|
1291
|
+
if (urls.some(u => u.includes('.m3u8') || u.includes('.mp4') || u.includes('stream'))) {
|
|
1292
|
+
return { decoded: current, layers, depth, foundUrls: urls };
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return { decoded: current, layers, depth, foundUrls: extractUrlsFromText(current) };
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* JSFuck Decoder
|
|
1299
|
+
* Decodes JSFuck obfuscated code (uses only []()!+)
|
|
1300
|
+
*/
|
|
1301
|
+
function decodeJSFuck(input) {
|
|
1302
|
+
try {
|
|
1303
|
+
// JSFuck can only be safely decoded by evaluating it
|
|
1304
|
+
// We use a sandboxed approach
|
|
1305
|
+
if (!/^[\[\]\(\)\+\!\s]+$/.test(input)) {
|
|
1306
|
+
return input; // Not JSFuck
|
|
1307
|
+
}
|
|
1308
|
+
// Common JSFuck patterns that decode to strings
|
|
1309
|
+
const patterns = {
|
|
1310
|
+
'[]': '',
|
|
1311
|
+
'![]': 'false',
|
|
1312
|
+
'!![]': 'true',
|
|
1313
|
+
'+[]': '0',
|
|
1314
|
+
'+!![]': '1',
|
|
1315
|
+
};
|
|
1316
|
+
// This is a simplified decoder - full JSFuck requires eval
|
|
1317
|
+
// Return indication that it's JSFuck for browser-side decoding
|
|
1318
|
+
return `[JSFuck detected - length: ${input.length} - use execute_js with eval() for full decode]`;
|
|
1319
|
+
}
|
|
1320
|
+
catch {
|
|
1321
|
+
return input;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Try decryption with common streaming site keys
|
|
1326
|
+
*/
|
|
1327
|
+
async function tryCommonKeys(inputHex, page) {
|
|
1328
|
+
for (const [site, config] of Object.entries(COMMON_STREAMING_KEYS)) {
|
|
1329
|
+
for (const key of config.keys) {
|
|
1330
|
+
for (const iv of config.ivs) {
|
|
1331
|
+
try {
|
|
1332
|
+
const result = await page.evaluate(async (params) => {
|
|
1333
|
+
try {
|
|
1334
|
+
const hexToBytes = (hex) => {
|
|
1335
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
1336
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
1337
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
1338
|
+
}
|
|
1339
|
+
return bytes;
|
|
1340
|
+
};
|
|
1341
|
+
const keyData = new TextEncoder().encode(params.key);
|
|
1342
|
+
const ivData = new TextEncoder().encode(params.iv);
|
|
1343
|
+
const encryptedData = hexToBytes(params.input);
|
|
1344
|
+
const cryptoKey = await crypto.subtle.importKey("raw", keyData, "AES-CBC", false, ["decrypt"]);
|
|
1345
|
+
const decrypted = await crypto.subtle.decrypt({ name: "AES-CBC", iv: ivData }, cryptoKey, encryptedData);
|
|
1346
|
+
return new TextDecoder().decode(decrypted);
|
|
1347
|
+
}
|
|
1348
|
+
catch {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
}, { input: inputHex, key, iv });
|
|
1352
|
+
if (result && (result.includes('http') || result.includes('source'))) {
|
|
1353
|
+
return { success: true, key, iv, decrypted: result, site };
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
catch {
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
return { success: false };
|
|
1363
|
+
}
|
|
1051
1364
|
/**
|
|
1052
1365
|
* AES-CBC decryption for encrypted streaming responses
|
|
1053
1366
|
* Used by sites like hubstream.art that encrypt video URLs
|
|
@@ -1249,6 +1562,9 @@ function applyDecodeLayer(input, layer) {
|
|
|
1249
1562
|
case 'rot13': return decodeRot13(input);
|
|
1250
1563
|
case 'reverse': return reverseString(input);
|
|
1251
1564
|
case 'unpackJs': return unpackJs(input);
|
|
1565
|
+
case 'xor': return decryptXOR(input, 0xFF, 'text'); // Default XOR with 0xFF
|
|
1566
|
+
case 'rc4': return input; // RC4 requires key - use decryptRC4 directly
|
|
1567
|
+
case 'jsfuck': return decodeJSFuck(input);
|
|
1252
1568
|
default: return input;
|
|
1253
1569
|
}
|
|
1254
1570
|
}
|
|
@@ -1531,7 +1847,220 @@ export async function handleExtractJson(page, args) {
|
|
|
1531
1847
|
}
|
|
1532
1848
|
}
|
|
1533
1849
|
// ============================================================
|
|
1534
|
-
// 6.
|
|
1850
|
+
// 6. ULTRA ADVANCED DECODE (NEW!)
|
|
1851
|
+
// ============================================================
|
|
1852
|
+
if (args.ultraDecode) {
|
|
1853
|
+
try {
|
|
1854
|
+
let input = args.ultraDecode.input || '';
|
|
1855
|
+
// Extract from page if requested
|
|
1856
|
+
if (args.ultraDecode.extractFromPage) {
|
|
1857
|
+
input = await page.evaluate((selector) => {
|
|
1858
|
+
if (selector) {
|
|
1859
|
+
const el = document.querySelector(selector);
|
|
1860
|
+
return el?.textContent || '';
|
|
1861
|
+
}
|
|
1862
|
+
return document.body?.textContent || '';
|
|
1863
|
+
}, args.ultraDecode.pageSelector);
|
|
1864
|
+
}
|
|
1865
|
+
if (input) {
|
|
1866
|
+
// Auto-detect mode
|
|
1867
|
+
if (args.ultraDecode.autoDetect) {
|
|
1868
|
+
const detection = autoDetectEncryption(input);
|
|
1869
|
+
decoded = {
|
|
1870
|
+
source: 'ultraDecode-autoDetect',
|
|
1871
|
+
detectedType: detection.type,
|
|
1872
|
+
confidence: detection.confidence,
|
|
1873
|
+
details: detection.details,
|
|
1874
|
+
};
|
|
1875
|
+
// Try to decode based on detection
|
|
1876
|
+
if (detection.confidence >= 70) {
|
|
1877
|
+
const recursiveResult = recursiveDecode(input, args.ultraDecode.maxDepth || 10);
|
|
1878
|
+
decoded = {
|
|
1879
|
+
...decoded,
|
|
1880
|
+
decoded: recursiveResult.decoded,
|
|
1881
|
+
layers: recursiveResult.layers,
|
|
1882
|
+
depth: recursiveResult.depth,
|
|
1883
|
+
foundUrls: recursiveResult.foundUrls,
|
|
1884
|
+
streamUrls: recursiveResult.foundUrls.filter(u => u.includes('.m3u8') || u.includes('.mp4') || u.includes('stream')),
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
data.push(decoded);
|
|
1888
|
+
}
|
|
1889
|
+
// Recursive decode mode
|
|
1890
|
+
else if (args.ultraDecode.recursive) {
|
|
1891
|
+
const result = recursiveDecode(input, args.ultraDecode.maxDepth || 10);
|
|
1892
|
+
decoded = {
|
|
1893
|
+
source: 'ultraDecode-recursive',
|
|
1894
|
+
decoded: result.decoded,
|
|
1895
|
+
layers: result.layers,
|
|
1896
|
+
depth: result.depth,
|
|
1897
|
+
foundUrls: result.foundUrls,
|
|
1898
|
+
streamUrls: result.foundUrls.filter(u => u.includes('.m3u8') || u.includes('.mp4') || u.includes('stream')),
|
|
1899
|
+
};
|
|
1900
|
+
data.push(decoded);
|
|
1901
|
+
}
|
|
1902
|
+
// Try common keys mode
|
|
1903
|
+
else if (args.ultraDecode.tryCommonKeys) {
|
|
1904
|
+
const result = await tryCommonKeys(input, page);
|
|
1905
|
+
if (result.success) {
|
|
1906
|
+
decoded = {
|
|
1907
|
+
source: 'ultraDecode-commonKeys',
|
|
1908
|
+
success: true,
|
|
1909
|
+
matchedSite: result.site,
|
|
1910
|
+
key: result.key,
|
|
1911
|
+
iv: result.iv,
|
|
1912
|
+
decrypted: result.decrypted,
|
|
1913
|
+
extractedUrls: extractUrlsFromText(result.decrypted || ''),
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
else {
|
|
1917
|
+
decoded = {
|
|
1918
|
+
source: 'ultraDecode-commonKeys',
|
|
1919
|
+
success: false,
|
|
1920
|
+
message: 'No matching key found in common keys database',
|
|
1921
|
+
triedSites: Object.keys(COMMON_STREAMING_KEYS),
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
data.push(decoded);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
catch (e) {
|
|
1929
|
+
decoded = { source: 'ultraDecode', error: String(e) };
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
// ============================================================
|
|
1933
|
+
// 7. XOR CIPHER DECRYPTION
|
|
1934
|
+
// ============================================================
|
|
1935
|
+
if (args.decryptXOR) {
|
|
1936
|
+
try {
|
|
1937
|
+
const result = decryptXOR(args.decryptXOR.input, args.decryptXOR.key, args.decryptXOR.inputFormat || 'hex');
|
|
1938
|
+
decoded = {
|
|
1939
|
+
source: 'decryptXOR',
|
|
1940
|
+
input: args.decryptXOR.input.substring(0, 50) + '...',
|
|
1941
|
+
key: String(args.decryptXOR.key),
|
|
1942
|
+
format: args.decryptXOR.inputFormat || 'hex',
|
|
1943
|
+
decrypted: result,
|
|
1944
|
+
foundUrls: extractUrlsFromText(result),
|
|
1945
|
+
};
|
|
1946
|
+
data.push(decoded);
|
|
1947
|
+
}
|
|
1948
|
+
catch (e) {
|
|
1949
|
+
decoded = { source: 'decryptXOR', error: String(e) };
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
// ============================================================
|
|
1953
|
+
// 8. RC4 STREAM CIPHER DECRYPTION
|
|
1954
|
+
// ============================================================
|
|
1955
|
+
if (args.decryptRC4) {
|
|
1956
|
+
try {
|
|
1957
|
+
const result = decryptRC4(args.decryptRC4.input, args.decryptRC4.key);
|
|
1958
|
+
decoded = {
|
|
1959
|
+
source: 'decryptRC4',
|
|
1960
|
+
input: args.decryptRC4.input.substring(0, 50) + '...',
|
|
1961
|
+
key: args.decryptRC4.key,
|
|
1962
|
+
decrypted: result,
|
|
1963
|
+
foundUrls: extractUrlsFromText(result),
|
|
1964
|
+
};
|
|
1965
|
+
data.push(decoded);
|
|
1966
|
+
}
|
|
1967
|
+
catch (e) {
|
|
1968
|
+
decoded = { source: 'decryptRC4', error: String(e) };
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
// ============================================================
|
|
1972
|
+
// 9. OBFUSCATED JS DECODER (eval interceptor)
|
|
1973
|
+
// ============================================================
|
|
1974
|
+
if (args.decodeObfuscatedJs) {
|
|
1975
|
+
try {
|
|
1976
|
+
// Intercept eval calls if requested
|
|
1977
|
+
const obfuscatedResults = await page.evaluate((options) => {
|
|
1978
|
+
const results = [];
|
|
1979
|
+
// Find JSFuck or heavily obfuscated scripts
|
|
1980
|
+
const scripts = options.selector
|
|
1981
|
+
? document.querySelectorAll(options.selector)
|
|
1982
|
+
: document.querySelectorAll('script');
|
|
1983
|
+
scripts.forEach((script) => {
|
|
1984
|
+
const content = script.textContent || '';
|
|
1985
|
+
// Detect JSFuck
|
|
1986
|
+
if (/^[\[\]\(\)\+\!\s]+$/.test(content.trim()) && content.length > 50) {
|
|
1987
|
+
results.push({ type: 'jsfuck', content: content.substring(0, 500) });
|
|
1988
|
+
}
|
|
1989
|
+
// Detect eval-based obfuscation
|
|
1990
|
+
if (content.includes('eval(') || content.includes('Function(')) {
|
|
1991
|
+
results.push({ type: 'eval-obfuscated', content: content.substring(0, 1000) });
|
|
1992
|
+
}
|
|
1993
|
+
// Detect hex-encoded strings
|
|
1994
|
+
const hexMatches = content.match(/\\x[0-9a-fA-F]{2}/g);
|
|
1995
|
+
if (hexMatches && hexMatches.length > 10) {
|
|
1996
|
+
results.push({ type: 'hex-encoded', content: content.substring(0, 1000) });
|
|
1997
|
+
}
|
|
1998
|
+
});
|
|
1999
|
+
return results;
|
|
2000
|
+
}, { selector: args.decodeObfuscatedJs.selector, evalIntercept: args.decodeObfuscatedJs.evalIntercept });
|
|
2001
|
+
decoded = {
|
|
2002
|
+
source: 'decodeObfuscatedJs',
|
|
2003
|
+
foundObfuscated: obfuscatedResults.length,
|
|
2004
|
+
results: obfuscatedResults,
|
|
2005
|
+
hint: 'Use execute_js with eval() to decode JSFuck, or use ultraDecode with recursive:true',
|
|
2006
|
+
};
|
|
2007
|
+
data.push(decoded);
|
|
2008
|
+
}
|
|
2009
|
+
catch (e) {
|
|
2010
|
+
decoded = { source: 'decodeObfuscatedJs', error: String(e) };
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
// ============================================================
|
|
2014
|
+
// 10. CUSTOM CIPHER FUNCTION (user-provided decode logic)
|
|
2015
|
+
// ============================================================
|
|
2016
|
+
if (args.customCipher) {
|
|
2017
|
+
try {
|
|
2018
|
+
let input = args.customCipher.input;
|
|
2019
|
+
// Convert input format if needed
|
|
2020
|
+
if (args.customCipher.inputFormat === 'hex') {
|
|
2021
|
+
input = Buffer.from(input.replace(/\s/g, ''), 'hex').toString('utf-8');
|
|
2022
|
+
}
|
|
2023
|
+
else if (args.customCipher.inputFormat === 'base64') {
|
|
2024
|
+
input = decodeBase64(input);
|
|
2025
|
+
}
|
|
2026
|
+
// Execute custom decode function in browser context (safer)
|
|
2027
|
+
const result = await page.evaluate((params) => {
|
|
2028
|
+
try {
|
|
2029
|
+
// Create function from string and execute
|
|
2030
|
+
const decodeFn = eval(`(${params.fn})`);
|
|
2031
|
+
if (typeof decodeFn === 'function') {
|
|
2032
|
+
return { success: true, result: decodeFn(params.input) };
|
|
2033
|
+
}
|
|
2034
|
+
return { success: false, error: 'Not a function' };
|
|
2035
|
+
}
|
|
2036
|
+
catch (e) {
|
|
2037
|
+
return { success: false, error: e.message };
|
|
2038
|
+
}
|
|
2039
|
+
}, { input, fn: args.customCipher.decodeFunction });
|
|
2040
|
+
if (result.success) {
|
|
2041
|
+
decoded = {
|
|
2042
|
+
source: 'customCipher',
|
|
2043
|
+
input: args.customCipher.input.substring(0, 50) + '...',
|
|
2044
|
+
function: args.customCipher.decodeFunction.substring(0, 100),
|
|
2045
|
+
decrypted: result.result,
|
|
2046
|
+
foundUrls: extractUrlsFromText(String(result.result)),
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
else {
|
|
2050
|
+
decoded = {
|
|
2051
|
+
source: 'customCipher',
|
|
2052
|
+
error: result.error,
|
|
2053
|
+
hint: 'Function must be a valid JS arrow function like: input => input.split("").reverse().join("")',
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
data.push(decoded);
|
|
2057
|
+
}
|
|
2058
|
+
catch (e) {
|
|
2059
|
+
decoded = { source: 'customCipher', error: String(e) };
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
// ============================================================
|
|
2063
|
+
// 11. ORIGINAL JSON EXTRACTION (preserved)
|
|
1535
2064
|
// ============================================================
|
|
1536
2065
|
// Extract from script tags with type="application/json" or type="application/ld+json"
|
|
1537
2066
|
const scriptData = await page.evaluate((selector) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brave-real-browser-mcp-server",
|
|
3
|
-
"version": "2.27.
|
|
3
|
+
"version": "2.27.30",
|
|
4
4
|
"description": "🦁 MCP server for Brave Real Browser - NPM Workspaces Monorepo with anti-detection features, SSE streaming, and LSP compatibility",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@modelcontextprotocol/sdk": "latest",
|
|
52
52
|
"@types/turndown": "latest",
|
|
53
|
-
"brave-real-browser": "^2.8.
|
|
53
|
+
"brave-real-browser": "^2.8.30",
|
|
54
54
|
"puppeteer-core": "^24.35.0",
|
|
55
55
|
"turndown": "latest",
|
|
56
56
|
"vscode-languageserver": "^9.0.1",
|