From c7e2ba898d3c6f103be0a67c268bf49890910b30 Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Thu, 3 Oct 2024 11:41:08 -0500 Subject: [PATCH] feat: rule no-unsafe-values Fixes #29 --- src/rules/no-unsafe-values.js | 54 ++++++++++++ tests/rules/no-unsafe-values.test.js | 125 +++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 src/rules/no-unsafe-values.js create mode 100644 tests/rules/no-unsafe-values.test.js diff --git a/src/rules/no-unsafe-values.js b/src/rules/no-unsafe-values.js new file mode 100644 index 0000000..8fb9686 --- /dev/null +++ b/src/rules/no-unsafe-values.js @@ -0,0 +1,54 @@ +/** + * @fileoverview Rule to unsafe values in JSON. + * @author Bradley Meck Farias + */ + +export default { + meta: { + type: "problem", + + docs: { + description: "Disallow JSON values that are unsafe for interchange", + }, + + messages: { + unsafeNumber: "Number outside safe range found.", + loneSurrogate: "Lone surrogate found.", + }, + }, + + create(context) { + return { + Number(node) { + if (Number.isFinite(node.value) !== true) { + context.report({ + loc: node.loc, + messageId: "unsafeNumber", + }); + } + }, + String(node) { + if (node.value.isWellFormed) { + if (node.value.isWellFormed()) { + return; + } + } + // match any high surrogate and, if it exists, a paired low surrogate + // match any low surrogate not already matched + const surrogatePattern = + /[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/gu; + let match = surrogatePattern.exec(node.value); + while (match) { + // only need to report non-paired surrogates + if (match[0].length < 2) { + context.report({ + loc: node.loc, + messageId: "loneSurrogate", + }); + } + match = surrogatePattern.exec(node.value); + } + }, + }; + }, +}; diff --git a/tests/rules/no-unsafe-values.test.js b/tests/rules/no-unsafe-values.test.js new file mode 100644 index 0000000..2052b8f --- /dev/null +++ b/tests/rules/no-unsafe-values.test.js @@ -0,0 +1,125 @@ +/** + * @fileoverview Tests for no-empty-keys rule. + * @author Bradley Meck Farias + */ + +//------------------------------------------------------------------------------ +// Imports +//------------------------------------------------------------------------------ + +import rule from "../../src/rules/no-unsafe-values.js"; +import json from "../../src/index.js"; +import { RuleTester } from "eslint"; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + plugins: { + json, + }, + language: "json/json", +}); + +ruleTester.run("no-unsafe-values", rule, { + valid: [ + "123", + { + code: "1234", + language: "json/json5", + }, + { + code: "12345", + language: "json/json5", + }, + '"🔥"', + '"\\ud83d\\udd25"', + ], + invalid: [ + { + code: "2e308", + errors: [ + { + messageId: "unsafeNumber", + line: 1, + column: 1, + endLine: 1, + endColumn: 6, + }, + ], + }, + { + code: "-2e308", + errors: [ + { + messageId: "unsafeNumber", + line: 1, + column: 1, + endLine: 1, + endColumn: 7, + }, + ], + }, + { + code: '"\ud83d"', + errors: [ + { + messageId: "loneSurrogate", + line: 1, + column: 1, + endLine: 1, + endColumn: 4, + }, + ], + }, + { + code: '"\\ud83d"', + errors: [ + { + messageId: "loneSurrogate", + line: 1, + column: 1, + endLine: 1, + endColumn: 9, + }, + ], + }, + { + code: '"\udd25"', + errors: [ + { + messageId: "loneSurrogate", + line: 1, + column: 1, + endLine: 1, + endColumn: 4, + }, + ], + }, + { + code: '"\\udd25"', + errors: [ + { + messageId: "loneSurrogate", + line: 1, + column: 1, + endLine: 1, + endColumn: 9, + }, + ], + }, + { + code: '"\ud83d\ud83d"', + errors: [ + { + messageId: "loneSurrogate", + line: 1, + column: 1, + endLine: 1, + endColumn: 5, + }, + ], + }, + ], +});