fix(javascript): keep configuration setting reads out of endpoints (#2402)

* fix(javascript): keep configuration setting reads out of endpoints

Static endpoint extraction reads the first literal argument of any call
named get as an HTTP request path unless the receiver is a provable keyed
collection. VS Code extension bundles read settings through
`workspace.getConfiguration(section).get(key)`, so every setting key
became a network endpoint: the bundled Git extension's dist/main.js
reported 173 network endpoints, nearly all of them setting names such as
`autofetchPeriod` and `blame.statusBarItem.enabled`.

The lexical receiver classifier now records a `configuration` fact for a
`.get` call whose receiver is a `getConfiguration(...)` call, inline or
through a binding whose only visible write is one unconditional
initializer. Minifiers emit `let` for these bindings, so the proof relies
on the recorded writes rather than the declaration kind, as tRPC client
root resolution already does. Endpoint extraction skips non-URL literals
for that fact in both application and web bundle analysis. Reassigned,
conditional, and shadowed receivers, URL or path literals, and other
clients such as `api.get("users")` keep their endpoints.

The receiver fact union was duplicated in three modules; consumers now
import the classifier's type.

On the Git extension bundle, network endpoints drop from 173 to 7 with
unchanged analysis time (2.1 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): require the workspace host for configuration receivers

A bare or differently scoped `getConfiguration(...)` may be an
application's own HTTP client factory, so `.get("users")` on its result
lost its endpoint. Only `workspace.getConfiguration(...)` and
`<x>.workspace.getConfiguration(...)` now produce the configuration fact.
Minifiers keep property names, so the bundled VS Code Git extension still
reports 7 network endpoints instead of 173.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): recognize default VS Code namespace imports

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
alfredo1491
2026-10-13 07:45:07 -06:00
committed by GitHub
parent 5d12a0b4b6
commit 23f72141d5
4 changed files with 159 additions and 34 deletions
@@ -17,6 +17,7 @@ import {
semanticStaticPropertyName,
unwrapJavaScriptExpression,
} from "./javascriptAstValues.js";
import type { JavaScriptOpenReceiverFact } from "./javascriptSemanticAnalysis.js";
import {
compareUnicodeCodePoints,
compositeKey,
@@ -470,13 +471,7 @@ const xhrOpenUrl = (
name: string,
methodNode: t.Node | null | undefined,
urlNode: t.Node | null | undefined,
receiverFact?:
| "window"
| "indexed-db"
| "cache-storage"
| "local"
| "local-indexed-db"
| "local-cache-storage",
receiverFact?: JavaScriptOpenReceiverFact,
): string | undefined => {
const url = stringValue(urlNode);
if (url === undefined) return undefined;
@@ -555,13 +550,7 @@ export const endpointArgument = (
| t.ArgumentPlaceholder
)[],
callee: t.Node,
receiverFact?:
| "window"
| "indexed-db"
| "cache-storage"
| "local"
| "local-indexed-db"
| "local-cache-storage",
receiverFact?: JavaScriptOpenReceiverFact,
): string | undefined => {
if (receiverFact === "window") return undefined;
if (name === "fetch" || name.endsWith(".fetch") || name === "WebSocket")
@@ -575,10 +564,12 @@ export const endpointArgument = (
const value = stringValue(args[0]);
// Only get and delete also belong to the keyed-collection APIs, whose
// keys are header, field, and parameter names rather than URLs or paths.
// Configuration `get` reads setting keys.
return value !== undefined &&
(method === "get" || method === "delete") &&
!isRequestUrlLiteral(value) &&
isKeyedCollectionReceiver(callee)
(isKeyedCollectionReceiver(callee) ||
(method === "get" && receiverFact === "configuration"))
? undefined
: value;
}