From 19719a136fa5115634dc3c3290eb12951d5295d6 Mon Sep 17 00:00:00 2001 From: Johann Schopplich Date: Fri, 5 Dec 2025 14:12:55 +0100 Subject: [PATCH] fix: reject negative numbers with leading zeros --- packages/toon/package.json | 2 +- packages/toon/src/shared/literal-utils.ts | 7 +++---- packages/toon/src/shared/validation.ts | 5 ++++- pnpm-lock.yaml | 10 +++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/toon/package.json b/packages/toon/package.json index f3e4059..7827753 100644 --- a/packages/toon/package.json +++ b/packages/toon/package.json @@ -38,6 +38,6 @@ "test": "vitest" }, "devDependencies": { - "@toon-format/spec": "^3.0.0" + "@toon-format/spec": "^3.0.1" } } diff --git a/packages/toon/src/shared/literal-utils.ts b/packages/toon/src/shared/literal-utils.ts index b296b97..8e8ff80 100644 --- a/packages/toon/src/shared/literal-utils.ts +++ b/packages/toon/src/shared/literal-utils.ts @@ -14,12 +14,11 @@ export function isNumericLiteral(token: string): boolean { if (!token) return false - // Must not have leading zeros (except for `"0"` itself or decimals like `"0.5"`) - if (token.length > 1 && token[0] === '0' && token[1] !== '.') { + // Enforce JSON-like grammar with no forbidden leading zeros + const numericPattern = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:e[+-]?\d+)?$/i + if (!numericPattern.test(token)) return false - } - // Check if it's a valid number const numericValue = Number(token) return !Number.isNaN(numericValue) && Number.isFinite(numericValue) } diff --git a/packages/toon/src/shared/validation.ts b/packages/toon/src/shared/validation.ts index 2270766..4a0b40a 100644 --- a/packages/toon/src/shared/validation.ts +++ b/packages/toon/src/shared/validation.ts @@ -1,6 +1,9 @@ import { DEFAULT_DELIMITER, LIST_ITEM_MARKER } from '../constants' import { isBooleanOrNullLiteral } from './literal-utils' +const NUMERIC_LIKE_PATTERN = /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i +const LEADING_ZERO_PATTERN = /^0\d+$/ + /** * Checks if a key can be used without quotes. * @@ -93,5 +96,5 @@ export function isSafeUnquoted(value: string, delimiter: string = DEFAULT_DELIMI * Match numbers like `42`, `-3.14`, `1e-6`, `05`, etc. */ function isNumericLike(value: string): boolean { - return /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(value) || /^0\d+$/.test(value) + return NUMERIC_LIKE_PATTERN.test(value) || LEADING_ZERO_PATTERN.test(value) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffa4198..920df9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,8 +132,8 @@ importers: packages/toon: devDependencies: '@toon-format/spec': - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^3.0.1 + version: 3.0.1 packages: @@ -1171,8 +1171,8 @@ packages: peerDependencies: eslint: '>=9.0.0' - '@toon-format/spec@3.0.0': - resolution: {integrity: sha512-Xm22jj7TpirgQRlQOAjLMKxRLJfz0Q/BkOdV6d27mHnJ1VQxzDV54Rl03zsFtY3D8wk7t04CV/PS/KjVs2Ys7Q==} + '@toon-format/spec@3.0.1': + resolution: {integrity: sha512-f9YADLDxH/j06biiTBKEMx0i8dqgo7cfVQe7lCP5NS59M7Ys0pKIXuAzQx4VDW3NgiHaKjWRlbK+C/yuUqS5dA==} '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -4449,7 +4449,7 @@ snapshots: estraverse: 5.3.0 picomatch: 4.0.3 - '@toon-format/spec@3.0.0': {} + '@toon-format/spec@3.0.1': {} '@tybys/wasm-util@0.10.1': dependencies: