fix(security): инлайновые regex-барьеры для CodeQL в openAppScheme и redirect.html

Хелпер-функции (isSafeToOpen/isSafeAppLink) CodeQL как санитайзеры не
распознаёт — js/xss и js/client-side-unvalidated-url-redirection
продолжали флаговаться на sink'ах. Проверки перенесены инлайн к sink'ам
как префикс-якорные RegExp-гарды (распознаваемый барьер): scheme://
обязателен, javascript/data/vbscript/file/blob/about/intent/content/
filesystem (и http для redirect.html) отбрасываются. Семантика та же.
This commit is contained in:
Fringg
2026-07-25 00:32:00 +03:00
parent aa6bf44db9
commit 51d0f0431a
2 changed files with 20 additions and 41 deletions

View File

@@ -129,19 +129,21 @@
// `file:` URI (DOM-XSS) is not scheme://-shaped, and http(s)/intent
// are blocked (open redirect / intent abuse) — so only VPN app
// schemes (happ://, vless://, ...) pass. Mirrors the validation in
// src/pages/DeepLinkRedirect.tsx.
function isSafeAppLink(raw) {
var s = String(raw || '').trim().toLowerCase();
var m = s.match(/^([a-z][a-z0-9+.\-]*):\/\//);
if (!m) return false;
var blocked = ['http', 'https', 'file', 'blob', 'about', 'javascript', 'data', 'vbscript', 'intent', 'content'];
return blocked.indexOf(m[1]) === -1;
}
if (!url || !isSafeAppLink(url)) {
// src/pages/DeepLinkRedirect.tsx. Both checks are INLINE
// prefix-anchored RegExp tests so CodeQL recognizes them as taint
// barriers (js/xss, js/client-side-unvalidated-url-redirection).
function showNoUrl() {
document.getElementById('title').textContent = t.noUrl;
document.getElementById('subtitle').textContent = '';
document.querySelector('.spinner').style.display = 'none';
}
if (!url || !/^[a-z][a-z0-9+.\-]*:\/\//i.test(url)) {
showNoUrl();
return;
}
if (/^(https?|file|blob|about|javascript|data|vbscript|intent|content|filesystem):/i.test(url)) {
showNoUrl();
return;
}

View File

@@ -31,38 +31,15 @@ function isIOS(): boolean {
return navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
}
// Schemes that must never reach a navigation sink even if a caller forgot to
// validate: javascript:/data: are XSS vectors, the rest are abuse surfaces.
// Mirrors isSafeAppLink in public/miniapp/redirect.html.
const BLOCKED_SCHEMES = new Set([
'javascript',
'data',
'vbscript',
'file',
'blob',
'about',
'intent',
'content',
'filesystem',
]);
/**
* Defense-in-depth: callers (DeepLinkRedirect allowlist, Connection config)
* validate upstream, but the utility itself must not trust its input — a
* missed validation at a future call site would otherwise become client-side
* XSS via `location.href = 'javascript:…'`.
*/
function isSafeToOpen(url: string): boolean {
const match = url
.trim()
.toLowerCase()
.match(/^([a-z][a-z0-9+.-]*):\/\//);
if (!match) return false;
return !BLOCKED_SCHEMES.has(match[1]);
}
export function openAppScheme(url: string): void {
if (!isSafeToOpen(url)) return;
// Defense-in-depth: callers (DeepLinkRedirect allowlist, Connection config)
// validate upstream, but the utility itself must not trust its input — a
// missed validation at a future call site would otherwise become client-side
// XSS via `location.href = 'javascript:…'`. Both guards are INLINE
// prefix-anchored RegExp tests so CodeQL recognizes them as taint barriers
// (js/xss, js/client-side-unvalidated-url-redirection).
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(url)) return;
if (/^(javascript|data|vbscript|file|blob|about|intent|content|filesystem):/i.test(url)) return;
const isHttp = /^https?:\/\//i.test(url);
if (isHttp) {