@anonympins/fingerprint 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/package.json +1 -1
- package/src/js/fingerprint.client.js +1271 -1191
- package/src/js/fingerprint.js +202 -108
- package/src/js/fingerprint.utils.js +213 -213
- package/src/js/library.js +24 -8
- package/src/js/mongodb-store.js +165 -79
- package/src/js/pow.solver.inline.js +500 -14
- package/src/js/pow.solver.js +323 -14
- package/src/js/problem-manager.js +1 -1
- package/src/js/redis-store.js +114 -42
- package/src/js/tests/dns-circuit-breaker.test.js +55 -0
- package/src/js/tests/fingerprint.client.init.test.js +143 -143
- package/src/js/tests/fingerprint.client.test.js +128 -128
- package/src/js/tests/fingerprint.test.js +32 -2
- package/src/js/tests/ip-reputation.test.js +1 -1
- package/src/js/tests/pow.solver.test.js +5 -2
- package/src/js/tests/usefulWorkSolver.test.js +162 -0
- package/src/php/Challenge/ChallengeUtils.php +21 -8
- package/src/php/FingerprintClient.php +22 -5
- package/src/php/FingerprintEngine.php +232 -56
- package/src/php/Optimization/Optimization.php +23 -16
- package/src/php/Optimization/OptimizationOperators.php +27 -0
- package/src/php/ProblemManager.php +1 -1
- package/src/php/Store/MongoDbStore.php +184 -104
- package/src/php/Store/RedisStore.php +91 -11
- package/src/php/Tests/ChallengeUtilsTest.php +285 -285
- package/src/php/Tests/DnsCircuitBreakerTest.php +106 -0
- package/src/php/Tests/IpReputationTest.php +1 -1
- package/src/php/Tests/PowTest.php +1 -1
- package/src/php/Tests/RequestUtilsTest.php +29 -0
- package/src/php/Tests/TLSClientHelloParserTest.php +177 -117
- package/src/php/Utils/BigInt.php +202 -202
- package/src/php/Utils/RequestUtils.php +74 -21
- package/src/php/Utils/TLSClientHelloParser.php +212 -116
package/src/js/mongodb-store.js
CHANGED
|
@@ -1,80 +1,166 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Creates a store adapter for MongoDB.
|
|
3
|
-
* This adapter uses a collection as a key-value store and leverages MongoDB's TTL indexes
|
|
4
|
-
* for automatic expiration of documents.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Creates a store adapter for a MongoDB collection.
|
|
9
|
-
* It's recommended to pass the `db` object and let the adapter handle the collection.
|
|
10
|
-
*
|
|
11
|
-
* **Note:** For TTL to work, you must create a TTL index on the `expiresAt` field in your collection.
|
|
12
|
-
* In the mongo shell, run:
|
|
13
|
-
* `db.yourCollectionName.createIndex({ "expiresAt": 1 }, { expireAfterSeconds: 0 })`
|
|
14
|
-
*
|
|
15
|
-
* @param {import('mongodb').Db} db - An instance of a MongoDB Db object.
|
|
16
|
-
* @param {string} [collectionName='fingerprint_store'] - The name of the collection to use.
|
|
17
|
-
* @returns {import('./fingerprint.js').IStore} An object that complies with the IStore interface.
|
|
18
|
-
*/
|
|
19
|
-
export function createMongoDbStore(db, collectionName = 'fingerprint_store') {
|
|
20
|
-
const collection = db.collection(collectionName);
|
|
21
|
-
|
|
22
|
-
// Custom replacer/reviver to handle Set serialization (identical to Redis/SQL stores)
|
|
23
|
-
const replacer = (k, v) => (v instanceof Set ? Array.from(v) : v);
|
|
24
|
-
const reviver = (k, v) => (k === 'ips' && Array.isArray(v) ? new Set(v) : v);
|
|
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
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @file Creates a store adapter for MongoDB.
|
|
3
|
+
* This adapter uses a collection as a key-value store and leverages MongoDB's TTL indexes
|
|
4
|
+
* for automatic expiration of documents.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a store adapter for a MongoDB collection.
|
|
9
|
+
* It's recommended to pass the `db` object and let the adapter handle the collection.
|
|
10
|
+
*
|
|
11
|
+
* **Note:** For TTL to work, you must create a TTL index on the `expiresAt` field in your collection.
|
|
12
|
+
* In the mongo shell, run:
|
|
13
|
+
* `db.yourCollectionName.createIndex({ "expiresAt": 1 }, { expireAfterSeconds: 0 })`
|
|
14
|
+
*
|
|
15
|
+
* @param {import('mongodb').Db} db - An instance of a MongoDB Db object.
|
|
16
|
+
* @param {string} [collectionName='fingerprint_store'] - The name of the collection to use.
|
|
17
|
+
* @returns {import('./fingerprint.js').IStore} An object that complies with the IStore interface.
|
|
18
|
+
*/
|
|
19
|
+
export function createMongoDbStore(db, collectionName = 'fingerprint_store') {
|
|
20
|
+
const collection = db.collection(collectionName);
|
|
21
|
+
|
|
22
|
+
// Custom replacer/reviver to handle Set serialization (identical to Redis/SQL stores)
|
|
23
|
+
const replacer = (k, v) => (v instanceof Set ? Array.from(v) : v);
|
|
24
|
+
const reviver = (k, v) => (k === 'ips' && Array.isArray(v) ? new Set(v) : v);
|
|
25
|
+
|
|
26
|
+
const localStore = new Map();
|
|
27
|
+
const localTimeouts = new Map();
|
|
28
|
+
let isDown = false;
|
|
29
|
+
let reconnecting = false;
|
|
30
|
+
|
|
31
|
+
const triggerFallback = () => {
|
|
32
|
+
if (!isDown) {
|
|
33
|
+
isDown = true;
|
|
34
|
+
attemptReconnection();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const attemptReconnection = () => {
|
|
39
|
+
if (reconnecting) return;
|
|
40
|
+
reconnecting = true;
|
|
41
|
+
const interval = setInterval(async () => {
|
|
42
|
+
try {
|
|
43
|
+
if (db.admin) {
|
|
44
|
+
await db.admin().ping();
|
|
45
|
+
} else {
|
|
46
|
+
await collection.findOne({}, { projection: { _id: 1 } });
|
|
47
|
+
}
|
|
48
|
+
isDown = false;
|
|
49
|
+
reconnecting = false;
|
|
50
|
+
clearInterval(interval);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
// Toujours déconnecté
|
|
53
|
+
}
|
|
54
|
+
}, 5000);
|
|
55
|
+
if (interval.unref) interval.unref();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const setLocal = (key, value, ttl) => {
|
|
59
|
+
localStore.set(key, value);
|
|
60
|
+
if (localTimeouts.has(key)) {
|
|
61
|
+
clearTimeout(localTimeouts.get(key));
|
|
62
|
+
}
|
|
63
|
+
if (ttl && ttl > 0) {
|
|
64
|
+
const timeout = setTimeout(() => {
|
|
65
|
+
localStore.delete(key);
|
|
66
|
+
localTimeouts.delete(key);
|
|
67
|
+
}, ttl * 1000);
|
|
68
|
+
if (timeout.unref) timeout.unref();
|
|
69
|
+
localTimeouts.set(key, timeout);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
async get(key) {
|
|
75
|
+
if (isDown) {
|
|
76
|
+
return localStore.get(key) || null;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const doc = await collection.findOne({ _id: key });
|
|
80
|
+
if (!doc) return null;
|
|
81
|
+
|
|
82
|
+
// Active expiration check to bypass eventual consistency of MongoDB's 60s TTL cleanup daemon
|
|
83
|
+
if (doc.expiresAt && new Date(doc.expiresAt) < new Date()) {
|
|
84
|
+
await this.delete(key);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
return JSON.parse(doc.value, reviver);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
// Fallback for legacy un-serialized raw values
|
|
92
|
+
return doc.value;
|
|
93
|
+
}
|
|
94
|
+
} catch (e) {
|
|
95
|
+
triggerFallback();
|
|
96
|
+
return localStore.get(key) || null;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
async set(key, value, ttl) {
|
|
100
|
+
if (isDown) {
|
|
101
|
+
setLocal(key, value, ttl);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const stringValue = JSON.stringify(value, replacer);
|
|
106
|
+
const doc = {
|
|
107
|
+
_id: key,
|
|
108
|
+
value: stringValue,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (ttl && ttl > 0) {
|
|
112
|
+
// Set the expiration date for the TTL index.
|
|
113
|
+
doc.expiresAt = new Date(Date.now() + ttl * 1000);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await collection.updateOne(
|
|
117
|
+
{ _id: key },
|
|
118
|
+
{ $set: doc },
|
|
119
|
+
{ upsert: true }
|
|
120
|
+
);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
triggerFallback();
|
|
123
|
+
setLocal(key, value, ttl);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
async has(key) {
|
|
127
|
+
if (isDown) {
|
|
128
|
+
return localStore.has(key);
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const doc = await collection.findOne({ _id: key }, { projection: { expiresAt: 1 } });
|
|
132
|
+
if (!doc) return false;
|
|
133
|
+
|
|
134
|
+
if (doc.expiresAt && new Date(doc.expiresAt) < new Date()) {
|
|
135
|
+
await this.delete(key);
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
} catch (e) {
|
|
140
|
+
triggerFallback();
|
|
141
|
+
return localStore.has(key);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
async delete(key) {
|
|
145
|
+
if (localTimeouts.has(key)) {
|
|
146
|
+
clearTimeout(localTimeouts.get(key));
|
|
147
|
+
localTimeouts.delete(key);
|
|
148
|
+
}
|
|
149
|
+
localStore.delete(key);
|
|
150
|
+
if (isDown) return;
|
|
151
|
+
try {
|
|
152
|
+
await collection.deleteOne({ _id: key });
|
|
153
|
+
} catch (e) {
|
|
154
|
+
triggerFallback();
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
async init() {
|
|
158
|
+
try {
|
|
159
|
+
// Automates index configuration
|
|
160
|
+
await collection.createIndex({ "expiresAt": 1 }, { expireAfterSeconds: 0 });
|
|
161
|
+
} catch (e) {
|
|
162
|
+
triggerFallback();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
80
166
|
}
|