Skip to main content

Circular Reference Detection

Overview

Circular reference detection prevents attackers from sending self-referencing objects that can cause infinite loops, memory exhaustion, and application crashes.

Why This Is Important

The Problem: Self-Referencing Objects

Attackers can craft payloads where objects reference themselves, creating circular dependencies:

1. Infinite Loop Attack

// Attacker creates circular reference
const malicious = { name: "user" };
malicious.self = malicious; // References itself

// Without protection, this happens:
JSON.stringify(malicious);
// TypeError: Converting circular structure to JSON

// Serialization libraries try to traverse:
function serialize(obj) {
for (const key in obj) {
serialize(obj[key]); // Infinite recursion
}
}

What Happens Without Protection:

  • Serialization fails
  • Stack overflow errors
  • Application crashes
  • Server becomes unresponsive

Impact: Server crashes and immediate downtime Infinite loops consume CPU Stack overflow errors Denial of Service (DoS)

2. Memory Exhaustion

// Circular reference with deep nesting
const attack = { level1: {} };
attack.level1.level2 = { level3: attack };

// Validator tries to traverse:
function validate(obj, seen = []) {
seen.push(obj);
for (const value of Object.values(obj)) {
if (!seen.includes(value)) {
validate(value, seen); // Keeps growing 'seen' array
}
}
}

What Happens Without Protection:

  • Validation code traverses repeatedly
  • Memory usage grows exponentially
  • Garbage collector struggles
  • Server runs out of memory

Impact:

  • Memory exhaustion and OOM errors
  • Performance degradation
  • CPU spikes during GC
  • Affects all users

3. JSON Serialization DoS

// Many APIs serialize responses
app.post('/api/user', async (req, res) => {
const userData = await req.json(); // Contains circular ref

// Later in the code:
await db.save(JSON.stringify(userData)); // Crash

// Or
return res.json(userData); // Crash
});

What Happens Without Protection:

  • JSON.stringify() throws error
  • API endpoint crashes
  • Error propagates
  • Service becomes unavailable

Impact: API endpoint crashes Service unavailability Error cascades Lost revenue

4. Database Corruption

// Circular data sent to database
const user = {
name: "attacker",
profile: {}
};
user.profile.owner = user; // Circular

// Without protection:
await db.users.insert(user); // Database tries to serialize
// Error: cannot insert circular structure
// But connection may be left in bad state

What Happens Without Protection:

  • Database driver fails
  • Connection corruption
  • Transaction rollback
  • Data inconsistency

Impact:

  • Database connection issues
  • Transaction failures
  • Data inconsistency
  • Application instability

How nextjs-fortress Solves This

The Algorithm

/**
* Check for circular references using WeakSet
*/
private checkCircularReferences(
obj: unknown,
seen = new WeakSet()
): ValidationResult {
if (obj === null || typeof obj !== 'object') {
return { valid: true };
}

// Check if we've seen this object before
if (seen.has(obj)) {
return {
valid: false,
severity: 'high',
message: 'Circular reference detected in payload',
rule: 'circular_reference',
confidence: 1.0,
};
}

// Mark this object as seen
seen.add(obj);

// Recursively check all nested values
for (const value of Object.values(obj)) {
if (value !== null && typeof value === 'object') {
const result = this.checkCircularReferences(value, seen);
if (!result.valid) {
return result;
}
}
}

return { valid: true };
}

Why This Works

1. WeakSet Tracking - Efficiently tracks seen objects without preventing garbage collection 2. Early Detection - Stops immediately when circular reference found 3. Memory Safe - WeakSet doesn't prevent GC of tracked objects 4. Fast Performance - O(n) time complexity

WeakSet Advantages

// Traditional approach (BAD)
const seen = []; // Array grows indefinitely
seen.includes(obj); // O(n) lookup

// Fortress approach (GOOD)
const seen = new WeakSet(); // Doesn't prevent GC
seen.has(obj); // O(1) lookup

How to Initialize

import { FortressConfig } from '@mindfiredigital/nextjs-fortress';

export const fortressConfig: FortressConfig = {
enabled: true,
mode: 'development',

modules: {
deserialization: {
enabled: true,
maxDepth: 10,
detectCircular: true, // Enable this!

blockList: [
'__proto__',
'constructor',
'prototype',
],
},
},

onSecurityEvent: async (event) => {
if (event.detection.rule === 'circular_reference') {
console.warn('Circular reference attack blocked:', {
ip: event.request.ip,
path: event.request.path,
});
}
},
};

Summary

What happens without circular reference detection:

  • Infinite loops crash server
  • Memory exhaustion
  • JSON serialization failures
  • Database corruption

What nextjs-fortress provides:

  • WeakSet-based detection
  • Early attack prevention
  • Memory-safe implementation
  • Zero performance impact

How to initialize:

deserialization: {
enabled: true,
detectCircular: true, // Enable circular detection
maxDepth: 10,
}

Related Documentation: