Skip to content

feat(examples): add secure-uploads example with Pompelmi security scanning#15571

Open
SonoTommy wants to merge 1 commit intopayloadcms:mainfrom
SonoTommy:main
Open

feat(examples): add secure-uploads example with Pompelmi security scanning#15571
SonoTommy wants to merge 1 commit intopayloadcms:mainfrom
SonoTommy:main

Conversation

@SonoTommy
Copy link

What?

Added a new official example in examples/secure-uploads that demonstrates how to implement a secure file upload pipeline using Pompelmi. This toolkit performs deep file inspection directly within the Node.js process.

Why?

File uploads are one of the most common attack vectors in web applications. Standard validation (like file extension or size checks) often fails to detect:

  • ZIP Bombs: Which can crash the Node.js process via memory exhaustion.
  • Malware: Using YARA signatures to identify malicious payloads.
  • Polyglot Files: Malicious scripts disguised as valid images.

This example provides Payload users with a privacy-first (no external cloud APIs) and high-performance pattern to harden their Media collections.

How?

  • Created a new example project using the SQLite adapter for easy testing.
  • Integrated a beforeOperation hook in the Media collection.
  • Used the pompelmi library to scan file buffers before they are processed or stored.
  • Implemented clean error handling using Payload's native APIError to provide feedback to the UI.

Social Proof: Pompelmi has recently been featured in Help Net Security and the Bytes.dev newsletter as a robust security toolkit for the Node.js ecosystem.

Fixes # (None)

Copilot AI review requested due to automatic review settings February 10, 2026 14:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new examples/secure-uploads example project demonstrating a “secure uploads” pipeline for Payload by scanning uploaded file buffers with the Pompelmi library before accepting them.

Changes:

  • Introduces a new Next.js + Payload example app scaffolded under examples/secure-uploads
  • Adds a Media collection beforeOperation hook that scans req.file.data with scanBytes and rejects non-clean verdicts via APIError
  • Adds accompanying docs and project config (README, env example, tsconfig, Next config, gitignore)

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
examples/secure-uploads/tsconfig.json TypeScript configuration for the example project
examples/secure-uploads/src/payload.config.ts Payload config using SQLite adapter + admin setup
examples/secure-uploads/src/payload-types.ts Generated Payload types for the example
examples/secure-uploads/src/collections/Users.ts Minimal auth-enabled Users collection
examples/secure-uploads/src/collections/Media.ts Media upload collection with Pompelmi scanning hook
examples/secure-uploads/src/app/(payload)/layout.tsx Generated Payload Next layout wiring server functions
examples/secure-uploads/src/app/(payload)/custom.scss Placeholder for admin UI styling overrides
examples/secure-uploads/src/app/(payload)/api/graphql/route.ts Generated GraphQL route handler
examples/secure-uploads/src/app/(payload)/api/[...slug]/route.ts Generated REST route handlers
examples/secure-uploads/src/app/(payload)/admin/importMap.js Generated admin import map
examples/secure-uploads/src/app/(payload)/admin/[[...segments]]/page.tsx Generated admin catch-all page
examples/secure-uploads/src/app/(payload)/admin/[[...segments]]/not-found.tsx Generated admin not-found page
examples/secure-uploads/src/app/(app)/page.tsx Simple landing page describing the example
examples/secure-uploads/src/app/(app)/layout.tsx Root app layout
examples/secure-uploads/package.json Example dependencies/scripts (adds pompelmi, sqlite adapter, etc.)
examples/secure-uploads/next.config.mjs Next config wrapped with withPayload
examples/secure-uploads/next-env.d.ts Next TypeScript env declarations
examples/secure-uploads/README.md Example documentation and usage instructions
examples/secure-uploads/INSTALL.md Extra installation workaround note
examples/secure-uploads/.gitignore Ignores build artifacts, sqlite db files, and uploaded media
examples/secure-uploads/.env.example Example environment variables

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +75 to +79
Update the following variables:

- \`DATABASE_URL\`: Your MongoDB connection string
- \`PAYLOAD_SECRET\`: A secure random string for JWT encryption

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README setup instructions mention setting DATABASE_URL to a MongoDB connection string, but this example’s config uses the SQLite adapter and never reads process.env.DATABASE_URL. Update the README to describe SQLite setup (or switch the config to read DATABASE_URL) so the instructions match the actual runtime behavior.

Copilot uses AI. Check for mistakes.
},
db: sqliteAdapter({
client: {
url: 'file:./payload.db',
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SQLite adapter is configured with a hard-coded file:./payload.db URL, while the example includes a DATABASE_URL entry in .env.example and the README references it. Consider reading the DB URL from process.env.DATABASE_URL (with a SQLite default) to keep config/docs consistent and make the example easier to reconfigure.

Suggested change
url: 'file:./payload.db',
url: process.env.DATABASE_URL || 'file:./payload.db',

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +65
throw new APIError('File security scan failed. Please try again.', 500, {
originalError: errorMessage,
})
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new APIError('File security scan failed. Please try again.', 500, ...) will default isPublic to false (because status is 500), so the client will typically receive a generic "Something went wrong." response instead of this message. If the intent is to show this safe, generic message in the Admin UI, pass isPublic: true (4th ctor arg) and avoid returning originalError details in the public response.

Suggested change
throw new APIError('File security scan failed. Please try again.', 500, {
originalError: errorMessage,
})
// Expose a generic, safe message to the admin UI and avoid leaking internal error details
throw new APIError('File security scan failed. Please try again.', 500, undefined, true)

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +13
There is currently a Corepack signature verification issue preventing `pnpm` from working properly.
This is a known issue with Corepack in some environments.

## Workaround

You can install dependencies using npm instead:

```bash
cd examples/secure-uploads
npm install --legacy-peer-deps
```
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This INSTALL note claims pnpm is currently broken due to a Corepack signature issue and recommends npm install --legacy-peer-deps. This is likely to become outdated quickly and it also conflicts with the README which instructs pnpm install. Recommend removing this file or replacing it with a short, evergreen troubleshooting note that links to a specific upstream issue.

Copilot uses AI. Check for mistakes.
# Used to encrypt JWT tokens
PAYLOAD_SECRET=YOUR_SECRET_HERE

# Used to configure CORS, format links and more. No trailing slash
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env.example documents NEXT_PUBLIC_SERVER_URL, but the example payload.config.ts does not use it for cors/csrf (unlike other Next-based examples). Either wire this env var into the config or remove/adjust the comment to avoid suggesting configuration that has no effect.

Suggested change
# Used to configure CORS, format links and more. No trailing slash
# Used by the app (e.g. to format links). No trailing slash

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant