Files
supabase/apps/studio/csp.js
T
Ivan Vasilov 554a91e1b2 fix: update storage to use the new URL endpoint (#42974)
This pull request introduces improvements to the handling of storage
endpoints in the Studio app, ensuring that storage operations utilize
the correct endpoint and enhancing maintainability through import path
updates and code clarity. The most significant changes are grouped
below:

**Storage Endpoint Handling:**

* The `useProjectEndpointQuery` hook now returns both `clientEndpoint`
and `storageEndpoint`, allowing storage operations to use a dedicated
endpoint when available.
* In the `StorageExplorerStateContextProvider`, the logic for
constructing the resumable upload URL now prefers `storageEndpoint` if
present, falling back to `clientEndpoint` otherwise. This ensures
uploads use the most appropriate endpoint.
* The context provider now sets `clientEndpoint` to `storageEndpoint`
when available, further aligning storage requests with the correct
endpoint.
* The dependency array for the provider effect now includes
`storageEndpoint`, ensuring state updates when the storage endpoint
changes.

**Code Quality and Maintainability:**

* Import paths in `storage-explorer.tsx` have been updated to use alias
(`@/`) references, improving clarity and maintainability. Type-only
imports are also used where appropriate, reducing bundle size and
potential circular dependencies.

**Code Clarity:**

* Added comments to chunk size calculations in the upload logic,
clarifying the rationale behind chunk sizing for different file sizes.
2026-02-18 14:53:17 +01:00

211 lines
6.8 KiB
JavaScript

const API_URL = process.env.NEXT_PUBLIC_API_URL
? new URL(process.env.NEXT_PUBLIC_API_URL).origin
: ''
const SUPABASE_URL = process.env.SUPABASE_URL ? new URL(process.env.SUPABASE_URL).origin : ''
const GOTRUE_URL = process.env.NEXT_PUBLIC_GOTRUE_URL
? new URL(process.env.NEXT_PUBLIC_GOTRUE_URL).origin
: ''
const SUPABASE_PROJECTS_URL = 'https://*.supabase.co https://*.storage.supabase.co'
const SUPABASE_PROJECTS_URL_WS = 'wss://*.supabase.co'
// construct the URL for the Websocket Local URLs
let SUPABASE_LOCAL_PROJECTS_URL_WS = ''
if (SUPABASE_URL) {
const url = new URL(SUPABASE_URL)
const wsUrl = `${url.hostname}:${url.port}`
SUPABASE_LOCAL_PROJECTS_URL_WS = `ws://${wsUrl} wss://${wsUrl}`
}
// Needed to test docs search in local dev
const SUPABASE_DOCS_PROJECT_URL = process.env.NEXT_PUBLIC_SUPABASE_URL
? new URL(process.env.NEXT_PUBLIC_SUPABASE_URL).origin
: ''
// Needed to test docs content API in local dev
const SUPABASE_CONTENT_API_URL = process.env.NEXT_PUBLIC_CONTENT_API_URL
? new URL(process.env.NEXT_PUBLIC_CONTENT_API_URL).origin
: ''
const isDevOrStaging =
process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview' ||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'local' ||
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
const NIMBUS_STAGING_PROJECTS_URL = 'https://*.nmb-proj.com'
const NIMBUS_STAGING_PROJECTS_URL_WS = 'wss://*.nmb-proj.com'
const NIMBUS_PROD_PROJECTS_URL = process.env.NIMBUS_PROD_PROJECTS_URL || ''
const NIMBUS_PROD_PROJECTS_URL_WS = process.env.NIMBUS_PROD_PROJECTS_URL_WS || ''
const SUPABASE_STAGING_PROJECTS_URL = 'https://*.supabase.red https://*.storage.supabase.red'
const SUPABASE_STAGING_PROJECTS_URL_WS = 'wss://*.supabase.red'
const SUPABASE_COM_URL = 'https://supabase.com'
const CLOUDFLARE_CDN_URL = 'https://cdnjs.cloudflare.com'
const HCAPTCHA_SUBDOMAINS_URL = 'https://*.hcaptcha.com'
const HCAPTCHA_ASSET_URL = 'https://newassets.hcaptcha.com'
const HCAPTCHA_JS_URL = 'https://js.hcaptcha.com'
const CONFIGCAT_URL = 'https://cdn-global.configcat.com'
const CONFIGCAT_PROXY_URL = ['staging', 'local'].includes(process.env.NEXT_PUBLIC_ENVIRONMENT ?? '')
? 'https://configcat.supabase.green'
: 'https://configcat.supabase.com'
const STRIPE_SUBDOMAINS_URL = 'https://*.stripe.com'
const STRIPE_JS_URL = 'https://js.stripe.com'
const STRIPE_NETWORK_URL = 'https://*.stripe.network'
const CLOUDFLARE_URL = 'https://www.cloudflare.com'
const VERCEL_URL = 'https://vercel.com'
const VERCEL_INSIGHTS_URL = 'https://*.vercel-insights.com'
const GITHUB_API_URL = 'https://api.github.com'
const GITHUB_USER_CONTENT_URL = 'https://raw.githubusercontent.com'
const GITHUB_USER_AVATAR_URL = 'https://avatars.githubusercontent.com'
const GOOGLE_USER_AVATAR_URL = 'https://lh3.googleusercontent.com'
// This is a custom domain for Stape, which isused for GTM servers
const STAPE_URL = 'https://ss.supabase.com'
const VERCEL_LIVE_URL = 'https://vercel.live'
const SENTRY_URL =
'https://*.ingest.sentry.io https://*.ingest.us.sentry.io https://*.ingest.de.sentry.io'
const SUPABASE_ASSETS_URL =
process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
? 'https://frontend-assets.supabase.green'
: 'https://frontend-assets.supabase.com'
const POSTHOG_URL = isDevOrStaging ? 'https://ph.supabase.green' : 'https://ph.supabase.com'
const USERCENTRICS_URLS = 'https://*.usercentrics.eu'
const USERCENTRICS_APP_URL = 'https://app.usercentrics.eu'
// used by vercel live preview
const PUSHER_URL = 'https://*.pusher.com'
const PUSHER_URL_WS = 'wss://*.pusher.com'
const GOOGLE_MAPS_API_URL = 'https://maps.googleapis.com'
module.exports.getCSP = function getCSP() {
const DEFAULT_SRC_URLS = [
API_URL,
SUPABASE_URL,
GOTRUE_URL,
SUPABASE_LOCAL_PROJECTS_URL_WS,
SUPABASE_PROJECTS_URL,
SUPABASE_PROJECTS_URL_WS,
HCAPTCHA_SUBDOMAINS_URL,
CONFIGCAT_URL,
CONFIGCAT_PROXY_URL,
STRIPE_SUBDOMAINS_URL,
STRIPE_NETWORK_URL,
CLOUDFLARE_URL,
VERCEL_INSIGHTS_URL,
GITHUB_API_URL,
GITHUB_USER_CONTENT_URL,
SUPABASE_ASSETS_URL,
USERCENTRICS_URLS,
STAPE_URL,
GOOGLE_MAPS_API_URL,
POSTHOG_URL,
...(!!NIMBUS_PROD_PROJECTS_URL ? [NIMBUS_PROD_PROJECTS_URL, NIMBUS_PROD_PROJECTS_URL_WS] : []),
CLOUDFLARE_CDN_URL,
]
const SCRIPT_SRC_URLS = [
CLOUDFLARE_CDN_URL,
HCAPTCHA_JS_URL,
STRIPE_JS_URL,
SUPABASE_ASSETS_URL,
STAPE_URL,
POSTHOG_URL,
]
const FRAME_SRC_URLS = [
HCAPTCHA_ASSET_URL,
STRIPE_JS_URL,
STAPE_URL,
...(isDevOrStaging ? [POSTHOG_URL] : []),
]
const IMG_SRC_URLS = [
SUPABASE_URL,
SUPABASE_COM_URL,
SUPABASE_PROJECTS_URL,
GITHUB_USER_AVATAR_URL,
GOOGLE_USER_AVATAR_URL,
SUPABASE_ASSETS_URL,
USERCENTRICS_APP_URL,
STAPE_URL,
...(!!NIMBUS_PROD_PROJECTS_URL ? [NIMBUS_PROD_PROJECTS_URL, NIMBUS_PROD_PROJECTS_URL_WS] : []),
]
const STYLE_SRC_URLS = [CLOUDFLARE_CDN_URL, SUPABASE_ASSETS_URL]
const FONT_SRC_URLS = [CLOUDFLARE_CDN_URL, SUPABASE_ASSETS_URL]
const defaultSrcDirective = [
`default-src 'self'`,
...DEFAULT_SRC_URLS,
...(isDevOrStaging
? [
SUPABASE_STAGING_PROJECTS_URL,
SUPABASE_STAGING_PROJECTS_URL_WS,
NIMBUS_STAGING_PROJECTS_URL,
NIMBUS_STAGING_PROJECTS_URL_WS,
VERCEL_LIVE_URL,
SUPABASE_DOCS_PROJECT_URL,
SUPABASE_CONTENT_API_URL,
]
: []),
PUSHER_URL_WS,
SENTRY_URL,
].join(' ')
const imgSrcDirective = [
`img-src 'self'`,
`blob:`,
`data:`,
...IMG_SRC_URLS,
...(isDevOrStaging
? [SUPABASE_STAGING_PROJECTS_URL, NIMBUS_STAGING_PROJECTS_URL, VERCEL_URL]
: []),
].join(' ')
const scriptSrcDirective = [
`script-src 'self'`,
`'unsafe-eval'`,
`'unsafe-inline'`,
...SCRIPT_SRC_URLS,
VERCEL_LIVE_URL,
PUSHER_URL,
GOOGLE_MAPS_API_URL,
].join(' ')
const frameSrcDirective = [`frame-src 'self'`, ...FRAME_SRC_URLS, VERCEL_LIVE_URL].join(' ')
const styleSrcDirective = [
`style-src 'self'`,
`'unsafe-inline'`,
...STYLE_SRC_URLS,
VERCEL_LIVE_URL,
].join(' ')
const fontSrcDirective = [`font-src 'self'`, ...FONT_SRC_URLS, VERCEL_LIVE_URL].join(' ')
const workerSrcDirective = [`worker-src 'self'`, `blob:`, `data:`].join(' ')
const cspDirectives = [
defaultSrcDirective,
imgSrcDirective,
scriptSrcDirective,
frameSrcDirective,
styleSrcDirective,
fontSrcDirective,
workerSrcDirective,
`object-src 'none'`,
`base-uri 'self'`,
`form-action 'self'`,
`frame-ancestors 'none'`,
`block-all-mixed-content`,
...(process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' &&
process.env.NEXT_PUBLIC_ENVIRONMENT === 'prod'
? [`upgrade-insecure-requests`]
: []),
]
const csp = cspDirectives.join('; ') + ';'
// Replace newline characters and spaces
return csp.replace(/\s{2,}/g, ' ').trim()
}