why does my MSE code get `Security Error: Content at http://localhost:3000/ may not load data from blob:...`?

471 Views Asked by At

I'm trying to use the HTTP Media Source Extension API in React + MUI. I have a "camera driver" class that maintains a MediaSource, and am linking it to a video element.

I'm currently doing this by maintaining the driver as react state, created in an effect. And referring to its url = URL.createObjectURL(this.src); from the video element after the driver gets set.

function App() {
  // ...
  const [driver, setDriver] = React.useState<null | Driver>(null);
  React.useEffect(() => {
    // ...
    setDriver(driver);
  }, [camera]);
  const videoElement = <video src={driver?.url} autoPlay muted />;
  return (/* ... */ videoElement /* ... */);

Full working reproduction here.

My code seems to work in Chrome and Safari but fails in Firefox 118.0.1 with the following error in console:

Security Error: Content at http://localhost:3000/ may not load data from blob:http://localhost:3000/26794ddf-bbc7-4aec-8eec-d2bc0cd61eef. 

Why?

If I instead use a React.useRef<HTMLVideoElement>(null), and update videoRef.current!.src to point to the driver's URL, it seems to work. But I'd like to understand why, so I can know what I'm doing wrong and reliably avoid this problem as I modify and extend this software...


edit:

I've also tried adding a src/setupProxy.js that sets Content-Security-Policy:

// https://stackoverflow.com/a/68172973/23584

module.exports = function(app) {
  app.use((req, res, next) => {
    res.set({
        'Content-Security-Policy': "default-src 'self' 'unsafe-eval' 'unsafe-inline' blob:;"
    });
      next();
  });
};

See exact diff.

I can see in Firefox dev tool's network tab that the header value I set is making it through. Still get the error though.


If I use the older React 17-like ReactDOM.render, everything works. exact diff But obviously I can't stay there forever; I should be using the newer ReactDOM.createRoot(...).render(...) instead.

2

There are 2 best solutions below

2
Rainy sidewalks On

i am not really much expercienced with the reactjs but think you should consider the following thing as a try

  1. instead of using URL.createObjectURL(this.src) to create the blob URL, use the URL.createObjectURL() method directly on the video file itself so that it ensures that the blob URL is created within the same origin as the video file. 2.assign the blob URL to the src attribute of the video element using videoRef.current.src instead of driver?.url. so pleas give it a try with the above approach.

    import React, { useEffect, useRef } from 'react';

         function App() {
           const [driver, setDriver] = React.useState<null | Driver>(null);
           const videoRef = useRef<HTMLVideoElement>(null);
    
           useEffect(() => {
             if (driver && videoRef.current) {
               const mediaSource = new MediaSource();
               videoRef.current.src = URL.createObjectURL(mediaSource);
    
               mediaSource.addEventListener('sourceopen', () => {
                 const sourceBuffer = mediaSource.addSourceBuffer('video/mp4');
                 fetch(driver.url)
                   .then((response) => response.arrayBuffer())
                   .then((buffer) => {
                     sourceBuffer.addEventListener('updateend', () => {
                       mediaSource.endOfStream();
                     });
                     sourceBuffer.appendBuffer(buffer);
                   });
               });
             }
           }, [driver]);
    
           return <video ref={videoRef} autoPlay muted />;
         }
    

please run it and let me know if any.

3
LSerni On

This is a... feature of Firefox. If you check the docs, it says (ask me not why), emphasis mine:

Refers to the origin from which the protected document is being served, including the same URL scheme and port number. You must include the single quotes. Some browsers specifically exclude blob and filesystem from source directives. Sites needing to allow these content types can specify them using the Data attribute.

The net result is that localhost, when requesting a blob from localhost, is considered a different source and therefore needs explicit authorization.

You can try adding "blob:" explicitly in the CSP header; it should work.

I'm not familiar enough with your framework to suggest how, though. Knowing that it was CSP that bit you led me to this other question (I tried "reactjs CSP"), which might shed further light on the subject.