@webdecoy/fcaptcha 1.35.0 → 1.36.0
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/engine.js +27 -10
- package/index.js +1 -1
- package/package.json +2 -2
- package/server.js +1 -1
package/engine.js
CHANGED
|
@@ -495,7 +495,10 @@ function detectCDP(signals) {
|
|
|
495
495
|
if (env.cdpRuntime && env.cdpRuntime.consoleAttached) {
|
|
496
496
|
detections.push({
|
|
497
497
|
category: 'cdp', score: 0.6, confidence: 0.5,
|
|
498
|
-
reason: 'CDP/DevTools console consumer attached (automation protocol or open DevTools)'
|
|
498
|
+
reason: 'CDP/DevTools console consumer attached (automation protocol or open DevTools)',
|
|
499
|
+
// A developer with DevTools open produces exactly this signal, so it
|
|
500
|
+
// cannot be one of the independent views the corroboration floor counts.
|
|
501
|
+
nonCorroborating: true
|
|
499
502
|
});
|
|
500
503
|
}
|
|
501
504
|
|
|
@@ -912,33 +915,47 @@ function applyDispositiveFloor(score, detections) {
|
|
|
912
915
|
* product. It treats absence of environmental evidence as evidence of absence — but
|
|
913
916
|
* on this adversary a clean environment is the attack working, not innocence.
|
|
914
917
|
*
|
|
915
|
-
* The rule: when two or more behavioural categories independently reach 0.
|
|
916
|
-
* the score at 0.6.
|
|
918
|
+
* The rule: when two or more behavioural categories independently reach 0.4, floor
|
|
919
|
+
* the score at 0.6. A signal a developer's own tooling produces is not an
|
|
920
|
+
* independent view: the DevTools console-attach probe is marked nonCorroborating,
|
|
921
|
+
* so it still scores but cannot be one of the two that agree.
|
|
917
922
|
*
|
|
918
923
|
* Constants swept over a 40-point grid against the labelled corpus
|
|
919
924
|
* (bench/tools/sweep-corroboration.js), not reasoned about. No human in the
|
|
920
925
|
* 126-sample panel reaches two agreeing behavioural categories at any threshold
|
|
921
|
-
* tested,
|
|
922
|
-
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
926
|
+
* tested, and 0.3, 0.4 and 0.5 catch the same 66 of 75 agents. The bar moved from
|
|
927
|
+
* 0.5 to 0.4 on a live measurement the corpus cannot make (2026-09-09): an
|
|
928
|
+
* extension-driven click in a real Chrome on the public demo scored vision_ai 0.40
|
|
929
|
+
* and behavioral 0.44 with every environmental category clean — weighted sum
|
|
930
|
+
* 0.189, allowed at 0.5, floored at 0.4. The exclusion is what keeps the lower
|
|
931
|
+
* bar honest: the human-looking sessions beside it sat at behavioral 0.37 with cdp
|
|
932
|
+
* 0.30 from the console probe alone. Requiring three fails outright — all sixteen
|
|
933
|
+
* such combinations leave the adversary allowed. The floor value does not affect
|
|
934
|
+
* separation, so 0.6 is a policy choice: the block boundary, kept distinct from
|
|
935
|
+
* the 0.9 reserved for a browser that declares its own automation.
|
|
925
936
|
*
|
|
926
937
|
* Caveat: the adversary is one synthetic, hand-authored sample. This shows the
|
|
927
938
|
* arithmetic works on the shape the corpus describes, not that it works on a real
|
|
928
939
|
* source-patched browser.
|
|
929
940
|
*/
|
|
930
|
-
const CORROBORATION_AGREE_AT = 0.
|
|
941
|
+
const CORROBORATION_AGREE_AT = 0.4;
|
|
931
942
|
const CORROBORATION_MIN_AGREE = 2;
|
|
932
943
|
const CORROBORATION_FLOOR = 0.6;
|
|
944
|
+
// Absorbs the few ulps a noisy-OR product lands from a round number (0.75×0.8 is
|
|
945
|
+
// 0.3999999999999999, not 0.4), so agreement does not depend on arithmetic order.
|
|
946
|
+
const CORROBORATION_EPSILON = 1e-9;
|
|
933
947
|
|
|
934
948
|
// Categories a browser trips by how it moves rather than by what it is. A
|
|
935
949
|
// patched binary can hide what it is; it cannot hide that nothing is moving the
|
|
936
950
|
// pointer like a hand.
|
|
937
951
|
const BEHAVIOURAL_CATEGORIES = ['vision_ai', 'behavioral', 'automation', 'cdp'];
|
|
938
952
|
|
|
939
|
-
function applyCorroborationFloor(score,
|
|
953
|
+
function applyCorroborationFloor(score, detections) {
|
|
954
|
+
// Only independent views count. A signal that a developer's own tooling
|
|
955
|
+
// produces (nonCorroborating) still scores, but cannot be one of the two.
|
|
956
|
+
const categoryScores = calculateCategoryScores(detections.filter((d) => !d.nonCorroborating));
|
|
940
957
|
const agreeing = BEHAVIOURAL_CATEGORIES
|
|
941
|
-
.filter((c) => (categoryScores[c] || 0) >= CORROBORATION_AGREE_AT).length;
|
|
958
|
+
.filter((c) => (categoryScores[c] || 0) >= CORROBORATION_AGREE_AT - CORROBORATION_EPSILON).length;
|
|
942
959
|
return agreeing >= CORROBORATION_MIN_AGREE ? Math.max(score, CORROBORATION_FLOOR) : score;
|
|
943
960
|
}
|
|
944
961
|
|
package/index.js
CHANGED
|
@@ -345,7 +345,7 @@ class ScoringEngine {
|
|
|
345
345
|
const categoryScores = calculateCategoryScores(detections, this.weights);
|
|
346
346
|
const finalScore = applyCorroborationFloor(
|
|
347
347
|
applyDispositiveFloor(calculateFinalScore(categoryScores, this.weights), detections),
|
|
348
|
-
|
|
348
|
+
detections
|
|
349
349
|
);
|
|
350
350
|
|
|
351
351
|
let recommendation;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webdecoy/fcaptcha",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.36.0",
|
|
4
4
|
"description": "Open source CAPTCHA with PoW, bot detection, and Vision AI protection",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test:clientip": "node clientip.test.js",
|
|
16
16
|
"test:limits": "node limits.test.js",
|
|
17
17
|
"test:detection": "node detection.test.js",
|
|
18
|
-
"test": "node version.test.js && node config.test.js && node clientip.test.js && node limits.test.js && node detection.test.js && node inputforensics.test.js && node interactionmode.test.js && node exemptions.test.js && node webbotauth.test.js && node siteverify.test.js && node redis-state.test.js && node index.test.js && node --test suspicion.test.js security.test.js",
|
|
18
|
+
"test": "node version.test.js && node config.test.js && node clientip.test.js && node limits.test.js && node detection.test.js && node inputforensics.test.js && node interactionmode.test.js && node exemptions.test.js && node webbotauth.test.js && node siteverify.test.js && node redis-state.test.js && node index.test.js && node --test suspicion.test.js security.test.js corroboration.test.js",
|
|
19
19
|
"test:siteverify": "node siteverify.test.js",
|
|
20
20
|
"test:forensics": "node inputforensics.test.js",
|
|
21
21
|
"test:suspicion": "node --test suspicion.test.js"
|
package/server.js
CHANGED
|
@@ -728,7 +728,7 @@ async function runVerification(signals, ip, siteKey, userAgent, headers = {}, ja
|
|
|
728
728
|
const categoryScores = calculateCategoryScores(detections);
|
|
729
729
|
const finalScore = applyCorroborationFloor(
|
|
730
730
|
applyDispositiveFloor(calculateFinalScore(categoryScores), detections),
|
|
731
|
-
|
|
731
|
+
detections
|
|
732
732
|
);
|
|
733
733
|
|
|
734
734
|
let recommendation;
|