(
urlStr: string | undefined,
origin?: string | URL,
options: ResolveUrlOptions = {},
)
| 47 | options?: ResolveUrlOptions, |
| 48 | ): URL; |
| 49 | export function resolveUrl( |
| 50 | urlStr: string | undefined, |
| 51 | origin?: string | URL, |
| 52 | options: ResolveUrlOptions = {}, |
| 53 | ): URL | null { |
| 54 | const originUrl = typeof origin === 'string' ? new URL('/', origin) : origin; |
| 55 | |
| 56 | if (!urlStr) { |
| 57 | return originUrl || null; |
| 58 | } |
| 59 | |
| 60 | urlStr = urlStr.trim(); |
| 61 | |
| 62 | // Fast-path: if the URL is a valid, standard absolute URL, parse and return it immediately. |
| 63 | let resolved: URL | undefined; |
| 64 | try { |
| 65 | resolved = new URL(urlStr); |
| 66 | } catch {} |
| 67 | const {allowProtocolRelative = false, allowOriginChange = true} = options; |
| 68 | |
| 69 | if (resolved) { |
| 70 | if (originUrl && !isSafeOriginChange(resolved, originUrl, urlStr, allowOriginChange)) { |
| 71 | throwSuspiciousUrlError(urlStr); |
| 72 | } |
| 73 | |
| 74 | return resolved; |
| 75 | } |
| 76 | |
| 77 | // We identify and throw on malformed absolute URLs (like double port). |
| 78 | // Per the WHATWG URL standard, parsing an input starting with a scheme (like 'http:') against |
| 79 | // a standard base (like 'http://fake') ignores the base argument and parses strictly as an |
| 80 | // absolute URL. Since it is malformed, the native URL constructor will throw a validation |
| 81 | // error. Standard relative/protocol-relative paths parse successfully, allowing the flow to continue. |
| 82 | if (!URL.canParse(urlStr, 'http://fake')) { |
| 83 | throw new RuntimeError( |
| 84 | RuntimeErrorCode.INVALID_URL, |
| 85 | typeof ngDevMode === 'undefined' || ngDevMode ? `Invalid URL: ${urlStr}` : urlStr, |
| 86 | ); |
| 87 | } |
| 88 | |
| 89 | if (!originUrl) { |
| 90 | return null; |
| 91 | } |
| 92 | |
| 93 | // Check if we have a legitimate protocol-relative URL (starts with '//' and not a duplicate/backslash bypass) |
| 94 | // and we are configured to allow and preserve standard cross-origin protocol-relative requests. |
| 95 | if (urlStr.startsWith('//')) { |
| 96 | if (!allowProtocolRelative) { |
| 97 | throw new RuntimeError( |
| 98 | RuntimeErrorCode.PROTOCOL_RELATIVE_URL_NOT_ALLOWED, |
| 99 | typeof ngDevMode === 'undefined' || ngDevMode |
| 100 | ? `Protocol relative URLs are not allowed in this context. URL: ${urlStr}` |
| 101 | : urlStr, |
| 102 | ); |
| 103 | } |
| 104 | |
| 105 | return new URL(urlStr, origin); |
| 106 | } |
no test coverage detected
searching dependent graphs…