Skip to content

Commit 1197be3

Browse files
authored
feat: unsafe string format (#686)
1 parent 5d49f80 commit 1197be3

File tree

6 files changed

+88
-0
lines changed

6 files changed

+88
-0
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,24 @@ integer-like values, such as:
630630
- `'2e4'` - _note this will be converted to `2`, not `20000`_
631631
- `1.5` - _note this will be converted to `1`_
632632

633+
<a name="unsafe"></a>
634+
#### Unsafe string
635+
By default, the library escapes all strings. With the 'unsafe' format, the string isn't escaped. This has a potentially dangerous security issue. You can use it only if you are sure that your data doesn't need escaping. The advantage is a significant performance improvement.
636+
637+
Example:
638+
```javascript
639+
const stringify = fastJson({
640+
title: 'Example Schema',
641+
type: 'object',
642+
properties: {
643+
'code': {
644+
type: 'string',
645+
format 'unsafe'
646+
}
647+
}
648+
})
649+
```
650+
633651
##### Benchmarks
634652

635653
For reference, here goes some benchmarks for comparison over the three

benchmark/bench.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ const benchmarks = [
4747
},
4848
input: 'hello world'
4949
},
50+
{
51+
name: 'unsafe short string',
52+
schema: {
53+
type: 'string',
54+
format: 'unsafe'
55+
},
56+
input: 'hello world'
57+
},
5058
{
5159
name: 'short string with double quote',
5260
schema: {
@@ -61,13 +69,29 @@ const benchmarks = [
6169
},
6270
input: longSimpleString
6371
},
72+
{
73+
name: 'unsafe long string without double quotes',
74+
schema: {
75+
type: 'string',
76+
format: 'unsafe'
77+
},
78+
input: longSimpleString
79+
},
6480
{
6581
name: 'long string',
6682
schema: {
6783
type: 'string'
6884
},
6985
input: longString
7086
},
87+
{
88+
name: 'unsafe long string',
89+
schema: {
90+
type: 'string',
91+
format: 'unsafe'
92+
},
93+
input: longString
94+
},
7195
{
7296
name: 'number',
7397
schema: {

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,8 @@ function buildSingleTypeSerializer (context, location, input) {
725725
return `json += serializer.asDate(${input})`
726726
} else if (schema.format === 'time') {
727727
return `json += serializer.asTime(${input})`
728+
} else if (schema.format === 'unsafe') {
729+
return `json += serializer.asUnsafeString(${input})`
728730
} else {
729731
return `json += serializer.asString(${input})`
730732
}

lib/serializer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ module.exports = class Serializer {
122122
}
123123
}
124124

125+
asUnsafeString (str) {
126+
return '"' + str + '"'
127+
}
128+
125129
// magically escape strings for json
126130
// relying on their charCodeAt
127131
// everything below 32 needs JSON.stringify()

test/basic.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ function buildTest (schema, toStringify) {
1818
})
1919
}
2020

21+
buildTest({
22+
title: 'string',
23+
type: 'string',
24+
format: 'unsafe'
25+
}, 'hello world')
26+
2127
buildTest({
2228
title: 'basic',
2329
type: 'object',

test/string.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,37 @@ test('serialize long string', (t) => {
4848
t.equal(output, `"${new Array(2e4).fill('\\u0000').join('')}"`)
4949
t.equal(JSON.parse(output), input)
5050
})
51+
52+
test('unsafe string', (t) => {
53+
t.plan(2)
54+
55+
const schema = {
56+
type: 'string',
57+
format: 'unsafe'
58+
}
59+
60+
const input = 'abcd'
61+
const stringify = build(schema)
62+
const output = stringify(input)
63+
64+
t.equal(output, `"${input}"`)
65+
t.equal(JSON.parse(output), input)
66+
})
67+
68+
test('unsafe unescaped string', (t) => {
69+
t.plan(2)
70+
71+
const schema = {
72+
type: 'string',
73+
format: 'unsafe'
74+
}
75+
76+
const input = 'abcd "abcd"'
77+
const stringify = build(schema)
78+
const output = stringify(input)
79+
80+
t.equal(output, `"${input}"`)
81+
t.throws(function () {
82+
JSON.parse(output)
83+
})
84+
})

0 commit comments

Comments
 (0)