brave-real-browser-mcp-server 2.27.13 → 2.27.14

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/README.md CHANGED
@@ -31,7 +31,12 @@
31
31
  "mcpServers": {
32
32
  "brave-real-browser": {
33
33
  "command": "node",
34
- "args": ["c:\\Users\\Admin\\Desktop\\Workspace-For-Brave-Real-browser-Mcp-Server\\Brave-Real-Browser-Mcp-Server\\dist\\index.js"]
34
+ "args": [
35
+ "c:\\Users\\Admin\\Desktop\\Workspace-For-Brave-Real-browser-Mcp-Server\\Brave-Real-Browser-Mcp-Server\\dist\\index.js"
36
+ ],
37
+ "env": {
38
+ "headless": "false"
39
+ }
35
40
  }
36
41
  }
37
42
  }
@@ -408,6 +408,95 @@ function decodeRot13(input) {
408
408
  function reverseString(input) {
409
409
  return input.split('').reverse().join('');
410
410
  }
411
+ /**
412
+ * Unpack obfuscated JavaScript code using eval(function(p,a,c,k,e,d)) pattern
413
+ * This is the most common packer used by streaming sites
414
+ */
415
+ function unpackJs(input) {
416
+ try {
417
+ // Pattern: eval(function(p,a,c,k,e,d){...}('packed_code',radix,count,'dictionary'.split('|'),0,{}))
418
+ const packedRegex = /eval\s*\(\s*function\s*\(\s*p\s*,\s*a\s*,\s*c\s*,\s*k\s*,\s*e\s*,\s*d\s*\)\s*\{[^}]+\}\s*\(\s*'([^']+)'\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*'([^']+)'\.split\s*\(\s*'\|'\s*\)/;
419
+ const match = input.match(packedRegex);
420
+ if (!match) {
421
+ // Try alternate pattern without word boundary
422
+ const altRegex = /function\s*\(\s*p\s*,\s*a\s*,\s*c\s*,\s*k\s*,\s*e\s*,\s*d\s*\)[^}]+\}\s*\(\s*'([^']+)'\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*'([^']+)'\.split/;
423
+ const altMatch = input.match(altRegex);
424
+ if (!altMatch)
425
+ return input;
426
+ return unpackPACKer(altMatch[1], parseInt(altMatch[2]), parseInt(altMatch[3]), altMatch[4].split('|'));
427
+ }
428
+ const packed = match[1];
429
+ const radix = parseInt(match[2]);
430
+ const count = parseInt(match[3]);
431
+ const dictionary = match[4].split('|');
432
+ return unpackPACKer(packed, radix, count, dictionary);
433
+ }
434
+ catch (e) {
435
+ return input;
436
+ }
437
+ }
438
+ /**
439
+ * Core P.A.C.K.E.R. decoding algorithm
440
+ * Replaces placeholders in packed code with dictionary values
441
+ */
442
+ function unpackPACKer(p, a, c, k) {
443
+ // Decode function to convert number to base-a representation
444
+ function decode(d) {
445
+ const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
446
+ let result = '';
447
+ while (d > 0) {
448
+ result = chars[d % a] + result;
449
+ d = Math.floor(d / a);
450
+ }
451
+ return result || '0';
452
+ }
453
+ // Replace encoded values with dictionary words
454
+ while (c--) {
455
+ if (k[c]) {
456
+ // Match word boundaries using regex based on radix
457
+ const pattern = new RegExp('\\b' + decode(c) + '\\b', 'g');
458
+ p = p.replace(pattern, k[c]);
459
+ }
460
+ }
461
+ return p;
462
+ }
463
+ /**
464
+ * Extract all packed scripts from HTML and decode them
465
+ */
466
+ function extractAndUnpackAll(html) {
467
+ const scripts = [];
468
+ const urls = [];
469
+ // Find all packed script blocks
470
+ const packedPattern = /eval\s*\(\s*function\s*\(\s*p\s*,\s*a\s*,\s*c\s*,\s*k\s*,\s*e\s*,\s*d\s*\)[^)]+\([^)]+\)\s*\)/g;
471
+ let match;
472
+ while ((match = packedPattern.exec(html)) !== null) {
473
+ try {
474
+ const unpacked = unpackJs(match[0]);
475
+ if (unpacked && unpacked !== match[0]) {
476
+ scripts.push(unpacked);
477
+ // Extract URLs from unpacked script
478
+ const urlPatterns = [
479
+ /https?:\/\/[^\s"'<>\\]+\.m3u8[^\s"'<>\\]*/gi,
480
+ /https?:\/\/[^\s"'<>\\]+\.txt\?[^\s"'<>\\]*/gi,
481
+ /https?:\/\/[^\s"'<>\\]+\/stream\/[^\s"'<>\\]*/gi,
482
+ /https?:\/\/[^\s"'<>\\]+\/hls\/[^\s"'<>\\]*/gi,
483
+ ];
484
+ for (const pattern of urlPatterns) {
485
+ let urlMatch;
486
+ while ((urlMatch = pattern.exec(unpacked)) !== null) {
487
+ if (!urls.includes(urlMatch[0])) {
488
+ urls.push(urlMatch[0]);
489
+ }
490
+ }
491
+ }
492
+ }
493
+ }
494
+ catch (e) {
495
+ // Skip failed unpacking
496
+ }
497
+ }
498
+ return { scripts, urls };
499
+ }
411
500
  function applyDecodeLayer(input, layer) {
412
501
  switch (layer) {
413
502
  case 'base64': return decodeBase64(input);
@@ -415,6 +504,7 @@ function applyDecodeLayer(input, layer) {
415
504
  case 'hex': return decodeHex(input);
416
505
  case 'rot13': return decodeRot13(input);
417
506
  case 'reverse': return reverseString(input);
507
+ case 'unpackJs': return unpackJs(input);
418
508
  default: return input;
419
509
  }
420
510
  }
@@ -577,7 +667,48 @@ export async function handleExtractJson(page, args) {
577
667
  }
578
668
  }
579
669
  // ============================================================
580
- // 5. ORIGINAL JSON EXTRACTION (preserved)
670
+ // 5. DECODE PACKED JAVASCRIPT (AUTO-DETECT)
671
+ // ============================================================
672
+ if (args.decodePackedJs) {
673
+ try {
674
+ // Get all script contents from page
675
+ const scriptContents = await page.evaluate(() => {
676
+ const scripts = Array.from(document.querySelectorAll('script'));
677
+ return scripts.map(s => s.textContent || '').filter(s => s.length > 0);
678
+ });
679
+ const allUnpacked = { scripts: [], urls: [] };
680
+ for (const script of scriptContents) {
681
+ const result = extractAndUnpackAll(script);
682
+ allUnpacked.scripts.push(...result.scripts);
683
+ allUnpacked.urls.push(...result.urls);
684
+ }
685
+ // Remove duplicates from URLs
686
+ const uniqueUrls = [...new Set(allUnpacked.urls)];
687
+ if (allUnpacked.scripts.length > 0 || uniqueUrls.length > 0) {
688
+ decoded = {
689
+ source: 'decodePackedJs',
690
+ packedScriptsFound: allUnpacked.scripts.length,
691
+ unpackedScripts: allUnpacked.scripts.slice(0, 5), // Limit to first 5
692
+ extractedUrls: uniqueUrls,
693
+ streamUrls: uniqueUrls.filter(u => u.includes('m3u8') || u.includes('.txt?') ||
694
+ u.includes('/stream/') || u.includes('/hls/')),
695
+ };
696
+ data.push(decoded);
697
+ }
698
+ else {
699
+ decoded = {
700
+ source: 'decodePackedJs',
701
+ message: 'No packed JavaScript found in page',
702
+ hint: 'Try using execute_js or player_api_hook for dynamically loaded content',
703
+ };
704
+ }
705
+ }
706
+ catch (e) {
707
+ decoded = { source: 'decodePackedJs', error: String(e) };
708
+ }
709
+ }
710
+ // ============================================================
711
+ // 6. ORIGINAL JSON EXTRACTION (preserved)
581
712
  // ============================================================
582
713
  // Extract from script tags with type="application/json" or type="application/ld+json"
583
714
  const scriptData = await page.evaluate((selector) => {
@@ -2,7 +2,7 @@
2
2
  // Server metadata
3
3
  export const SERVER_INFO = {
4
4
  name: 'brave-real-browser-mcp-server',
5
- version: '2.24.0',
5
+ version: '2.25.0',
6
6
  };
7
7
  // MCP capabilities with LSP-compatible streaming support
8
8
  export const CAPABILITIES = {
@@ -470,7 +470,7 @@ export const TOOLS = [
470
470
  },
471
471
  {
472
472
  name: 'extract_json',
473
- description: 'Extract embedded JSON/API data from page (LD+JSON, __NEXT_DATA__, etc.) + Advanced Decode capabilities for complex websites (base64, URL, hex, rot13, multi-layer)',
473
+ description: 'Extract embedded JSON/API data from page (LD+JSON, __NEXT_DATA__, etc.) + Advanced Decode capabilities for complex websites (base64, URL, hex, rot13, multi-layer) + Packed JavaScript decoder (eval/function(p,a,c,k,e,d) unpacker)',
474
474
  inputSchema: {
475
475
  type: 'object',
476
476
  additionalProperties: false,
@@ -484,15 +484,17 @@ export const TOOLS = [
484
484
  decodeFromUrl: { type: 'string', description: 'URL to extract and decode encoded parameter from (e.g., gadgetsweb.xyz/?id=xxx)' },
485
485
  decodeUrlParam: { type: 'string', description: 'URL parameter name to decode (default: id)', default: 'id' },
486
486
  decodePattern: { type: 'string', description: 'Regex pattern to find and decode base64 strings in page content' },
487
+ // NEW: Packed JavaScript decoder
488
+ decodePackedJs: { type: 'boolean', description: 'Auto-detect and decode packed JavaScript (eval/function(p,a,c,k,e,d)) in page scripts. Extracts m3u8/stream URLs automatically.', default: false },
487
489
  advancedDecode: {
488
490
  type: 'object',
489
- description: 'Multi-layer decode for complex obfuscation (supports base64, url, hex, rot13, reverse)',
491
+ description: 'Multi-layer decode for complex obfuscation (supports base64, url, hex, rot13, reverse, unpackJs)',
490
492
  properties: {
491
493
  input: { type: 'string', description: 'Input string to decode (if not using page content)' },
492
494
  layers: {
493
495
  type: 'array',
494
- items: { type: 'string', enum: ['base64', 'url', 'hex', 'rot13', 'reverse'] },
495
- description: 'Decode layers to apply in order (e.g., ["base64", "url"])'
496
+ items: { type: 'string', enum: ['base64', 'url', 'hex', 'rot13', 'reverse', 'unpackJs'] },
497
+ description: 'Decode layers to apply in order (e.g., ["base64", "url", "unpackJs"])'
496
498
  },
497
499
  extractFromPage: { type: 'boolean', description: 'Extract input from current page content', default: false },
498
500
  pageSelector: { type: 'string', description: 'CSS selector to extract from (for extractFromPage)' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.27.13",
3
+ "version": "2.27.14",
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.13",
53
+ "brave-real-browser": "^2.8.14",
54
54
  "puppeteer-core": "^24.35.0",
55
55
  "turndown": "latest",
56
56
  "vscode-languageserver": "^9.0.1",