fix(javascript): keep bare relative URLs for computed-method XHR

`xhr.open(method, "api/users")` is a same-origin request, but the
computed-method fallback required an absolute URL or a path prefix.
Keep the literal unless it is provably not a URL: a Node fs open flag,
a reserved browsing-context name, or a call on a receiver spelled as a
browsing context or document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822 2026-10-07 00:01:16 -07:00
parent e3e7664665
commit e32028d1b5
2 changed files with 48 additions and 7 deletions

View File

@ -371,12 +371,47 @@ const isRequestUrlLiteral = (value: string): boolean =>
const WINDOW_OPEN_CALL =
/(?:^|\.)(?:window|self|globalThis|top|parent|opener|frames|document)\.open$/u;
/** Node `fs.open` flag strings, which are never request URLs. */
const FS_OPEN_FLAGS = new Set([
"a",
"a+",
"as",
"as+",
"ax",
"ax+",
"r",
"r+",
"rs",
"rs+",
"sa",
"sa+",
"sr",
"sr+",
"w",
"w+",
"wx",
"wx+",
"xa",
"xa+",
"xw",
"xw+",
]);
/** Reserved browsing-context names (ASCII case-insensitive). */
const BROWSING_CONTEXT_KEYWORDS = new Set([
"_blank",
"_parent",
"_self",
"_top",
]);
/**
* Read `XMLHttpRequest.open(method, url)`. `fs.open(path, "r")` and
* `window.open(url, "_blank")` share the callee name, so a literal method
* must be an HTTP method, and a computed method needs a request URL literal
* on a receiver not spelled as a browsing context. XHR normalizes the
* standard methods' case; extension methods such as WebDAV `PROPFIND` are
* `window.open(url, "_blank")` share the callee name. A literal method must
* be an HTTP method; with a computed method, the second literal is a URL
* unless it is provably an fs flag or a browsing-context keyword, or the
* receiver is spelled as a browsing context. XHR normalizes the standard
* methods' case; extension methods such as WebDAV `PROPFIND` are
* conventionally uppercase tokens.
*/
const xhrOpenUrl = (
@ -388,9 +423,11 @@ const xhrOpenUrl = (
if (url === undefined) return undefined;
const method = stringValue(methodNode);
if (method === undefined)
return !WINDOW_OPEN_CALL.test(name) && isRequestUrlLiteral(url)
? url
: undefined;
return WINDOW_OPEN_CALL.test(name) ||
FS_OPEN_FLAGS.has(url) ||
BROWSING_CONTEXT_KEYWORDS.has(url.toLowerCase())
? undefined
: url;
return XHR_METHODS.has(method.toUpperCase()) || /^[A-Z][A-Z-]*$/u.test(method)
? url
: undefined;

View File

@ -190,6 +190,9 @@ describe("web bundle static-analysis parity", () => {
document.open("text/html", "/replace");
parent.open("GET", "/parent-xhr");
self.open("post", "/self-xhr");
xhr.open(method, "api/relative");
fs.open(path, "w+", done);
popup.open(url, "_TOP");
`),
);
expect(
@ -200,6 +203,7 @@ describe("web bundle static-analysis parity", () => {
"/parent-xhr",
"/self-xhr",
"/xhr-get",
"api/relative",
"https://xhr.example.test/submit",
]);
});