Skip to content

Add spam filter middleware to block common attack patterns#570

Merged
adambarito merged 2 commits intomainfrom
claude/add-spam-filter-middleware-avMtz
Mar 23, 2026
Merged

Add spam filter middleware to block common attack patterns#570
adambarito merged 2 commits intomainfrom
claude/add-spam-filter-middleware-avMtz

Conversation

@adambarito
Copy link
Contributor

Summary

This PR introduces a spam filter middleware that blocks requests matching common attack patterns, particularly WordPress-related probes and other malicious requests.

Key Changes

  • New spam filter module (packages/lib/src/middleware/spam-filter.ts):

    • Added isSpamRequest() function that detects requests containing common spam/attack patterns (WordPress paths, PHP files, environment files, etc.)
    • Added spamResponse() function that returns a 418 I'm a teapot response with a humorous message
    • Exported the module in packages/lib/package.json for use across applications
  • Integrated spam filter into all middleware:

    • apps/bio/src/middleware.ts
    • apps/cart/src/middleware.ts
    • apps/link/src/middleware.ts
    • apps/nyc/src/middleware.ts
    • apps/vip/src/middleware.ts
    • Each now checks incoming requests early and returns the spam response if patterns match

Implementation Details

  • The filter checks against patterns including: .php, license.txt, wp-* paths, xmlrpc, .trash, and .env
  • Pattern matching is case-insensitive to catch obfuscated attempts
  • The check is performed at the beginning of middleware execution to fail fast
  • Uses HTTP 418 status code (I'm a teapot) as a playful but clear rejection

https://claude.ai/code/session_01JeXrk5mVcXKjXPsEt9Li2f

Blocks requests to .php, wp-content, wp-admin, wp-login, .env, .trash,
license.txt, and other common exploit-probing paths with a 418 response.
Applied to all app middlewares (bio, cart, link, nyc, vip).

https://claude.ai/code/session_01JeXrk5mVcXKjXPsEt9Li2f
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 22, 2026

Greptile Summary

This PR introduces a shared spam-filter middleware module that short-circuits requests matching common WordPress/PHP probe patterns, returning an HTTP 418 response. The filter is correctly integrated at the top of all five app middlewares (bio, cart, link, nyc, vip), and the package export entry is properly wired up.

Key observations:

  • The fast-fail placement (before any DB queries, analytics, or cookie handling) is the right approach and avoids unnecessary downstream work for bot traffic.
  • Pattern matching is case-insensitive and covers the most common WordPress attack vectors (.php, wp-*, .env, xmlrpc, etc.).
  • The response body contains an expletive that could surface in browser windows, logs, or monitoring dashboards — particularly if a legitimate user ever triggers a false positive. This should be replaced with a professional message before merging.
  • Blocked requests are not logged anywhere, which makes it difficult to monitor attack volume or validate that the filter is working as intended in production.

Confidence Score: 4/5

  • Safe to merge after replacing the profane response message with a professional one.
  • The implementation is correct and well-placed. The only concrete change needed before merge is swapping the expletive in the response body for a clean message; everything else (pattern list, export, middleware integration) is solid.
  • packages/lib/src/middleware/spam-filter.ts — response body wording and missing observability logging.

Important Files Changed

Filename Overview
packages/lib/src/middleware/spam-filter.ts New module introducing isSpamRequest (pathname substring matching against common attack patterns) and spamResponse (418 response). Contains profanity in the response body and lacks logging for blocked requests.
packages/lib/package.json Added ./middleware/spam-filter export entry matching the pattern of the existing ./middleware/request-parsing export. Change is minimal and correct.
apps/link/src/middleware.ts Added early isSpamRequest guard before the DB query and analytics recording, which is the correct fast-fail position to avoid unnecessary work.
apps/bio/src/middleware.ts Spam filter guard added before URL parsing; integration is straightforward and correct.
apps/cart/src/middleware.ts Spam filter guard added before domain/pathname parsing; integration is straightforward and correct.
apps/nyc/src/middleware.ts Spam filter guard added before visitor cookie handling; integration is straightforward and correct.
apps/vip/src/middleware.ts Spam filter guard added before VIP URL parsing; integration is straightforward and correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B{isSpamRequest\npathname}
    B -- match found --> C[spamResponse\nHTTP 418]
    B -- no match --> D[Normal Middleware Logic\nbio / cart / link / nyc / vip]
    D --> E[DB queries / analytics\ncookie handling / routing]
Loading

Reviews (1): Last reviewed commit: "feat: add spam filter middleware to bloc..." | Re-trigger Greptile

}

export function spamResponse() {
return new NextResponse('Nice try. This isn\'t WordPress. Stop fucking with us.', {
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Profanity in production response body

The response message contains an expletive ("Stop fucking with us."). While this is returned to bots in the vast majority of cases, it can also be triggered by legitimate users who accidentally hit a false positive (e.g., a developer testing a URL that contains .php or .env in the path), and it would show up in browser windows, logs, or monitoring dashboards. A professional but still dismissive message is a better fit.

Suggested change
return new NextResponse('Nice try. This isn\'t WordPress. Stop fucking with us.', {
return new NextResponse("Nice try. This isn't WordPress.", {

Comment on lines +15 to +18
export function isSpamRequest(pathname: string): boolean {
const lower = pathname.toLowerCase();
return SPAM_PATTERNS.some(pattern => lower.includes(pattern));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 No logging for blocked requests

When a spam request is blocked, nothing is logged. Adding a log line here would let you monitor attack frequency and patterns over time, and verify the filter is working as expected in production. The log utility is already imported in the consuming middlewares, or it could be imported directly here:

Suggested change
export function isSpamRequest(pathname: string): boolean {
const lower = pathname.toLowerCase();
return SPAM_PATTERNS.some(pattern => lower.includes(pattern));
}
export function isSpamRequest(pathname: string): boolean {
const lower = pathname.toLowerCase();
const matched = SPAM_PATTERNS.find(pattern => lower.includes(pattern));
if (matched) console.log(`[spam-filter] blocked "${pathname}" (matched: ${matched})`);
return !!matched;
}

@github-actions
Copy link

apps

📱 app 🚀 preview 🔍 inspect
📱 app preview inspect
📻 app-fm preview inspect
🧾 app-invoice preview inspect
🌱 bio preview inspect
🛒 cart preview inspect
📻 fm preview inspect
🧾 invoice preview inspect
🔗 link preview inspect
📧 manage-email preview inspect
🌆 nyc preview inspect
📄 page preview inspect
📰 press preview inspect
⭐ vip preview inspect
🌐 www preview inspect

db

💾 db 🔍 inspect
🐘 neon inspect

- Change response message to "Nice try. Pls go away."
- Log blocked requests with matched pattern for observability

https://claude.ai/code/session_01JeXrk5mVcXKjXPsEt9Li2f
@vercel
Copy link

vercel bot commented Mar 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

14 Skipped Deployments
Project Deployment Actions Updated (UTC)
app-bio Ignored Ignored Mar 22, 2026 11:11pm
app-chat Ignored Ignored Mar 22, 2026 11:11pm
app-email Ignored Ignored Mar 22, 2026 11:11pm
app-fm Ignored Ignored Mar 22, 2026 11:11pm
app-link Ignored Ignored Mar 22, 2026 11:11pm
app-pub Ignored Ignored Mar 22, 2026 11:11pm
app-vip Ignored Ignored Mar 22, 2026 11:11pm
app-work Ignored Ignored Mar 22, 2026 11:11pm
art Ignored Ignored Mar 22, 2026 11:11pm
baresky Ignored Ignored Mar 22, 2026 11:11pm
bio Ignored Ignored Mar 22, 2026 11:11pm
chat Ignored Ignored Mar 22, 2026 11:11pm
pub Ignored Ignored Mar 22, 2026 11:11pm
work Ignored Ignored Mar 22, 2026 11:11pm

Request Review

@github-actions
Copy link

apps

📱 app 🚀 preview 🔍 inspect
📱 app preview inspect
📻 app-fm preview inspect
🧾 app-invoice preview inspect
🌱 bio preview inspect
🛒 cart preview inspect
📻 fm preview inspect
🧾 invoice preview inspect
🔗 link preview inspect
📧 manage-email preview inspect
🌆 nyc preview inspect
📄 page preview inspect
📰 press preview inspect
⭐ vip preview inspect
🌐 www preview inspect

db

💾 db 🔍 inspect
🐘 neon inspect

@adambarito adambarito added this pull request to the merge queue Mar 23, 2026
Merged via the queue into main with commit 13baa58 Mar 23, 2026
100 checks passed
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.

2 participants