Mini Apps: Support web_app_allowed_protocols (#6888)

This commit is contained in:
zubiden 2026-04-27 14:29:12 +02:00 committed by Alexander Zinchuk
parent ee5bacb048
commit 5841fddd0e
6 changed files with 35 additions and 2 deletions

View File

@ -52,6 +52,7 @@ export interface GramJsAppConfig extends LimitsConfig {
autologin_domains: string[];
autologin_token: string;
url_auth_domains: string[];
web_app_allowed_protocols?: string[];
whitelisted_domains: string[];
premium_purchase_blocked: boolean;
giveaway_gifts_purchase_available: boolean;
@ -184,6 +185,7 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
autologinDomains: appConfig.autologin_domains || [],
urlAuthDomains: appConfig.url_auth_domains || [],
whitelistedDomains: appConfig.whitelisted_domains || [],
webAppAllowedProtocols: appConfig.web_app_allowed_protocols,
maxUniqueReactions: appConfig.reactions_uniq_max,
premiumBotUsername: appConfig.premium_bot_username,
premiumInvoiceSlug: appConfig.premium_invoice_slug,

View File

@ -257,6 +257,7 @@ export interface ApiAppConfig {
autologinDomains: string[];
urlAuthDomains: string[];
whitelistedDomains: string[];
webAppAllowedProtocols: string[];
premiumInvoiceSlug?: string;
premiumBotUsername: string;
isPremiumPurchaseBlocked: boolean;

View File

@ -7,6 +7,7 @@ import type { WebApp, WebAppInboundEvent, WebAppOutboundEvent } from '../../../.
import { VERIFY_AGE_MIN_DEFAULT } from '../../../../config';
import { getWebAppKey } from '../../../../global/helpers';
import { isMessageFromIframe } from '../../../../util/browser/iframe';
import { isValidProtocol } from '../../../../util/browser/url';
import { extractCurrentThemeParams } from '../../../../util/themeStyle';
import { REM } from '../../../common/helpers/mediaDimensions';
@ -241,8 +242,11 @@ const useWebAppFrame = (
}
if (eventType === 'web_app_open_link') {
const linkUrl = eventData.url;
window.open(linkUrl, '_blank', 'noreferrer');
if (!isValidProtocol(eventData.url, getGlobal().appConfig.webAppAllowedProtocols)) {
return;
}
window.open(eventData.url, '_blank', 'noopener,noreferrer');
}
if (eventType === 'web_app_biometry_get_info') {

View File

@ -369,6 +369,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
cached.appConfig = initialState.appConfig;
}
if (cached.appConfig.webAppAllowedProtocols === undefined) {
cached.appConfig.webAppAllowedProtocols = initialState.appConfig.webAppAllowedProtocols;
}
if (untypedCached.sharedState?.settings?.shouldWarnAboutSvg) {
cached.sharedState.settings.shouldWarnAboutFiles = true;
untypedCached.sharedState.settings.shouldWarnAboutSvg = undefined;

View File

@ -150,6 +150,10 @@ export const DEFAULT_APP_CONFIG: ApiAppConfig = {
'z.t.me',
'a.t.me',
],
webAppAllowedProtocols: [
'http',
'https',
],
whitelistedDomains: [
'telegram.dog',
'telegram.me',

View File

@ -4,6 +4,10 @@ import convertPunycode from '../../lib/punycode';
const PROTOCOL_WHITELIST = new Set(['http:', 'https:', 'tg:', 'ton:', 'mailto:', 'tel:']);
const FALLBACK_PREFIX = 'https://';
function normalizeProtocol(protocol: string) {
return protocol.replace(/:$/, '').trim().toLowerCase();
}
export function ensureProtocol(url: string) {
try {
const parsedUrl = new URL(url);
@ -65,3 +69,17 @@ export function isMixedScriptUrl(url: string): boolean {
return false;
}
export function isValidProtocol(url: string, allowedProtocols: string[]) {
if (typeof url !== 'string') {
return false;
}
try {
const parsedUrl = new URL(url);
return allowedProtocols.includes(normalizeProtocol(parsedUrl.protocol));
} catch (err) {
return false;
}
}