fix(javascript): read open() endpoints only from XHR method/URL pairs

Static endpoint extraction treated any `.open(_, "literal")` call as
`XMLHttpRequest.open(method, url)`. In application and web bundle
analysis, `fs.open("/tmp/data.txt", "r")` became the network endpoint
`r` and `window.open(url, "_blank")` became the endpoint `_blank`.

Read the URL only when the first argument is a literal HTTP method: a
standard method in any case, or an uppercase extension token such as
WebDAV `PROPFIND`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iwakurakaoru0822 2026-10-06 23:02:58 -07:00
parent e0fd8207a1
commit 1be370543f
2 changed files with 44 additions and 1 deletions

View File

@ -344,6 +344,32 @@ const isFileIdentity = (node: t.Node | undefined): boolean => {
);
};
const XHR_METHODS = new Set([
"CONNECT",
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
"TRACE",
]);
/**
* Require a literal HTTP method: `fs.open(path, "r")` and
* `window.open(url, "_blank")` share the callee name but take no URL second.
* XHR normalizes the standard methods' case; extension methods such as
* WebDAV `PROPFIND` are conventionally uppercase tokens.
*/
const isXhrMethodArgument = (node: t.Node | null | undefined): boolean => {
const value = stringValue(node);
return (
value !== undefined &&
(XHR_METHODS.has(value.toUpperCase()) || /^[A-Z][A-Z-]*$/u.test(value))
);
};
/** Select the literal URL argument for recognized network callees. */
export const endpointArgument = (
name: string,
@ -356,7 +382,8 @@ export const endpointArgument = (
): string | undefined => {
if (name === "fetch" || name.endsWith(".fetch") || name === "WebSocket")
return stringValue(args[0]);
if (name.endsWith(".open") && stringValue(args[1]) !== undefined)
// `XMLHttpRequest.open(method, url)`.
if (name.endsWith(".open") && isXhrMethodArgument(args[0]))
return stringValue(args[1]);
if (
["get", "post", "put", "patch", "delete", "request"].some(

View File

@ -174,6 +174,22 @@ describe("web bundle static-analysis parity", () => {
);
});
it("reads open endpoints only from XMLHttpRequest method and URL pairs", () => {
const result = analyzeCapturedWebBundle(
inspection(`
xhr.open("GET", "/xhr-get");
xhr.open("post", "https://xhr.example.test/submit");
dav.open("PROPFIND", "/dav/");
fs.open("/tmp/data.txt", "r", done);
window.open("https://help.example.test/", "_blank");
xhr.open(method, "/dynamic-method");
`),
);
expect(
result.observations.endpoints.map(({ value }) => value).sort(),
).toEqual(["/dav/", "/xhr-get", "https://xhr.example.test/submit"]);
});
it("recognizes loadURL endpoints like static analysis", () => {
const result = analyzeCapturedWebBundle(
inspection('window.win.loadURL("https://embedded.test/app");'),