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

@axosolaman/secure-next-upload

v1.1.0

Published

Production-grade, zero-server-bandwidth secure multi-entity file and media upload system with 5-layer security verification for Next.js and modern web apps.

Readme

🚀 Secure Next Upload (@axosolaman/secure-next-upload)

npm version Security Researcher: axosolaman Research: Axo Security CWE-434 Mitigated Zero Server Bandwidth Bug Bounty Hardened TypeScript License: MIT

The definitive high-performance, zero-server-bandwidth secure file and media upload engine for Next.js, React, and modern web architectures.
Built by security researchers to systematically eliminate the #1 highest-paying vulnerability classes (CWE-434, CWE-22, CWE-200, Stored XSS) while saving companies thousands in cloud bandwidth, server RAM crashes, and storage bills.


⚡ Why Secure Next Upload?

Traditional file upload architectures stream massive binary payloads directly through your backend API servers. This saturates bandwidth, crashes serverless runtimes (e.g. Vercel 4.5MB payload limits), inflates hosting bills, and exposes applications to malicious webshell uploads.

Secure Next Upload delivers a modern, frictionless architecture:

  • ⚡ Zero Server Bandwidth: Uploads stream directly from the browser to Cloudflare R2 / AWS S3 via cryptographic Presigned PUT URLs. Your web servers never touch, buffer, or proxy the binary files.
  • 🚀 3x–5x Faster Parallel Uploads: Upload multi-file batches simultaneously using a built-in asynchronous worker pool (concurrency: 3–5) with per-file progress tracking and fault-tolerant completion.
  • 🎨 Client-Side Web Worker Optimization: Automatically compresses 15MB mobile photos to ~800KB web-optimized images and strips high-precision GPS/EXIF metadata in browser background threads before uploading.
  • 🛡️ Military-Grade 5-Layer Security: Pre-flight validation, presigned token locks, S3 HeadObject verification, 16-byte binary magic byte inspection, and single-use intent state machines.
  • 🧩 Drop-in React Components & Headless Hook: Includes sleek, fully accessible UI components (FileDropzone, AvatarUploader, DocumentUploader, UploadProgress) and a headless useFileUpload hook.
  • 🗄️ Database Agnostic: Includes production-ready schemas for both Prisma and Drizzle ORM.

🛡️ Security at a Glance: Vulnerabilities Secure Next Upload Neutralizes

File upload endpoints are historically the most exploited attack surface on bug bounty platforms (HackerOne, Bugcrowd, Intigriti). Secure Next Upload provides defense-in-depth immunity against the most severe vulnerability classes:

  • 💥 CWE-434 (Unrestricted File Upload / Webshell RCE) ➔ Neutralized by server-side 16-byte binary magic byte inspection (0x89PNG, %PDF, FF D8 FF, RIFF...WEBP), strict MIME whitelisting, and cloud-edge content locks.
  • 📍 CWE-200 / CWE-359 (EXIF & GPS Geolocation Leakage) ➔ Neutralized by in-memory browser Web Worker stripping before upload, protecting user privacy and preventing GDPR/HIPAA compliance fines.
  • 🛑 CWE-22 / CWE-646 (Path Traversal & Filename Tampering) ➔ Neutralized by generating unpredictable 7-character randomized object keys (avatars/xK9_m2Q.webp), completely discarding untrusted user filenames and path sequences.
  • 💣 CWE-400 (Denial of Service & Serverless RAM Exhaustion) ➔ Neutralized by routing binary streams directly to cloud storage, completely bypassing Node.js/Next.js memory buffers.
  • 🦠 CWE-79 (Stored Cross-Site Scripting via SVG/HTML) ➔ Neutralized by strict MIME isolation and sandboxed delivery.
┌─────────────────────────────────────────────────────────────────────────────────────────────┐
│ 🔬 Deep Security & Threat Model Research                                                    │
│                                                                                             │
│ For the complete Master CWE Mitigation Matrix, bug bounty payout data ($3k-$30k+ RCEs),    │
│ and why server-side EXIF stripping creates RCE/SSRF/DoS (CVE-2021-22204 / ImageTragick):    │
│                                                                                             │
│ 👉 Read the Full Defensive Security & Threat Model in SECURITY.md ───────────────►         │
└─────────────────────────────────────────────────────────────────────────────────────────────┘

👉 Read the Complete Defensive Security & Threat Model in SECURITY.md →


💼 Immense Business & Financial Impact

| Impact Metric | Traditional Server Uploads | Secure Next Upload | | :--- | :--- | :--- | | Server Bandwidth & RAM | ❌ High (Full file payload streams through server) | ✅ 0 KB Server Load (Direct-to-Cloud PUT) | | Multi-File Upload Speed | ⏱️ Sequential (30–45s for 10 files) | ⚡ Parallel Pool (7–9s for 10 files) | | Cloud Storage Costs | 💸 Uncompressed 15MB images fill buckets | 📉 70%+ Savings (Compressed in Web Worker) | | Serverless Execution Limits | 💥 Crashes on Vercel/Lambda (4.5MB limit) | ✅ Unlimited File Sizes Supported | | Security Breach Risk | 🚨 Critical (CWE-434 RCE webshell payouts) | 🛡️ Hardened 5-Layer Verification |


📊 Deep Comparison: Secure Next Upload vs. Alternatives

1. Open Source Ecosystem Packages

| Project | Similarity | Key Strengths | Missing / Weaker Compared to Secure Next Upload | Best For | | :--- | :--- | :--- | :--- | :--- | | @axosolaman/secure-next-upload | ⭐️ Current | Zero Server Bandwidth, 5-layer security verification, 16-byte magic byte check, Web Worker EXIF stripping, parallel concurrency pool (concurrency: 3-5), atomic replacement, multi-entity registry. | None — complete end-to-end production architecture. | Production apps wanting maximum security, speed & $0 cloud markup. | | next-upload (TimMikeladze) | High | Presigned URLs, Next.js focused, optional DB metadata, S3/R2/MinIO support. | Weaker security (no deep 16-byte magic byte inspection, no multi-entity registry, no client-side Web Worker EXIF/compression). | Simple Next.js apps needing basic presigned uploads. | | vs3 | High | Type-safe, presigned uploads, magic-byte detection, React hooks, Next.js handlers, Zod schemas. | Less opinionated multi-entity registry, no built-in atomic asset replace (swapMode: "atomic_replace"), or 5-layer pipeline. | Developers wanting basic type safety + single-file validation. | | octoload | Medium-High | Direct-to-S3/R2, CLI for schema generation, Drizzle integration, TypeScript-first. | Image-focused, lacks comprehensive 5-layer security pipeline and client-side EXIF/compression. | Teams using Drizzle wanting CLI-generated code. | | @circulo-ai/upload | Medium | Multi-provider (S3, Azure Blob), presigned + multipart, path traversal protection. | No deep binary magic byte inspection, no client-side Web Worker image optimization. | Multi-cloud projects needing Azure + S3 coverage. |


2. Managed SaaS Services & Client Libraries

| Service / Architecture | Category | Pros | Cons vs. Secure Next Upload | | :--- | :--- | :--- | :--- | | UploadThing | Managed SaaS + SDK | Extremely easy setup for Next.js, type-safe route handler, good developer experience. | ❌ You don't own the underlying storage infrastructure, monthly recurring subscription fees, less defensive binary inspection layers. | | Vercel Blob | Managed Cloud Storage | Native Vercel integration, simple presigned upload workflow. | ❌ Vendor lock-in to Vercel ecosystem, proprietary bandwidth pricing, less advanced binary security verification. | | Uppy + S3 Companion | Client UI Library | Highly customizable UI dashboard, resumable tus/multipart uploads, multi-source file picking. | ❌ Client-only library. You must still build, maintain, and secure the backend validation and database intent pipeline yourself. | | DIY Raw S3 Presigned URLs | Custom In-House Code | Complete custom architectural freedom. | ❌ Massive engineering burden. You must build rate limiting, 5-layer security, magic-byte checking, EXIF stripping, and orphan garbage collection from scratch. |


⚠️ What Vulnerabilities & Risks Do Alternatives Create?

If you build with or rely on traditional alternatives, your application remains exposed to specific architectural attack vectors:

| Alternative Architecture / Pattern | Vulnerabilities & Security Risks Introduced | Real-World Impact on Your App | | :--- | :--- | :--- | | Traditional Server Buffering (Multer, Busboy, raw API routes) | CWE-400 (Denial of Service / OOM Crashes)• Full binary payload buffers into server memory.• Serverless functions (Vercel / AWS Lambda) crash instantly on 4.5MB limit.• Heavy server CPU spikes and bandwidth saturation. | 💥 Server Outages & Wasted BandwidthThousands in bloated cloud bills; memory crashes during concurrent traffic spikes. | | Basic Presigned URLs (next-upload, DIY S3 scripts) | CWE-434 & CWE-646 (MIME & Extension Spoofing)• Lacks deep 16-byte binary magic byte inspection.• Blindly trusts client-provided Content-Type headers.• Polyglot webshells (shell.php.png) pass through to cloud storage. | 🚨 Remote Code Execution (RCE) / Malware HostingAttackers host phishing pages or executable malware directly on your CDN domain. | | Server-Side EXIF Processing (ExifTool, ImageMagick) | Command Injection / SSRF / Memory Corruption• Backend server invokes CLI parsers on untrusted binary inputs.• Exposes infrastructure to CVE-2021-22204 (GitLab RCE) and ImageTragick. | 💀 Critical Server Takeover (CVSS 10.0)Attackers execute arbitrary shell commands inside your server environment. | | Unstripped Raw Uploads (No Client Web Worker Stripping) | CWE-200 / CWE-359 (EXIF Geolocation Leakage)• Uploads unstripped camera photos to public storage buckets.• Leaks precise GPS latitude/longitude, home addresses, device serials. | ⚖️ User Doxxing & GDPR/HIPAA FinesMassive regulatory privacy violation penalties and permanent user trust destruction. | | Client-Only Libraries without Intent States (Uppy alone) | CWE-20 & CWE-862 (Missing Authorization & Replay)• Relies solely on frontend JavaScript checks (easily bypassed with curl/Burp).• No atomic single-use confirmation state machines. | 🔓 Unauthorized Asset Overwrite & Race ConditionsAttackers overwrite other users' files or upload unauthorized payloads. | | No Atomic Replacement (swapMode: "append" only) | Storage Bloat & Dead Orphan Accumulation• Updating an avatar leaves old files orphaned indefinitely on S3.• Failed/abandoned presigned URLs accumulate unverified storage bills. | 💸 Exponential Cloud Storage BillingPaying for thousands of gigabytes of dead, unlinked zombie files. |


💡 Real-World Use Cases

  1. User Profile & Avatar Management:
    • Circular avatar uploader with client-side cropping and automatic atomic deletion of old avatars from storage (swapMode: "atomic_replace").
  2. E-Commerce & Photo Galleries:
    • Drag-and-drop batch uploader that compresses 15MB mobile photos to ~800KB web-optimized images in Web Workers and uploads 10–20 files in parallel.
  3. Documents & Invoice Vaults:
    • Strict PDF, DOCX, XLSX, and TXT upload verification with magic-byte checking and per-document download/removal actions.
  4. Chat & Ticket Attachments:
    • Headless useFileUpload hook for seamless custom file inputs with individual and aggregate progress bars.

📦 Installation

# Using npm
npm install @axosolaman/secure-next-upload @aws-sdk/client-s3 @aws-sdk/s3-request-presigner browser-image-compression zod sonner

# Using pnpm
pnpm add @axosolaman/secure-next-upload @aws-sdk/client-s3 @aws-sdk/s3-request-presigner browser-image-compression zod sonner

🚀 3-Minute Quick Start

1. Configure Environment Variables (.env.local)

# Cloudflare R2 / AWS S3 Credentials
R2_ACCOUNT_ID=your_cloudflare_account_id
R2_ACCESS_KEY_ID=your_access_key_id
R2_SECRET_ACCESS_KEY=your_secret_access_key
R2_BUCKET_NAME=your_bucket_name
R2_PUBLIC_URL=https://pub-your-id.r2.dev

# Database Connection
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

2. Define Upload Rules (src/config/uploader.ts)

import { defineUploadRegistry } from "@axosolaman/secure-next-upload/config";

export const uploadRegistry = defineUploadRegistry({
  avatar: {
    folder: "avatars",
    allowedMimes: ["image/jpeg", "image/png", "image/webp"],
    maxSizeBytes: 5 * 1024 * 1024, // 5MB
    magicByteCheck: true,
    compressClientSide: true,
    compressionOptions: { maxSizeMB: 1.5, maxWidthOrHeight: 1024 },
    swapMode: "atomic_replace", // Deletes previous avatar automatically
    requiresAuth: true,
  },
  document: {
    folder: "documents",
    allowedMimes: ["application/pdf", "text/plain", "application/zip"],
    maxSizeBytes: 50 * 1024 * 1024, // 50MB
    magicByteCheck: true,
    compressClientSide: false,
    swapMode: "append",
    requiresAuth: true,
  },
  gallery: {
    folder: "gallery",
    allowedMimes: ["image/jpeg", "image/png", "image/webp"],
    maxSizeBytes: 20 * 1024 * 1024,
    magicByteCheck: true,
    compressClientSide: true,
    compressionOptions: { maxSizeMB: 4, maxWidthOrHeight: 2560 },
    swapMode: "append",
    requiresAuth: true,
  },
});

3. Create Next.js API Routes (App Router)

src/app/api/upload/request/route.ts

import { createUploadRequestHandler } from "@axosolaman/secure-next-upload/server";
import { uploadRegistry } from "@/config/uploader";
import { db } from "@/db";

export const POST = createUploadRequestHandler({
  registry: uploadRegistry,
  getAuthUser: async (req) => {
    // Return authenticated user or null
    return { id: "user_123", authId: "user_123" };
  },
  db: {
    getUser: async (authId) => ({ id: authId }),
    createIntent: async (data) => db.uploadIntent.create({ data }),
    getIntent: async (id, userId) => db.uploadIntent.findFirst({ where: { id } }),
    updateIntentStatus: async (id, status, completedAt) => {
      await db.uploadIntent.update({ where: { id }, data: { status, completedAt } });
    },
  },
});

src/app/api/upload/confirm/route.ts

import { createUploadConfirmHandler } from "@axosolaman/secure-next-upload/server";
import { uploadRegistry } from "@/config/uploader";
import { db } from "@/db";

export const POST = createUploadConfirmHandler({
  registry: uploadRegistry,
  db: {
    getUser: async (authId) => ({ id: authId }),
    createIntent: async (data) => db.uploadIntent.create({ data }),
    getIntent: async (id, userId) => db.uploadIntent.findFirst({ where: { id } }),
    updateIntentStatus: async (id, status, completedAt) => {
      await db.uploadIntent.update({ where: { id }, data: { status, completedAt } });
    },
  },
});

🎨 Frontend Usage & React Components

1. Headless Hook useFileUpload (Single & Parallel Uploads)

"use client";

import { useFileUpload } from "@axosolaman/secure-next-upload/client";

export function GalleryUpload() {
  const {
    uploadMultiple,
    isUploading,
    progress,
    status,
    fileItems,
    error,
  } = useFileUpload({
    entityType: "gallery",
    concurrency: 4, // Upload up to 4 files simultaneously
    onFileSuccess: (res, file) => console.log(`✓ Uploaded ${file.name}:`, res.fileUrl),
    onFileError: (err, file) => console.error(`✗ Failed ${file.name}:`, err.message),
  });

  return (
    <div>
      <input
        type="file"
        multiple
        onChange={(e) => e.target.files && uploadMultiple(Array.from(e.target.files))}
      />

      {isUploading && (
        <div className="mt-4">
          <p>Overall Progress: {progress}% ({status})</p>
          {fileItems.map((item) => (
            <div key={item.id}>
              {item.file.name}: {item.status} ({item.progress}%)
            </div>
          ))}
        </div>
      )}
      {error && <p className="text-red-500">{error}</p>}
    </div>
  );
}

2. Multi-File Cloud Dropzone

import { FileDropzone } from "@axosolaman/secure-next-upload/client";

export function GallerySection() {
  return (
    <FileDropzone
      entityType="gallery"
      multiple={true}
      maxFiles={10}
      label="Drop gallery photos here"
      sublabel="PNG, JPEG, WebP (Compressed in Web Worker)"
      onSuccess={(results) => console.log("Uploaded batch:", results)}
    />
  );
}

3. Circular Avatar Uploader

import { AvatarUploader } from "@axosolaman/secure-next-upload/client";

export function ProfileHeader({ user }) {
  return (
    <AvatarUploader
      entityType="avatar"
      currentAvatarUrl={user.avatarUrl}
      onAvatarUpdate={async (newUrl) => {
        await updateUserAvatar(user.id, newUrl);
      }}
    />
  );
}

4. Document & Attachment Vault

import { DocumentUploader } from "@axosolaman/secure-next-upload/client";

export function AttachmentsList() {
  return (
    <DocumentUploader
      entityType="document"
      onUploadSuccess={(doc) => console.log("Attached document:", doc)}
      onRemoveDocument={(id) => console.log("Removed document:", id)}
    />
  );
}

🗄️ Database Schemas

Drizzle ORM (schema.ts)

import { pgTable, uuid, varchar, integer, timestamp, jsonb, index } from "drizzle-orm/pg-core";

export const uploadIntents = pgTable("upload_intents", {
  id: uuid("id").defaultRandom().primaryKey(),
  userId: uuid("user_id"),
  entityType: varchar("entity_type", { length: 50 }).default("general").notNull(),
  objectKey: varchar("object_key", { length: 512 }).notNull().unique(),
  originalFileName: varchar("original_file_name", { length: 255 }).notNull(),
  mimeType: varchar("mime_type", { length: 100 }).notNull(),
  fileSize: integer("file_size").notNull(),
  status: varchar("status", { length: 20 }).default("pending").notNull(), // pending | completed | failed | expired
  metadata: jsonb("metadata"),
  presignedUrlExpiresAt: timestamp("presigned_url_expires_at").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  completedAt: timestamp("completed_at"),
}, (table) => ({
  userStatusIdx: index("upload_intents_user_status_idx").on(table.userId, table.status),
  entityStatusIdx: index("upload_intents_entity_status_idx").on(table.entityType, table.status),
}));

Prisma ORM (schema.prisma)

model UploadIntent {
  id                    String    @id @default(uuid())
  userId                String?   @map("user_id")
  entityType            String    @default("general") @map("entity_type")
  objectKey             String    @unique @map("object_key")
  originalFileName      String    @map("original_file_name")
  mimeType              String    @map("mime_type")
  fileSize              Int       @map("file_size")
  status                String    @default("pending")
  metadata              Json?
  presignedUrlExpiresAt DateTime  @map("presigned_url_expires_at")
  createdAt             DateTime  @default(now()) @map("created_at")
  completedAt           DateTime? @map("completed_at")

  @@index([userId, status])
  @@index([entityType, status])
  @@map("upload_intents")
}

🌐 Storage Bucket CORS Configuration

Add this CORS configuration to your Cloudflare R2 or AWS S3 bucket:

[
  {
    "AllowedOrigins": [
      "http://localhost:3000",
      "https://yourdomain.com"
    ],
    "AllowedMethods": [
      "GET",
      "PUT",
      "HEAD"
    ],
    "AllowedHeaders": [
      "Content-Type",
      "Content-Length"
    ],
    "ExposeHeaders": [
      "ETag",
      "Content-Length"
    ],
    "MaxAgeSeconds": 3600
  }
]

👨‍💻 Author & Security Researcher Profile

Secure Next Upload (@axosolaman/secure-next-upload) is created, architected, and maintained by axosolaman (Axo Security).

  • Lead Security Researcher: axosolaman
  • GitHub Profile: @axosolaman
  • Organization: Axo Security (@axosecurity)
  • Core Expertise: Web Application Security, Bug Bounty Research, Threat Modeling, Zero-Trust Cloud Architectures, and Defensive AppSec Engineering.

💬 Feedback & Security Audits: If you are using @axosecurity/secure-next-upload in your production stack, feel free to star the repo or connect directly on GitHub with axosolaman.


📄 License

MIT License © 2026 axosolaman (Axo Security). Open source for commercial and enterprise production use.