-
-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sidebar-ContentScript (manifest V3) not working #47
Comments
Installing my test extension on Edge I can see at least the meaning of the exception: It seems that somewhere in code ther's still a usage of Any idea about how to solve this? |
I have investigated with a simple wasm extension and it produces the same behaviour. I have filed a bug report in Chromium here. |
@mingyaulee or @radiolondra with this Chromium bug in place, does it mean that no Blazor based browser extension will work with Manifest V3? |
All web assembly extensions, not just Blazor, are affected by this bug. The extensions that loads web assembly in content scripts are subjected to the CSP of the page the user is browsing, instead of the CSP declared in the extension manifest. Therefore, the content script works in some pages and not in those with strict CSP. |
Thanks @mingyaulee . That's quite the show stopper for web assembly based extensions. |
"The extensions that loads web assembly in content scripts are subjected to the CSP of the page the user is browsing" @mingyaulee so could you create a browser extension with Blazor, as long as the content scripts were written in just JavaScript?
|
Yes that is correct. |
I have my Blazor WASM V3 browser extension working on every page using the below declarativeNetRequest rule to remove CSP rules in page response headers. Not a perfect solution, but it works now. {
id: 1,
action: {
type: 'modifyHeaders',
responseHeaders: [
{
header: 'content-security-policy',
operation: 'remove'
}
]
},
condition: {
urlFilter: "|https*",
resourceTypes: ["main_frame", "sub_frame"]
}
} I am not using this repo but it is helpful. @mingyaulee Thank you. |
I'd imagine this would be rejected when publishing to the Chrome extension store because installing the extension which removes the content security policy header will make the user's browser vulnerable to XSS attacks. |
I'm sure it would be. Another method is to have the content page message the background script (extension service worker) when loading Blazor WASM fails due to CSP by listening for the onsecuritypolicyviolation event. Then the background script can add 'wasm-unsafe-eval' to the 'originalPolicy' (supplied by the onsecuritypolicyviolation event) 'script-src' section and create a dynamic rule with a urlFilter for that page that will 'set' the 'content-security-policy' header to your updated security policy next page load. Still not ideal, but much better than removing CSP completely or not working at all. |
I am using github.com to test fixes / workarounds for the WebAssembly.instantiateStreaming issue due to CSP violations in extension content mode. My Blazor extension is working on github.com with the method above. Hopefully they get this fixed soon. @mingyaulee Thank you for filing the bug report and getting that ball rolling. |
The below is working in Chrome. I haven't tested Firefox yet. This patches CSP rules to include 'wasm-unsafe-eval' and reloads the tab to allow Blazor to load. in content script before trying to load Blazor WASM function onSecurityPolicyViolation(e) {
// chrome - e.blockedURI === "wasm-eval"
if ((e.blockedURI === "wasm-eval" || e.blockedURI === "wasm-unsafe-eval")
&& e.violatedDirective === "script-src"
&& e.originalPolicy.indexOf('wasm-unsafe-eval') === -1) {
document.removeEventListener('securitypolicyviolation', onSecurityPolicyViolation);
var cspViolation = {
documentURI: e.documentURI, // document.location.href
originalPolicy: e.originalPolicy, // csp header value
// atm only the above to vars are used in the background script. below are informative
blockedURI: e.blockedURI, // "wasm-eval"
disposition: e.disposition, // "enforce"
effectiveDirective: e.effectiveDirective, // "script-src"
sourceFile: e.sourceFile, // "chrome-extension" (not sure on firefox, others)
violatedDirective: e.violatedDirective, // "script-src"
};
browser.runtime.sendMessage({ cspViolation });
}
}
document.addEventListener('securitypolicyviolation', onSecurityPolicyViolation); in background (service worker) script browser.runtime.onMessage.addListener(function (request, sender) {
if (request.cspViolation) {
patchCSP(request, sender);
}
});
async function patchCSP(request, sender) {
if (request.cspViolation) {
var originalPolicy = request.cspViolation.originalPolicy;
var updatedPolicy = originalPolicy;
if (originalPolicy.indexOf('wasm-unsafe-eval') === -1) {
updatedPolicy = originalPolicy.replace('script-src ', "script-src 'wasm-unsafe-eval' ");
} else {
// rule already has 'wasm-unsafe-eval'
// if this happens, there is another problem
return;
}
var url = new URL(request.cspViolation.documentURI);
// separate paths on the same domain may have different csp rules
// the query string and hash shouldn't have any effect on csp rules
var pageUrl = url.origin + url.pathname;
var pageUrlEscaped = escapeRegExp(pageUrl);
var cspRule = {
id: await getFreeSessionRuleId(),
action: {
type: 'modifyHeaders',
responseHeaders: [
{
header: 'content-security-policy',
operation: 'set',
value: updatedPolicy,
}
]
},
condition: {
regexFilter: `^${pageUrlEscaped}(\\?.*)?(#.*)?$`,
resourceTypes: ["main_frame", "sub_frame", "xmlhttprequest"]
}
};
console.log('Adding rule', cspRule);
// save rule
await browser.declarativeNetRequest.updateSessionRules({
addRules: [cspRule]
});
// reload tab so the new rule can take effect
browser.tabs.reload(sender.tab.id);
}
}
async function getFreeSessionRuleId() {
var previousRules = await browser.declarativeNetRequest.getDynamicRules();
var previousRuleIds = previousRules.map(rule => rule.id);
if (previousRuleIds.length === browser.declarativeNetRequest.MAX_NUMBER_OF_SESSION_RULES) {
await browser.declarativeNetRequest.updateSessionRules({ removeRules: previousRuleIds });
previousRuleIds = [];
}
var availId = Math.floor(Math.random() * 1000000) + 1;
while (previousRuleIds.indexOf(availId) !== -1) availId = Math.floor(Math.random() * 1000000) + 1;
return availId;
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
} |
From my understanding of the API, the rules are isolated from other extensions, so you can just keep track of the latest running ID in the local storage. |
After finally getting my Blazor extension working in Firefox I am surprised to see that Firefox does not have the 'wasm-unsafe-eval' issue that Chrome has. Tested GitHub and it is working. Yay! What was surprising is that, in extension content scripts/pages, Firefox wraps the return value from Resposne.json, Resposne.arrayBuffer, and Resposne.blob in an XrayWrapper. The cryptic error |
Just to confirm this post on Samples.
The error is always the same (see picture below)
This happens while opening the majority of websites (e.g. github.com and tons of others).
The text was updated successfully, but these errors were encountered: