-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathcontent_scripts.js
More file actions
419 lines (378 loc) · 23.3 KB
/
content_scripts.js
File metadata and controls
419 lines (378 loc) · 23.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
/**
* FrogPost Extension
* Originally Created by thisis0xczar/Lidor
* Refined on: 2025-10-22
* Updated: 2025-11-15 - Enhanced Symbol-based message filtering
*/
// ============================================================================
// INJECT DOM AGENT INTO MAIN WORLD (CRITICAL!)
// ============================================================================
// The DOM agent MUST run in the MAIN world to intercept page's addEventListener
(async () => {
try {
// Inject the DOM agent script into MAIN world
const script = document.createElement('script');
script.src = chrome.runtime.getURL('dom_injection_agent.js');
script.onload = () => script.remove();
(document.head || document.documentElement).appendChild(script);
} catch (e) {
console.error('FrogPost: Failed to inject DOM agent into MAIN world:', e);
}
})();
// ============================================================================
// CONTENT FORWARDER (ISOLATED WORLD)
// ============================================================================
(() => {
const FORWARDER_FLAG = '__frogPostForwarderInjected_v2';
if (window[FORWARDER_FLAG]) return;
window[FORWARDER_FLAG] = true;
function safeGetLocation(win) {
try {
if (win?.location?.href) return win.location.href;
} catch (e) {}
return 'access-denied-or-invalid';
}
// Handle messages from extension to send to iframe
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// console.log('Content script received message:', request);
if (request.action === 'sendPostMessageToIframe') {
// Use async to allow waiting for iframes to load
(async () => {
try {
const targetUrl = request.targetUrl;
let iframe = null;
// Wait for iframes to load (Azure Portal loads them dynamically)
let allIframes = document.querySelectorAll('iframe');
let retries = 0;
const maxRetries = 10; // Wait up to 1 second
while (allIframes.length === 0 && retries < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 100));
allIframes = document.querySelectorAll('iframe');
retries++;
}
console.log('[FrogPost Content] Received send request for:', targetUrl);
console.log('[FrogPost Content] Total iframes on page:', allIframes.length, `(after ${retries * 100}ms wait)`);
// Log all iframe sources for debugging
allIframes.forEach((frame, index) => {
console.log(`[FrogPost Content] Iframe ${index}:`, {
src: frame.src || '(no src)',
id: frame.id || '(no id)',
name: frame.name || '(no name)'
});
});
// Try to find iframe matching the target URL
if (targetUrl) {
try {
const targetUrlObj = new URL(targetUrl);
console.log('[FrogPost Content] Looking for:', targetUrlObj.hostname, targetUrlObj.pathname);
// Try exact src match first
iframe = document.querySelector(`iframe[src="${targetUrl}"]`);
if (iframe) console.log('[FrogPost Content] Found by exact match');
// Try partial match (hostname + pathname)
if (!iframe) {
for (const frame of allIframes) {
if (frame.src) {
try {
const frameSrc = new URL(frame.src);
// Match if hostname and pathname match
if (frameSrc.hostname === targetUrlObj.hostname &&
frameSrc.pathname === targetUrlObj.pathname) {
iframe = frame;
console.log('[FrogPost Content] Found by hostname+path match');
break;
}
} catch (e) {
// Invalid frame src, skip
}
}
}
}
// If still not found, try matching just hostname
if (!iframe) {
for (const frame of allIframes) {
if (frame.src) {
try {
const frameSrc = new URL(frame.src);
if (frameSrc.hostname === targetUrlObj.hostname) {
iframe = frame;
console.log('[FrogPost Content] Found by hostname match');
break;
}
} catch (e) {
// Invalid frame src, skip
}
}
}
}
} catch (e) {
console.log('[FrogPost Content] URL parsing error:', e.message);
}
}
// Final fallback: use first iframe if no match found
if (!iframe && allIframes.length > 0) {
iframe = allIframes[0];
console.log('[FrogPost Content] Using first available iframe as fallback');
}
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage(request.message, '*');
console.log('[FrogPost Content] ✓ Message sent to iframe');
sendResponse({
success: true,
targetMatched: !!targetUrl,
debug: `Sent to iframe (${allIframes.length} total on page)`
});
} else {
console.warn('[FrogPost Content] ✗ No iframe found or no contentWindow');
const iframeList = Array.from(allIframes).map((f, i) => `${i}: ${f.src || '(no src)'}`).join(', ');
sendResponse({
success: false,
error: allIframes.length === 0 ? 'No iframes on this page (waited 1s)' : `Found ${allIframes.length} iframes but none matched. Iframes: ${iframeList}`
});
}
} catch (error) {
console.error('[FrogPost Content] Error:', error);
sendResponse({ success: false, error: error.message });
}
})();
return true; // Keep message channel open for async response
}
});
window.addEventListener('message', (event) => {
// NEW: Enhanced message forwarding
// Check for FrogPost telemetry messages using Symbol marker or type
const FROGPOST_TELEMETRY_SYMBOL = Symbol.for('__frogPostTelemetry__');
const isFrogPostMessage = event.source === window &&
(event.data?.[FROGPOST_TELEMETRY_SYMBOL] === true ||
event.data?.type === 'frogPostAgent->ForwardToBackground' ||
event.data?.__frogPostInternal === true);
if (isFrogPostMessage) {
if (chrome?.runtime?.id && chrome.runtime.sendMessage) {
try {
const payload = event.data.payload;
const topic = payload?.topic;
// Forward with proper topic-based type
// Map topics to message types that background script expects
let messageType;
switch(topic) {
case 'handler-detected': // Real-time handler capture (NEW)
case 'handlers-telemetry': // Old telemetry (DEPRECATED)
case 'handler-added':
case 'received-message':
case 'outgoing-message':
case 'agent-ready':
case 'agent-scan-complete': // Inline script scan complete
messageType = topic;
break;
default:
// Skip unknown topics (they're likely internal)
return;
}
// Logging disabled to prevent flood
// console.log(`%c[FrogPost Forwarder]`, 'color: #ff6600; font-weight: bold', '🔄 Forwarding:', messageType);
chrome.runtime.sendMessage({
type: messageType,
payload: payload
}, (response) => {
if (chrome.runtime.lastError) {
// Silently ignore
}
});
} catch (e) {
// Silently ignore
}
}
} else if (event.data && event.data.type === '__FROGPOST_SET_INDEX__') {
return;
} else if (event.data && event.data.type && event.data.type.startsWith('frogPostDOMAgent')) {
// Check if this is a message from our DOM agent
try {
// Forward to background script (only if chrome.runtime is available)
if (chrome?.runtime?.sendMessage) {
chrome.runtime.sendMessage({
type: event.data.type,
payload: event.data.data
}).catch(error => {
// console.error('FrogPost DOM Agent Forwarder: Error sending message to background:', error);
});
} else {
// console.warn('FrogPost DOM Agent Forwarder: chrome.runtime not available, skipping message forwarding');
}
} catch (error) {
// console.error('FrogPost DOM Agent Forwarder: Error processing message:', error);
}
return;
} else {
const messageInternalType = event.data?.type;
if (typeof messageInternalType === 'string' && (messageInternalType.startsWith('frogPostAgent') || messageInternalType.startsWith('frogPostDOMAgent'))) {
return;
}
if (!event.source) return;
try {
let messageType = 'unknown';
const data = event.data;
if (data === undefined) messageType = "undefined";
else if (data === null) messageType = "null";
else if (Array.isArray(data)) messageType = "array";
else if (typeof data === 'object') messageType = data.constructor === Object ? "object" : "special_object";
else if (typeof data === 'string') messageType = (data.startsWith('{') && data.endsWith('}')) || (data.startsWith('[') && data.endsWith(']')) ? "potential_json_string" : "string";
else messageType = typeof data;
const destination = safeGetLocation(window);
if (chrome?.runtime?.id) {
// Filter out extension-generated messages by common markers
if (typeof data === 'object' && data && typeof data.type === 'string') {
const t = data.type;
if (t.startsWith('frogPost') || t.startsWith('FROGPOST_') || t === 'realTimeDetectorReady' || t === 'realTimeHandlerDetected' || t === 'realTimeMessageSent') {
return;
}
}
// Filter the special breakpoint probe messages
if (typeof data === 'string' && data === 'FrogPost::BreakpointTest') {
return;
}
if (typeof data === 'object' && data !== null && data.FrogPost === 'BreakpointTest') {
return;
}
chrome.runtime.sendMessage({
type: "postMessageCaptured",
payload: { origin: event.origin || 'unknown-origin', destinationUrl: destination, data: data, messageType: messageType, timestamp: new Date().toISOString(), }
}).catch(error => {});
}
} catch (e) { }
}
}, true);
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === "forwardedPostMessage") {
window.postMessage(message.data, '*');
if (sendResponse) sendResponse({ success: true });
return false;
}
// Forward debug mode changes to DOM agent
if (message.type === '__FROGPOST_SET_DEBUG_MODE__') {
window.postMessage({
type: '__FROGPOST_SET_DEBUG_MODE__',
enabled: message.enabled
}, '*');
if (sendResponse) sendResponse({ success: true });
return false;
}
return false;
});
if(chrome?.runtime?.id) {
chrome.runtime.sendMessage({ type: "contentScriptReady", url: window.location.href }).catch(error => {});
}
})();
// ============================================================================
// CONTENT MONITOR
// ============================================================================
(() => {
const MONITOR_FLAG = '__frogPostMonitorInjected_v3';
const CONSOLE_FLAG = '__frogPostConsoleHooked_v2';
if (window[MONITOR_FLAG]) {
return;
}
window[MONITOR_FLAG] = true;
const CONSOLE_MARKER = "FROGPWNED_CONSOLE_XSS";
let lastKnownPayloadIndexFromFuzzer = -1;
try {
window.addEventListener('message', (event) => {
if (event.source === window.parent && event.data && event.data.type === '__FROGPOST_SET_INDEX__' && typeof event.data.index === 'number') {
lastKnownPayloadIndexFromFuzzer = event.data.index;
}
}, false);
} catch(e) {
// console.error("FrogPost Monitor: Failed to add index listener", e);
}
if (!window[CONSOLE_FLAG]) {
try {
const originalConsoleLog = window.console.log;
window.console.log = function(...args) {
let markerFound = false;
let detectedPayloadIndex = lastKnownPayloadIndexFromFuzzer;
try {
if (args.some(arg => typeof arg === 'string' && arg.includes(CONSOLE_MARKER))) {
markerFound = true;
if (chrome?.runtime?.id) {
chrome.runtime.sendMessage({
type: "FROGPOST_CONSOLE_SUCCESS",
detail: { markerFound: true, firstArg: String(args[0]).substring(0, 100), timestamp: new Date().toISOString() },
location: window.location.href,
payloadIndex: detectedPayloadIndex
}).catch(e => {});
}
}
} catch (e) {
// console.warn("FrogPost Monitor: Error processing console log hook", e);
}
originalConsoleLog.apply(console, args);
};
window[CONSOLE_FLAG] = true;
} catch (e) {
// console.error("FrogPost Monitor: Failed to hook console.log", e);
}
}
const SUSPICIOUS_TAGS = new Set(['SCRIPT', 'IFRAME', 'OBJECT', 'EMBED', 'APPLET', 'VIDEO', 'AUDIO', 'LINK', 'FORM', 'DETAILS', 'MARQUEE', 'SVG', 'MATH', 'BUTTON']);
const SUSPICIOUS_ATTRS = new Set(['onerror', 'onload', 'onclick', 'onmouseover', 'onfocus', 'onpageshow', 'onwheel', 'ontoggle', 'onbegin', 'formaction', 'srcdoc', 'background', 'style']);
const SUSPICIOUS_ATTR_VALUES = /^(javascript:|vbscript:|data:)/i;
const SUSPICIOUS_SRC_HREF_ATTRS = new Set(['src', 'href', 'action', 'formaction', 'background', 'data']);
function getElementDescription(node) { if (!node || node.nodeType !== Node.ELEMENT_NODE) return 'NonElementNode'; let desc = `<${node.nodeName.toLowerCase()}`; for (const attr of node.attributes) { desc += ` ${attr.name}="${String(attr.value || '').substring(0, 20)}..."`; } return desc.substring(0, 100) + (desc.length > 100 ? '>...' : '>'); }
function isSuspiciousMutation(mutation) { try { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { const nodeName = node.nodeName.toUpperCase(); if (SUSPICIOUS_TAGS.has(nodeName)) { return { reason: `Added suspicious tag: <${nodeName}>`, nodeInfo: node.outerHTML?.substring(0, 150) }; } if (node.matches && node.matches('[onerror], [onload], [onclick], [onmouseover], [onfocus]')) { return { reason: `Added node with suspicious event handler`, nodeInfo: node.outerHTML.substring(0, 100) }; } const suspiciousAttr = node.getAttributeNames().find(attr => SUSPICIOUS_ATTRS.has(attr.toLowerCase())); if(suspiciousAttr) { return { reason: `Added node with suspicious attribute: ${suspiciousAttr}`, nodeInfo: getElementDescription(node), attributeValue: node.getAttribute(suspiciousAttr)?.substring(0, 50) }; } for(const attrName of node.getAttributeNames()) { const lowerAttrName = attrName.toLowerCase(); if (SUSPICIOUS_SRC_HREF_ATTRS.has(lowerAttrName)) { const value = node.getAttribute(attrName); if(value && SUSPICIOUS_ATTR_VALUES.test(value)) { return { reason: `Added node with suspicious protocol in attribute: ${lowerAttrName}`, nodeInfo: getElementDescription(node), attributeValue: value.substring(0, 50) }; } } } if (nodeName === 'SCRIPT' && node.innerHTML?.length > 0) { return { reason: `Added script tag with content`, nodeInfo: node.outerHTML?.substring(0, 150) }; } } } } else if (mutation.type === 'attributes') { const attrName = mutation.attributeName?.toLowerCase(); const targetNode = mutation.target; if (targetNode?.nodeType !== Node.ELEMENT_NODE) return null; const targetDesc = getElementDescription(targetNode); if (SUSPICIOUS_ATTRS.has(attrName)) { const value = targetNode.getAttribute(mutation.attributeName); return { reason: `Suspicious attribute modified/added: ${attrName}`, target: targetNode.nodeName, value: value?.substring(0, 100), nodeInfo: targetDesc }; } if (SUSPICIOUS_SRC_HREF_ATTRS.has(attrName)) { const value = targetNode.getAttribute(mutation.attributeName); if(value && SUSPICIOUS_ATTR_VALUES.test(value)) { return { reason: `Suspicious protocol set for attribute: ${attrName}`, target: targetNode.nodeName, value: value.substring(0, 100), nodeInfo: targetDesc }; } } } } catch(e) { /* console.warn("FrogPost Monitor: Error checking mutation", e); */ } return null; }
// Throttle mutation processing to prevent performance issues
let mutationThrottleTimeout = null;
const mutationQueue = [];
const processMutations = () => {
if (mutationQueue.length === 0) return;
let currentPayloadIndex = lastKnownPayloadIndexFromFuzzer;
const mutationsToProcess = mutationQueue.splice(0, 10); // Process max 10 mutations at a time
for (const mutation of mutationsToProcess) {
const suspiciousDetail = isSuspiciousMutation(mutation);
if (suspiciousDetail) {
try {
suspiciousDetail.timestamp = new Date().toISOString();
if (chrome?.runtime?.id) {
chrome.runtime.sendMessage({ type: "FROGPOST_MUTATION", detail: suspiciousDetail, location: window.location.href, payloadIndex: currentPayloadIndex }).catch(e => {});
} else { observer.disconnect(); break; }
} catch (e) { /* console.warn("FrogPost Monitor: Failed to send mutation message", e); */ }
}
}
// Continue processing if there are more mutations
if (mutationQueue.length > 0) {
mutationThrottleTimeout = setTimeout(processMutations, 50);
}
};
const observerCallback = (mutationsList, observer) => {
// Add mutations to queue
mutationQueue.push(...mutationsList);
// Throttle processing to prevent excessive CPU usage
if (!mutationThrottleTimeout) {
mutationThrottleTimeout = setTimeout(processMutations, 100);
}
};
const observer = new MutationObserver(observerCallback);
// Optimized config: reduced scope and added throttling
const config = {
attributes: true,
childList: true,
subtree: false, // Only observe direct children, not entire subtree
attributeOldValue: false,
attributeFilter: ['onerror', 'onload', 'onclick', 'onmouseover', 'onfocus', 'onpageshow', 'onwheel', 'ontoggle', 'onbegin', 'formaction', 'srcdoc', 'background', 'style'] // Only watch specific attributes
};
const startObserving = () => {
const initialTarget = document.documentElement;
let bodyObserverActive = false;
const observeBody = () => { if (document.body && !bodyObserverActive) { try { observer.disconnect(); } catch(e){} try { observer.observe(document.body, config); bodyObserverActive = true; } catch(e) { /* console.error("FrogPost Monitor: Failed to observe document.body", e); */ } } };
try { observer.observe(initialTarget, { childList: true, subtree: true }); } catch(e) { /* console.error("FrogPost Monitor: Failed to observe documentElement", e); */ return; }
if (document.body) { observeBody(); }
else { const bodyWaitObserver = new MutationObserver(() => { if (document.body) { bodyWaitObserver.disconnect(); observeBody(); } }); try { bodyWaitObserver.observe(document.documentElement, { childList: true }); } catch(e) { /* console.error("FrogPost Monitor: Failed to observe documentElement for body wait", e); */ if(document.body) observeBody(); } }
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startObserving, { once: true });
} else {
startObserving();
}
})();
// ============================================================================
// REAL TIME DETECTOR (DEPRECATED - REPLACED BY ADVANCED DOM AGENT)
// ============================================================================
// The old RealTimeHandlerDetector has been REMOVED and replaced by the new
// FrogPost DOM agent in dom_injection_agent.js which provides 95%+ accuracy.
// Code removed to prevent conflicts and reduce extension size.