global.window = undefined function getCspUrls(env) { // we want to have the following allowed connect-src for a given staticUrl like https://app(.local/.test).tuta.com: // // wss://app(.local/.test).tuta.com for websocket // http(s)://*.api(.local/.test).tuta.com for the web app // api://app(.local/.test).tuta.com for the mobile apps api protocol (intercepted by native part) // https://app(.local/.test).tuta.com for the staticUrl itself // https://(local./test.)tuta.com for the website if (env.staticUrl) { const url = new URL(env.staticUrl) const staticUrlParts = env.staticUrl.split("//") const apiUrl = staticUrlParts[0] + "//*.api." + staticUrlParts[1] const webSocketUrl = `ws${env.staticUrl.substring(4)}` const appApiUrl = `${env.staticUrl.replace(/^https?/, "api")}` const websiteUrl = env.domainConfigs[url.hostname]?.websiteBaseUrl ?? "https://tuta.com" return `${env.staticUrl} ${webSocketUrl} ${apiUrl} ${appApiUrl} ${websiteUrl}` } else { return "" } } /** * Renders the initial HTML page to bootstrap Tutanota for different environments */ export async function renderHtml(scripts, env) { return ` ${csp(env)} ${scripts.map(renderScriptImport).join("\n\t")} ${env.mode === "App" || env.mode === "Desktop" ? "Tuta Mail" : "Tuta Mail: Login & Sign up for free"} ` } function csp(env) { if (env.dist) { if (env.mode === "App" || env.mode === "Desktop") { // differences in comparison to web csp: // * Content Security Policies delivered via a element may not contain the frame-ancestors directive. const cspContent = "default-src 'none';" + " script-src 'self' 'wasm-unsafe-eval';" + " worker-src 'self';" + " frame-src 'none';" + " font-src 'self';" + " img-src http: blob: data: *;" + " media-src blob: data: *;" + " style-src 'unsafe-inline';" + "base-uri 'none';" + ` connect-src 'self' ${getCspUrls(env)};` return `` } else { // csp is in the response headers return "" } } else { const cspContent = "default-src * 'unsafe-inline';" + " script-src * 'unsafe-inline' 'wasm-unsafe-eval';" + " img-src * data: blob: 'unsafe-inline';" + " media-src * data: blob: 'unsafe-inline';" + " style-src * 'unsafe-inline';" + " frame-src *;" + ` connect-src *;` return `` } } function renderScriptImport({ src, type }) { const typeString = type ? ` type="${type}"` : "" return `` }