# Spam patterns and duplicate content are detected

- **Pattern:** `ab-000695` (`community-moderation-safety.content-filtering.spam-detection`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000695
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000695)

## Why it matters

Without spam detection, a single automated account can flood a community feed with duplicate or rapid-fire posts, drowning legitimate content and degrading the user experience to the point where real users leave. This is a CWE-770 (Allocation of Resources Without Limits) failure: uncapped post creation burns database storage and CDN bandwidth at attacker-controlled rates. Beyond quality, spam floods can mask coordinated manipulation campaigns — link farms, referral abuse, or coordinated inauthentic behavior — that are invisible without rate tracking.

## Severity rationale

Low because spam degrades platform quality and inflates infrastructure costs but does not expose user data or enable direct account compromise.

## Remediation

Implement per-user rate limiting and content-hash deduplication on all post/comment submission handlers. In `src/middleware/rateLimiter.ts`:

```javascript
import rateLimit from 'express-rate-limit';

export const postLimiter = rateLimit({
  windowMs: 5 * 60 * 1000, // 5 minutes
  max: 5,
  keyGenerator: (req) => req.user.id,
  message: 'Too many posts. Please wait before posting again.'
});
```

Pair rate limiting with a content-hash check: compute `sha256(content)` per user and reject exact duplicates submitted within the last hour. Reject at the API level before writing to the database.

## Detection

- **ID:** `spam-detection`
- **Severity:** `low`
- **What to look for:** Check for logic that detects spam: rapid repeated posting, duplicate content, suspicious patterns (many links, excessive hashtags), or third-party spam detection services. Look for rate limiting on post/comment submissions, or duplicate content comparison before save.
- **Pass criteria:** The system detects and handles spam: either rejects rapid postings from the same user, deduplicates identical content, or uses a third-party spam detection service. Count all post/comment submission handlers and verify that at least 1 spam-detection mechanism (rate limit, dedup, or spam API) is applied. Actions are taken before or immediately after content creation. Report the ratio of spam-protected endpoints to total submission endpoints even on pass.
- **Fail criteria:** No spam detection is implemented. Users can flood the platform with duplicate or rapid-fire content. A TODO comment or disabled spam check does not count as pass.
- **Skip (N/A) when:** Platform has <500 active users AND uses manual-only moderation.
- **Detail on fail:** `"No spam detection implemented. A user can post identical content repeatedly or flood the platform with rapid postings."`
- **Remediation:** Implement rate limiting and duplicate detection on post submissions:

  ```javascript
  import { rateLimit } from 'express-rate-limit';

  // Rate limiting: 5 posts per 5 minutes per user
  const postLimiter = rateLimit({
    windowMs: 5 * 60 * 1000,
    max: 5,
    keyGenerator: (req) => req.user.id,
    message: 'Too many posts. Please wait before posting again.'
  });

  app.post('/api/posts', postLimiter, async (req, res) => {
    const { content } = req.body;
    const userId = req.user.id;

    // Check for duplicate content in the last hour
    const recentPost = await db.posts.findOne({
      content,
      userId,
      createdAt: { $gte: new Date(Date.now() - 3600000) }
    });

    if (recentPost) {
      return res.status(400).json({ error: 'You already posted this content recently' });
    }

    // Check for similar content using simple hash-based deduplication
    const contentHash = require('crypto').createHash('md5').update(content).digest('hex');
    const similar = await db.postHashes.findOne({ hash: contentHash, userId });
    if (similar) {
      return res.status(400).json({ error: 'Similar content already posted' });
    }

    // Save post
    const post = await db.posts.create({ content, userId, createdAt: new Date() });
    await db.postHashes.create({ hash: contentHash, userId, postId: post.id });

    res.status(201).json(post);
  });
  ```

  Reject posts that exceed the rate limit and flag exact duplicates.

## External references

- cwe CWE-770

Taxons: injection-and-input-trust, cost-efficiency

HTML version: https://auditbuffet.com/patterns/ab-000695
