CesiumGS / cesium

An open-source JavaScript library for world-class 3D globes and maps :earth_americas:
https://cesium.com/cesiumjs/
Apache License 2.0
12.76k stars 3.46k forks source link

Resource becomes tainted if a same-origin URL redirects to a different origin #11134

Open daguej opened 1 year ago

daguej commented 1 year ago

Consider a Resource fetched from the local origin:

Resource.fetchImage('/some/image.jpg');

If the server issues a HTTP redirection to some other origin, the image is loaded from the redirected destination, but the image becomes tainted. If the image is used, this results in Cesium crashing:

SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-origin data, and may not be loaded.

Error: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-origin data, and may not be loaded.
    at new Texture (webpack-internal:///./node_modules/@cesium/engine/Source/Renderer/Texture.js:355:10)
    at Material.update (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Material.js:502:17)
    at Primitive.update (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Primitive.js:2261:20)
    at PrimitiveCollection.update (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/PrimitiveCollection.js:383:19)
    at PrimitiveCollection.update (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/PrimitiveCollection.js:383:19)
    at PrimitiveCollection.update (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/PrimitiveCollection.js:383:19)
    at updateAndRenderPrimitives (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Scene.js:3404:21)
    at executeCommandsInViewport (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Scene.js:3180:3)
    at Scene.updateAndExecuteCommands (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Scene.js:2925:5)
    at render (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Scene.js:3815:9)
    at tryAndCatchError (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Scene.js:3834:5)
    at Scene.render (webpack-internal:///./node_modules/@cesium/engine/Source/Scene/Scene.js:3924:5)
    at CesiumWidget.render (webpack-internal:///./node_modules/@cesium/engine/Source/Widget/CesiumWidget.js:824:17)
    at render (webpack-internal:///./node_modules/@cesium/engine/Source/Widget/CesiumWidget.js:63:18)

Normally, Resource auto-detects whether the provided url is cross-origin, and sets an internal flag that results in <img crossorigin> getting set. Since the url appears same-origin, this won't happen, and the image is loaded tainted.

The Resource API provides no way to force CORS loading of a resource. As a workaround, it is possible to hack it by overriding the object's getter:

const resource = new Resource({ url: '/some/image.jpg' });
Object.defineProperty(resource, 'isCrossOriginUrl', {
    get: () => true
});
resource.fetchImage();

…but this is not ideal.

The fetchImage API should include an option that allows you to specify that the request will be cross-origin despite a URL that starts off same-origin. eg

Resource.fetchImage({ url: '/some/image.jpg', crossOrigin: true });
ggetz commented 1 year ago

Thanks for the detailed report @daguej!

Since I don't think it's possible to detect this before making the request, we would need an option that overrides existing isCrossOriginUrl behavior. I think it would make sense to add an option to the Resource constructor, isCrossOrigin, which keeps track of an internal _isCrossOrigin property. The getter would then return this value if specified, otherwise default to isCrossOriginUrl. The intended behavior would then work fetchImage for both the static and prototype implementations.

Would you have any bandwidth for contributing a fix in a Pull Request? 😃