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:
parent
e3e7664665
commit
e32028d1b5
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue