mosch / react-avatar-editor

Small avatar & profile picture component. Resize and crop uploaded images using a intuitive user interface.
https://react-avatar-editor.netlify.app/
MIT License
2.37k stars 369 forks source link

SecurityError: The operation is insecure #382

Open magician11 opened 2 years ago

magician11 commented 2 years ago

I have a component that looks like this...

import { useState, useRef } from 'react';
import {
  Button,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogContentText,
  Slider,
  Box
} from '@mui/material';
import AvatarEditor from 'react-avatar-editor';
import { CloudUpload } from '@mui/icons-material';

const ImageEditor = ({
  editImage,
  setEditImage,
  editedImage,
  initialImage
}) => {
  const [imageFile, setImageFile] = useState(initialImage);
  const [scale, setScale] = useState(1);
  const setEditorRef = useRef();

  const saveImage = () => {
    if (setEditorRef) {
      const canvas = setEditorRef.current.getImage();
      canvas.toBlob(blob => editedImage(blob));
      setEditImage(false);
    }
  };

  const editorDimenson = 272;

  return (
    <Dialog open={editImage} onClose={() => setEditImage(false)}>
      <DialogTitle>Profile image</DialogTitle>
      <DialogContent>
        <DialogContentText gutterBottom>
          Choose your profile image
        </DialogContentText>

        <AvatarEditor
          ref={setEditorRef}
          image={imageFile}
          width={editorDimenson}
          height={editorDimenson}
          border={8}
          color={[0, 0, 0, 0.8]}
          scale={scale}
          rotate={0}
        />
        <Box>
          <input
            color="primary"
            accept="image/*"
            type="file"
            onChange={event => setImageFile(event.target.files[0])}
            id="icon-button-file"
            hidden
          />
          <label htmlFor="icon-button-file">
            <Button
              variant="outlined"
              component="span"
              size="small"
              startIcon={<CloudUpload />}
            >
              Select profile photo
            </Button>
          </label>
        </Box>

        <Slider
          aria-label="Volume"
          value={scale}
          step={0.1}
          min={1}
          max={3}
          onChange={(evt, newValue) => setScale(newValue)}
        />
        <Button onClick={() => saveImage()} variant="contained">
          Update
        </Button>
      </DialogContent>
    </Dialog>
  );
};

export default ImageEditor;

Basically clicking "Update" and triggering the saveImage() function, results in this security error. In Firefox I get the above error "The operation is insecure". In Chrome I get a slightly more detailed message which says "Tainted canvases may not be exported."

Specifically it's from the line where canvas.toBlob is called.

I can see from the docs https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob a reference to this error. I'm just not sure how to fix it.

dmitry-kovalev commented 2 years ago

Maybe it's too late for @magician11, but useful for other users. You can try to set the crossOrigin property to anonymous.

        <AvatarEditor
          ref={setEditorRef}
          image={imageFile}
          width={editorDimenson}
          height={editorDimenson}
          border={8}
          color={[0, 0, 0, 0.8]}
          scale={scale}
          rotate={0}
          crossOrigin={'anonymous'}
        />