mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 01:23:47 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user