Tampermonkey / tampermonkey

Tampermonkey is the most popular userscript manager, with over 10 million users. It's available for Chrome, Microsoft Edge, Safari, Opera Next, and Firefox.
GNU General Public License v3.0
4.24k stars 419 forks source link

CSPs, @require and webpack #871

Closed martixy closed 4 years ago

martixy commented 4 years ago

Expected Behavior

Userscripts should work regardless of CSP.

Actual Behavior

I've encountered a situation where my userscript refuses to run because of a site's content security policy.

After some investigation I think I have successfully tracked down the problem. See, I use @require as a method of developing scripts locally, using all nifty modern tools - vscode, webpack, etc.

From what I can tell, @requires with the file:/// schema use eval. And many sites CSPs disallow 'unsafe-eval'. And you get a problem.
(I tried requiring jquery from the web and it worked, leading me to believe this is a problem with file urls only. Why tho?)

I know there's other CSP discussions, but I'm pretty sure this is a new problem.

Specifications

derjanb commented 4 years ago

Local file requires are injected the same way all other requires are, but they are not cached and loaded every time which might lead to slightly different injection speed. See #721. Maybe this is the issue?

Can you please name a page where this is happening?

martixy commented 4 years ago

Twitter, for example.

I use webpack as my build tool, and I checked the generated code too - it doesn't have any evals inside.

The file schema being the reason is just my best guess.

721 might have something to do with it, but I doubt it - I saw some other discussions on the topic (e.g. #296, #776 ) and I did come across the suggestion to @run-at document-start, but it didn't work for me, maybe because of the issue you quoted.

Doesn't TM modify the CSP anyway? I saw the option to "Add Tampermonkey to the site's content security policy (CSP) if there is one"
Actually I was able to run a script that required jquery on twitter with or without that setting - at document-idle even. TBH, I didn't see a difference when inspecting CSP even with it enabled.

martixy commented 4 years ago

Update: And after a whole day of wondering what the heck is going on, now it is mysteriously working. I have absolutely no idea why.

OTOH if it comes back, there are extensions to strip CSPs. I can just use that while developing, then manually copy over the finished script to TM, without having a require.

martixy commented 4 years ago

Update 2: I investigated some more today, and the only logical explanation I can arrive at is that I botched something around the build process, even though I could have sworn I did it properly (I hadn't yet written the command that causes this when it first happened, wtf?).

See, when webpack is watching files in development mode it builds scripts that use eval. So an eval somehow ended up on the page. Given how often I'm wrong, it was probably my fault somehow.

As far as how to get around the issue during development - some sites (like twitter) make it extremely tricky. Or should I say chrome makes it extremely tricky because the extension APIs don't interact well with service workers. It seems for example webRequest.onHeadersReceived callback does not fire when a request is served by the service worker.

See this: https://bugs.chromium.org/p/chromium/issues/detail?id=766433&q=webrequest%20service%20worker&can=2
(star it if you can to get them off their asses)

For now a workaround must be used to allow extensions to manage CSPs:
Open devtools and go to Application>Service Workers and force the Bypass for network.

I'll edit the title to signify its a webpack problem, then close the issue.