From 6acb6afc8d7c97cf960db876fa11527897606909 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:58:13 +0000 Subject: [PATCH 1/3] Initial plan From e13bc9c8b4e6e0b56e73e5af87b612f0018fa6c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:03:58 +0000 Subject: [PATCH 2/3] Resolve conditional HTTP module bindings Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- ...quire-http-response-error-listener.test.ts | 21 +++++++++ .../require-http-response-error-listener.ts | 46 ++++++++++++------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/eslint-factory/src/rules/require-http-response-error-listener.test.ts b/eslint-factory/src/rules/require-http-response-error-listener.test.ts index 53410888502..7935bb2586b 100644 --- a/eslint-factory/src/rules/require-http-response-error-listener.test.ts +++ b/eslint-factory/src/rules/require-http-response-error-listener.test.ts @@ -38,6 +38,27 @@ describe("require-http-response-error-listener", () => { code: `const http = require("node:http"); http.request(options, function (res) { res.resume(); });`, errors: [{ messageId: "missingResponseErrorListener" }], }, + { + code: ` + const https = require("https"); + const http = require("http"); + const protocol = url.startsWith("https") ? https : http; + protocol + .get(url, res => { + if (res.statusCode !== 200) { + reject(new Error(\`Failed to fetch URL \${url}: HTTP \${res.statusCode}\`)); + return; + } + let data = ""; + res.on("data", chunk => { data += chunk; }); + res.on("end", () => { resolve(data); }); + }) + .on("error", err => { + reject(new Error(\`Failed to fetch URL \${url}: \${err.message}\`)); + }); + `, + errors: [{ messageId: "missingResponseErrorListener" }], + }, ], }); }); diff --git a/eslint-factory/src/rules/require-http-response-error-listener.ts b/eslint-factory/src/rules/require-http-response-error-listener.ts index 94ae3db1f98..c0005da7e7b 100644 --- a/eslint-factory/src/rules/require-http-response-error-listener.ts +++ b/eslint-factory/src/rules/require-http-response-error-listener.ts @@ -34,24 +34,38 @@ function isRequireHttpModule(node: TSESTree.Node | null | undefined, sourceCode: return typeof firstArg.value === "string" && HTTP_MODULE_SPECIFIERS.has(firstArg.value); } -/** Returns true when `name` resolves to a variable bound to Node's `http`/`https` module via `require(...)` and never reassigned. */ -function isHttpModuleBinding(name: string, scopeNode: TSESTree.Node, sourceCode: TSESLint.SourceCode): boolean { +/** Returns true when `node` resolves to Node's `http`/`https` module. */ +function isHttpModuleExpression(node: TSESTree.Node | null | undefined, sourceCode: TSESLint.SourceCode, visited: Set): boolean { + if (!node) return false; + if (isRequireHttpModule(node, sourceCode)) return true; + if (node.type === AST_NODE_TYPES.Identifier) return isHttpModuleBinding(node.name, node, sourceCode, visited); + if (node.type !== AST_NODE_TYPES.ConditionalExpression) return false; + return isHttpModuleExpression(node.consequent, sourceCode, new Set(visited)) && isHttpModuleExpression(node.alternate, sourceCode, new Set(visited)); +} + +/** Returns true when `name` resolves to a variable bound to Node's `http`/`https` module and never reassigned. */ +function isHttpModuleBinding(name: string, scopeNode: TSESTree.Node, sourceCode: TSESLint.SourceCode, visited = new Set()): boolean { const variable = resolveVariable(name, scopeNode, sourceCode); - if (!variable || variable.defs.length === 0) return false; - // Any write other than a `require("http")`-style initializer means the binding may no longer denote the module. - for (const reference of variable.references) { - if (!reference.isWrite()) continue; - if (!isRequireHttpModule(reference.writeExpr, sourceCode)) return false; - } - let bound = false; - for (const def of variable.defs) { - if (def.type !== "Variable") return false; - const declarator = def.node as TSESTree.VariableDeclarator; - if (declarator.id.type !== AST_NODE_TYPES.Identifier) return false; - if (!isRequireHttpModule(declarator.init, sourceCode)) return false; - bound = true; + if (!variable || variable.defs.length === 0 || visited.has(variable)) return false; + visited.add(variable); + try { + // Any write other than an HTTP module expression means the binding may no longer denote the module. + for (const reference of variable.references) { + if (!reference.isWrite()) continue; + if (!isHttpModuleExpression(reference.writeExpr, sourceCode, visited)) return false; + } + let bound = false; + for (const def of variable.defs) { + if (def.type !== "Variable") return false; + const declarator = def.node as TSESTree.VariableDeclarator; + if (declarator.id.type !== AST_NODE_TYPES.Identifier) return false; + if (!isHttpModuleExpression(declarator.init, sourceCode, visited)) return false; + bound = true; + } + return bound; + } finally { + visited.delete(variable); } - return bound; } /** Returns true when `call` is `.request(...)` / `.get(...)` on a resolved http/https module binding. */ From cd3cad17aeedda9bfe6fcd74bc7397a6293cf815 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:05:19 +0000 Subject: [PATCH 3/3] Preserve conditional binding cycle checks Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../src/rules/require-http-response-error-listener.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint-factory/src/rules/require-http-response-error-listener.ts b/eslint-factory/src/rules/require-http-response-error-listener.ts index c0005da7e7b..e3618da0446 100644 --- a/eslint-factory/src/rules/require-http-response-error-listener.ts +++ b/eslint-factory/src/rules/require-http-response-error-listener.ts @@ -40,7 +40,7 @@ function isHttpModuleExpression(node: TSESTree.Node | null | undefined, sourceCo if (isRequireHttpModule(node, sourceCode)) return true; if (node.type === AST_NODE_TYPES.Identifier) return isHttpModuleBinding(node.name, node, sourceCode, visited); if (node.type !== AST_NODE_TYPES.ConditionalExpression) return false; - return isHttpModuleExpression(node.consequent, sourceCode, new Set(visited)) && isHttpModuleExpression(node.alternate, sourceCode, new Set(visited)); + return isHttpModuleExpression(node.consequent, sourceCode, visited) && isHttpModuleExpression(node.alternate, sourceCode, visited); } /** Returns true when `name` resolves to a variable bound to Node's `http`/`https` module and never reassigned. */