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(); + }); +});