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:
parent
e0fd8207a1
commit
1be370543f
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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");'),
|
||||
|
|
|
|||
Loading…
Reference in New Issue