Introduce CSP (#3512)
This commit is contained in:
parent
c3e86fb157
commit
42fe08cb6e
@ -3,8 +3,10 @@
|
||||
cp -R ./public/* ${1:-"dist"}
|
||||
|
||||
cp ./src/lib/rlottie/rlottie-wasm.wasm ${1:-"dist"}
|
||||
|
||||
cp ./src/lib/video-preview/libav-3.10.5.1.2-webcodecs.wasm.js ${1:-"dist"}
|
||||
cp ./src/lib/video-preview/libav-3.10.5.1.2-webcodecs.wasm.wasm ${1:-"dist"}
|
||||
|
||||
cp ./src/lib/webp/webp_wasm.wasm ${1:-"dist"}
|
||||
|
||||
cp ./node_modules/opus-recorder/dist/decoderWorker.min.wasm ${1:-"dist"}
|
||||
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@ -19478,6 +19478,8 @@
|
||||
},
|
||||
"node_modules/raw-loader": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
|
||||
"integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/rc": {
|
||||
@ -20204,8 +20206,9 @@
|
||||
},
|
||||
"node_modules/script-loader": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz",
|
||||
"integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"raw-loader": "~0.5.1"
|
||||
}
|
||||
|
||||
25
public/compatTest.js
Normal file
25
public/compatTest.js
Normal file
@ -0,0 +1,25 @@
|
||||
function compatTest() {
|
||||
var hasPromise = typeof Promise !== 'undefined';
|
||||
var hasWebSockets = typeof WebSocket !== 'undefined';
|
||||
var hasWebCrypto = window.crypto && typeof window.crypto.subtle !== 'undefined';
|
||||
var hasObjectFromEntries = typeof Object.fromEntries !== 'undefined';
|
||||
|
||||
var isCompatible = hasPromise && hasWebSockets && hasWebCrypto && hasObjectFromEntries;
|
||||
|
||||
if (isCompatible || (window.localStorage && window.localStorage.getItem('tt-ignore-compat'))) {
|
||||
window.isCompatTestPassed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.console && console.warn) {
|
||||
console.warn('Compatibility test report:');
|
||||
console.warn('Promise', hasPromise);
|
||||
console.warn('WebSocket', hasWebSockets);
|
||||
console.warn('WebCrypto', hasWebCrypto);
|
||||
console.warn('Object.fromEntries', hasObjectFromEntries);
|
||||
}
|
||||
|
||||
document.body.innerHTML = '<iframe src="./unsupported.html" width="100%" height="100%">';
|
||||
}
|
||||
|
||||
compatTest();
|
||||
11
public/redirect.js
Normal file
11
public/redirect.js
Normal file
@ -0,0 +1,11 @@
|
||||
const { pathname, hostname, href } = window.location;
|
||||
|
||||
if (pathname.startsWith('/z')) {
|
||||
window.location.href = href.replace('/z', '/a');
|
||||
}
|
||||
|
||||
if (
|
||||
(hostname === 'weba.telegram.org' || hostname === 'webz.telegram.org') && !localStorage.getItem('tt-global-state')
|
||||
) {
|
||||
window.location.href = 'https://web.telegram.org/a';
|
||||
}
|
||||
@ -17,7 +17,7 @@ export const DEBUG_MORE = false;
|
||||
export const STRICTERDOM_ENABLED = DEBUG && !IS_ELECTRON;
|
||||
|
||||
export const BETA_CHANGELOG_URL = 'https://telegra.ph/WebA-Beta-03-20';
|
||||
export const ELECTRON_HOST_URL = 'https://telegram-a-host';
|
||||
export const ELECTRON_HOST_URL = process.env.ELECTRON_HOST_URL!;
|
||||
|
||||
export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.';
|
||||
export const DEBUG_GRAMJS = false;
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<meta name="description"
|
||||
content="Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no, viewport-fit=cover">
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no, viewport-fit=cover">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-title" content="<%= htmlWebpackPlugin.options.appTitle %>">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
@ -23,12 +23,14 @@
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<meta name="google" content="notranslate">
|
||||
|
||||
<meta http-equiv="Content-Security-Policy" content="<%= htmlWebpackPlugin.options.csp %>">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="<%= htmlWebpackPlugin.options.baseUrl %>">
|
||||
<meta property="og:title" content="<%= htmlWebpackPlugin.options.appTitle %>">
|
||||
<meta property="og:description"
|
||||
content="Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.">
|
||||
content="Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.">
|
||||
<meta property="og:image" content="./<%= htmlWebpackPlugin.options.mainIcon %>.png">
|
||||
|
||||
<!-- Twitter -->
|
||||
@ -48,17 +50,8 @@
|
||||
<link rel="alternate icon" href="./favicon.ico" type="image/x-icon">
|
||||
<link rel="manifest" id="the-manifest-placeholder" href="./<%= htmlWebpackPlugin.options.manifest %>">
|
||||
|
||||
<script>
|
||||
const { pathname, hostname, href } = window.location;
|
||||
|
||||
if (pathname.startsWith('/z')) {
|
||||
window.location.href = href.replace('/z', '/a');
|
||||
}
|
||||
|
||||
if ((hostname === 'weba.telegram.org' || hostname === 'webz.telegram.org') && !localStorage.getItem('tt-global-state')) {
|
||||
window.location.href = 'https://web.telegram.org/a';
|
||||
}
|
||||
</script>
|
||||
<script src="./compatTest.js"></script>
|
||||
<script src="./redirect.js"></script>
|
||||
</head>
|
||||
|
||||
<body id="root">
|
||||
@ -68,33 +61,6 @@
|
||||
<p>Please, enable JavaScript to open the app.</p>
|
||||
</noscript>
|
||||
<div id="portals"></div>
|
||||
<script>
|
||||
function compatTest() {
|
||||
var hasPromise = typeof Promise !== 'undefined';
|
||||
var hasWebSockets = typeof WebSocket !== 'undefined';
|
||||
var hasWebCrypto = window.crypto && typeof window.crypto.subtle !== 'undefined';
|
||||
var hasObjectFromEntries = typeof Object.fromEntries !== 'undefined';
|
||||
|
||||
var isCompatible = hasPromise && hasWebSockets && hasWebCrypto && hasObjectFromEntries;
|
||||
|
||||
if (isCompatible || (window.localStorage && window.localStorage.getItem('tt-ignore-compat'))) {
|
||||
window.isCompatTestPassed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.console && console.warn) {
|
||||
console.warn('Compatibility test report:');
|
||||
console.warn('Promise', hasPromise);
|
||||
console.warn('WebSocket', hasWebSockets);
|
||||
console.warn('WebCrypto', hasWebCrypto);
|
||||
console.warn('Object.fromEntries', hasObjectFromEntries);
|
||||
}
|
||||
|
||||
document.body.innerHTML = '<iframe src="./unsupported.html" width="100%" height="100%">';
|
||||
}
|
||||
|
||||
compatTest();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@ import { inflate } from 'pako/dist/pako_inflate';
|
||||
import { createWorkerInterface } from '../../util/createPostMessageInterface';
|
||||
import type { CancellableCallback } from '../../util/PostMessageConnector';
|
||||
|
||||
import 'script-loader!./rlottie-wasm';
|
||||
importScripts(new URL('./rlottie-wasm.js', import.meta.url));
|
||||
|
||||
declare const Module: any;
|
||||
|
||||
|
||||
@ -75,12 +75,13 @@ function destroy() {
|
||||
|
||||
async function loadLibAV() {
|
||||
if (isLoaded) return;
|
||||
// @ts-ignore
|
||||
await import('script-loader!./libav-3.10.5.1.2-webcodecs');
|
||||
|
||||
importScripts(new URL('./libav-3.10.5.1.2-webcodecs.js', import.meta.url));
|
||||
await LibAVWebCodecs.load({
|
||||
polyfill: true,
|
||||
libavOptions: { noworker: true, nosimd: true },
|
||||
});
|
||||
|
||||
isLoaded = true;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import 'script-loader!./webp_wasm';
|
||||
importScripts(new URL('./webp_wasm.js', import.meta.url));
|
||||
|
||||
Module.onRuntimeInitialized = async () => {
|
||||
self.postMessage({ type: 'initialized' });
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { DEBUG } from './config';
|
||||
import { respondForProgressive } from './serviceWorker/progressive';
|
||||
import { respondForDownload } from './serviceWorker/download';
|
||||
import { respondWithCache, clearAssetCache, respondWithCacheNetworkFirst } from './serviceWorker/assetCache';
|
||||
import { DEBUG, ELECTRON_HOST_URL, IS_ELECTRON } from '../config';
|
||||
import { respondForProgressive } from './progressive';
|
||||
import { respondForDownload } from './download';
|
||||
import { respondWithCache, clearAssetCache, respondWithCacheNetworkFirst } from './assetCache';
|
||||
import {
|
||||
handlePush,
|
||||
handleNotificationClick,
|
||||
handleClientMessage as handleNotificationMessage,
|
||||
} from './serviceWorker/pushNotification';
|
||||
import { respondForShare, handleClientMessage as handleShareMessage } from './serviceWorker/share';
|
||||
} from './pushNotification';
|
||||
import { respondForShare, handleClientMessage as handleShareMessage } from './share';
|
||||
|
||||
import { pause } from './util/schedulers';
|
||||
import { pause } from '../util/schedulers';
|
||||
|
||||
declare const self: ServiceWorkerGlobalScope;
|
||||
|
||||
@ -48,28 +48,35 @@ self.addEventListener('activate', (e) => {
|
||||
|
||||
self.addEventListener('fetch', (e: FetchEvent) => {
|
||||
const { url } = e.request;
|
||||
const scope = IS_ELECTRON ? ELECTRON_HOST_URL : self.registration.scope;
|
||||
if (!url.startsWith(scope)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url.includes('/progressive/')) {
|
||||
const { pathname, protocol } = new URL(url);
|
||||
const { pathname: scopePathname } = new URL(scope);
|
||||
|
||||
if (pathname.startsWith('/progressive/')) {
|
||||
e.respondWith(respondForProgressive(e));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url.includes('/download/')) {
|
||||
if (pathname.startsWith('/download/')) {
|
||||
e.respondWith(respondForDownload(e));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url.includes('/share/')) {
|
||||
if (pathname.startsWith('/share/')) {
|
||||
e.respondWith(respondForShare(e));
|
||||
}
|
||||
|
||||
if (url.startsWith('http')) {
|
||||
if (new URL(url).pathname === '/' || url.match(RE_NETWORK_FIRST_ASSETS)) {
|
||||
if (protocol === 'http:' || protocol === 'https:') {
|
||||
if (pathname === scopePathname || pathname.match(RE_NETWORK_FIRST_ASSETS)) {
|
||||
e.respondWith(respondWithCacheNetworkFirst(e));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url.match(RE_CACHE_FIRST_ASSETS)) {
|
||||
if (pathname.match(RE_CACHE_FIRST_ASSETS)) {
|
||||
e.respondWith(respondWithCache(e));
|
||||
return true;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { IS_ELECTRON, ELECTRON_HOST_URL } from '../config';
|
||||
import { ELECTRON_HOST_URL, IS_ELECTRON } from '../config';
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const cacheApi = self.caches;
|
||||
|
||||
@ -60,7 +60,7 @@ if (IS_SERVICE_WORKER_SUPPORTED) {
|
||||
}
|
||||
}
|
||||
|
||||
await navigator.serviceWorker.register(new URL('../serviceWorker.ts', import.meta.url));
|
||||
await navigator.serviceWorker.register(new URL('../serviceWorker', import.meta.url));
|
||||
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@ -7,7 +7,7 @@ import { hasStoredSession } from './sessions';
|
||||
const WEBSYNC_URLS = [
|
||||
't.me',
|
||||
'telegram.me',
|
||||
].map((domain) => `//${domain}/_websync_?`);
|
||||
].map((domain) => `https://${domain}/_websync_?`);
|
||||
const WEBSYNC_VERSION = `${APP_VERSION} Z`;
|
||||
const WEBSYNC_KEY = 'tgme_sync';
|
||||
const WEBSYNC_TIMEOUT = 86400;
|
||||
|
||||
@ -27,15 +27,30 @@ const {
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const STATOSCOPE_REFERENCE_URL = 'https://tga.dev/build-stats.json';
|
||||
let isReferenceFetched = false;
|
||||
const DEFAULT_APP_TITLE = `Telegram${APP_ENV !== 'production' ? ' Beta' : ''}`;
|
||||
|
||||
const {
|
||||
BASE_URL = 'https://web.telegram.org/a/',
|
||||
ELECTRON_HOST_URL = 'https://telegram-a-host',
|
||||
APP_TITLE = DEFAULT_APP_TITLE,
|
||||
} = process.env;
|
||||
|
||||
const CSP = `
|
||||
default-src 'self';
|
||||
connect-src 'self' wss://*.web.telegram.org blob: http: https:;
|
||||
script-src 'self' 'wasm-unsafe-eval' https://t.me/_websync_ https://telegram.me/_websync_;
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: blob: https://ss3.4sqi.net/img/categories_v2/ ${IS_ELECTRON ? BASE_URL : ''};
|
||||
media-src 'self' blob: data: ${IS_ELECTRON ? [BASE_URL, ELECTRON_HOST_URL].join(' ') : ''};
|
||||
object-src 'none';
|
||||
frame-src http: https:;
|
||||
base-uri 'none';
|
||||
form-action 'none';`
|
||||
.replace(/\s+/g, ' ').trim();
|
||||
|
||||
const STATOSCOPE_REFERENCE_URL = 'https://tga.dev/build-stats.json';
|
||||
let isReferenceFetched = false;
|
||||
|
||||
export default function createConfig(
|
||||
_: any,
|
||||
{ mode = 'production' }: { mode: 'none' | 'development' | 'production' },
|
||||
@ -76,6 +91,9 @@ export default function createConfig(
|
||||
devMiddleware: {
|
||||
stats: 'minimal',
|
||||
},
|
||||
headers: {
|
||||
'Content-Security-Policy': CSP,
|
||||
},
|
||||
},
|
||||
|
||||
output: {
|
||||
@ -180,6 +198,7 @@ export default function createConfig(
|
||||
mainIcon: APP_ENV === 'production' ? 'icon-192x192' : 'icon-dev-192x192',
|
||||
manifest: APP_ENV === 'production' ? 'site.webmanifest' : 'site_dev.webmanifest',
|
||||
baseUrl: BASE_URL,
|
||||
csp: CSP,
|
||||
template: 'src/index.html',
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
@ -199,7 +218,9 @@ export default function createConfig(
|
||||
TELEGRAM_T_API_HASH: undefined,
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
TEST_SESSION: null,
|
||||
ELECTRON_HOST_URL,
|
||||
}),
|
||||
// Updates each dev re-build to provide current git branch or commit hash
|
||||
new DefinePlugin({
|
||||
APP_VERSION: JSON.stringify(appVersion),
|
||||
APP_REVISION: DefinePlugin.runtimeValue(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user