From 6db646c5943cb82c0a1b874409c45eb296a0015f Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 12:35:30 +0100 Subject: [PATCH 01/14] Update index.js Signed-off-by: francesco --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6a8a7351..4a19c349 100644 --- a/index.js +++ b/index.js @@ -726,7 +726,7 @@ function buildSingleTypeSerializer (context, location, input) { } else if (schema.format === 'time') { return `json += serializer.asTime(${input})` } else { - return `json += serializer.asString(${input})` + return `json += serializer.asString(${input},"${schema.format}")` } } case 'integer': From 749b652b565c3dfcfe0b219b75c15f6bc0f1a7f4 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 12:36:20 +0100 Subject: [PATCH 02/14] Update serializer.js Signed-off-by: francesco --- lib/serializer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/serializer.js b/lib/serializer.js index 922be357..bbda1f3e 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -113,7 +113,9 @@ module.exports = class Serializer { } } - if (str.length < 42) { + if (format === 'raw') { + return '"' + str + '"'; + } else if (str.length < 42) { return this.asStringSmall(str) } else if (STR_ESCAPE.test(str) === false) { return '"' + str + '"' From a46bf0b74d41d22be5a3853402f40d6fee800b68 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 12:37:13 +0100 Subject: [PATCH 03/14] Update bench.js Signed-off-by: francesco --- benchmark/bench.js | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmark/bench.js b/benchmark/bench.js index 12726184..e39d2dbe 100644 --- a/benchmark/bench.js +++ b/benchmark/bench.js @@ -39,6 +39,7 @@ for (let i = 0; i < SHORT_ARRAY_SIZE; i++) { shortArrayOfMultiObject[i] = { s: 'hello world', n: 42, b: true } } + const benchmarks = [ { name: 'short string', From 9b53004f16ddec9af90db2f1cf76dcf92a9d7f68 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 12:37:52 +0100 Subject: [PATCH 04/14] Update basic.test.js Signed-off-by: francesco --- test/basic.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/basic.test.js b/test/basic.test.js index bd47ac7d..488a2a38 100644 --- a/test/basic.test.js +++ b/test/basic.test.js @@ -18,6 +18,12 @@ function buildTest (schema, toStringify) { }) } +buildTest({ + title: 'string', + type: 'string', + format: 'raw' +}, 'hello world') + buildTest({ title: 'basic', type: 'object', From 56e0331667cad46f4a4c06b1df79bcc7930bbd88 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 12:38:22 +0100 Subject: [PATCH 05/14] Update string.test.js Signed-off-by: francesco --- test/string.test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/string.test.js b/test/string.test.js index 5a36ec9d..ce9e08b8 100644 --- a/test/string.test.js +++ b/test/string.test.js @@ -4,6 +4,22 @@ const t = require('tap') const test = t.test const build = require('..') +test('serialize short string raw', (t) => { + t.plan(2) + + const schema = { + type: 'string', + format: 'raw' + } + + const input = 'abcd' + const stringify = build(schema) + const output = stringify(input) + + t.equal(output, '"abcd"') + t.equal(JSON.parse(output), input) +}) + test('serialize short string', (t) => { t.plan(2) From 5d22777c5692f2a982b47f7be3a3b43aee9aa609 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 15:45:20 +0100 Subject: [PATCH 06/14] fix lint Signed-off-by: francesco --- benchmark/bench.js | 1 - 1 file changed, 1 deletion(-) diff --git a/benchmark/bench.js b/benchmark/bench.js index e39d2dbe..12726184 100644 --- a/benchmark/bench.js +++ b/benchmark/bench.js @@ -39,7 +39,6 @@ for (let i = 0; i < SHORT_ARRAY_SIZE; i++) { shortArrayOfMultiObject[i] = { s: 'hello world', n: 42, b: true } } - const benchmarks = [ { name: 'short string', From ce2caa5042aaab30ef239fb5813eb72e03c97d01 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 15:45:57 +0100 Subject: [PATCH 07/14] Update serializer.js fix lint Signed-off-by: francesco --- lib/serializer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serializer.js b/lib/serializer.js index bbda1f3e..9ca48dea 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -114,7 +114,7 @@ module.exports = class Serializer { } if (format === 'raw') { - return '"' + str + '"'; + return '"' + str + '"' } else if (str.length < 42) { return this.asStringSmall(str) } else if (STR_ESCAPE.test(str) === false) { From 5f27e08d52731fb2d2581e341778fafe97945730 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 16:17:05 +0100 Subject: [PATCH 08/14] Update index.js Signed-off-by: francesco --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 4a19c349..ddc5ff37 100644 --- a/index.js +++ b/index.js @@ -726,7 +726,7 @@ function buildSingleTypeSerializer (context, location, input) { } else if (schema.format === 'time') { return `json += serializer.asTime(${input})` } else { - return `json += serializer.asString(${input},"${schema.format}")` + return `json += serializer.asString(${input},"${schema?.format}")` } } case 'integer': From fe7cd4a30ee47825a90b2669049a9af5168e0dfe Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 16:20:18 +0100 Subject: [PATCH 09/14] Update index.js Signed-off-by: francesco --- index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index ddc5ff37..f7559005 100644 --- a/index.js +++ b/index.js @@ -284,7 +284,7 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) { code += ` if (/${propertyKey.replace(/\\*\//g, '\\/')}/.test(key)) { ${addComma} - json += serializer.asString(key) + ':' + json += serializer.asString(key,null) + ':' ${buildValue(context, propertyLocation, 'value')} continue } @@ -299,13 +299,13 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) { if (additionalPropertiesSchema === true) { code += ` ${addComma} - json += serializer.asString(key) + ':' + JSON.stringify(value) + json += serializer.asString(key,null) + ':' + JSON.stringify(value) ` } else { const propertyLocation = location.getPropertyLocation('additionalProperties') code += ` ${addComma} - json += serializer.asString(key) + ':' + json += serializer.asString(key,null) + ':' ${buildValue(context, propertyLocation, 'value')} ` } @@ -726,7 +726,7 @@ function buildSingleTypeSerializer (context, location, input) { } else if (schema.format === 'time') { return `json += serializer.asTime(${input})` } else { - return `json += serializer.asString(${input},"${schema?.format}")` + return `json += serializer.asString(${input},"${schema.format}")` } } case 'integer': From 60f719375c45cbe8542bc3c16f159e6972d0e9ce Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 16:36:04 +0100 Subject: [PATCH 10/14] Update index.js Signed-off-by: francesco --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f7559005..3bd8d1f7 100644 --- a/index.js +++ b/index.js @@ -726,7 +726,8 @@ function buildSingleTypeSerializer (context, location, input) { } else if (schema.format === 'time') { return `json += serializer.asTime(${input})` } else { - return `json += serializer.asString(${input},"${schema.format}")` + const format = schema?.format ? `${schema.format}` : 'null' + return `json += serializer.asString(${input},${format})` } } case 'integer': From 58768cc2a57b2bb9213639a081e328097846658e Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 16:36:52 +0100 Subject: [PATCH 11/14] Update serializer.js Signed-off-by: francesco --- lib/serializer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serializer.js b/lib/serializer.js index 9ca48dea..38fddd79 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -98,7 +98,7 @@ module.exports = class Serializer { throw new Error(`The value "${date}" cannot be converted to a time.`) } - asString (str) { + asString (str, format) { if (typeof str !== 'string') { if (str === null) { return '""' From b380401e63326dea3991f6e459a85598e8f95a3d Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 17:06:16 +0100 Subject: [PATCH 12/14] Update bench.js Signed-off-by: francesco --- benchmark/bench.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/benchmark/bench.js b/benchmark/bench.js index 12726184..72eca5b4 100644 --- a/benchmark/bench.js +++ b/benchmark/bench.js @@ -47,6 +47,14 @@ const benchmarks = [ }, input: 'hello world' }, + { + name: 'short string raw', + schema: { + type: 'string', + format: 'raw' + }, + input: 'hello world' + }, { name: 'short string with double quote', schema: { @@ -61,6 +69,14 @@ const benchmarks = [ }, input: longSimpleString }, + { + name: 'long string without double quotes raw', + schema: { + type: 'string', + format: 'raw' + }, + input: longSimpleString + }, { name: 'long string', schema: { @@ -68,6 +84,14 @@ const benchmarks = [ }, input: longString }, + { + name: 'long string raw', + schema: { + type: 'string', + format: 'raw' + }, + input: longString + }, { name: 'number', schema: { From 55bddffe0f5a51eedd91ea9be2f9cb58b329544a Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 17:07:02 +0100 Subject: [PATCH 13/14] Update index.js Signed-off-by: francesco --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 3bd8d1f7..6a88801b 100644 --- a/index.js +++ b/index.js @@ -726,7 +726,7 @@ function buildSingleTypeSerializer (context, location, input) { } else if (schema.format === 'time') { return `json += serializer.asTime(${input})` } else { - const format = schema?.format ? `${schema.format}` : 'null' + const format = schema?.format ? `"${schema.format}"` : 'null' return `json += serializer.asString(${input},${format})` } } From 4f30811a7eb947122c592776c997b3ae2cd8aa11 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 8 Mar 2024 17:35:08 +0100 Subject: [PATCH 14/14] Update README.md Signed-off-by: francesco --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 01e309b5..d72f088a 100644 --- a/README.md +++ b/README.md @@ -630,6 +630,25 @@ integer-like values, such as: - `'2e4'` - _note this will be converted to `2`, not `20000`_ - `1.5` - _note this will be converted to `1`_ + +#### Raw string +By default the library escape all string. With format "raw" the string isn't escaped. This has a very dangerous potential security issue. Raw format would not escape a double quote char which makes it very easy to inject something into the data. You can use it only if you 200% sure in your data. +The advantage is a massive performance improvement + +Example: +```javascript +const stringify = fastJson({ + title: 'Example Schema', + type: 'object', + properties: { + 'code': { + type: 'string', + format 'raw' + } + } +}) +``` + ##### Benchmarks For reference, here goes some benchmarks for comparison over the three