Skip to content
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

Uses restricted feature: worker #1210

Open
ChrisMiami opened this issue Oct 5, 2023 · 5 comments
Open

Uses restricted feature: worker #1210

ChrisMiami opened this issue Oct 5, 2023 · 5 comments

Comments

@ChrisMiami
Copy link

ChrisMiami commented Oct 5, 2023

Describe the bug

In Chrome Version 117.0.5938.149 (Official Build) (arm64), I got the following error when Tone.js.min was loaded:

Refused to create a worker from 'blob:<my-server/guid>' because it violates the following Content Security Policy directive: "script-src * 'unsafe-inline' 'unsafe-eval'". Note that 'worker-src' was not explicitly set, so 'script-src' is used as a fallback. Note that '*' matches only URLs with network schemes ('http', 'https', 'ws', 'wss'), or URLs whose scheme matches self's scheme. The scheme 'blob:' must be added explicitly.

also

[Deprecation] The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead. (https://bit.ly/audio-worklet)

To Reproduce

Add tone.js.min to a web page (yes, you have to rename to tone.js.min.js) and then look at the DevTools. The error is self-replicating.

The code that causes this problem is in Ticker.ts:

	/**
	 * Generate a web worker
	 */
	private _createWorker(): void {

		const blob = new Blob([
			/* javascript */`
			// the initial timeout time
			let timeoutTime =  ${(this._updateInterval * 1000).toFixed(1)};
			// onmessage callback
			self.onmessage = function(msg){
				timeoutTime = parseInt(msg.data);
			};
			// the tick function which posts a message
			// and schedules a new tick
			function tick(){
				setTimeout(tick, timeoutTime);
				self.postMessage('tick');
			}
			// call tick initially
			tick();
			`
		], { type: "text/javascript" });
		const blobUrl = URL.createObjectURL(blob);
		const worker = new Worker(blobUrl);

		worker.onmessage = this._callback.bind(this);

		this._worker = worker;
	}

For some reason, the best solution here is to attempt to inject code into the page and then evaluate it. Super bad idea for pages where security is even a minor concern! Is there no other way to accomplish this feature?

Expected behavior
Not having an error.

What I've tried
Learning TypeScript, learning why this code is here, asking ChatGPT to figure out a better way to do it, trying to figure out how this project is built. Too much to do within my little deadline.

Additional context
There MUST be some way to do this that doesn't involve hacking the page. I know setTimeout() might be inferior, but maybe it'd be better than having to not use the entire project.

@ChrisMiami
Copy link
Author

Note: this is only a bug in a page with a Content-Security-Policy as described in the Chrome error message. It works fine in a wide-open test page. Not so much in a business environment.

@ChrisMiami ChrisMiami changed the title Uses defunct patterns Uses restricted feature: worker Oct 6, 2023
@ChrisMiami
Copy link
Author

ChrisMiami commented Oct 7, 2023

As I look deeper into this (illegally, as nobody but me thinks audio feedback is of any importance), I realize that the code mentioned is written so that Tone.js is entirely self-contained and deliverable via CDN. If I just excise the JS inside the blob and put it into a co-resident file, and serve the lib from my servers, then heck, I bet it could actually work! Maybe...

So, I extracted the template literal into its own file (TickWorker.js) and am creating the blob using 'TickWorker.js' as its argument. Ironically, I find that the worker is entirely optional but the error it causes doesn't raise an exception that would cause Tone to use the setTimeout fallback in Ticker._createClock(). If the side-by-side file doesn't work to create the blob, I'll probably just comment out the whole thing and go with setTimeout.

@chrisguttandin
Copy link
Contributor

It should work if you add 'blob:' as a script-src. It's not the safest thing to do but given that the page already allows any URL as well as inlined and eval-ed JavaScript I would argue it doesn't further increase the potential risk of an attack.

@ChrisMiami
Copy link
Author

I don't have access to either the server or the source of the page - the content I create is hosted inside an iFrame that's contained in a commercial solution. I tried everything to get the Worker created with "known" source code, but it just wasn't possible. I ended up just throwing so setTimeout would be used. The hosting app does offer a worker that performs a setTimeout but I had to move on before being able to figure out how to match up the different APIs (between their worker timer and Tone's ticker worker).

@chrisguttandin
Copy link
Contributor

Okay, I see. I took another look at the source code you referenced above and it actually catches the error and handles it correctly.

private _createClock(): void {
if (this._type === "worker") {
try {
this._createWorker();
} catch (e) {
// workers not supported, fallback to timeout
this._type = "timeout";
this._createClock();
}
} else if (this._type === "timeout") {
this._createTimeout();
}
}

Running console.log(Tone.context.clockSource); should log 'timeout' in your case.

I believe this is Chrome's strange behavior of logging network errors even though they are handled programmatically. As far as I know there is no way to avoid this despite not triggering the error in the first place. You could do this by changing the code linked above to not create a worker when asked to do so.

private _createClock(): void {
    if (this._type === "worker") {
        // workers not supported, fallback to timeout
        this._type = "timeout";
        this._createClock();
    } else if (this._type === "timeout") {
        this._createTimeout();
    }
}

By the way the ScriptProcessorNode deprecation warning in the console is also unavoidable in Chrome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants