aboutsummaryrefslogtreecommitdiffstats
path: root/server/core/rateLimiter.js
blob: 0c2a384e4864dc1056ec351bd792a72d57ef3dbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
  * Tracks frequency of occurances based on `id` (remote address), then allows or
  * denies command execution based on comparison with `threshold`
  *
  * Version: v2.0.0
  * Developer: Marzavec ( https://github.com/marzavec )
  * License: WTFPL ( http://www.wtfpl.net/txt/copying/ )
  *
  */

class Police {
  /**
   * Create a ratelimiter instance.
   */
  constructor () {
    this._records = {};
    this._halflife = 30 * 1000; // milliseconds
    this._threshold = 25;
    this._hashes = [];
  }

  /**
    * Finds current score by `id`
    *
    * @param {String} id target id / address
    * @public
    *
    * @memberof Police
    */
  search (id) {
    let record = this._records[id];

    if (!record) {
      record = this._records[id] = {
        time: Date.now(),
        score: 0
      }
    }

    return record;
  }

  /**
    * Adjusts the current ratelimit score by `deltaScore`
    *
    * @param {String} id target id / address
    * @param {Number} deltaScore amount to adjust current score by
    * @public
    *
    * @memberof Police
    */
  frisk (id, deltaScore) {
    let record = this.search(id);

    if (record.arrested) {
      return true;
    }

    record.score *= Math.pow(2, -(Date.now() - record.time ) / this._halflife);
    record.score += deltaScore;
    record.time = Date.now();

    if (record.score >= this._threshold) {
      return true;
    }

    return false;
  }

  /**
    * Statically set server to no longer accept traffic from `id`
    *
    * @param {String} id target id / address
    * @public
    *
    * @memberof Police
    */
  arrest (id, hash) {
    let record = this.search(id);

    record.arrested = true;
    this._hashes[hash] = id;
  }

  /**
    * Remove statically assigned limit from `id`
    *
    * @param {String} id target id / address
    * @public
    *
    * @memberof Police
    */
  pardon (id) {
    if (typeof this._hashes[id] !== 'undefined') {
      id = this._hashes[id];
    }
    
    let record = this.search(id);
    record.arrested = false;
  }
}

module.exports = Police;