Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unsafe string format #686

Merged
merged 1 commit into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,24 @@ integer-like values, such as:
- `'2e4'` - _note this will be converted to `2`, not `20000`_
- `1.5` - _note this will be converted to `1`_

<a name="unsafe"></a>
#### Unsafe string
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.

Example:
```javascript
const stringify = fastJson({
title: 'Example Schema',
type: 'object',
properties: {
'code': {
type: 'string',
format 'unsafe'
}
Comment on lines +643 to +646
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'code': {
type: 'string',
format 'unsafe'
}
'code': {
type: 'string',
format 'unsafe'
}

}
})
```

##### Benchmarks

For reference, here goes some benchmarks for comparison over the three
Expand Down
24 changes: 24 additions & 0 deletions benchmark/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ const benchmarks = [
},
input: 'hello world'
},
{
name: 'unsafe short string',
schema: {
type: 'string',
format: 'unsafe'
},
input: 'hello world'
},
{
name: 'short string with double quote',
schema: {
Expand All @@ -61,13 +69,29 @@ const benchmarks = [
},
input: longSimpleString
},
{
name: 'unsafe long string without double quotes',
schema: {
type: 'string',
format: 'unsafe'
},
input: longSimpleString
},
{
name: 'long string',
schema: {
type: 'string'
},
input: longString
},
{
name: 'unsafe long string',
schema: {
type: 'string',
format: 'unsafe'
},
input: longString
},
{
name: 'number',
schema: {
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,8 @@ function buildSingleTypeSerializer (context, location, input) {
return `json += serializer.asDate(${input})`
} else if (schema.format === 'time') {
return `json += serializer.asTime(${input})`
} else if (schema.format === 'unsafe') {
return `json += serializer.asUnsafeString(${input})`
} else {
return `json += serializer.asString(${input})`
}
Expand Down
4 changes: 4 additions & 0 deletions lib/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ module.exports = class Serializer {
}
}

asUnsafeString (str) {
return '"' + str + '"'
}

// magically escape strings for json
// relying on their charCodeAt
// everything below 32 needs JSON.stringify()
Expand Down
6 changes: 6 additions & 0 deletions test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ function buildTest (schema, toStringify) {
})
}

buildTest({
title: 'string',
type: 'string',
format: 'unsafe'
}, 'hello world')

buildTest({
title: 'basic',
type: 'object',
Expand Down
34 changes: 34 additions & 0 deletions test/string.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,37 @@ test('serialize long string', (t) => {
t.equal(output, `"${new Array(2e4).fill('\\u0000').join('')}"`)
t.equal(JSON.parse(output), input)
})

test('unsafe string', (t) => {
t.plan(2)

const schema = {
type: 'string',
format: 'unsafe'
}

const input = 'abcd'
const stringify = build(schema)
const output = stringify(input)

t.equal(output, `"${input}"`)
t.equal(JSON.parse(output), input)
})

test('unsafe unescaped string', (t) => {
t.plan(2)

const schema = {
type: 'string',
format: 'unsafe'
}

const input = 'abcd "abcd"'
const stringify = build(schema)
const output = stringify(input)

t.equal(output, `"${input}"`)
t.throws(function () {
JSON.parse(output)
})
})