@anonympins/fingerprint 0.6.4 → 0.6.5

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.
@@ -4843,11 +4843,23 @@ export class FingerprintEngine {
4843
4843
  const [zkpY, zkpT, zkpS] = parts;
4844
4844
  // 1. Valider cryptographiquement la preuve avant de bannir/diffuser
4845
4845
  if (verifyZkpProof(zkpY, zkpT, zkpS)) {
4846
- // 2. Dédoublonner : Ne diffuser que si la clé n'est pas déjà connue comme bannie
4847
- if (!(await store.has(`banned-zkp-y:${zkpY}`))) {
4848
- await store.set(`banned-zkp-y:${zkpY}`, true, 86400 * 30); // Banni pour 30 jours
4849
- broadcastBannedZkp(zkpY, this.securityConfig).catch(() => {});
4846
+ const peersKey = `fed-peers:${zkpY}`;
4847
+ let reportedPeers = await store.get(peersKey) || [];
4848
+ if (!Array.isArray(reportedPeers)) {
4849
+ reportedPeers = [];
4850
4850
  }
4851
+ if (!reportedPeers.includes('local')) {
4852
+ reportedPeers.push('local');
4853
+ await store.set(peersKey, reportedPeers, 86400 * 30);
4854
+ }
4855
+
4856
+ const threshold = this.securityConfig.federationConsensusThreshold || 3;
4857
+ if (reportedPeers.length >= threshold) {
4858
+ if (!(await store.has(`banned-zkp-y:${zkpY}`))) {
4859
+ await store.set(`banned-zkp-y:${zkpY}`, true, 86400 * 30); // Banni pour 30 jours
4860
+ }
4861
+ }
4862
+ broadcastBannedZkp(zkpY, this.securityConfig).catch(() => {});
4851
4863
  }
4852
4864
  }
4853
4865
  }
@@ -5339,8 +5351,22 @@ export async function handleCooperativeRequest(params, clientIp = '127.0.0.1', c
5339
5351
  return { error: 'Missing signature' };
5340
5352
  }
5341
5353
 
5342
- await store.set(`banned-zkp-y:${zkpY}`, true, 86400 * 30); // 30 jours
5343
- return { status: 'synchronized' };
5354
+ const peersKey = `fed-peers:${zkpY}`;
5355
+ let reportedPeers = await store.get(peersKey) || [];
5356
+ if (!Array.isArray(reportedPeers)) {
5357
+ reportedPeers = [];
5358
+ }
5359
+ if (!reportedPeers.includes(clientIp)) {
5360
+ reportedPeers.push(clientIp);
5361
+ await store.set(peersKey, reportedPeers, 86400 * 30);
5362
+ }
5363
+
5364
+ const threshold = config.federationConsensusThreshold || 3;
5365
+ if (reportedPeers.length >= threshold) {
5366
+ await store.set(`banned-zkp-y:${zkpY}`, true, 86400 * 30);
5367
+ return { status: 'synchronized', banned: true };
5368
+ }
5369
+ return { status: 'synchronized', banned: false, reportsCount: reportedPeers.length };
5344
5370
  }
5345
5371
 
5346
5372
  const nodeId = params.node_id || '';
@@ -6704,6 +6730,50 @@ export function startThresholdAutoTuning(options) {
6704
6730
  if (!securityConfig || !trafficData) {
6705
6731
  throw new Error("[AutoTuning] `securityConfig` et `trafficData` sont requis.");
6706
6732
  }
6733
+ if (securityConfig.logger && typeof securityConfig.logger === 'function' && !securityConfig.logger_wrapped) {
6734
+ const originalLogger = securityConfig.logger;
6735
+ const maxPercentage = securityConfig.autotuning?.maxDensityPercentage || 0.02;
6736
+
6737
+ securityConfig.logger = function (log) {
6738
+ const ip = log.clientIp || log.ip;
6739
+ const subnet = ip ? __internal.getIpSubnet(ip, 24, 48) : null;
6740
+ const fp = log.deviceHash || log.fingerprint || log.deviceFingerprint;
6741
+ const stableFp = fp ? extractStablePart(fp) : null;
6742
+
6743
+ if (trafficData.length > 0) {
6744
+ const total = trafficData.length;
6745
+ let ipMatchCount = 0;
6746
+ let fpMatchCount = 0;
6747
+ let matchedKey = null;
6748
+
6749
+ for (let i = 0; i < trafficData.length; i++) {
6750
+ const existingLog = trafficData[i];
6751
+ const logIp = existingLog.clientIp || existingLog.ip;
6752
+ const logSubnet = logIp ? __internal.getIpSubnet(logIp, 24, 48) : null;
6753
+ const logFp = existingLog.deviceHash || existingLog.fingerprint || existingLog.deviceFingerprint;
6754
+ const logStableFp = logFp ? extractStablePart(logFp) : null;
6755
+
6756
+ const isIpMatch = subnet && logSubnet === subnet;
6757
+ const isFpMatch = stableFp && logStableFp === stableFp;
6758
+
6759
+ if (isIpMatch) ipMatchCount++;
6760
+ if (isFpMatch) fpMatchCount++;
6761
+
6762
+ if (isIpMatch || isFpMatch) matchedKey = i;
6763
+ }
6764
+
6765
+ if ((subnet && (ipMatchCount / total) > maxPercentage) || (stableFp && (fpMatchCount / total) > maxPercentage)) {
6766
+ if (matchedKey !== null) {
6767
+ trafficData[matchedKey].instancesCount = (trafficData[matchedKey].instancesCount || 1) + 1;
6768
+ trafficData[matchedKey].weight = (trafficData[matchedKey].weight || 1.0) + 1.0;
6769
+ }
6770
+ return;
6771
+ }
6772
+ }
6773
+ originalLogger(log);
6774
+ };
6775
+ securityConfig.logger_wrapped = true;
6776
+ }
6707
6777
 
6708
6778
  console.log(`[AutoTuning] Job d'optimisation des seuils démarré. Prochain cycle dans ${interval / 60000} minutes.`);
6709
6779
 
@@ -53,26 +53,57 @@ export class GpuPowSolver {
53
53
  }
54
54
  }
55
55
 
56
+ static deriveSampleIndices(clientIp, secret) {
57
+ const timeWindow = Math.floor(Date.now() / (1000 * 60 * 5));
58
+ const message = `${clientIp}:${timeWindow}`;
59
+ let hmacBytes;
60
+ if (typeof window === 'undefined') {
61
+ try {
62
+ const crypto = require('node:crypto');
63
+ const hmac = crypto.createHmac('sha256', secret || 'gpu-pow-salt').update(message).digest();
64
+ hmacBytes = new Uint8Array(hmac);
65
+ } catch (e) {
66
+ hmacBytes = new TextEncoder().encode(message);
67
+ }
68
+ } else {
69
+ hmacBytes = new TextEncoder().encode(message);
70
+ }
71
+
72
+ const indices = [];
73
+ for (let i = 0; i < 4; i++) {
74
+ const offset = (i * 2) % hmacBytes.length;
75
+ const value = (hmacBytes[offset] << 8) | hmacBytes[(offset + 1) % hmacBytes.length];
76
+ let idx = value % 64;
77
+ while (indices.includes(idx)) {
78
+ idx = (idx + 1) % 64;
79
+ }
80
+ indices.push(idx);
81
+ }
82
+ return indices;
83
+ }
84
+
56
85
  /**
57
86
  * Verifies a GPU PoW solution.
58
87
  * To prevent server-side DoS, it verifies a sample of the 64 channels.
59
88
  * @param {string} seed - The challenge seed.
60
89
  * @param {number} iterations - Number of iterations.
61
90
  * @param {string} solution - The comma-separated solution string.
62
- * @param {Array<number>} [sampleIndices=[0, 12, 35, 57]] - Indices to verify.
91
+ * @param {string} [clientIp='127.0.0.1'] - Client IP address.
92
+ * @param {string} [secret='gpu-pow-salt'] - Session secret key.
63
93
  * @returns {boolean} True if the solution is valid.
64
94
  */
65
- static verify(seed, iterations, solution, sampleIndices = [0, 12, 35, 57]) {
95
+ static verify(seed, iterations, solution, clientIp = '127.0.0.1', secret = 'gpu-pow-salt') {
66
96
  if (!solution || typeof solution !== 'string') return false;
67
97
  const values = solution.split(',');
68
98
  if (values.length !== 64) return false;
69
99
 
100
+ const sampleIndices = this.deriveSampleIndices(clientIp, secret);
70
101
  const numericSeed = this._hashSeedToFloat(seed);
71
102
  const r = 3.9999;
72
103
 
73
104
  for (const idx of sampleIndices) {
74
105
  if (idx < 0 || idx >= 64) return false;
75
- let x = Math.fround(numericSeed + idx * 0.015);
106
+ let x = Math.fround((numericSeed + idx * 0.015) % 1);
76
107
  const rFloat = Math.fround(r);
77
108
  for (let i = 0; i < iterations; i++) {
78
109
  x = Math.fround(rFloat * x * Math.fround(1.0 - x));
package/src/js/library.js CHANGED
@@ -16,10 +16,10 @@ import os from "node:os";
16
16
  import crypto from "node:crypto";
17
17
 
18
18
  /**
19
- * Génère un nombre flottant aléatoire cryptographiquement sûr entre 0 (inclus) et 1 (exclus).
19
+ * Génère un nombre flottant aléatoire entre 0 (inclus) et 1 (exclus).
20
20
  * @returns {number}
21
21
  */
22
- const secureRandom = () => crypto.randomBytes(4).readUInt32LE(0) / 0x100000000;
22
+ const random = () => Math.random();
23
23
 
24
24
  const Optimization = {
25
25
  // eslint-disable-line no-unused-vars
@@ -62,7 +62,7 @@ const Optimization = {
62
62
  );
63
63
 
64
64
  // Décide si on se déplace vers la nouvelle solution.
65
- if (newEnergy < currentEnergy || secureRandom() < acceptanceProbability) {
65
+ if (newEnergy < currentEnergy || random() < acceptanceProbability) {
66
66
  currentSolution = newSolution;
67
67
  currentEnergy = newEnergy;
68
68
  }
@@ -142,7 +142,7 @@ const Optimization = {
142
142
 
143
143
  let offspringChromosome;
144
144
  // 4. Croisement
145
- if (secureRandom() < crossoverRate) {
145
+ if (random() < crossoverRate) {
146
146
  offspringChromosome = crossover(
147
147
  parent1.chromosome,
148
148
  parent2.chromosome,
@@ -152,7 +152,7 @@ const Optimization = {
152
152
  }
153
153
 
154
154
  // 5. Mutation
155
- if (secureRandom() < mutationRate) {
155
+ if (random() < mutationRate) {
156
156
  offspringChromosome = mutate(offspringChromosome);
157
157
  }
158
158
 
@@ -524,7 +524,7 @@ Optimization.geneticAlgorithmMultiObjective = function (
524
524
  const parent1 = population[crypto.randomInt(0, population.length)];
525
525
  const parent2 = population[crypto.randomInt(0, population.length)];
526
526
  let childIndividual = crossover(parent1.individual, parent2.individual);
527
- if (secureRandom() < mutationRate) {
527
+ if (random() < mutationRate) {
528
528
  childIndividual = mutate(childIndividual, currentConfig); // mutate doit maintenant utiliser currentConfig
529
529
  }
530
530
  const child = { individual: childIndividual };
@@ -668,7 +668,7 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
668
668
  }
669
669
 
670
670
  for (let i = 0; i < populationSize; i++) {
671
- const z = Array.from({ length: n }, () => secureRandom() * 2 - 1); // Vecteur normal standard
671
+ const z = Array.from({ length: n }, () => random() * 2 - 1); // Vecteur normal standard
672
672
  const y = Array(n).fill(0); // z transformé par L
673
673
  for (let r = 0; r < n; r++) {
674
674
  for (let c = 0; c < n; c++) {
@@ -926,7 +926,7 @@ Optimization.Operators.solveTSP = (cities, options = {}) => {
926
926
 
927
927
  // Solution initiale : un chemin aléatoire.
928
928
  const initialPath = Array.from({ length: cities.length }, (_, i) => i).sort(
929
- () => secureRandom() - 0.5,
929
+ () => random() - 0.5,
930
930
  );
931
931
 
932
932
  // Paramètres par défaut pour le TSP, pouvant être surchargés par `options`.
@@ -968,14 +968,14 @@ Optimization.Operators.solvePortfolio = (
968
968
 
969
969
  // Fonctions spécifiques au problème pour l'AG, maintenant encapsulées.
970
970
  const createIndividual = () =>
971
- Array.from({ length: assets.length }, () => secureRandom());
971
+ Array.from({ length: assets.length }, () => random());
972
972
 
973
973
  const crossover = (p1, p2) => p1.map((w1, i) => (w1 + p2[i]) / 2); // Moyenne des poids
974
974
 
975
975
  const mutate = (p) => {
976
976
  const newP = [...p];
977
977
  const i = crypto.randomInt(0, newP.length);
978
- newP[i] += (secureRandom() - 0.5) * 0.2; // Mutation douce
978
+ newP[i] += (random() - 0.5) * 0.2; // Mutation douce
979
979
  newP[i] = Math.max(0, newP[i]); // Les poids ne peuvent être négatifs
980
980
  return newP;
981
981
  };
@@ -1109,8 +1109,8 @@ Optimization.Operators.solveFacilityLocation = (
1109
1109
  const facilityNeighbor = (facilities) => {
1110
1110
  const newFacilities = facilities.map((f) => ({ ...f }));
1111
1111
  const i = crypto.randomInt(0, numFacilities);
1112
- const moveX = (secureRandom() - 0.5) * (bounds.maxX - bounds.minX) * 0.1;
1113
- const moveY = (secureRandom() - 0.5) * (bounds.maxY - bounds.minY) * 0.1;
1112
+ const moveX = (random() - 0.5) * (bounds.maxX - bounds.minX) * 0.1;
1113
+ const moveY = (random() - 0.5) * (bounds.maxY - bounds.minY) * 0.1;
1114
1114
 
1115
1115
  newFacilities[i].x = Math.max(
1116
1116
  bounds.minX,
@@ -1126,8 +1126,8 @@ Optimization.Operators.solveFacilityLocation = (
1126
1126
 
1127
1127
  // Solution initiale : place les infrastructures au hasard sur la carte.
1128
1128
  const initialFacilities = Array.from({ length: numFacilities }, () => ({
1129
- x: bounds.minX + secureRandom() * (bounds.maxX - bounds.minX),
1130
- y: bounds.minY + secureRandom() * (bounds.maxY - bounds.minY),
1129
+ x: bounds.minX + random() * (bounds.maxX - bounds.minX),
1130
+ y: bounds.minY + random() * (bounds.maxY - bounds.minY),
1131
1131
  }));
1132
1132
 
1133
1133
  const saOptions = {
@@ -1244,7 +1244,7 @@ Optimization.Operators.solveOptimalCPC = (context, options = {}) => {
1244
1244
  // Un "individu" est simplement une valeur de CPC.
1245
1245
  const createIndividual = () => {
1246
1246
  // Le CPC peut varier, par exemple, entre 0.5 et 5 PRIM'S.
1247
- return 0.5 + secureRandom() * 4.5;
1247
+ return 0.5 + random() * 4.5;
1248
1248
  };
1249
1249
 
1250
1250
  // Croisement : moyenne des CPC des parents.
@@ -1254,7 +1254,7 @@ Optimization.Operators.solveOptimalCPC = (context, options = {}) => {
1254
1254
 
1255
1255
  // Mutation : légère variation aléatoire du CPC.
1256
1256
  const mutate = (cpc) => {
1257
- const newCpc = cpc + (secureRandom() - 0.5) * 0.5;
1257
+ const newCpc = cpc + (random() - 0.5) * 0.5;
1258
1258
  return Math.max(0.1, newCpc); // Assurer un CPC minimum.
1259
1259
  };
1260
1260
 
@@ -1501,9 +1501,9 @@ Optimization.Operators.solveFraudDetection = (context, options = {}) => {
1501
1501
 
1502
1502
  // Un "individu" est un tableau de 4 seuils : [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents]
1503
1503
  const createIndividual = () => {
1504
- const minTimeToClick = 100 + secureRandom() * 4900; // entre 100ms et 5s
1505
- const maxClickVariance = 1 + secureRandom() * 9999; // entre 1 et 10000
1506
- const minMouseEntropy = secureRandom() * 0.5; // entre 0 et 0.5
1504
+ const minTimeToClick = 100 + random() * 4900; // entre 100ms et 5s
1505
+ const maxClickVariance = 1 + random() * 9999; // entre 1 et 10000
1506
+ const minMouseEntropy = random() * 0.5; // entre 0 et 0.5
1507
1507
  const minScrollEvents = crypto.randomInt(0, 10); // entre 0 et 10
1508
1508
  return [minTimeToClick, maxClickVariance, minMouseEntropy, minScrollEvents];
1509
1509
  };
@@ -1555,6 +1555,40 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1555
1555
  const trafficData = context.trafficData || [];
1556
1556
  const currentConfig = context.currentConfig || null;
1557
1557
 
1558
+
1559
+ const THREAT_PROFILES = {
1560
+ account_takeover: {
1561
+ importance: 10.0,
1562
+ ux_vs_security_ratio: 0.1, // 10% FPR / 90% FNR (Priorité sécurité maximale)
1563
+ target_threshold: 'block',
1564
+ indicators: ['requestPatternScore', 'behaviorScore', 'timeInconsistencyScore', 'clickVarianceScore']
1565
+ },
1566
+ active_exploitation: {
1567
+ importance: 8.0,
1568
+ ux_vs_security_ratio: 0.2, // 20% FPR / 80% FNR (Sécurité prioritaire)
1569
+ target_threshold: 'block',
1570
+ indicators: ['honeypotScore', 'headerAnomalyScore']
1571
+ },
1572
+ mass_scraping: {
1573
+ importance: 3.0,
1574
+ ux_vs_security_ratio: 0.8, // 80% FPR / 20% FNR (UX prioritaire)
1575
+ target_threshold: 'low',
1576
+ indicators: ['requestPatternScore', 'renderingAnomalyScore', 'clientHintsInconsistencyScore']
1577
+ },
1578
+ distributed_botnets: {
1579
+ importance: 6.0,
1580
+ ux_vs_security_ratio: 0.5, // Équilibré
1581
+ target_threshold: 'high',
1582
+ indicators: ['subnetScore', 'botnetClusterScore', 'ipReputationScore', 'tlsSpoofingScore']
1583
+ },
1584
+ basic_automation: {
1585
+ importance: 5.0,
1586
+ ux_vs_security_ratio: 0.4, // 40% FPR / 60% FNR
1587
+ target_threshold: 'medium',
1588
+ indicators: ['botScore', 'tlsSpoofingScore', 'tcpAnomalyScore']
1589
+ }
1590
+ };
1591
+
1558
1592
  // Ancres immuables (Baseline Anchors) pour forcer le calibrage d'échelle de suspicion
1559
1593
  const STATIC_ANCHORS = [
1560
1594
  {
@@ -1608,10 +1642,10 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1608
1642
  ];
1609
1643
 
1610
1644
  return function fullConfigFitness(config) {
1611
- let falsePositives = 0;
1612
- let falseNegatives = 0;
1613
- let totalHumans = 0;
1614
- let totalBots = 0;
1645
+ const threatStats = {};
1646
+ for (const threatName in THREAT_PROFILES) {
1647
+ threatStats[threatName] = { fp: 0, fn: 0, totalHumans: 0, totalBots: 0 };
1648
+ }
1615
1649
 
1616
1650
  let maxHumanScore = 0;
1617
1651
  let minBotScore = 100;
@@ -1642,42 +1676,96 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1642
1676
  const score = calculateScore(log);
1643
1677
 
1644
1678
  if (isLikelyBot) {
1645
- totalBots += confidence;
1646
1679
  minBotScore = Math.min(minBotScore, score);
1647
- if (score < config.thresholds.low) {
1648
- falseNegatives += confidence;
1649
- }
1650
1680
  } else if (isLikelyHuman) {
1651
- totalHumans += confidence;
1652
1681
  maxHumanScore = Math.max(maxHumanScore, score);
1653
- if (score >= config.thresholds.low) {
1654
- falsePositives += confidence;
1682
+ }
1683
+
1684
+ for (const threatName in THREAT_PROFILES) {
1685
+ const profile = THREAT_PROFILES[threatName];
1686
+ let threatActivity = 0.0;
1687
+ for (const indicator of profile.indicators) {
1688
+ threatActivity += (log.vector?.[indicator] || 0.0);
1689
+ }
1690
+ if (threatActivity <= 0.0) {
1691
+ continue;
1692
+ }
1693
+ const effectiveConfidence = confidence * (threatActivity / 100.0);
1694
+ const targetThreshold = config.thresholds[profile.target_threshold] || 20;
1695
+
1696
+ if (isLikelyBot) {
1697
+ threatStats[threatName].totalBots += effectiveConfidence;
1698
+ if (score < targetThreshold) {
1699
+ threatStats[threatName].fn += effectiveConfidence;
1700
+ }
1701
+ } else if (isLikelyHuman) {
1702
+ threatStats[threatName].totalHumans += effectiveConfidence;
1703
+ if (score >= targetThreshold) {
1704
+ threatStats[threatName].fp += effectiveConfidence;
1705
+ }
1655
1706
  }
1656
1707
  }
1657
- }
1658
1708
 
1709
+ }
1659
1710
  // 2. Évaluation sur les ancres immuables pour fixer l'échelle
1660
1711
  for (const anchor of STATIC_ANCHORS) {
1661
1712
  const score = calculateScore(anchor);
1662
1713
  const weight = anchor.weight;
1714
+ const isBot = anchor.type === 'request_blocked' || anchor.type === 'challenge_issued';
1663
1715
 
1664
- if (anchor.type === 'request_blocked' || anchor.type === 'challenge_issued') {
1665
- totalBots += weight;
1716
+ if (isBot){
1666
1717
  minBotScore = Math.min(minBotScore, score);
1667
- if (score < config.thresholds.low) {
1668
- falseNegatives += weight * 10; // Pénalité punitive forte
1669
- }
1670
1718
  } else {
1671
- totalHumans += weight;
1672
1719
  maxHumanScore = Math.max(maxHumanScore, score);
1673
- if (score >= config.thresholds.low) {
1674
- falsePositives += weight * 10; // Pénalité punitive forte
1720
+ }
1721
+
1722
+ for (const threatName in THREAT_PROFILES) {
1723
+ const profile = THREAT_PROFILES[threatName];
1724
+ let threatActivity = 0.0;
1725
+ for (const indicator of profile.indicators) {
1726
+ threatActivity += (anchor.vector?.[indicator] || 0.0);
1727
+ }
1728
+ if (threatActivity <= 0.0) {
1729
+ continue;
1730
+ }
1731
+ const effectiveWeight = weight * (threatActivity / 100.0);
1732
+ const targetThreshold = config.thresholds[profile.target_threshold] || 20;
1733
+
1734
+ if (isBot) {
1735
+ threatStats[threatName].totalBots += effectiveWeight;
1736
+ if (score < targetThreshold) {
1737
+ threatStats[threatName].fn += effectiveWeight * 10; // Pénalité punitive forte
1738
+ }
1739
+ } else {
1740
+ threatStats[threatName].totalHumans += effectiveWeight;
1741
+ if (score >= targetThreshold) {
1742
+ threatStats[threatName].fp += effectiveWeight * 10; // Pénalité punitive forte
1743
+ }
1675
1744
  }
1676
1745
  }
1677
1746
  }
1747
+ let weightedFpr = 0;
1748
+ let weightedFnr = 0;
1749
+ let totalImportance = 0;
1750
+
1751
+ for (const threatName in THREAT_PROFILES) {
1752
+ totalImportance += THREAT_PROFILES[threatName].importance;
1753
+ }
1754
+
1755
+ for (const threatName in THREAT_PROFILES) {
1756
+ const profile = THREAT_PROFILES[threatName];
1757
+ const stats = threatStats[threatName];
1758
+
1759
+ const fpr = stats.totalHumans > 0 ? stats.fp / stats.totalHumans : 0;
1760
+ const fnr = stats.totalBots > 0 ? stats.fn / stats.totalBots : 0;
1761
+
1762
+ const importanceWeight = profile.importance / totalImportance;
1763
+ const uxRatio = profile.ux_vs_security_ratio;
1764
+ const secRatio = 1.0 - uxRatio;
1678
1765
 
1679
- const falsePositiveRate = totalHumans > 0 ? falsePositives / totalHumans : 0;
1680
- const falseNegativeRate = totalBots > 0 ? falseNegatives / totalBots : 0;
1766
+ weightedFpr += fpr * importanceWeight * uxRatio;
1767
+ weightedFnr += fnr * importanceWeight * secRatio;
1768
+ }
1681
1769
 
1682
1770
  // 3. Pénalité de dérive d'échelle L2 (Régularisation par rapport au point d'origine)
1683
1771
  let regularizationPenalty = 0;
@@ -1692,9 +1780,8 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1692
1780
  const marginOverlap = Math.max(0, maxHumanScore - minBotScore);
1693
1781
  const marginPenalty = marginOverlap / 100;
1694
1782
 
1695
- const obj1 = falsePositiveRate + (regularizationPenalty * 0.05);
1696
- const obj2 = falseNegativeRate + marginPenalty;
1697
-
1783
+ const obj1 = weightedFpr + (regularizationPenalty * 0.05);
1784
+ const obj2 = weightedFnr + marginPenalty;
1698
1785
  return [obj1, obj2];
1699
1786
  };
1700
1787
  };
@@ -1707,47 +1794,56 @@ Optimization.Operators.createFullSecurityConfigEvaluator = (context) => {
1707
1794
  */ // eslint-disable-line max-len
1708
1795
  Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1709
1796
  const fitnessFunction = Optimization.Operators.createFullSecurityConfigEvaluator(context);
1797
+ const currentConfig = context.currentConfig || {};
1798
+ const baseWeights = currentConfig.weights || {
1799
+ historyScore: 0.3,
1800
+ rotationScore: 0.5,
1801
+ headerAnomalyScore: 0.1,
1802
+ requestPatternScore: 0.6,
1803
+ inconsistencyScore: 0.8,
1804
+ honeypotScore: 1.0,
1805
+ behaviorScore: 0.7,
1806
+ crossLayerInconsistencyScore: 0.4,
1807
+ timeInconsistencyScore: 0.9,
1808
+ tlsSpoofingScore: 0.8,
1809
+ botScore: 1.0,
1810
+ subnetScore: 0.4,
1811
+ ipReputationScore: 0.5,
1812
+ botnetClusterScore: 0.6,
1813
+ tcpAnomalyScore: 0.8,
1814
+ quicAnomalyScore: 0.8,
1815
+ renderingAnomalyScore: 0.8,
1816
+ threatIntelScore: 1.0
1817
+ };
1710
1818
 
1711
1819
  // Un "individu" est un objet de configuration complet
1712
1820
  const createIndividual = () => ({
1713
1821
  thresholds: {
1714
- low: 15 + secureRandom() * 20, // 15-35
1715
- medium: 40 + secureRandom() * 25,
1716
- high: 70 + secureRandom() * 20,
1717
- },
1718
- weights: {
1719
- historyScore: secureRandom(),
1720
- rotationScore: secureRandom(),
1721
- headerAnomalyScore: secureRandom(),
1722
- requestPatternScore: 0.5 + secureRandom(),
1723
- inconsistencyScore: secureRandom(),
1724
- honeypotScore: 1.0, // Garder le honeypot à 1.0 est une bonne pratique
1725
- behaviorScore: secureRandom(),
1726
- crossLayerInconsistencyScore: secureRandom(),
1727
- timeInconsistencyScore: secureRandom(),
1728
- tlsSpoofingScore: secureRandom(),
1729
- botScore: secureRandom(),
1730
- subnetScore: secureRandom(),
1731
- ipReputationScore: secureRandom(),
1732
- botnetClusterScore: secureRandom(),
1733
- tcpAnomalyScore: secureRandom(),
1734
- quicAnomalyScore: secureRandom(),
1735
- clickVarianceScore: secureRandom(),
1736
- clientHintsInconsistencyScore: secureRandom()
1822
+ low: 15 + random() * 20, // 15-35
1823
+ medium: 40 + random() * 25,
1824
+ high: 70 + random() * 20,
1825
+ block: 90 + random() * 9,
1737
1826
  },
1827
+ weights: (() => {
1828
+ const w = {};
1829
+ for (const key in baseWeights) {
1830
+ w[key] = baseWeights[key] * (0.75 + random() * 0.5); // +/- 25%
1831
+ }
1832
+ return w;
1833
+ })(),
1738
1834
  patterns: {
1739
- velocityThreshold: 100 + secureRandom() * 400,
1740
- velocityWeight: 10 + secureRandom() * 40,
1741
- burstThreshold: 300 + secureRandom() * 700,
1742
- burstWeight: 20 + secureRandom() * 40,
1743
- scrapeThreshold: 500 + secureRandom() * 1000,
1744
- scrapeWeight: 15 + secureRandom() * 35,
1835
+ velocityThreshold: 100 + random() * 400,
1836
+ velocityWeight: 10 + random() * 40,
1837
+ burstThreshold: 300 + random() * 700,
1838
+ burstWeight: 20 + random() * 40,
1839
+ scrapeThreshold: 500 + random() * 1000,
1840
+ scrapeWeight: 15 + random() * 35,
1745
1841
  sequenceLength: 3 + crypto.randomInt(0, 3),
1746
- sequenceWeight: 20 + secureRandom() * 50,
1747
- regularityThreshold: 50 + secureRandom() * 200,
1748
- regularityWeight: 20 + secureRandom() * 40,
1749
- decayFactor: 0.85 + secureRandom() * 0.14,
1750
- inactivityReset: 15000 + secureRandom() * 45000,
1842
+ sequenceWeight: 20 + random() * 50,
1843
+ regularityThreshold: 50 + random() * 200,
1844
+ regularityWeight: 20 + random() * 40,
1845
+ decayFactor: 0.85 + random() * 0.14,
1846
+ inactivityReset: 15000 + random() * 45000,
1751
1847
  }
1752
1848
  });
1753
1849
 
@@ -1759,9 +1855,7 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1759
1855
  child.thresholds[key] = (c1.thresholds[key] + c2.thresholds[key]) / 2;
1760
1856
  }
1761
1857
  for (const key in child.weights) {
1762
- if (key !== 'honeypotScore') { // Ne pas croiser le poids du honeypot
1763
- child.weights[key] = (c1.weights[key] + c2.weights[key]) / 2;
1764
- }
1858
+ child.weights[key] = (c1.weights[key] + c2.weights[key]) / 2;
1765
1859
  }
1766
1860
  for (const key in child.patterns) {
1767
1861
  child.patterns[key] = (c1.patterns[key] + c2.patterns[key]) / 2;
@@ -1776,11 +1870,11 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1776
1870
  // On donne plus de poids à la mutation des 'patterns' et des 'weights',
1777
1871
  // car ils ont un impact plus direct sur la détection que les seuils.
1778
1872
  const sections = [
1779
- { name: 'patterns', weight: 0.5 }, // 50% de chance
1780
- { name: 'weights', weight: 0.35 }, // 35% de chance
1781
- { name: 'thresholds', weight: 0.15 } // 15% de chance
1873
+ { name: 'patterns', weight: 0.50 },
1874
+ { name: 'thresholds', weight: 0.25 },
1875
+ { name: 'weights', weight: 0.25 }
1782
1876
  ];
1783
- const rand = secureRandom();
1877
+ const rand = random();
1784
1878
  let cumulativeWeight = 0;
1785
1879
  let sectionToMutate = 'patterns'; // Fallback
1786
1880
  for (const section of sections) {
@@ -1794,21 +1888,18 @@ Optimization.Operators.solveFullSecurityTuning = (context, options = {}) => {
1794
1888
  const keys = Object.keys(newConfig[sectionToMutate]);
1795
1889
  const keyToMutate = keys[crypto.randomInt(0, keys.length)];
1796
1890
 
1797
- if (keyToMutate === 'honeypotScore') return newConfig; // Ne pas muter le poids du honeypot
1798
-
1799
- // Appliquer une mutation avec une amplitude variable
1800
- const mutationAmount = (secureRandom() - 0.5) * 0.4; // +/- 20%
1801
- newConfig[sectionToMutate][keyToMutate] *= (1 + mutationAmount);
1802
1891
 
1803
1892
  // S'assurer que les valeurs restent dans des limites raisonnables
1804
1893
  if (sectionToMutate === 'weights') {
1805
- newConfig[sectionToMutate][keyToMutate] = Math.max(0, Math.min(1.5, newConfig[sectionToMutate][keyToMutate]));
1806
- }
1807
- if (keyToMutate === 'decayFactor') {
1808
- newConfig.patterns.decayFactor = Math.max(0.8, Math.min(0.999, newConfig.patterns.decayFactor));
1809
- }
1810
- if (keyToMutate.includes('Threshold') || keyToMutate.includes('Reset')) {
1811
- newConfig.patterns[keyToMutate] = Math.max(50, newConfig.patterns[keyToMutate]);
1894
+ newConfig[sectionToMutate][keyToMutate] = Math.max(0.05, Math.min(1.5, newConfig[sectionToMutate][keyToMutate] + (random() - 0.5) * 0.1));
1895
+ } else if (sectionToMutate === 'thresholds') {
1896
+ newConfig[sectionToMutate][keyToMutate] = Math.round(newConfig[sectionToMutate][keyToMutate] + (random() - 0.5) * 5.0);
1897
+ } else {
1898
+ if (keyToMutate === 'decayFactor') {
1899
+ newConfig.patterns.decayFactor = Math.max(0.8, Math.min(0.999, newConfig.patterns.decayFactor + (random() - 0.5) * 0.05));
1900
+ } else if (keyToMutate.includes('Threshold') || keyToMutate.includes('Reset')) {
1901
+ newConfig.patterns[keyToMutate] = Math.max(50, newConfig.patterns[keyToMutate] + (random() - 0.5) * 50.0);
1902
+ }
1812
1903
  }
1813
1904
 
1814
1905
  // Contrainte de dérive maximale (±30% par rapport à la configuration actuelle)
@@ -27,7 +27,7 @@ describe('GpuPowSolver - Chaotic Logistic Map PoW', () => {
27
27
  const solutions = [];
28
28
 
29
29
  for (let idx = 0; idx < 64; idx++) {
30
- let x = Math.fround(numericSeed + idx * 0.015);
30
+ let x = Math.fround((numericSeed + idx * 0.015) % 1); // Ensure x starts in [0, 1)
31
31
  for (let i = 0; i < iterations; i++) {
32
32
  x = Math.fround(rFloat * x * Math.fround(1.0 - x));
33
33
  }
@@ -53,19 +53,21 @@ describe('GpuPowSolver - Chaotic Logistic Map PoW', () => {
53
53
  const solutions = [];
54
54
 
55
55
  for (let idx = 0; idx < 64; idx++) {
56
- let x = Math.fround(numericSeed + idx * 0.015);
56
+ let x = Math.fround((numericSeed + idx * 0.015) % 1); // Ensure x starts in [0, 1)
57
57
  for (let i = 0; i < iterations; i++) {
58
58
  x = Math.fround(rFloat * x * Math.fround(1.0 - x));
59
59
  }
60
60
  solutions.push(x.toFixed(6));
61
61
  }
62
62
 
63
- // Tamper with the 12th channel (one of the default sample indices [0, 12, 35, 57])
63
+ // Tamper with one of the dynamically derived sample indices
64
+ const sampleIndices = GpuPowSolver.deriveSampleIndices('127.0.0.1', 'gpu-pow-salt');
65
+ const tamperedIndex = sampleIndices[0];
64
66
  const tamperedSolutions = [...solutions];
65
- const originalVal = parseFloat(tamperedSolutions[12]);
66
- tamperedSolutions[12] = (originalVal + 0.0002).toFixed(6); // exceeding 1e-4 tolerance
67
-
68
- expect(GpuPowSolver.verify(seed, iterations, tamperedSolutions.join(','))).toBe(false);
67
+ const originalVal = parseFloat(tamperedSolutions[tamperedIndex]);
68
+ tamperedSolutions[tamperedIndex] = (originalVal + 0.1).toFixed(6); // exceeding tolerance
69
+ const verificationResult = GpuPowSolver.verify(seed, iterations, tamperedSolutions.join(','), '127.0.0.1', 'gpu-pow-salt');
70
+ expect(verificationResult).toBe(false);
69
71
  });
70
72
 
71
73
  it('should fallback to WebGL2 if WebGPU is not supported', async () => {