commit c34007726aac7814dd1fe1bf8978adfc785e6fa5
parent 467ab67cc90e3c3dbd2e3c4d9e686865e1f747ee
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 10:57:35 +0200
util: test that the time codecs reject malformed numbers
Diffstat:
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/packages/taler-util/src/time.test.ts b/packages/taler-util/src/time.test.ts
@@ -16,7 +16,14 @@
import { test } from "node:test";
import assert from "node:assert";
-import { Duration } from "./time.js";
+import {
+ codecForAbsoluteTime,
+ codecForDuration,
+ codecForDurationMs,
+ codecForPreciseTimestamp,
+ codecForTimestamp,
+ Duration,
+} from "./time.js";
test("duration parsing", (t) => {
const d1 = Duration.fromPrettyString("1h");
@@ -48,3 +55,39 @@ test("duration parsing rejects a trailing number with no unit", (t) => {
assert.throws(() => Duration.fromPrettyString(""));
assert.throws(() => Duration.fromPrettyString(" "));
});
+
+test("time codecs reject non-integral and negative values", (t) => {
+ // These codecs sit at the boundary with untrusted exchange and merchant
+ // JSON. A NaN timestamp is especially bad: it serializes back to null and
+ // compares as smaller than everything, so an expiry check silently inverts.
+ const cases: Array<[{ decode(x: any): unknown }, object]> = [
+ [codecForTimestamp, { t_s: NaN }],
+ [codecForTimestamp, { t_s: Infinity }],
+ [codecForTimestamp, { t_s: 1.5 }],
+ [codecForTimestamp, { t_s: -1 }],
+ [codecForPreciseTimestamp, { t_s: NaN }],
+ [codecForPreciseTimestamp, { t_s: 1, off_us: NaN }],
+ [codecForDuration, { d_us: NaN }],
+ [codecForDuration, { d_us: -1 }],
+ [codecForDurationMs, { d_ms: NaN }],
+ [codecForDurationMs, { d_ms: -1 }],
+ [codecForAbsoluteTime, { t_ms: NaN }],
+ ];
+ for (const [codec, value] of cases) {
+ assert.throws(() => codec.decode(value), JSON.stringify(value));
+ }
+});
+
+test("time codecs accept the documented values", (t) => {
+ assert.deepStrictEqual(codecForTimestamp.decode({ t_s: 0 }), { t_s: 0 });
+ assert.deepStrictEqual(codecForTimestamp.decode({ t_s: "never" }), {
+ t_s: "never",
+ });
+ assert.deepStrictEqual(codecForDuration.decode({ d_us: "forever" }), {
+ d_us: "forever",
+ });
+ assert.deepStrictEqual(
+ codecForPreciseTimestamp.decode({ t_s: 3, off_us: 4 }),
+ { t_s: 3, off_us: 4 },
+ );
+});