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

secure-file-validator

v2.0.0

Published

Secure file validation library with signature checking and content validation

Readme

secure-file-validator

npm version CI License: MIT Node.js

Zero-dependency Node.js helper for upload type checks: extension, size, magic numbers, plus a small PDF and SVG content policy.

Aligned with the file-type checking part of OWASP Unrestricted File Upload and CWE-434. Not an antivirus. Not a complete upload-security stack.

Current release: 2.0.0. See CHANGELOG.md.

Install

npm install secure-file-validator

Requires Node.js 14.16+ (ESM).

Quick start

import { validateFile } from "secure-file-validator";

const result = await validateFile("uploads/photo.jpg");

if (result.ok) {
  // result.code === "OK"
} else {
  // result.code === "INVALID_SIGNATURE" | "PDF_JAVASCRIPT" | ...
  console.error(result.code, result.message);
}

result.status still exists as a deprecated alias of result.ok.

From an upload buffer

import { validateFile, validateBytes } from "secure-file-validator";

const result = await validateFile(req.file.buffer, {
  filename: req.file.originalname, // or extension: ".png"
});

if (!result.ok) {
  throw new Error(result.code);
}

const sync = validateBytes(req.file.buffer, { extension: ".png" });

Buffer / Uint8Array input must include filename or extension. Size is checked with byteLength.

Supported types

| Extension | Magic check | | --- | --- | | .jpg / .jpeg | FF D8 FF (any marker after SOI) | | .png | 89 50 4E 47 | | .gif | 47 49 46 38 | | .pdf | %PDF + %%EOF, then token policy | | .svg | <?xml or <svg, then SVG policy |

Default size cap: 5MB (options.maxSizeInBytes).

PDF policy

The checker looks for PDF name tokens, including hex-escaped names (/J#53 → /JS) and names inside inflated Flate streams. The letters JS in ordinary text or XMP are not a hit.

| Token | Default | code | | --- | --- | --- | | /Metadata | allow | PDF_METADATA | | /Annots | allow | PDF_ANNOTS | | /OpenAction | allow | PDF_OPEN_ACTION | | /JS, /JavaScript | deny | PDF_JAVASCRIPT | | /Launch | deny | PDF_LAUNCH | | /EmbeddedFile | deny | PDF_EMBEDDED_FILE | | /XFA | deny | PDF_XFA | | /RichMedia | deny | PDF_RICH_MEDIA |

const strict = await validateFile(pdfPath, {
  pdf: { allowOpenAction: false },
});

// Dangerous: turns the script check off
const trusted = await validateFile(pdfPath, {
  pdf: { allowJavaScript: true },
});

pdfWhitelist still works as a deprecated alias (JS and JavaScript both set allowJavaScript). Do not allow JavaScript only to silence metadata — metadata already passes. See #1.

SVG policy

Always denied: <script>, javascript:, event handlers (onload= …), <!ENTITY.

Denied by default, overridable with options.svg:

| Rule | Default | code | | --- | --- | --- | | foreignObject | deny | SVG_FOREIGN_OBJECT | | data: URI | deny | SVG_DATA_URI | | external href | deny | SVG_EXTERNAL_HREF |

Allowed: DOCTYPE without ENTITY, fragment href="#id".

await validateFile(svgPath, {
  svg: { allowDataUri: true },
});

Result

{
  ok: false,
  status: false,          // deprecated alias of ok
  code: "PDF_JAVASCRIPT",
  message: "Suspicious PDF name token detected: /JavaScript",
  details: { token: "JavaScript" }
}

| code | Meaning | | --- | --- | | OK | Passed | | UNSUPPORTED_TYPE | Extension not in the allow list | | INVALID_EXTENSION | Bad or missing extension (buffers need filename / extension) | | TOO_LARGE | Over maxSizeInBytes | | INVALID_SIGNATURE | Magic number does not match the extension | | IO_ERROR | Missing file or unreadable input | | PDF_* / SVG_* | See the policy tables |

Switch on code. Do not parse message.

API

validateFile(input, options?): Promise<ValidationResult>
validateFileContent(input, options?): Promise<ValidationResult>
validateBytes(bytes, options?): ValidationResult

input is a filesystem path, Buffer, or Uint8Array.

| Option | Type | Default | | --- | --- | --- | | maxSizeInBytes | number | 5 * 1024 * 1024 | | filename | string | required for buffers if extension is omitted | | extension | string | .png or png | | pdf | PdfPolicy | see table | | svg | SvgPolicy | see table | | pdfWhitelist | string[] | deprecated alias |

Migrating from 1.x

| 1.x | 2.x | | --- | --- | | result.status | result.ok (status still works) | | result.message only | also result.code | | path string only | path, Buffer, or Uint8Array | | JPEG only FF D8 FF E0/E1 | any FF D8 FF | | PDF substring /JS/, /Metadata/ | name tokens; metadata allowed by default | | pdfWhitelist: ['JS'] to dodge false positives | not needed for metadata; use pdf.allowJavaScript only if you mean it |

Scope

Does: type vs magic-number check, size cap, PDF name-token policy, SVG active-content policy.

Does not: antivirus, Office/ZIP unpacking, zip-bomb detection, filename sanitization, storage layout, Content-Type on the way out, auth, or rate limits.

Suggested pipeline:

  1. Validate bytes (validateFile(buffer, { filename })).
  2. Store under a generated name, not the client filename.
  3. Keep files outside the webroot.
  4. Serve a Content-Type you chose, not one taken from the client.

FAQ

Legitimate PDFs used to fail with /JS/ or /Metadata/.
That was a substring false positive (#1). 2.0.0 matches name tokens only. Metadata / Annots / OpenAction pass by default.

Is a passing result enough to call an upload safe?
No. This reduces wrong-type and obvious active-content mistakes. Pair it with generated names, isolated storage, and a tight serving policy.

License

MIT — see LICENSE.