express-range-tracker
Advanced tools
Comparing version 1.0.5 to 1.0.6
21
index.js
@@ -8,2 +8,3 @@ const defaultStorage = {}; | ||
maxDelay, | ||
onRobotic, | ||
onDownloaded, | ||
@@ -20,6 +21,14 @@ onSimilarTrait, | ||
function (req, res, next) { | ||
if (!req.headers.range) { | ||
if (typeof req.headers.range === "undefined") { | ||
if (typeof onRobotic === "function") { | ||
onRobotic(req, "absent"); | ||
} | ||
return next(); | ||
} | ||
if (!req.headers.range.length && typeof onRobotic === "function") { | ||
onRobotic(req, "empty"); | ||
} | ||
const ranges = req.headers.range | ||
@@ -45,2 +54,12 @@ .replace("bytes=", "") | ||
if (typeof onRobotic === "function") { | ||
if (!/^bytes=\d+-\d*(,\d+-\d*)*$/g.test(req.headers.range)) { | ||
onRobotic(req, "malformed"); | ||
} | ||
if (from > to) { | ||
onRobotic(req, "digits"); | ||
} | ||
} | ||
if (!storage[ip]) { | ||
@@ -47,0 +66,0 @@ storage[ip] = []; |
{ | ||
"name": "express-range-tracker", | ||
"version": "1.0.5", | ||
"version": "1.0.6", | ||
"description": "Detects bots by tracking the timings of range header", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -62,2 +62,13 @@ # express-range-tracker | ||
`onRobotic` - fires when range is malformed in some way: | ||
- `malformed` - does not conform to range spec | ||
- `digits` - `from` is bigger than `to` in range segment | ||
- `empty` - range header is present but length of string is 0 | ||
- `absent` - request of content that should have range header but does not exist | ||
```js | ||
onRobotic(req, reason); | ||
``` | ||
# Test | ||
@@ -64,0 +75,0 @@ |
@@ -159,3 +159,3 @@ const assert = require("node:assert"); | ||
it("should emit downloaded event when all parts downloaded", (done) => { | ||
it("should emit downloaded event when all parts downloaded", async () => { | ||
const expected = { | ||
@@ -194,20 +194,22 @@ ip: "::1", | ||
const track = rangeTracker({ | ||
timestampFunction: () => 1, | ||
storage, | ||
onDownloaded: (req, res, next) => { | ||
assert.deepStrictEqual(req, expected); | ||
assert.ok(res); | ||
assert.ok(next); | ||
await new Promise((resolve) => { | ||
const track = rangeTracker({ | ||
timestampFunction: () => 1, | ||
storage, | ||
onDownloaded: (req, res, next) => { | ||
assert.deepStrictEqual(req, expected); | ||
assert.ok(res); | ||
assert.ok(next); | ||
done(); | ||
}, | ||
max: 100, | ||
resolve(); | ||
}, | ||
max: 100, | ||
}); | ||
track(req1, res, next); | ||
track(req2, res, next); | ||
}); | ||
track(req1, res, next); | ||
track(req2, res, next); | ||
}); | ||
it("should emit deadline event when chunk requested slowly", (done) => { | ||
it("should emit deadline event when chunk requested slowly", async () => { | ||
const expected = { | ||
@@ -235,24 +237,26 @@ ip: "::1", | ||
const track = rangeTracker({ | ||
timestampFunction: () => { | ||
const current = timestamp; | ||
await new Promise((resolve) => { | ||
const track = rangeTracker({ | ||
timestampFunction: () => { | ||
const current = timestamp; | ||
timestamp += 2; | ||
timestamp += 2; | ||
return current; | ||
}, | ||
storage, | ||
onDeadlineReached: (req, res, next) => { | ||
assert.deepStrictEqual(req, expected); | ||
assert.ok(res); | ||
assert.ok(next); | ||
return current; | ||
}, | ||
storage, | ||
onDeadlineReached: (req, res, next) => { | ||
assert.deepStrictEqual(req, expected); | ||
assert.ok(res); | ||
assert.ok(next); | ||
done(); | ||
}, | ||
max: 100, | ||
maxDelay: 1, | ||
resolve(); | ||
}, | ||
max: 100, | ||
maxDelay: 1, | ||
}); | ||
track(req1, res, next); | ||
track(req2, res, next); | ||
}); | ||
track(req1, res, next); | ||
track(req2, res, next); | ||
}); | ||
@@ -472,2 +476,104 @@ | ||
}); | ||
it("should fire onrobotic event on malformed range", async () => { | ||
const expected = "malformed"; | ||
const storage = {}; | ||
const req = { | ||
ip: "::1", | ||
headers: { | ||
range: "btes=2-50", | ||
}, | ||
}; | ||
await new Promise((resolve) => { | ||
const track = rangeTracker({ | ||
storage, | ||
onRobotic: (badReq, reason) => { | ||
assert.strictEqual(reason, expected); | ||
assert.deepStrictEqual(badReq, req); | ||
resolve(); | ||
}, | ||
}); | ||
track(req, res, next); | ||
}); | ||
}); | ||
it("should fire onrobotic digits event on wrong range from and to numbers", async () => { | ||
const expected = "digits"; | ||
const storage = {}; | ||
const req = { | ||
ip: "::1", | ||
headers: { | ||
range: "bytes=50-2", | ||
}, | ||
}; | ||
await new Promise((resolve) => { | ||
const track = rangeTracker({ | ||
storage, | ||
onRobotic: (badReq, reason) => { | ||
assert.strictEqual(reason, expected); | ||
assert.deepStrictEqual(badReq, req); | ||
resolve(); | ||
}, | ||
}); | ||
track(req, res, next); | ||
}); | ||
}); | ||
it("should fire onrobotic empty event on empty range", async () => { | ||
const expected = "empty"; | ||
const storage = {}; | ||
const req = { | ||
ip: "::1", | ||
headers: { | ||
range: "", | ||
}, | ||
}; | ||
await new Promise((resolve) => { | ||
const track = rangeTracker({ | ||
storage, | ||
onRobotic: (badReq, reason) => { | ||
assert.strictEqual(reason, expected); | ||
assert.deepStrictEqual(badReq, req); | ||
resolve(); | ||
}, | ||
}); | ||
track(req, res, next); | ||
}); | ||
}); | ||
it("should fire onrobotic absent event on no range", async () => { | ||
const expected = "absent"; | ||
const storage = {}; | ||
const req = { | ||
ip: "::1", | ||
headers: {}, | ||
}; | ||
await new Promise((resolve) => { | ||
const track = rangeTracker({ | ||
storage, | ||
onRobotic: (badReq, reason) => { | ||
assert.strictEqual(reason, expected); | ||
assert.deepStrictEqual(badReq, req); | ||
resolve(); | ||
}, | ||
}); | ||
track(req, res, next); | ||
}); | ||
}); | ||
}); |
16679
5
614
78