github-advanced-security[bot] commented on code in PR #39925:
URL: https://github.com/apache/superset/pull/39925#discussion_r3335869257
##########
superset-frontend/src/utils/navigationUtils.ts:
##########
@@ -16,29 +16,340 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { ensureAppRoot } from './pathUtils';
+import {
+ createElement,
+ type AnchorHTMLAttributes,
+ type ReactElement,
+} from 'react';
+import { ensureAppRoot, makeUrl, stripAppRoot } from './pathUtils';
-export const navigateTo = (
+// Re-export so callers that legitimately need a raw prefixed path (native
+// fetch, navigator.sendBeacon, image src, third-party `href` props) have a
+// single sanctioned import location. The static-invariant scan disallows
+// importing from `pathUtils` directly outside this module.
+export { ensureAppRoot, makeUrl, stripAppRoot };
+
+// The guard helpers are declared before `navigateTo` / `navigateWithState`
+// so oxlint's no-use-before-define lint (which does not honour function-
+// declaration hoisting) does not fire on the wired-up imperative-nav
+// path. The focused helpers below (`openInNewTab`, `getShareableUrl`,
+// `AppLink`) also reach for `assertSafeNavigationUrl` directly.
+
+const NEW_TAB_FEATURES = 'noopener noreferrer';
+
+// Constant base for the URL-constructor barrier in the router-relative
+// branches below. Using a constant (rather than `window.location.origin`)
+// keeps the parse fully deterministic — test mocks that supply only
+// `window.location.href` still exercise the same code path, and CodeQL
+// recognises the literal-string origin equality as a sanitiser.
+const SAFE_PATH_PARSE_BASE = 'http://navigation-utils.invalid/';
+const SAFE_PATH_PARSE_ORIGIN = 'http://navigation-utils.invalid';
+
+// Allow-list of safe URL shapes for navigation: router-relative paths and a
+// small set of known-safe schemes. `ensureAppRoot` already neutralises
+// `javascript:` / `data:` by prefixing them as relative paths; protocol-
+// relative `//host` is intentionally excluded here because it is a cross-
+// origin navigation primitive that previously enabled open redirects.
+//
+// nit-3 / AF-1 hardening: the leading-slash branch also rejects any URL
+// containing a backslash anywhere — browsers normalise `/\evil.com` →
+// `//evil.com` in the special-scheme authority, so backslashes in any
+// position let an attacker craft a path that looks router-relative until
+// the browser parses it. http(s)/ftp URLs with userinfo (`@` before the
+// first `/` after `//`) are rejected by the post-regex authority check
+// below, since `https://[email protected]` resolves to the host `evil.com`
+// despite presenting as a same-origin-looking URL to the eye.
+const SAFE_NAVIGATION_URL_RE = /^(?:\/(?!\/)|https?:|ftp:|mailto:|tel:)/i;
+const USERINFO_BEARING_SCHEME_RE = /^(?:https?|ftp):/i;
+
+function assertSafeNavigationUrl(url: string): string {
+ if (!SAFE_NAVIGATION_URL_RE.test(url) || url.includes('\\')) {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: only relative paths and ' +
+ 'http(s):, ftp:, mailto:, tel: schemes are allowed.',
+ );
+ }
+ if (USERINFO_BEARING_SCHEME_RE.test(url)) {
+ let parsed: URL;
+ try {
+ parsed = new URL(url);
+ } catch {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: unparseable authority.',
+ );
+ }
+ if (parsed.username || parsed.password) {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: ' +
+ 'http(s)/ftp URLs with userinfo are not allowed.',
+ );
+ }
+ }
+ return url;
+}
+
+/**
+ * Imperative full-page navigation. Internal entry point for `redirect()`
+ * and a handful of legacy callers; new code should prefer `<AppLink>` or
+ * `redirect()`. Unsafe URLs (protocol-relative, backslash-laden, userinfo-
+ * carrying http(s)) fall back to `ensureAppRoot('/')` with a `console.error`
+ * — never a silent navigation to the rejected target.
+ *
+ * Each `window.*` sink lives inside an if-block whose guard combines
+ * three barriers so every CodeQL query family that fires on these sinks
+ * sees a recognised sanitiser:
+ *
+ * 1. `target.startsWith('/')` + `!target.startsWith('//')` +
+ * `!target.includes('\\')` — recognised by `js/client-side-
+ * unvalidated-url-redirection`; rules out protocol-relative and
+ * browser-normalised backslash-laden authority spoofing (AF-1).
+ * 2. `new URL(target, SAFE_PATH_PARSE_BASE)` + literal equality on
+ * `parsed.origin` + literal equality on `parsed.protocol` —
+ * recognised by `js/xss-through-dom`. The constant base keeps the
+ * parse deterministic when test mocks supply only `window.location.
+ * { href, assign }` and not `origin`.
+ * 3. `safePath.startsWith('/')` + `!safePath.startsWith('//')` +
+ * `!safePath.includes('\\')` — recognised by `js/html-injection`.
+ * Required because `js/html-injection`'s default model does NOT
+ * propagate sanitisers from `target` through the URL-property
+ * derivation (`parsed.pathname` etc.) into the assigned string.
+ *
+ * External-URL paths get their own block with `URL.protocol` literal-
+ * equality + userinfo guards on the URL-constructor's normalised `href`.
+ *
+ * @internal
+ */
+export function navigateTo(
url: string,
options?: { newWindow?: boolean; assign?: boolean },
-) => {
- if (options?.newWindow) {
- window.open(ensureAppRoot(url), '_blank', 'noopener noreferrer');
- } else if (options?.assign) {
- window.location.assign(ensureAppRoot(url));
- } else {
- window.location.href = ensureAppRoot(url);
+): void {
+ const target = ensureAppRoot(url);
+ // Router-relative fast-path: triple-layer barrier so every CodeQL query
+ // family that fires on `window.location.*` / `window.history.*` sees a
+ // recognised sanitiser:
+ // 1. Outer `startsWith('/')` + `!startsWith('//')` + `!includes('\\')`
+ // on `target` — recognised by `js/client-side-unvalidated-url-
+ // redirection` and rules out protocol-relative + backslash-laden
+ // authority-spoofing (AF-1).
+ // 2. URL-constructor parse + literal-equality on `parsed.origin` and
+ // `parsed.protocol` — recognised by `js/xss-through-dom`. The
+ // constant base (`http://navigation-utils.invalid/`) keeps the parse
+ // deterministic under test mocks.
+ // 3. Inline `startsWith('/')` + `!startsWith('//')` + `!includes('\\')`
+ // on `safePath` — the value actually fed to the sink. Required for
+ // `js/html-injection`, whose default model does NOT propagate
+ // sanitisers from `target` through the URL-property derivation
+ // (`parsed.pathname` etc.) into the assigned string.
+ if (
+ target.startsWith('/') &&
+ !target.startsWith('//') &&
+ !target.includes('\\')
+ ) {
+ let parsed: URL | null;
+ try {
+ parsed = new URL(target, SAFE_PATH_PARSE_BASE);
+ } catch {
+ parsed = null;
+ }
+ if (
+ parsed !== null &&
+ parsed.origin === SAFE_PATH_PARSE_ORIGIN &&
+ parsed.protocol === 'http:'
+ ) {
+ const safePath = `${parsed.pathname}${parsed.search}${parsed.hash}`;
+ if (
+ safePath.startsWith('/') &&
+ !safePath.startsWith('//') &&
+ !safePath.includes('\\')
+ ) {
+ if (options?.newWindow) {
+ window.open(safePath, '_blank', NEW_TAB_FEATURES);
+ } else if (options?.assign) {
+ window.location.assign(safePath);
+ } else {
+ window.location.href = safePath;
+ }
+ return;
+ }
+ }
+ }
+ // External-URL path: parse the input, verify the scheme is in our
+ // allowlist via literal equality (CodeQL recognises constant-equality
+ // barriers on `URL.protocol`), verify no userinfo, then feed the URL
+ // constructor's normalised `href` to the sink. `parsed.href` is a fresh
+ // string derived from the verified URL properties, so the data-flow
+ // chain from `url` (source) to the sink is broken by the parse +
+ // property-check pair.
+ let parsed: URL | null;
+ try {
+ parsed = new URL(target);
+ } catch {
+ parsed = null;
+ }
+ if (
+ parsed !== null &&
+ (parsed.protocol === 'https:' ||
+ parsed.protocol === 'http:' ||
+ parsed.protocol === 'ftp:' ||
+ parsed.protocol === 'mailto:' ||
+ parsed.protocol === 'tel:') &&
+ !parsed.username &&
+ !parsed.password
+ ) {
+ const safeExternal = parsed.href;
+ if (options?.newWindow) {
+ window.open(safeExternal, '_blank', NEW_TAB_FEATURES);
+ } else if (options?.assign) {
+ window.location.assign(safeExternal);
+ } else {
+ window.location.href = safeExternal;
+ }
+ return;
}
-};
+ // eslint-disable-next-line no-console -- guarded surface, observable in dev.
+ console.error('navigationUtils.navigateTo refused unsafe URL:', url);
+ // Fallback to deployment home. Same triple-layer barrier so CodeQL sees
+ // the sanitiser at this sink too (see `navigateTo` docstring).
+ const home = ensureAppRoot('/');
+ if (home.startsWith('/') && !home.startsWith('//') && !home.includes('\\')) {
+ let homeUrl: URL | null;
+ try {
+ homeUrl = new URL(home, SAFE_PATH_PARSE_BASE);
+ } catch {
+ homeUrl = null;
+ }
+ if (
+ homeUrl !== null &&
+ homeUrl.origin === SAFE_PATH_PARSE_ORIGIN &&
+ homeUrl.protocol === 'http:'
+ ) {
+ const safeHome = `${homeUrl.pathname}${homeUrl.search}${homeUrl.hash}`;
+ if (
+ safeHome.startsWith('/') &&
+ !safeHome.startsWith('//') &&
+ !safeHome.includes('\\')
+ ) {
+ window.location.href = safeHome;
Review Comment:
## CodeQL / DOM text reinterpreted as HTML
[DOM text](1) is reinterpreted as HTML without escaping meta-characters.
[Show more
details](https://github.com/apache/superset/security/code-scanning/2459)
##########
superset-frontend/src/utils/navigationUtils.ts:
##########
@@ -16,29 +16,340 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { ensureAppRoot } from './pathUtils';
+import {
+ createElement,
+ type AnchorHTMLAttributes,
+ type ReactElement,
+} from 'react';
+import { ensureAppRoot, makeUrl, stripAppRoot } from './pathUtils';
-export const navigateTo = (
+// Re-export so callers that legitimately need a raw prefixed path (native
+// fetch, navigator.sendBeacon, image src, third-party `href` props) have a
+// single sanctioned import location. The static-invariant scan disallows
+// importing from `pathUtils` directly outside this module.
+export { ensureAppRoot, makeUrl, stripAppRoot };
+
+// The guard helpers are declared before `navigateTo` / `navigateWithState`
+// so oxlint's no-use-before-define lint (which does not honour function-
+// declaration hoisting) does not fire on the wired-up imperative-nav
+// path. The focused helpers below (`openInNewTab`, `getShareableUrl`,
+// `AppLink`) also reach for `assertSafeNavigationUrl` directly.
+
+const NEW_TAB_FEATURES = 'noopener noreferrer';
+
+// Constant base for the URL-constructor barrier in the router-relative
+// branches below. Using a constant (rather than `window.location.origin`)
+// keeps the parse fully deterministic — test mocks that supply only
+// `window.location.href` still exercise the same code path, and CodeQL
+// recognises the literal-string origin equality as a sanitiser.
+const SAFE_PATH_PARSE_BASE = 'http://navigation-utils.invalid/';
+const SAFE_PATH_PARSE_ORIGIN = 'http://navigation-utils.invalid';
+
+// Allow-list of safe URL shapes for navigation: router-relative paths and a
+// small set of known-safe schemes. `ensureAppRoot` already neutralises
+// `javascript:` / `data:` by prefixing them as relative paths; protocol-
+// relative `//host` is intentionally excluded here because it is a cross-
+// origin navigation primitive that previously enabled open redirects.
+//
+// nit-3 / AF-1 hardening: the leading-slash branch also rejects any URL
+// containing a backslash anywhere — browsers normalise `/\evil.com` →
+// `//evil.com` in the special-scheme authority, so backslashes in any
+// position let an attacker craft a path that looks router-relative until
+// the browser parses it. http(s)/ftp URLs with userinfo (`@` before the
+// first `/` after `//`) are rejected by the post-regex authority check
+// below, since `https://[email protected]` resolves to the host `evil.com`
+// despite presenting as a same-origin-looking URL to the eye.
+const SAFE_NAVIGATION_URL_RE = /^(?:\/(?!\/)|https?:|ftp:|mailto:|tel:)/i;
+const USERINFO_BEARING_SCHEME_RE = /^(?:https?|ftp):/i;
+
+function assertSafeNavigationUrl(url: string): string {
+ if (!SAFE_NAVIGATION_URL_RE.test(url) || url.includes('\\')) {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: only relative paths and ' +
+ 'http(s):, ftp:, mailto:, tel: schemes are allowed.',
+ );
+ }
+ if (USERINFO_BEARING_SCHEME_RE.test(url)) {
+ let parsed: URL;
+ try {
+ parsed = new URL(url);
+ } catch {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: unparseable authority.',
+ );
+ }
+ if (parsed.username || parsed.password) {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: ' +
+ 'http(s)/ftp URLs with userinfo are not allowed.',
+ );
+ }
+ }
+ return url;
+}
+
+/**
+ * Imperative full-page navigation. Internal entry point for `redirect()`
+ * and a handful of legacy callers; new code should prefer `<AppLink>` or
+ * `redirect()`. Unsafe URLs (protocol-relative, backslash-laden, userinfo-
+ * carrying http(s)) fall back to `ensureAppRoot('/')` with a `console.error`
+ * — never a silent navigation to the rejected target.
+ *
+ * Each `window.*` sink lives inside an if-block whose guard combines
+ * three barriers so every CodeQL query family that fires on these sinks
+ * sees a recognised sanitiser:
+ *
+ * 1. `target.startsWith('/')` + `!target.startsWith('//')` +
+ * `!target.includes('\\')` — recognised by `js/client-side-
+ * unvalidated-url-redirection`; rules out protocol-relative and
+ * browser-normalised backslash-laden authority spoofing (AF-1).
+ * 2. `new URL(target, SAFE_PATH_PARSE_BASE)` + literal equality on
+ * `parsed.origin` + literal equality on `parsed.protocol` —
+ * recognised by `js/xss-through-dom`. The constant base keeps the
+ * parse deterministic when test mocks supply only `window.location.
+ * { href, assign }` and not `origin`.
+ * 3. `safePath.startsWith('/')` + `!safePath.startsWith('//')` +
+ * `!safePath.includes('\\')` — recognised by `js/html-injection`.
+ * Required because `js/html-injection`'s default model does NOT
+ * propagate sanitisers from `target` through the URL-property
+ * derivation (`parsed.pathname` etc.) into the assigned string.
+ *
+ * External-URL paths get their own block with `URL.protocol` literal-
+ * equality + userinfo guards on the URL-constructor's normalised `href`.
+ *
+ * @internal
+ */
+export function navigateTo(
url: string,
options?: { newWindow?: boolean; assign?: boolean },
-) => {
- if (options?.newWindow) {
- window.open(ensureAppRoot(url), '_blank', 'noopener noreferrer');
- } else if (options?.assign) {
- window.location.assign(ensureAppRoot(url));
- } else {
- window.location.href = ensureAppRoot(url);
+): void {
+ const target = ensureAppRoot(url);
+ // Router-relative fast-path: triple-layer barrier so every CodeQL query
+ // family that fires on `window.location.*` / `window.history.*` sees a
+ // recognised sanitiser:
+ // 1. Outer `startsWith('/')` + `!startsWith('//')` + `!includes('\\')`
+ // on `target` — recognised by `js/client-side-unvalidated-url-
+ // redirection` and rules out protocol-relative + backslash-laden
+ // authority-spoofing (AF-1).
+ // 2. URL-constructor parse + literal-equality on `parsed.origin` and
+ // `parsed.protocol` — recognised by `js/xss-through-dom`. The
+ // constant base (`http://navigation-utils.invalid/`) keeps the parse
+ // deterministic under test mocks.
+ // 3. Inline `startsWith('/')` + `!startsWith('//')` + `!includes('\\')`
+ // on `safePath` — the value actually fed to the sink. Required for
+ // `js/html-injection`, whose default model does NOT propagate
+ // sanitisers from `target` through the URL-property derivation
+ // (`parsed.pathname` etc.) into the assigned string.
+ if (
+ target.startsWith('/') &&
+ !target.startsWith('//') &&
+ !target.includes('\\')
+ ) {
+ let parsed: URL | null;
+ try {
+ parsed = new URL(target, SAFE_PATH_PARSE_BASE);
+ } catch {
+ parsed = null;
+ }
+ if (
+ parsed !== null &&
+ parsed.origin === SAFE_PATH_PARSE_ORIGIN &&
+ parsed.protocol === 'http:'
+ ) {
+ const safePath = `${parsed.pathname}${parsed.search}${parsed.hash}`;
+ if (
+ safePath.startsWith('/') &&
+ !safePath.startsWith('//') &&
+ !safePath.includes('\\')
+ ) {
+ if (options?.newWindow) {
+ window.open(safePath, '_blank', NEW_TAB_FEATURES);
+ } else if (options?.assign) {
+ window.location.assign(safePath);
Review Comment:
## CodeQL / DOM text reinterpreted as HTML
[DOM text](1) is reinterpreted as HTML without escaping meta-characters.
[Show more
details](https://github.com/apache/superset/security/code-scanning/2457)
##########
superset-frontend/src/utils/navigationUtils.ts:
##########
@@ -16,29 +16,340 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { ensureAppRoot } from './pathUtils';
+import {
+ createElement,
+ type AnchorHTMLAttributes,
+ type ReactElement,
+} from 'react';
+import { ensureAppRoot, makeUrl, stripAppRoot } from './pathUtils';
-export const navigateTo = (
+// Re-export so callers that legitimately need a raw prefixed path (native
+// fetch, navigator.sendBeacon, image src, third-party `href` props) have a
+// single sanctioned import location. The static-invariant scan disallows
+// importing from `pathUtils` directly outside this module.
+export { ensureAppRoot, makeUrl, stripAppRoot };
+
+// The guard helpers are declared before `navigateTo` / `navigateWithState`
+// so oxlint's no-use-before-define lint (which does not honour function-
+// declaration hoisting) does not fire on the wired-up imperative-nav
+// path. The focused helpers below (`openInNewTab`, `getShareableUrl`,
+// `AppLink`) also reach for `assertSafeNavigationUrl` directly.
+
+const NEW_TAB_FEATURES = 'noopener noreferrer';
+
+// Constant base for the URL-constructor barrier in the router-relative
+// branches below. Using a constant (rather than `window.location.origin`)
+// keeps the parse fully deterministic — test mocks that supply only
+// `window.location.href` still exercise the same code path, and CodeQL
+// recognises the literal-string origin equality as a sanitiser.
+const SAFE_PATH_PARSE_BASE = 'http://navigation-utils.invalid/';
+const SAFE_PATH_PARSE_ORIGIN = 'http://navigation-utils.invalid';
+
+// Allow-list of safe URL shapes for navigation: router-relative paths and a
+// small set of known-safe schemes. `ensureAppRoot` already neutralises
+// `javascript:` / `data:` by prefixing them as relative paths; protocol-
+// relative `//host` is intentionally excluded here because it is a cross-
+// origin navigation primitive that previously enabled open redirects.
+//
+// nit-3 / AF-1 hardening: the leading-slash branch also rejects any URL
+// containing a backslash anywhere — browsers normalise `/\evil.com` →
+// `//evil.com` in the special-scheme authority, so backslashes in any
+// position let an attacker craft a path that looks router-relative until
+// the browser parses it. http(s)/ftp URLs with userinfo (`@` before the
+// first `/` after `//`) are rejected by the post-regex authority check
+// below, since `https://[email protected]` resolves to the host `evil.com`
+// despite presenting as a same-origin-looking URL to the eye.
+const SAFE_NAVIGATION_URL_RE = /^(?:\/(?!\/)|https?:|ftp:|mailto:|tel:)/i;
+const USERINFO_BEARING_SCHEME_RE = /^(?:https?|ftp):/i;
+
+function assertSafeNavigationUrl(url: string): string {
+ if (!SAFE_NAVIGATION_URL_RE.test(url) || url.includes('\\')) {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: only relative paths and ' +
+ 'http(s):, ftp:, mailto:, tel: schemes are allowed.',
+ );
+ }
+ if (USERINFO_BEARING_SCHEME_RE.test(url)) {
+ let parsed: URL;
+ try {
+ parsed = new URL(url);
+ } catch {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: unparseable authority.',
+ );
+ }
+ if (parsed.username || parsed.password) {
+ throw new Error(
+ 'navigationUtils refused unsafe URL: ' +
+ 'http(s)/ftp URLs with userinfo are not allowed.',
+ );
+ }
+ }
+ return url;
+}
+
+/**
+ * Imperative full-page navigation. Internal entry point for `redirect()`
+ * and a handful of legacy callers; new code should prefer `<AppLink>` or
+ * `redirect()`. Unsafe URLs (protocol-relative, backslash-laden, userinfo-
+ * carrying http(s)) fall back to `ensureAppRoot('/')` with a `console.error`
+ * — never a silent navigation to the rejected target.
+ *
+ * Each `window.*` sink lives inside an if-block whose guard combines
+ * three barriers so every CodeQL query family that fires on these sinks
+ * sees a recognised sanitiser:
+ *
+ * 1. `target.startsWith('/')` + `!target.startsWith('//')` +
+ * `!target.includes('\\')` — recognised by `js/client-side-
+ * unvalidated-url-redirection`; rules out protocol-relative and
+ * browser-normalised backslash-laden authority spoofing (AF-1).
+ * 2. `new URL(target, SAFE_PATH_PARSE_BASE)` + literal equality on
+ * `parsed.origin` + literal equality on `parsed.protocol` —
+ * recognised by `js/xss-through-dom`. The constant base keeps the
+ * parse deterministic when test mocks supply only `window.location.
+ * { href, assign }` and not `origin`.
+ * 3. `safePath.startsWith('/')` + `!safePath.startsWith('//')` +
+ * `!safePath.includes('\\')` — recognised by `js/html-injection`.
+ * Required because `js/html-injection`'s default model does NOT
+ * propagate sanitisers from `target` through the URL-property
+ * derivation (`parsed.pathname` etc.) into the assigned string.
+ *
+ * External-URL paths get their own block with `URL.protocol` literal-
+ * equality + userinfo guards on the URL-constructor's normalised `href`.
+ *
+ * @internal
+ */
+export function navigateTo(
url: string,
options?: { newWindow?: boolean; assign?: boolean },
-) => {
- if (options?.newWindow) {
- window.open(ensureAppRoot(url), '_blank', 'noopener noreferrer');
- } else if (options?.assign) {
- window.location.assign(ensureAppRoot(url));
- } else {
- window.location.href = ensureAppRoot(url);
+): void {
+ const target = ensureAppRoot(url);
+ // Router-relative fast-path: triple-layer barrier so every CodeQL query
+ // family that fires on `window.location.*` / `window.history.*` sees a
+ // recognised sanitiser:
+ // 1. Outer `startsWith('/')` + `!startsWith('//')` + `!includes('\\')`
+ // on `target` — recognised by `js/client-side-unvalidated-url-
+ // redirection` and rules out protocol-relative + backslash-laden
+ // authority-spoofing (AF-1).
+ // 2. URL-constructor parse + literal-equality on `parsed.origin` and
+ // `parsed.protocol` — recognised by `js/xss-through-dom`. The
+ // constant base (`http://navigation-utils.invalid/`) keeps the parse
+ // deterministic under test mocks.
+ // 3. Inline `startsWith('/')` + `!startsWith('//')` + `!includes('\\')`
+ // on `safePath` — the value actually fed to the sink. Required for
+ // `js/html-injection`, whose default model does NOT propagate
+ // sanitisers from `target` through the URL-property derivation
+ // (`parsed.pathname` etc.) into the assigned string.
+ if (
+ target.startsWith('/') &&
+ !target.startsWith('//') &&
+ !target.includes('\\')
+ ) {
+ let parsed: URL | null;
+ try {
+ parsed = new URL(target, SAFE_PATH_PARSE_BASE);
+ } catch {
+ parsed = null;
+ }
+ if (
+ parsed !== null &&
+ parsed.origin === SAFE_PATH_PARSE_ORIGIN &&
+ parsed.protocol === 'http:'
+ ) {
+ const safePath = `${parsed.pathname}${parsed.search}${parsed.hash}`;
+ if (
+ safePath.startsWith('/') &&
+ !safePath.startsWith('//') &&
+ !safePath.includes('\\')
+ ) {
+ if (options?.newWindow) {
+ window.open(safePath, '_blank', NEW_TAB_FEATURES);
+ } else if (options?.assign) {
+ window.location.assign(safePath);
+ } else {
+ window.location.href = safePath;
Review Comment:
## CodeQL / DOM text reinterpreted as HTML
[DOM text](1) is reinterpreted as HTML without escaping meta-characters.
[Show more
details](https://github.com/apache/superset/security/code-scanning/2458)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]