@@ -51,7 +51,7 @@ type RedirectChange @table @export {
5151 createdAt: Date @createdTime
5252}
5353
54- type AbusCounter @table(expiration: 10) @export {
54+ type AbuseCounter @table(expiration: 10) @export {
5555 id: ID @primaryKey
5656 count: Int
5757}
@@ -86,7 +86,7 @@ export class RedirectRule extends tables.RedirectRule {
8686 }
8787 if (existing.length > 0) {
8888 const context = this.getContext();
89- context.response.status = 409;
89+ if (context?.response) context.response.status = 409;
9090 return { error: 'Chain redirect detected' };
9191 }
9292 const id = Math.random().toString(36).slice(2);
@@ -133,16 +133,16 @@ export class RoutingDecision extends Resource {
133133 }
134134}
135135
136- // AbusCounter : atomic counter with 403 threshold (Ford PasswordResetAbuse pattern)
137- export class AbusCounter extends tables.AbusCounter {
136+ // AbuseCounter : atomic counter with 403 threshold (Ford PasswordResetAbuse pattern)
137+ export class AbuseCounter extends tables.AbuseCounter {
138138 async put(body, ctx) {
139139 // this is the loaded record instance; get current count from the stored record
140140 const current = await this.get();
141141 const id = this.getId();
142142 const newCount = ((current && current.count) || 0) + 1;
143143 if (newCount > 5) {
144144 const context = this.getContext();
145- context.response.status = 403;
145+ if (context?.response) context.response.status = 403;
146146 return { error: 'Too many attempts' };
147147 }
148148 // Full update via single-arg super.put (legacy: update(record, true) + save)
@@ -421,25 +421,25 @@ suite('Custom resource patterns', { skip: skipSuite }, (ctx) => {
421421 // 6. Abuse counter with 403 threshold (Ford PasswordResetAbuse pattern)
422422 // -----------------------------------------------------------------------
423423
424- suite ( 'AbusCounter : threshold enforcement' , ( ) => {
425- test ( 'PUT AbusCounter increments count (attempts 1–5 succeed)' , async ( ) => {
424+ suite ( 'AbuseCounter : threshold enforcement' , ( ) => {
425+ test ( 'PUT AbuseCounter increments count (attempts 1–5 succeed)' , async ( ) => {
426426 for ( let i = 1 ; i <= 5 ; i ++ ) {
427- const res = await restReq ( httpURL , '/AbusCounter /counter1' , 'PUT' , { id : 'counter1' } ) ;
427+ const res = await restReq ( httpURL , '/AbuseCounter /counter1' , 'PUT' , { id : 'counter1' } ) ;
428428 strictEqual ( res . status , 200 , `attempt ${ i } unexpected status: ${ res . status } ` ) ;
429429 const body = ( await res . json ( ) ) as Record < string , unknown > ;
430430 strictEqual ( body . count , i , `attempt ${ i } expected count=${ i } , got: ${ JSON . stringify ( body ) } ` ) ;
431431 }
432432 } ) ;
433433
434- test ( '6th PUT AbusCounter returns 403' , async ( ) => {
435- const res = await restReq ( httpURL , '/AbusCounter /counter1' , 'PUT' , { id : 'counter1' } ) ;
434+ test ( '6th PUT AbuseCounter returns 403' , async ( ) => {
435+ const res = await restReq ( httpURL , '/AbuseCounter /counter1' , 'PUT' , { id : 'counter1' } ) ;
436436 strictEqual ( res . status , 403 , `expected 403 on 6th attempt, got: ${ res . status } ` ) ;
437437 const body = ( await res . json ( ) ) as Record < string , unknown > ;
438438 ok ( typeof body . error === 'string' , `expected error message on 403, got: ${ JSON . stringify ( body ) } ` ) ;
439439 } ) ;
440440
441441 test ( 'independent counters do not interfere' , async ( ) => {
442- const res = await restReq ( httpURL , '/AbusCounter /counter2' , 'PUT' , { id : 'counter2' } ) ;
442+ const res = await restReq ( httpURL , '/AbuseCounter /counter2' , 'PUT' , { id : 'counter2' } ) ;
443443 strictEqual ( res . status , 200 , `first attempt on counter2 unexpected status: ${ res . status } ` ) ;
444444 const body = ( await res . json ( ) ) as Record < string , unknown > ;
445445 strictEqual ( body . count , 1 , `expected count=1 for fresh counter, got: ${ JSON . stringify ( body ) } ` ) ;
0 commit comments