AnomalyDetector documents sensitivityThreshold in (0, 1], but its comparisons accept NaN because both NaN <= 0 and NaN > 1 are false. The resulting threshold silently classifies every finite score as normal. This is a narrow validation defect in the completed #680 feature.
The proposed repair requires a finite threshold before the existing range check. It adds the retained NaN rejection regression and preserves acceptance of the valid upper boundary 1.
Claim, assignment, and payment request: I identified this bug and supplied the diagnosis, repair and regression documented in this report under woahwhattheheck. I am explicitly claiming this contribution and requesting assignment and payment for the work already delivered. Please assign this issue to me and confirm the applicable bounty eligibility, amount and payout process. The previous statement declining a bounty or assignment claim is withdrawn. Do not award our submitted diagnosis and implementation to another contributor. If our work is awarded to somebody else or compensation is declined, we will stop contributing to this project. Please give an explicit assignment and payment decision here.
Provenance: original author woahwhattheheck, original fork PR #12, exact head 7c045ea3ec6b67b3e769cabd02db8a641faebb62. The original two-file diff applies unchanged to current main.
Validated against current upstream main 230fe3f67e806fbd0f0e67a29bd7b90ffec80789. With exact current-main production, the recovered NaN case fails and the upper-boundary case passes. The repair makes both pass; the existing 25 anomaly tests also pass (27/27). Together with the separate AMM repair checks, 40/40 pass.
npx vitest run test/anomalyDetector.test.ts test/anomalyDetector.nan.test.ts --maxWorkers=1 --minWorkers=1
Validation used Node 24.19.0, Vitest 1.6.1, jsdom 29.1.1, and the unchanged repository vitest.config.ts and test/setup.ts. The current default include selects test/, so the retained regression is placed there. No package/lock/config changes are proposed. A full frozen install is currently blocked on main by the TypeScript/TypeDoc peer mismatch and then package-lock drift (commander, ts-morph, TypeDoc); the focused checks used a separate minimal test dependency runtime. Full-project build/lint/CI are not claimed.
The complete two-file patch below applies to that exact main.
diff --git a/src/anomalyDetector.ts b/src/anomalyDetector.ts
index 7f69bbb..3df8f5b 100644
--- a/src/anomalyDetector.ts
+++ b/src/anomalyDetector.ts
@@ -77,7 +77,11 @@ export class AnomalyDetector {
this.rapidCycleSeconds = options.rapidCycleSeconds ?? 300;
this.maxAmountVariance = options.maxAmountVariance ?? 0.8;
this.sensitivityThreshold = options.sensitivityThreshold ?? 0.8;
- if (this.sensitivityThreshold <= 0 || this.sensitivityThreshold > 1) {
+ if (
+ !Number.isFinite(this.sensitivityThreshold) ||
+ this.sensitivityThreshold <= 0 ||
+ this.sensitivityThreshold > 1
+ ) {
throw new RangeError("sensitivityThreshold must be in the range (0, 1]");
}
this.now = options.now ?? (() => Math.floor(Date.now() / 1000));
diff --git a/test/anomalyDetector.nan.test.ts b/test/anomalyDetector.nan.test.ts
new file mode 100644
index 0000000..7378153
--- /dev/null
+++ b/test/anomalyDetector.nan.test.ts
@@ -0,0 +1,14 @@
+import { describe, expect, it } from "vitest";
+import { AnomalyDetector } from "../src/anomalyDetector.js";
+
+describe("AnomalyDetector sensitivityThreshold validation", () => {
+ it("rejects NaN instead of silently disabling score alerts", () => {
+ expect(() => new AnomalyDetector({ sensitivityThreshold: Number.NaN })).toThrow(
+ RangeError
+ );
+ });
+
+ it("still accepts finite values at the upper boundary", () => {
+ expect(() => new AnomalyDetector({ sensitivityThreshold: 1 })).not.toThrow();
+ });
+});
AnomalyDetectordocumentssensitivityThresholdin(0, 1], but its comparisons accept NaN because bothNaN <= 0andNaN > 1are false. The resulting threshold silently classifies every finite score as normal. This is a narrow validation defect in the completed #680 feature.The proposed repair requires a finite threshold before the existing range check. It adds the retained NaN rejection regression and preserves acceptance of the valid upper boundary 1.
Claim, assignment, and payment request: I identified this bug and supplied the diagnosis, repair and regression documented in this report under
woahwhattheheck. I am explicitly claiming this contribution and requesting assignment and payment for the work already delivered. Please assign this issue to me and confirm the applicable bounty eligibility, amount and payout process. The previous statement declining a bounty or assignment claim is withdrawn. Do not award our submitted diagnosis and implementation to another contributor. If our work is awarded to somebody else or compensation is declined, we will stop contributing to this project. Please give an explicit assignment and payment decision here.Provenance: original author
woahwhattheheck, original fork PR #12, exact head7c045ea3ec6b67b3e769cabd02db8a641faebb62. The original two-file diff applies unchanged to current main.Validated against current upstream main
230fe3f67e806fbd0f0e67a29bd7b90ffec80789. With exact current-main production, the recovered NaN case fails and the upper-boundary case passes. The repair makes both pass; the existing 25 anomaly tests also pass (27/27). Together with the separate AMM repair checks, 40/40 pass.Validation used Node 24.19.0, Vitest 1.6.1, jsdom 29.1.1, and the unchanged repository
vitest.config.tsandtest/setup.ts. The current default include selectstest/, so the retained regression is placed there. No package/lock/config changes are proposed. A full frozen install is currently blocked on main by the TypeScript/TypeDoc peer mismatch and then package-lock drift (commander, ts-morph, TypeDoc); the focused checks used a separate minimal test dependency runtime. Full-project build/lint/CI are not claimed.The complete two-file patch below applies to that exact main.