<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Prithwiraj Bose - HashNode]]></title><description><![CDATA[Prithwiraj Bose - HashNode]]></description><link>https://sribasu.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 05 Nov 2026 10:34:43 GMT</lastBuildDate><atom:link href="https://sribasu.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Safer Exit From `request`: Incremental Migration for Legacy Node.js Services]]></title><description><![CDATA[`request` is deprecated, but replacing it can break callbacks, streams, forms, cookies, and redirects. A practical path to migrate in controlled steps]]></description><link>https://sribasu.hashnode.dev/a-safer-exit-from-request-incremental-migration-for-legacy-node-js-services</link><guid isPermaLink="true">https://sribasu.hashnode.dev/a-safer-exit-from-request-incremental-migration-for-legacy-node-js-services</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[node]]></category><category><![CDATA[Application Security]]></category><category><![CDATA[technical-debt]]></category><category><![CDATA[migration]]></category><category><![CDATA[axios]]></category><category><![CDATA[npm]]></category><dc:creator><![CDATA[Prithwiraj Bose]]></dc:creator><pubDate>Sun, 01 Nov 2026 20:51:17 GMT</pubDate><content:encoded><![CDATA[<p><strong>Disclosure:</strong> I maintain <a href="https://www.npmjs.com/package/request-to-axios">request-to-axios</a>, the compatibility wrapper discussed below. This article is a practical account of the migration problem and the tradeoffs in that wrapper, not an independent customer testimonial.</p>
<p>An old HTTP dependency is rarely isolated to one line of code. In a mature Node.js service, <code>request</code> may be wrapped by internal helpers, used as a duplex stream, configured through <code>defaults()</code>, paired with a cookie jar, and expected to return a particular callback body type. A direct switch to Axios can change all of those behaviors at once.</p>
<p>That makes the migration question more specific than “Which HTTP client is better?” It is: how do we remove a deprecated dependency while controlling behavioral change in a system whose integration surface is only partly documented?</p>
<h2>Start With the Actual Dependency Graph</h2>
<p>The upstream <code>request</code> project was deprecated in 2020. Its repository says no further changes are expected. npm still publishes <code>request@2.88.2</code>, but the package's age and deprecation are separate from a finding that a given application is vulnerable. Check the resolved graph and the code paths that use it.</p>
<p>I ran a fresh install of <code>request@2.88.2</code> into an isolated directory and used npm's audit database on 2026-11-02. That resolved these notable versions:</p>
<table>
<thead>
<tr>
<th>Package</th>
<th>Resolved version</th>
<th>Advisory and important condition</th>
</tr>
</thead>
<tbody><tr>
<td><code>request</code></td>
<td><code>2.88.2</code></td>
<td><a href="https://github.com/advisories/GHSA-p8p7-x288-28g6"><code>CVE-2023-28155</code></a>: SSRF mitigation bypass via cross-protocol redirects. Relevant when a service depends on redirect restrictions to block requests to internal destinations. No fixed upstream <code>request</code> version is listed.</td>
</tr>
<tr>
<td><code>form-data</code></td>
<td><code>2.3.3</code></td>
<td><a href="https://github.com/advisories/GHSA-fjxv-7rqg-78g4"><code>CVE-2025-7783</code></a>: predictable multipart boundary generation using <code>Math.random()</code>. Exploitation requires an attacker to observe random outputs and influence multipart content.</td>
</tr>
<tr>
<td><code>form-data</code></td>
<td><code>2.3.3</code></td>
<td><a href="https://github.com/advisories/GHSA-hmw2-7cc7-3qxx"><code>CVE-2026-12143</code></a>: CRLF injection when untrusted data is used as multipart field names or filenames. Fixed in maintained release lines, but <code>request</code>'s old dependency range does not admit those patched versions.</td>
</tr>
<tr>
<td><code>qs</code></td>
<td><code>6.5.5</code></td>
<td><a href="https://github.com/advisories/GHSA-6rw7-vpxm-498p"><code>CVE-2025-15284</code></a>: <code>arrayLimit</code> is not consistently enforced for bracket notation. The default <code>parameterLimit</code> caps practical input; the risk changes if an application raises that limit substantially.</td>
</tr>
<tr>
<td><code>qs</code></td>
<td><code>6.5.5</code></td>
<td><a href="https://github.com/advisories/GHSA-4mjr-xmp4-gh2g"><code>CVE-2026-82417</code></a>: a denial of service in a <code>parse</code> then <code>stringify</code> flow with attacker-controlled <code>constructor.isBuffer</code> data when permissive parse options are used. This is not equivalent to every call to <code>request</code>'s query serializer being exploitable.</td>
</tr>
<tr>
<td><code>tough-cookie</code></td>
<td><code>2.5.0</code></td>
<td><a href="https://github.com/advisories/GHSA-72xf-g2v4-qvf3"><code>CVE-2023-26136</code></a>: prototype pollution under a specific <code>CookieJar</code> configuration (<code>rejectPublicSuffixes: false</code>).</td>
</tr>
<tr>
<td><code>uuid</code></td>
<td><code>3.4.0</code></td>
<td><a href="https://github.com/advisories/GHSA-w5hq-g745-h8pq"><code>CVE-2026-41907</code></a>: missing buffer bounds checks for UUID v3/v5/v6 APIs when a caller supplies a buffer and offset. A vulnerable package in a dependency graph does not prove that <code>request</code> itself calls the affected API with attacker-controlled values.</td>
</tr>
</tbody></table>
<p>npm reported five vulnerable package nodes in that audit; some nodes have more than one advisory. The important engineering distinction is between a vulnerable component in the tree and an exploitable path in the application. Review both. Also note that a clean audit is time-bound: new advisories may be published after the check.</p>
<h2>The Hidden Cost of a Direct Rewrite</h2>
<p>The public API may look small, but its contract is not. Common <code>request</code> patterns include:</p>
<ul>
<li>Callback form: <code>(error, response, body)</code> rather than a promise returning one Axios response object.</li>
<li>Streams: piping a response to a file, or piping an upload into the request.</li>
<li>Serialization: <code>qs</code>, <code>form</code>, <code>formData</code>, and JSON-specific response parsing.</li>
<li>Defaults: shared headers, base URLs, timeouts, proxies, and agents.</li>
<li>Session behavior: a cookie jar, redirects, and authentication.</li>
</ul>
<p>A mechanical replacement can compile and still change a service's behavior. Status handling, redirect method changes, stream lifetimes, body encoding, and error shapes are exactly the sorts of details that integration tests should pin down.</p>
<p>A practical migration plan is incremental:</p>
<ol>
<li>Search for direct calls and local wrappers, including <code>pipe()</code>, <code>.form()</code>, <code>.auth()</code>, and <code>defaults()</code>.</li>
<li>Record request options by call site. Do not assume every caller uses the same subset.</li>
<li>Add tests around the observable contract: status code, headers, body type, redirects, and upload/download behavior.</li>
<li>Introduce the new transport behind the existing import where the compatibility surface fits.</li>
<li>Migrate unsupported cases explicitly rather than silently accepting options that do nothing.</li>
<li>Run <code>npm audit</code> on the final lockfile and test against representative downstream services.</li>
</ol>
<h2>Using the Wrapper as a Transition Step</h2>
<p><a href="https://www.npmjs.com/package/request-to-axios"><code>request-to-axios</code></a> keeps a request-style callback and stream facade while using Axios for HTTP transport. To trial it without editing every <code>require('request')</code> statement first:</p>
<pre><code class="language-sh">npm install request@npm:request-to-axios
</code></pre>
<p>Common support includes method aliases, callbacks, response streams, <code>defaults()</code>, <code>uri</code>/<code>url</code>/<code>baseUrl</code>, <code>qs</code>, JSON serialization and parsing, URL-encoded and multipart forms, immediate Basic/Bearer auth, redirect limits, TLS/agent options, and an in-memory cookie jar.</p>
<p>It is not a full clone. OAuth 1, Hawk, AWS signing, HTTP Signature, HAR 1.2, digest retry, <code>multipart/related</code>, custom redirect policy callbacks, and exact <code>request</code> proxy-tunnel behavior are not implemented. Redirect options such as <code>followAllRedirects</code> and <code>followOriginalHttpMethod</code> do not reproduce the original semantics. The built-in cookie jar is host-only and in-memory; it does not implement public-suffix behavior or custom stores. The <a href="https://github.com/prithwirajbose/request-to-axios#compatibility-reference">compatibility reference</a> lists the current boundary.</p>
<p>The point of a wrapper is not to claim that migration is finished. It is to preserve enough of the familiar interface to let a team migrate call sites in smaller, testable batches, while making the exceptions explicit. Some services may be better served by a direct Axios rewrite; others may benefit from an intermediate compatibility layer.</p>
<h2>Security Is a Process, Not a Package Name</h2>
<p>Replacing <code>request</code> does not automatically make an application secure. The caller still needs to constrain user-controlled destinations, inspect redirect behavior, protect credentials, keep TLS certificate verification enabled, and review the current dependency tree. The wrapper itself also has dependencies and should be audited on release and in consuming applications.</p>
<p>For SSRF-sensitive clients, a particularly important question is where the destination URL comes from and what a redirect can reach. A new HTTP library can fix a known implementation flaw, but application-level URL allowlists and redirect policies remain necessary defense in depth.</p>
<p>If this compatibility approach fits, review the package source, <a href="https://www.npmjs.com/package/request-to-axios">npm release</a>, and <a href="https://github.com/prithwirajbose/request-to-axios#known-limitations">known limitations</a>. Test your own request patterns before switching production traffic. Feedback and reproducible gaps can be filed on the <a href="https://github.com/prithwirajbose/request-to-axios/issues">GitHub project</a>.</p>
<p>Keywords: <strong>Node.js</strong>, <strong>JavaScript</strong>, <strong>Application Security</strong>, <strong>Technical Debt</strong>, <strong>Axios</strong>, <strong>Migration</strong></p>
]]></content:encoded></item></channel></rss>