Skip to content

Patch needed to upload images or files in EmDash running in Google Cloud Run-Firebase and Google Cloud Storage for Firebase #433

@yoliverasPozo

Description

@yoliverasPozo

Description

EmDash Media Upload Issue & CSP Patch Report

The Problem

When running an instance of EmDash CMS backed by a Google Cloud Storage (GCS) or Firebase Storage bucket, uploading and displaying media assets in the admin dashboard resulted in persistent failures.

Symptoms:

  • UI Error Message: "Upload failed: Failed to fetch" or silent drop upon adding files to content pages or the Media Library.
  • Browser Console Error: Connecting to 'https://storage.googleapis.com/...' violates the following Content Security Policy directive: "connect-src 'self'". The action has been blocked.
  • Database records pending: Turso Database shows records of the media in a pending state.

Root Cause

The root cause was traced back to EmDash's built-in authentication middleware (node_modules/emdash/dist/astro/middleware/auth.mjs). This middleware statically generates a Content-Security-Policy (CSP) header onto the dashboard and API routes /_emdash/*.

By default, EmDash hardcodes the following directives:

"connect-src 'self'"
"img-src 'self' data: blob:"

The connect-src 'self' directive forces Google Chrome (and other browsers) to block any outgoing Ajax or fetch() connections to external domains. This inherently breaks EmDash's S3/GCS media adapter flow: when uploading a file, EmDash generates a presigned URL, and the client-side browser attempts to PUT the actual payload directly to https://storage.googleapis.com. The browser immediately terminates the upload because the origin disagrees with the CSP. For similar reasons, the img-src string prevents the CMS from displaying preview thumbnails populated directly from the bucket's URL.

The Solution

To resolve this issue, the auth.mjs middleware file requires tweaking to inject https://storage.googleapis.com into EmDash's allowlist. Since there is currently no exposed option in astro.config.mjs to append global CSP domains, we used patch-package to safely and persistently rewrite the compiled Node module.

Implementation Guide

1. Direct JS Patching:
We updated node_modules/emdash/dist/astro/middleware/auth.mjs inside the buildEmDashCsp functional scope.

Before:

"connect-src 'self'",
`img-src ${imgSources.join(" ")}`,

After:

"connect-src 'self' https://storage.googleapis.com",
`img-src ${imgSources.join(" ")} https://storage.googleapis.com`,

2. Preserving the Patch (NPM):
To preserve this change across different machines and environments, we captured the diff natively via patch-package.

npm install patch-package
npx patch-package emdash

This produced a patch blueprint stored safely into patches/emdash+0.1.0.patch.

3. Automatic Implementation (CI/CD):
We added a postinstall hook inside the project's package.json so every subsequent npm install automatically patches to EmDash gracefully:

"scripts": {
  "postinstall": "patch-package"
}

4. Docker Compatibility Adjustments:
In the application's Dockerfile, we adjusted the staging sequence to insert the patches/ folder adjacent to package.json before firing npm ci. Without this minor tweak, the Docker container's npm ci attempt would fail, since the postinstall script lacks the patch references.

# Install dependencies strictly
COPY package.json package-lock.json* ./
COPY patches ./patches
RUN npm ci

# Copy remaining project files
COPY . .

With this deployment configuration in place, developers and future Google Cloud Run deploy pipelines will automatically weave the CSP exemption into EmDash, resulting in a healthy, successful connection line to the Firebase Cloud Storage bucket.

Steps to reproduce

  1. Set up EmDash to un on Google Cloud Run - Firebase and Google Cloud Storage for Firebase (bucket).
  2. Create new page or go to the Media Library.
  3. Try to upload an image.
  4. Upload dialog erros: "Upload failed: Failed to fetch".
  5. Upload through Media Library directly simply silently drops.
  6. Using Chrome's "Inspect" feature, you will see errors and warnings in the console.

Environment

  • emdash 0.1.0
  • Google Cloud Run
  • Firebase
  • Cloud Store for Firebase
  • Google Chrome latest running in Linux

Logs / error output

PluginRegistry.hYnmbUrG.js:237 Connecting to 'https://storage.googleapis.com/fdp-emdash.firebasestorage.app/01KNT2F96ZHQA…cksum-crc32=AAAAAA%3D%3D&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject' violates the following Content Security Policy directive: "connect-src 'self'". The action has been blocked.
a$e	@	PluginRegistry.hYnmbUrG.js:237
ure	@	PluginRegistry.hYnmbUrG.js:237
await in ure		
mutationFn	@	PluginRegistry.hYnmbUrG.js:326
fn	@	PluginRegistry.hYnmbUrG.js:13
g	@	PluginRegistry.hYnmbUrG.js:13
start	@	PluginRegistry.hYnmbUrG.js:13
execute	@	PluginRegistry.hYnmbUrG.js:13
await in execute		
mutate	@	PluginRegistry.hYnmbUrG.js:13
(anonymous)	@	PluginRegistry.hYnmbUrG.js:13
onUpload	@	PluginRegistry.hYnmbUrG.js:326
A	@	PluginRegistry.hYnmbUrG.js:245
E1	@	client.CHlC-0c6.js:8
(anonymous)	@	client.CHlC-0c6.js:8
Hi	@	client.CHlC-0c6.js:8
Cc	@	client.CHlC-0c6.js:8
rc	@	client.CHlC-0c6.js:9
Bh	@	client.CHlC-0c6.js:9
PluginRegistry.hYnmbUrG.js:237 Fetch API cannot load https://storage.googleapis.com/fdp-emdash.firebasestorage.app/01KNT2F96ZHQA…cksum-crc32=AAAAAA%3D%3D&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject. Refused to connect because it violates the document's Content Security Policy.
a$e	@	PluginRegistry.hYnmbUrG.js:237
ure	@	PluginRegistry.hYnmbUrG.js:237
await in ure		
mutationFn	@	PluginRegistry.hYnmbUrG.js:326
fn	@	PluginRegistry.hYnmbUrG.js:13
g	@	PluginRegistry.hYnmbUrG.js:13
start	@	PluginRegistry.hYnmbUrG.js:13
execute	@	PluginRegistry.hYnmbUrG.js:13
await in execute		
mutate	@	PluginRegistry.hYnmbUrG.js:13
(anonymous)	@	PluginRegistry.hYnmbUrG.js:13
onUpload	@	PluginRegistry.hYnmbUrG.js:326
A	@	PluginRegistry.hYnmbUrG.js:245
E1	@	client.CHlC-0c6.js:8
(anonymous)	@	client.CHlC-0c6.js:8
Hi	@	client.CHlC-0c6.js:8
Cc	@	client.CHlC-0c6.js:8
rc	@	client.CHlC-0c6.js:9
Bh	@	client.CHlC-0c6.js:9

Also these warnings:

Some resources are blocked because their origin is not listed in your site's Content Security Policy (CSP). Your site's CSP is allowlist-based, so resources must be listed in the allowlist in order to be accessed.

A site's Content Security Policy is set either via an HTTP header (recommended), or via a meta HTML tag.

To fix this issue do one of the following:

(Recommended) If you're using an allowlist for 'script-src', consider switching from an allowlist CSP to a strict CSP, because strict CSPs are more robust against XSS. See how to set a strict CSP.
Or carefully check that all of the blocked resources are trustworthy; if they are, include their sources in the CSP of your site. ⚠️Never add a source you don't trust to your site's CSP. If you don't trust the source, consider hosting resources on your own site instead.
3 directives
Resource	Status	Directive	Source location
https://storage.googleapis.com/fdp-emdash.firebasestorage.app/01KNT23DHVAP39BXFKX9RC4DKK.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=GOOG1EGXWWAU54G2JHQSM7LE3VLLISVTPQBAYXZLLIYJ2AJQDQWDNMGLWXEUC%2F20260409%2Fus-east1%2Fs3%2Faws4_request&X-Amz-Date=20260409T212419Z&X-Amz-Expires=3600&X-Amz-Signature=b5b2461187f1bb9f249e7def9d02a52e9a74cef9694492b080b803b620464712&X-Amz-SignedHeaders=content-length%3Bhost&x-amz-checksum-crc32=AAAAAA%3D%3D&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject	blocked	connect-src	PluginRegistry.hYnmbUrG.js:237
https://storage.googleapis.com/fdp-emdash.firebasestorage.app/01KNT2C7QX2KDMD9K80FCAV3D6.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=GOOG1EGXWWAU54G2JHQSM7LE3VLLISVTPQBAYXZLLIYJ2AJQDQWDNMGLWXEUC%2F20260409%2Fus-east1%2Fs3%2Faws4_request&X-Amz-Date=20260409T212908Z&X-Amz-Expires=3600&X-Amz-Signature=a97045e7f40e97a199a22e2045f131b72973e7eb7663c181eb02a6d10498a4c0&X-Amz-SignedHeaders=content-length%3Bhost&x-amz-checksum-crc32=AAAAAA%3D%3D&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject	blocked	connect-src	PluginRegistry.hYnmbUrG.js:237
https://storage.googleapis.com/fdp-emdash.firebasestorage.app/01KNT2F96ZHQAM3QQ253X4Y9V5.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=GOOG1EGXWWAU54G2JHQSM7LE3VLLISVTPQBAYXZLLIYJ2AJQDQWDNMGLWXEUC%2F20260409%2Fus-east1%2Fs3%2Faws4_request&X-Amz-Date=20260409T213048Z&X-Amz-Expires=3600&X-Amz-Signature=8905c2c90c8b0f4107b4ef0bc8aace3042dba87bb6034a68ad4a2e3bb72880dd&X-Amz-SignedHeaders=content-length%3Bhost&x-amz-checksum-crc32=AAAAAA%3D%3D&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject	blocked	connect-src	PluginRegistry.hYnmbUrG.js:237
Learn more: Content Security Policy - Source Allowlists

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions