sicario-red-team 0.5.9 → 0.5.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sicario-red-team",
3
- "version": "0.5.9",
3
+ "version": "0.5.10",
4
4
  "type": "module",
5
5
  "description": "Autonomous Agentic Red-Teaming Swarm Protocol",
6
6
  "repository": {
@@ -41,12 +41,11 @@ export async function runBreacher(elements, personaType = 'GENERAL') {
41
41
  You are a specialized node in the Sicario Autonomous Swarm.
42
42
  ${personaContext}
43
43
 
44
- ### VULNERABILITY TARGETING (THE SNIPER RULE):
45
- RULE 1: Always prioritize basic Parameter Tampering. If you see hidden HTML inputs (type='hidden') related to pricing, IDs, or roles, you MUST attempt to mutate their values to 0, "", or admin before attempting any complex JavaScript or Event Listener exploitation.
46
-
47
- Pay special attention to inputs where type='hidden' or marked as isHighValueTarget.
48
- These often control critical business logic like pricing, user IDs, or privilege levels.
49
- Hunt for logic flaws by attempting to mutate these hidden values (e.g., set price/quantity to 0 or negative, change account roles to 'admin', or swap user session IDs).
44
+ ### VULNERABILITY TARGETING (DOM SUPREMACY MODE):
45
+ 1. PIERCE THE SHADOW: Elements marked as 'isShadow: true' are encapsulated. Developers often leave internal fields (like price or user_id) unvalidated within custom Web Components.
46
+ 2. BEHAVIORAL MAPPING: Analyze elements with custom roles (role="button") or pointer cursors. These are modern SPA action points that bypass traditional form-crawling scanners.
47
+ 3. BEHAVIORAL TRIGGERS: Identify the specific trigger selector required to submit the payload (e.g., a <div> with role="button" or an ID like #sync-profile).
48
+ 4. THE SNIPER RULE: Always prioritize basic Parameter Tampering. Mutate hidden HTML inputs (type='hidden') related to pricing, IDs, or roles to 0, "", or admin before attempting complex JS exploitation.
50
49
 
51
50
  ### PHASE 1: REASONING (THINK OUT LOUD)
52
51
  Before providing JSON, analyze the DOM elements according to your specific persona.
@@ -62,7 +61,8 @@ Return a VALID JSON object. DO NOT use "null".
62
61
  "targetElement": "CSS selector or name",
63
62
  "mutation": {
64
63
  "selector": "The CSS selector for the hidden field or input to target",
65
- "value": "The malicious value to inject (e.g. 0, -1, 'admin', '1e10')"
64
+ "value": "The malicious value to inject (e.g. 0, -1, 'admin', '1e10')",
65
+ "trigger": "The CSS selector for the trigger element to click (div[role='button'], #submit, etc.)"
66
66
  },
67
67
  "mitigation": "Code-level fix"
68
68
  }
@@ -26,16 +26,34 @@ export async function runExecutor(targetUrl, breachReport, options = {}) {
26
26
  if (selector && value !== undefined) {
27
27
  console.log(pc.yellow(` [Executor] : Mutating ${selector} -> ${value}`));
28
28
 
29
- // Apply the mutation to the target element
30
- await page.$eval(selector, (el, v) => {
29
+ // 1. Shadow-Piercing Mutation
30
+ const targetLocator = page.locator(selector);
31
+ await targetLocator.evaluate((el, v) => {
31
32
  el.value = v;
32
- // Trigger change events if any
33
+ // Trigger events to bypass React/Next.js state listeners
33
34
  el.dispatchEvent(new Event('change', { bubbles: true }));
34
35
  el.dispatchEvent(new Event('input', { bubbles: true }));
35
36
  }, value);
36
37
 
37
- // Pull the trigger: Find the nearest submit button
38
- console.log(pc.red(` [Executor] : Pulling the trigger (Live POST/PUT)...`));
38
+ // 2. The Wiretap: Listen for background API traffic (DOM Supremacy)
39
+ let interceptedNetworkTraffic = "";
40
+ const responseHandler = async (response) => {
41
+ const resType = response.request().resourceType();
42
+ if (resType === 'fetch' || resType === 'xhr') {
43
+ try {
44
+ const body = await response.text();
45
+ // Capture URL and condensed body to bridge the Asynchronous State gap
46
+ interceptedNetworkTraffic += `[${response.status()}] ${response.url().split('/').pop()}: ${body.substring(0, 500)} | `;
47
+ } catch (e) {
48
+ // Ignore opaque or failed body reads
49
+ }
50
+ }
51
+ };
52
+ page.on('response', responseHandler);
53
+
54
+ // 3. Behavioral Trigger Pull
55
+ const triggerSelector = mutation.trigger || 'button[type="submit"], input[type="submit"], [role="button"], button';
56
+ console.log(pc.red(` [Executor] : Pulling the trigger (${triggerSelector})...`));
39
57
 
40
58
  await Promise.all([
41
59
  // Wait for either navigation or network to go quiet
@@ -43,23 +61,26 @@ export async function runExecutor(targetUrl, breachReport, options = {}) {
43
61
  page.waitForNavigation({ timeout: 10000 }).catch(() => {}),
44
62
  page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {})
45
63
  ]),
46
- page.click('button[type="submit"], input[type="submit"], [form] button').catch(async () => {
47
- // Fallback to form submit
64
+ // Use locator.click() to natively pierce Shadow DOM
65
+ page.locator(triggerSelector).first().click().catch(async () => {
66
+ // Fallback to legacy form submit
48
67
  await page.$eval('form', f => f.submit()).catch(() => {});
49
68
  })
50
69
  ]);
51
70
 
52
- // Wait an extra 500ms for UI rendering
53
- await new Promise(r => setTimeout(r, 500));
71
+ // 4. Capture Window (Wait for async logic to resolve)
72
+ await new Promise(r => setTimeout(r, 1000));
73
+ page.off('response', responseHandler);
54
74
 
55
- // Capture the response
56
- const responseText = await page.innerText('body');
75
+ // 5. Final Intelligence Accumulation
76
+ const domText = await page.innerText('body');
77
+ const serverEvidence = `| NETWORK TRAFFIC |\n${interceptedNetworkTraffic || "No AJAX traffic detected."}\n\n| DOM TEXT |\n${domText}`;
57
78
  const screenshot = await page.screenshot({ type: 'jpeg', quality: 20 });
58
79
 
59
80
  await browser.close();
60
81
  return {
61
82
  success: true,
62
- evidence: responseText.substring(0, 2000), // Limit data
83
+ evidence: serverEvidence.substring(0, 3000), // Larger limit for network data
63
84
  screenshot
64
85
  };
65
86
  } else {
@@ -79,29 +79,51 @@ export async function runScout(url, options = {}) {
79
79
  const dismissButton = await page.$('button.close-dialog');
80
80
  if (dismissButton) await dismissButton.click();
81
81
 
82
- // 3. Extract the DOM
82
+ // 3. Extract the DOM (DOM Supremacy: Shadow Piercing & Behavioral Mapping)
83
83
  const interactiveElements = await page.evaluate(() => {
84
- const elements = document.querySelectorAll('button, input, a, form');
85
84
  const extracted = [];
85
+ const processedNodes = new Set(); // Prevent duplicates from recursion
86
86
 
87
- elements.forEach((el) => {
88
- const isHiddenInput = el.tagName === 'INPUT' && el.type === 'hidden';
89
- const isVisible = el.offsetWidth > 0 && el.offsetHeight > 0;
87
+ const walk = (root) => {
88
+ const nodes = root.querySelectorAll('*');
89
+ nodes.forEach(el => {
90
+ if (processedNodes.has(el)) return;
91
+ processedNodes.add(el);
90
92
 
91
- if (isVisible || isHiddenInput) {
92
- extracted.push({
93
- tag: el.tagName.toLowerCase(),
94
- id: el.id || null,
95
- name: el.name || null,
96
- type: el.type || null,
97
- value: el.value || null,
98
- isHighValueTarget: isHiddenInput && el.value !== '',
99
- text: el.innerText ? el.innerText.trim().substring(0, 50) : null,
100
- href: el.href || null
101
- });
102
- }
103
- });
93
+ const tag = el.tagName.toLowerCase();
94
+ const style = window.getComputedStyle(el);
95
+
96
+ // Behavioral Hooks
97
+ const isClickable = style.cursor === 'pointer' || el.getAttribute('onclick') || el.onclick;
98
+ const hasRole = ['button', 'link', 'input', 'form', 'checkbox', 'radio'].includes(el.getAttribute('role'));
99
+ const isInput = ['input', 'select', 'textarea', 'button'].includes(tag);
100
+ const isAnchor = tag === 'a';
101
+ const isHiddenInput = tag === 'input' && el.type === 'hidden';
102
+ const isVisible = el.offsetWidth > 0 && el.offsetHeight > 0;
103
+
104
+ if ((isVisible && (isInput || isAnchor || isClickable || hasRole)) || isHiddenInput) {
105
+ extracted.push({
106
+ tag,
107
+ id: el.id || null,
108
+ name: el.name || null,
109
+ type: el.type || null,
110
+ value: el.value || null,
111
+ role: el.getAttribute('role') || null,
112
+ isHighValueTarget: isHiddenInput && el.value !== '',
113
+ text: el.innerText ? el.innerText.trim().substring(0, 50) : null,
114
+ href: el.href || null,
115
+ isShadow: root !== document
116
+ });
117
+ }
118
+
119
+ // Shadow DOM: Pierce the boundary
120
+ if (el.shadowRoot) {
121
+ walk(el.shadowRoot);
122
+ }
123
+ });
124
+ };
104
125
 
126
+ walk(document);
105
127
  return extracted;
106
128
  });
107
129