brave-real-browser-mcp-server 2.27.8 → 2.27.9

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.
@@ -364,10 +364,221 @@ export async function handleFindElementAdvanced(page, args) {
364
364
  };
365
365
  }
366
366
  /**
367
- * Extract embedded JSON/API data from page
367
+ * Extract embedded JSON/API data from page + Advanced Decode capabilities
368
+ * Supports: base64, URL encoding, hex, rot13, reverse - with multi-layer decoding
368
369
  */
370
+ // ============================================================
371
+ // DECODE HELPER FUNCTIONS
372
+ // ============================================================
373
+ function decodeBase64(input) {
374
+ try {
375
+ // Handle URL-safe base64
376
+ const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
377
+ const padded = normalized + '='.repeat((4 - normalized.length % 4) % 4);
378
+ return Buffer.from(padded, 'base64').toString('utf-8');
379
+ }
380
+ catch {
381
+ return input;
382
+ }
383
+ }
384
+ function decodeUrl(input) {
385
+ try {
386
+ return decodeURIComponent(input);
387
+ }
388
+ catch {
389
+ return input;
390
+ }
391
+ }
392
+ function decodeHex(input) {
393
+ try {
394
+ // Remove 0x prefix if present
395
+ const hex = input.replace(/^0x/i, '').replace(/\s/g, '');
396
+ return Buffer.from(hex, 'hex').toString('utf-8');
397
+ }
398
+ catch {
399
+ return input;
400
+ }
401
+ }
402
+ function decodeRot13(input) {
403
+ return input.replace(/[a-zA-Z]/g, (char) => {
404
+ const base = char <= 'Z' ? 65 : 97;
405
+ return String.fromCharCode(((char.charCodeAt(0) - base + 13) % 26) + base);
406
+ });
407
+ }
408
+ function reverseString(input) {
409
+ return input.split('').reverse().join('');
410
+ }
411
+ function applyDecodeLayer(input, layer) {
412
+ switch (layer) {
413
+ case 'base64': return decodeBase64(input);
414
+ case 'url': return decodeUrl(input);
415
+ case 'hex': return decodeHex(input);
416
+ case 'rot13': return decodeRot13(input);
417
+ case 'reverse': return reverseString(input);
418
+ default: return input;
419
+ }
420
+ }
421
+ function multiLayerDecode(input, layers) {
422
+ let result = input;
423
+ for (const layer of layers) {
424
+ result = applyDecodeLayer(result, layer);
425
+ }
426
+ return result;
427
+ }
428
+ function extractUrlParam(url, param = 'id') {
429
+ try {
430
+ const urlObj = new URL(url);
431
+ return urlObj.searchParams.get(param);
432
+ }
433
+ catch {
434
+ // Try regex fallback for malformed URLs
435
+ const match = url.match(new RegExp(`[?&]${param}=([^&]+)`));
436
+ return match ? match[1] : null;
437
+ }
438
+ }
439
+ function findBase64InText(text, pattern) {
440
+ const results = [];
441
+ // Use custom pattern or default base64 pattern
442
+ const regex = pattern
443
+ ? new RegExp(pattern, 'g')
444
+ : /[A-Za-z0-9+/=_-]{20,}/g; // Match potential base64 strings
445
+ let match;
446
+ while ((match = regex.exec(text)) !== null) {
447
+ // Validate it looks like base64
448
+ const candidate = match[0];
449
+ if (candidate.length >= 20 && /^[A-Za-z0-9+/=_-]+$/.test(candidate)) {
450
+ try {
451
+ const decoded = decodeBase64(candidate);
452
+ // Only include if it decoded to something meaningful
453
+ if (decoded && decoded.length > 0 && /^[\x20-\x7E\s]+$/.test(decoded)) {
454
+ results.push(decoded);
455
+ }
456
+ }
457
+ catch {
458
+ // Skip invalid base64
459
+ }
460
+ }
461
+ if (results.length >= 50)
462
+ break; // Limit results
463
+ }
464
+ return results;
465
+ }
466
+ // ============================================================
467
+ // MAIN HANDLER
468
+ // ============================================================
369
469
  export async function handleExtractJson(page, args) {
370
470
  const data = [];
471
+ let decoded = null;
472
+ // ============================================================
473
+ // 1. DECODE FROM DIRECT BASE64 STRING
474
+ // ============================================================
475
+ if (args.decodeBase64) {
476
+ try {
477
+ const result = decodeBase64(args.decodeBase64);
478
+ decoded = {
479
+ source: 'decodeBase64',
480
+ input: args.decodeBase64.substring(0, 50) + '...',
481
+ output: result,
482
+ isUrl: result.startsWith('http'),
483
+ };
484
+ data.push(decoded);
485
+ }
486
+ catch (e) {
487
+ decoded = { source: 'decodeBase64', error: String(e) };
488
+ }
489
+ }
490
+ // ============================================================
491
+ // 2. DECODE FROM URL PARAMETER
492
+ // ============================================================
493
+ if (args.decodeFromUrl) {
494
+ try {
495
+ const paramName = args.decodeUrlParam || 'id';
496
+ const encoded = extractUrlParam(args.decodeFromUrl, paramName);
497
+ if (encoded) {
498
+ const result = decodeBase64(encoded);
499
+ decoded = {
500
+ source: 'decodeFromUrl',
501
+ url: args.decodeFromUrl.substring(0, 80) + '...',
502
+ param: paramName,
503
+ encoded: encoded.substring(0, 50) + '...',
504
+ decoded: result,
505
+ isUrl: result.startsWith('http'),
506
+ };
507
+ data.push(decoded);
508
+ }
509
+ else {
510
+ decoded = { source: 'decodeFromUrl', error: `Parameter '${paramName}' not found in URL` };
511
+ }
512
+ }
513
+ catch (e) {
514
+ decoded = { source: 'decodeFromUrl', error: String(e) };
515
+ }
516
+ }
517
+ // ============================================================
518
+ // 3. ADVANCED MULTI-LAYER DECODE
519
+ // ============================================================
520
+ if (args.advancedDecode) {
521
+ try {
522
+ let input = args.advancedDecode.input || '';
523
+ // Extract from page if requested
524
+ if (args.advancedDecode.extractFromPage) {
525
+ const pageContent = await page.evaluate((selector) => {
526
+ if (selector) {
527
+ const el = document.querySelector(selector);
528
+ return el?.textContent || '';
529
+ }
530
+ return document.body?.textContent || '';
531
+ }, args.advancedDecode.pageSelector);
532
+ input = pageContent;
533
+ }
534
+ if (input) {
535
+ const result = multiLayerDecode(input, args.advancedDecode.layers);
536
+ decoded = {
537
+ source: 'advancedDecode',
538
+ layers: args.advancedDecode.layers,
539
+ inputLength: input.length,
540
+ inputPreview: input.substring(0, 100) + (input.length > 100 ? '...' : ''),
541
+ output: result,
542
+ outputLength: result.length,
543
+ isUrl: result.startsWith('http'),
544
+ };
545
+ data.push(decoded);
546
+ }
547
+ }
548
+ catch (e) {
549
+ decoded = { source: 'advancedDecode', error: String(e) };
550
+ }
551
+ }
552
+ // ============================================================
553
+ // 4. FIND AND DECODE BASE64 PATTERNS IN PAGE
554
+ // ============================================================
555
+ if (args.decodePattern) {
556
+ try {
557
+ const pageText = await page.evaluate(() => {
558
+ // Get all script contents + page text
559
+ const scripts = Array.from(document.querySelectorAll('script'))
560
+ .map(s => s.textContent || '')
561
+ .join('\n');
562
+ const bodyText = document.body?.textContent || '';
563
+ return scripts + '\n' + bodyText;
564
+ });
565
+ const found = findBase64InText(pageText, args.decodePattern);
566
+ decoded = {
567
+ source: 'decodePattern',
568
+ pattern: args.decodePattern,
569
+ foundCount: found.length,
570
+ results: found.slice(0, 20), // Limit to first 20
571
+ urls: found.filter(f => f.startsWith('http')),
572
+ };
573
+ data.push(decoded);
574
+ }
575
+ catch (e) {
576
+ decoded = { source: 'decodePattern', error: String(e) };
577
+ }
578
+ }
579
+ // ============================================================
580
+ // 5. ORIGINAL JSON EXTRACTION (preserved)
581
+ // ============================================================
371
582
  // Extract from script tags with type="application/json" or type="application/ld+json"
372
583
  const scriptData = await page.evaluate((selector) => {
373
584
  const scripts = selector
@@ -415,6 +626,7 @@ export async function handleExtractJson(page, args) {
415
626
  found: data.length > 0,
416
627
  data,
417
628
  count: data.length,
629
+ decoded,
418
630
  };
419
631
  }
420
632
  /**
@@ -470,14 +470,34 @@ 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.)',
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)',
474
474
  inputSchema: {
475
475
  type: 'object',
476
476
  additionalProperties: false,
477
477
  properties: {
478
+ // Original JSON extraction
478
479
  selector: { type: 'string', description: 'CSS selector for script tags' },
479
480
  scriptIndex: { type: 'number', description: 'Index of script to extract' },
480
481
  variableName: { type: 'string', description: 'Window variable name to extract' },
482
+ // Advanced Decode capabilities
483
+ decodeBase64: { type: 'string', description: 'Direct base64 string to decode' },
484
+ decodeFromUrl: { type: 'string', description: 'URL to extract and decode encoded parameter from (e.g., gadgetsweb.xyz/?id=xxx)' },
485
+ decodeUrlParam: { type: 'string', description: 'URL parameter name to decode (default: id)', default: 'id' },
486
+ decodePattern: { type: 'string', description: 'Regex pattern to find and decode base64 strings in page content' },
487
+ advancedDecode: {
488
+ type: 'object',
489
+ description: 'Multi-layer decode for complex obfuscation (supports base64, url, hex, rot13, reverse)',
490
+ properties: {
491
+ input: { type: 'string', description: 'Input string to decode (if not using page content)' },
492
+ layers: {
493
+ 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
+ },
497
+ extractFromPage: { type: 'boolean', description: 'Extract input from current page content', default: false },
498
+ pageSelector: { type: 'string', description: 'CSS selector to extract from (for extractFromPage)' },
499
+ },
500
+ },
481
501
  },
482
502
  },
483
503
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.27.8",
3
+ "version": "2.27.9",
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.8",
53
+ "brave-real-browser": "^2.8.9",
54
54
  "puppeteer-core": "^24.35.0",
55
55
  "turndown": "latest",
56
56
  "vscode-languageserver": "^9.0.1",