mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-18 17:04:49 +00:00
Better detection of eval/<script> capability on current site wrt CSP
This commit is contained in:
parent
3481aeec26
commit
5520963841
1 changed files with 50 additions and 4 deletions
|
|
@ -47,7 +47,7 @@ The code is executed using eval() in the page context, so it can:
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
- console.log() - All output is captured as text
|
- console.log() - All output is captured as text
|
||||||
- await returnFile(filename, content, mimeType?) - Create downloadable files (async function!)
|
- await returnFile(filename, content, mimeType?) - Create downloadable files for the user (async function!)
|
||||||
* Always use await with returnFile
|
* Always use await with returnFile
|
||||||
* REQUIRED: For Blob/Uint8Array binary content, you MUST supply a proper MIME type (e.g., "image/png").
|
* REQUIRED: For Blob/Uint8Array binary content, you MUST supply a proper MIME type (e.g., "image/png").
|
||||||
If omitted, throws an Error with stack trace pointing to the offending line.
|
If omitted, throws an Error with stack trace pointing to the offending line.
|
||||||
|
|
@ -62,6 +62,8 @@ Output:
|
||||||
const links = Array.from(document.querySelectorAll('a')).map(a => ({text: a.textContent, href: a.href}));
|
const links = Array.from(document.querySelectorAll('a')).map(a => ({text: a.textContent, href: a.href}));
|
||||||
const csv = 'text,href\\n' + links.map(l => \`"\${l.text}","\${l.href}"\`).join('\\n');
|
const csv = 'text,href\\n' + links.map(l => \`"\${l.text}","\${l.href}"\`).join('\\n');
|
||||||
await returnFile('links.csv', csv, 'text/csv');
|
await returnFile('links.csv', csv, 'text/csv');
|
||||||
|
* You will not have access to the file content, only the filename, mimeType and size.
|
||||||
|
- NOT CAPTURED: returning values via return or a statement does NOT capture output. Use console.log() or returnFile().
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
- Get page title: document.title
|
- Get page title: document.title
|
||||||
|
|
@ -177,18 +179,47 @@ This ensures reliable execution.`,
|
||||||
world: "MAIN",
|
world: "MAIN",
|
||||||
func: () => {
|
func: () => {
|
||||||
// Try to detect if eval is allowed
|
// Try to detect if eval is allowed
|
||||||
|
let canEval = false;
|
||||||
try {
|
try {
|
||||||
// biome-ignore lint/security/noGlobalEval: CSP detection test
|
// biome-ignore lint/security/noGlobalEval: CSP detection test
|
||||||
// biome-ignore lint/complexity/noCommaOperator: indirect eval pattern
|
// biome-ignore lint/complexity/noCommaOperator: indirect eval pattern
|
||||||
(0, eval)("1");
|
(0, eval)("1");
|
||||||
return { canEval: true };
|
canEval = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { canEval: false, error: (e as Error).message };
|
// eval blocked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to detect if script tag injection works
|
||||||
|
let canUseScriptTag = false;
|
||||||
|
const testId = `__test_${Date.now()}`;
|
||||||
|
const testScript = document.createElement("script");
|
||||||
|
testScript.textContent = `window.${testId} = true;`;
|
||||||
|
try {
|
||||||
|
document.head.appendChild(testScript);
|
||||||
|
// Check if it executed synchronously
|
||||||
|
canUseScriptTag = !!(window as any)[testId];
|
||||||
|
delete (window as any)[testId];
|
||||||
|
testScript.remove();
|
||||||
|
} catch (e) {
|
||||||
|
// script injection failed
|
||||||
|
}
|
||||||
|
|
||||||
|
return { canEval, canUseScriptTag };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const canUseEval = cspCheckResults[0]?.result?.canEval ?? false;
|
const canUseEval = cspCheckResults[0]?.result?.canEval ?? false;
|
||||||
|
const canUseScriptTag = cspCheckResults[0]?.result?.canUseScriptTag ?? false;
|
||||||
|
|
||||||
|
// If neither method works, return error immediately
|
||||||
|
if (!canUseEval && !canUseScriptTag) {
|
||||||
|
return {
|
||||||
|
output:
|
||||||
|
"Cannot execute JavaScript on this page. The page's Content Security Policy blocks both eval() and inline script injection. This is common on sites with strict CSP.",
|
||||||
|
isError: true,
|
||||||
|
details: { files: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Execute the JavaScript in the tab context with abort handling
|
// Execute the JavaScript in the tab context with abort handling
|
||||||
const executePromise = browser.scripting.executeScript({
|
const executePromise = browser.scripting.executeScript({
|
||||||
|
|
@ -199,6 +230,7 @@ This ensures reliable execution.`,
|
||||||
// Capture console output
|
// Capture console output
|
||||||
const consoleOutput: Array<{ type: string; args: unknown[] }> = [];
|
const consoleOutput: Array<{ type: string; args: unknown[] }> = [];
|
||||||
const files: Array<{ fileName: string; content: string | Uint8Array; mimeType: string }> = [];
|
const files: Array<{ fileName: string; content: string | Uint8Array; mimeType: string }> = [];
|
||||||
|
let timeoutId: number;
|
||||||
|
|
||||||
const originalConsole = {
|
const originalConsole = {
|
||||||
log: console.log,
|
log: console.log,
|
||||||
|
|
@ -266,6 +298,9 @@ This ensures reliable execution.`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
|
// Clear timeout
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
|
||||||
// Restore console
|
// Restore console
|
||||||
console.log = originalConsole.log;
|
console.log = originalConsole.log;
|
||||||
console.warn = originalConsole.warn;
|
console.warn = originalConsole.warn;
|
||||||
|
|
@ -295,6 +330,17 @@ This ensures reliable execution.`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set timeout to prevent hanging indefinitely
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
cleanup();
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
error: "Execution timeout",
|
||||||
|
stack: "Code execution did not complete within 30 seconds",
|
||||||
|
console: consoleOutput,
|
||||||
|
});
|
||||||
|
}, 30000) as unknown as number;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (useScriptTag) {
|
if (useScriptTag) {
|
||||||
// Strategy 2: Inject as script tag (works with 'unsafe-inline' but not Trusted Types)
|
// Strategy 2: Inject as script tag (works with 'unsafe-inline' but not Trusted Types)
|
||||||
|
|
@ -350,7 +396,7 @@ This ensures reliable execution.`,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
args: [args.code, !canUseEval],
|
args: [args.code, canUseScriptTag && !canUseEval],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Race between execution and abort signal
|
// Race between execution and abort signal
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue