27749f473202
record only the redemption attempts that were allowed, capping the list at ten per address (v3.0.73)
- time
- 2026-09-28 05:29 UTC · 2 weeks ago
- machine
- Junes-MacBook-Pro.local
- agent
- claude-opus-5-5
- session
- fix a2 review findings
- parent
- 2cea84c3cba0
↓ kepr-27749f47.tar.gzatlas at this save
files changed3 files
package.json+1 −1src/server/connect.ts+3 −1tests/connect.test.ts+16 −0package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "kepr",3- "version": "3.0.72",3+ "version": "3.0.73",44 "private": true,55 "type": "module",66 "scripts": {src/server/connect.ts+3 −1
@@ -109,8 +109,10 @@109109 attempts = attempts.filter((attempt) => attempt.at > now - ATTEMPT_WINDOW_MS);110110 const address = createHash("sha256").update(attemptKey).update(clientIp(c)).digest("hex");111111 const used = attempts.filter((attempt) => attempt.address === address).length;112- attempts.push({ at: now, address });112+ // A refused attempt is not recorded: that caps the list at ten entries per address, and keeps113+ // hammering from pushing the block out past the hour.113114 if (used >= ATTEMPTS_PER_ADDRESS) return noCode(c);115+ attempts.push({ at: now, address });114116 let code = "";115117 try {116118 const fields = await c.req.parseBody();tests/connect.test.ts+16 −0
@@ -261,6 +261,22 @@261261 expect((await redeem(app, code, address)).status).toBe(200);262262 });263263 264+ test("refused attempts are not recorded, so hammering neither grows the list nor the block", async () => {265+ const { app, browser } = await connected();266+ const address = "192.0.2.90";267+ for (let i = 0; i < 10; i++) await redeem(app, "bad", address);268+ // Fifty more inside the hour are all refused, and none of them is remembered: kept, they would269+ // cost memory per request and push the block out an hour past the last one.270+ clock += 3_540_000;271+ setSystemTime(clock);272+ for (let i = 0; i < 50; i++) expect((await redeem(app, "bad", address)).status).toBe(404);273+ // An hour after the tenth counted attempt the peer is served again.274+ clock += 61_000;275+ setSystemTime(clock);276+ const { code } = await issue(browser, "after the block");277+ expect((await redeem(app, code, address)).status).toBe(200);278+ });279+264280 test("a forged X-Forwarded-For does not buy another bucketful", async () => {265281 const { app, browser } = await connected();266282 const { code } = await issue(browser);