|
7 | 7 | * RFC 8707 section 2 states that resource URIs "MUST NOT include a fragment component".
|
8 | 8 | * Keeps everything else unchanged (scheme, domain, port, path, query).
|
9 | 9 | */
|
10 |
| -export function resourceUrlFromServerUrl(url: URL): URL { |
11 |
| - const resourceURL = new URL(url.href); |
| 10 | +export function resourceUrlFromServerUrl(url: URL | string ): URL { |
| 11 | + const resourceURL = typeof url === "string" ? new URL(url) : new URL(url.href); |
12 | 12 | resourceURL.hash = ''; // Remove fragment
|
13 | 13 | return resourceURL;
|
14 | 14 | }
|
| 15 | + |
| 16 | +/** |
| 17 | + * Checks if a requested resource URL matches a configured resource URL. |
| 18 | + * A requested resource matches if it has the same scheme, domain, port, |
| 19 | + * and its path starts with the configured resource's path. |
| 20 | + * |
| 21 | + * @param requestedResource The resource URL being requested |
| 22 | + * @param configuredResource The resource URL that has been configured |
| 23 | + * @returns true if the requested resource matches the configured resource, false otherwise |
| 24 | + */ |
| 25 | + export function checkResourceAllowed( |
| 26 | + { requestedResource, configuredResource }: { |
| 27 | + requestedResource: URL | string; |
| 28 | + configuredResource: URL | string |
| 29 | + } |
| 30 | + ): boolean { |
| 31 | + const requested = typeof requestedResource === "string" ? new URL(requestedResource) : new URL(requestedResource.href); |
| 32 | + const configured = typeof configuredResource === "string" ? new URL(configuredResource) : new URL(configuredResource.href); |
| 33 | + |
| 34 | + // Compare the origin (scheme, domain, and port) |
| 35 | + if (requested.origin !== configured.origin) { |
| 36 | + return false; |
| 37 | + } |
| 38 | + |
| 39 | + // Handle cases like requested=/foo and configured=/foo/ |
| 40 | + if (requested.pathname.length < configured.pathname.length) { |
| 41 | + return false |
| 42 | + } |
| 43 | + |
| 44 | + // Check if the requested path starts with the configured path |
| 45 | + // Ensure both paths end with / for proper comparison |
| 46 | + // This ensures that if we have paths like "/api" and "/api/users", |
| 47 | + // we properly detect that "/api/users" is a subpath of "/api" |
| 48 | + // By adding a trailing slash if missing, we avoid false positives |
| 49 | + // where paths like "/api123" would incorrectly match "/api" |
| 50 | + const requestedPath = requested.pathname.endsWith('/') ? requested.pathname : requested.pathname + '/'; |
| 51 | + const configuredPath = configured.pathname.endsWith('/') ? configured.pathname : configured.pathname + '/'; |
| 52 | + |
| 53 | + return requestedPath.startsWith(configuredPath); |
| 54 | + } |
0 commit comments