npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@insitetechjp/dssrf

v2.0.0

Published

SSRF defense library for Node.js with safe URL validation and pinned fetching.

Readme

dssrf — SSRF defense for Node.js

License: MIT

dssrf validates untrusted URLs before your application fetches them, and, as of 2.0.0, can perform the fetch itself with every hop pinned to an address it has already checked.

One runtime dependency (ipaddr.js). No native addon, no install scripts.

Also the package moved from dssrf to a scope, please update for anyone your package.json, to use @insitetechjp/dssrf, other releases are not republished, however it is recommended to update to that release


Two ways to use it

safe_fetch(), recommended

Resolves once, validates every address, and connects by address. The hostname never reaches a connect call, so there is no second resolution for a DNS rebinding attack to poison. Redirects are followed manually and each hop re-enters the same checks, so an open redirect into 169.254.169.254 is refused on the hop that reaches it.

const { safe_fetch, SsrfRefused } = require("@insitetechjp/dssrf");

const userUrl = "https://169.254.169.254/metadata"

try {
  const res = await safe_fetch(userUrl);
  console.log(res.status, res.body, res.chain);   // chain shows every hop and the address used
} catch (err) {
  if (err instanceof SsrfRefused) {
    // err.reason distinguishes a policy block from a no reachable url
  }
}

is_url_safe(), unchanged, for existing code

const { is_url_safe } = require("@insitetechjp/dssrf");

const userUrl = "https://169.254.169.254/metadata"

if (!await is_url_safe(userUrl)) {
  throw new Error("This URL cannot be fetched.");
}

That is the classical function, kept for compability reasons. It validates a string; your HTTP client then resolves the hostname again when it connects. That gap is the reason safe_fetch() exists, and it cannot be closed by a function that returns a boolean, that why we recommend migrating from it.

CRITICAL: The is_url_safe function is vulnerable to DoS, that cannot be patched, meaning a malicious server that hangs forever, an attacker can freeze your app, besides that the dns rebinding protection, that design we choosed before is wrong, and can be bypassed via sophisticated dns rebinding attacks anyway, We recommend migrating to safe_fetch


Refusing is not accusing

safe_fetch throws SsrfRefused with a reason, so an application can tell a security event from a typo:

| reason | Meaning | Show the user | | --- | --- | --- | | host-not-found | The name does not exist | "We couldn't reach that address." Not an attack, That is a legitimate user typo or the url is not available anymore. | | resolution-failed | The resolver could not answer | Try again shortly. | | internal-address | Resolved into blocked space | An SSRF Attempt Blocked | | scheme-not-allowed, userinfo-present, port-not-allowed | Policy | That is an attempt to escalating SSRF | | redirect-without-location, too-many-redirects, response-too-large, timeout | Transport | Normal but unusual, it common to see in broken non compliant servers, if it is 304 it is expected |

A single boolean cannot make this distinction, which is why a UI built on is_url_safe() tends to accuse people who mistyped a domain.

API

| Function | Notes | | --- | --- | | safe_fetch(url, options?) | Pinned fetch. It supports the following options ports, allow, method, headers, maxRedirects, timeout, totalTimeout, maxBytes | | resolve_and_pin(url, options?) | Resolve + validate only; returns { url, hostname, port, addresses, servername } for use with your own client | | is_url_safe(url) | The legacy ssrf boolean function | | is_redirect_safe(url, options?) | Walks a redirect chain. Probes with GET and only when DSSRF_MAKE_REQUEST=1 or the config { makeRequest: true } | | is_hostname_resolve_to_internal_ip(host) | True when the host must not be reached | | resolve_host(host) | { kind: "addresses" \| "nxdomain" \| "unknown" }, That is a new api | | is_ip_internal(ip) | That API is present to check is a literal ip internal | | is_range_not_internal(cidr) | Accepts a CIDR string or any { start(), end() } object | | is_ipv6, normalize_ipv4, normalize_schema, is_proto_safe, Essential utilities, kept

Ports default to 80 and 443 in safe_fetch; widen with ports: [8080]. allow: ["10.2.0.7:5432"] is an explicit carve-out for your own services and should never be built from user input. Entries must name a port number, just a bare address would permit every port on that host, and the allowlist applies to each redirect hop, so a redirect could then reach any of them. totalTimeout (default 30s) bounds a whole redirect chain, not just each hop.

safe_fetch sets the Host header from the pinned target and ignores a caller-supplied host, so the Host cannot be decoupled from the address that was validated.

Limits

Stated plainly, because a security library that hides them is worse than none.

  • is_url_safe() still has a check-to-connect gap. It is kept for compatibility. Please Use safe_fetch(), That function have security risks, that you must consider.
  • is_redirect_safe() does not inspect redirects by default. It validates the first hop and returns unless DSSRF_MAKE_REQUEST=1 or { makeRequest: true } is passed.
  • Public destinations are not restricted. An attacker can still point you at any public host. Destination allowlisting is a separate control, as we saw in sandssrf that is another problem.
  • Only http and https. For other protocols use resolve_and_pin() and connect yourself, and note that protocol-level redirection, Raw sockets not supported unlike sandssrf which have a raw socket connect.
  • Proxies defeat pinning. safe_fetch connects directly and ignores HTTP_PROXY. If your egress requires a proxy, the proxy resolves the hostname and the pin is meaningless.
  • The range list still matters. Classification is total over the ranges it is given; those follow the IANA special-purpose registries and need to be kept current.
  • No connection reuse. A fresh agent per request, because a pooled socket performs no connect for anything to check.
  • Parsing Flaws: While we do'nt discover anything at the moment, but they a lot develop and exist, That why we created sandssrf, to solve the problem

Reporting

See SECURITY.md for report a vuln, for security researchers.