Description
Using a user-controlled object's length directly as a loop bound lets an attacker supply a very large value, such as 1e12 or 1e100. The resulting long or effectively endless loop may exhaust CPU or memory and deny service. For example, code expecting an array may receive { length: 1000000000000 } and iterate far longer than intended.
Potential impact
- Excessive iteration may delay requests or make the server unresponsive.
- A long loop may occupy CPU time needed by other requests.
- Operations such as
pushorconcatinside the loop may exhaust memory and crash the process. - Blocking the event loop may increase response times and trigger load-balancer or gateway timeouts.
Remediation
- Check for an actual array with
Array.isArraybefore reading its length. - Reject arrays exceeding a fixed application limit,
MAX_ITEMS, before copying or iterating over them. - If array-like objects are accepted, require a finite, nonnegative integer length and enforce
MAX_ITEMSbefore allocation or conversion. - Do not use
Array.from(obj).slice(0, MAX_ITEMS)as an input limit: the conversion processes the whole collection before slicing it. - Limit request size and validate each element for the intended operation.
Examples
Before
javascript
const express = require("express");
const app = express();
app.use(express.json());
// Use an untrusted length directly as the loop bound
app.post("/sum", (req, res) => {
const list = req.body.items; // Fully controlled by the requester
let total = 0;
// BAD: { items: { length: 1e12 } } causes excessive iteration
for (let i = 0; i < list.length; i++) {
total += Number(list[i] || 0);
}
res.json({ total });
});
app.listen(3000);
After
javascript
const express = require("express");
const app = express();
app.use(express.json());
const MAX_ITEMS = 10000; // Choose a limit appropriate to the operation
// Validate the type and length before iterating over a bounded array
app.post("/sum", (req, res) => {
const list = req.body.items;
// 1) Require an actual array
if (!Array.isArray(list)) {
return res.status(400).json({ error: "items must be an array" });
}
// 2) Enforce the length limit before iteration or copying
if (list.length > MAX_ITEMS) {
return res.status(400).json({ error: "too many items" });
}
let total = 0;
for (const v of list) {
total += Number(v || 0);
}
res.json({ total });
});
app.listen(3000);
The first example trusts the request body's items.length as its loop bound. The second requires an actual array and rejects excessive length before copying or iteration. Separately validate each element's allowed type and range, beyond the illustrated numeric conversion, and limit request size.