erdtman / canonicalize Goto Github PK
View Code? Open in Web Editor NEWJSON canonicalize function
License: Apache License 2.0
JSON canonicalize function
License: Apache License 2.0
According to the spec:
Note: Since Not a Number (NaN) and Infinity are not permitted in JSON, occurrences of NaN or Infinity MUST cause a compliant JCS implementation to terminate with an appropriate error.
I believe, this is currently not the case. Instead, JSON.canonicalize(Number.POSITIVE_INFINITY)
currently returns null
.
The update 1.0.6 has increased the size of the npm package to almost 200 MB due to the inclusion of the performence_test_data.json file.
Hello, this is not an issue, but just to say thanks for the code which served as part of the base for our Typescript port.
https://github.com/truestamp/truestamp-canonify
You library, along with the reference code, was super helpful.
We ported this to typescript as it is important for us to not only gain the security advantages, but to further flesh out the test suite and allow use of the library in not only Node.js but also in Deno and the browser.
We'd love for you to take a look, and if there are any comments about our friendly fork please do let me know.
Out of curiosity I also copied your most current code over and ran our test suite against it. The following was the output (and will show a couple of the differences in how we're handling certain cases. I went through a number of manual test cases to see how JSON.stringify()
is documented to work and tried to get the output to align closely with that.
Some of the differences are the handling of:
BigInt
values should throw an Error as JSON.stringify()
does. The user would need to call .toString()
on the BigInt.function
values to in Arrays/Objects to null
, not undefined
which is not valid JSONundefined
(e.g. Symbol() values)Here's the test output.
❯ npm t
> @truestamp/[email protected] test
> jest
PASS tests/testdata.spec.ts
FAIL tests/basics.spec.ts
● serializing › should behave like JSON.stringify() for › BigInt should throw a TypeError
expect(received).toThrow(expected)
Expected substring: "BigInt value can't be serialized in JSON"
Received message: "Do not know how to serialize a BigInt"
2 | export default function canonify(object: any): string | undefined {
3 | if (object === null || typeof object !== 'object') {
> 4 | return JSON.stringify(object);
| ^
5 | }
6 |
7 | if (object.toJSON instanceof Function) {
at canonify (src/index.ts:4:17)
at t (tests/basics.spec.ts:69:17)
at Object.<anonymous> (node_modules/expect/build/toThrowMatchers.js:83:11)
at Object.throwingMatcher [as toThrow] (node_modules/expect/build/index.js:382:21)
at Object.<anonymous> (tests/basics.spec.ts:72:17)
70 | };
71 | expect(t).toThrow(TypeError);
> 72 | expect(t).toThrow("BigInt value can't be serialized in JSON");
| ^
73 | });
74 |
75 | // JSON.stringify('foo')
at Object.<anonymous> (tests/basics.spec.ts:72:17)
● serializing › should behave like JSON.stringify() for › Array
expect(received).toEqual(expected) // deep equality
Expected: "[null,null,true,false,\"foo\",42,\"42\",null,null]"
Received: "[null,null,true,false,\"foo\",42,\"42\",null,undefined]"
93 | test('Array', () => {
94 | const a = [undefined, null, true, false, "foo", 42, BigInt(42).toString(), Symbol('hello'), () => { }]
> 95 | expect(canonify(a)).toEqual('[null,null,true,false,"foo",42,"42",null,null]')
| ^
96 | })
97 |
98 | test('Array with String keys', () => {
at Object.<anonymous> (tests/basics.spec.ts:95:27)
● serializing › should behave like JSON.stringify() for › Object
expect(received).toEqual(expected) // deep equality
Expected: "{\"big\":\"42\",\"f\":false,\"n\":null,\"num\":42,\"s\":\"string\",\"t\":true}"
Received: "{\"big\":\"42\",\"f\":false,\"fun\":undefined,\"n\":null,\"num\":42,\"s\":\"string\",\"t\":true}"
107 | test('Object', () => {
108 | const o = { big: BigInt(42).toString(), f: false, fun: () => { }, n: null, num: 42, s: "string", sym: Symbol('hello'), t: true, u: undefined }
> 109 | expect(canonify(o)).toEqual('{"big":"42","f":false,"n":null,"num":42,"s":"string","t":true}')
| ^
110 | })
111 |
112 | // Standard data structures
at Object.<anonymous> (tests/basics.spec.ts:109:27)
● serializing › should behave like JSON.stringify() for › Symbols
expect(received).toEqual(expected) // deep equality
Expected: "{}"
Received: "{\"y\":undefined}"
146 | // @ts-ignore-next-line
147 | const e1 = { x: undefined, y: Object, z: Symbol('') }
> 148 | expect(canonify(e1)).toEqual('{}')
| ^
149 |
150 | // @ts-ignore-next-line
151 | const e2 = { [Symbol('foo')]: 'foo' }
at Object.<anonymous> (tests/basics.spec.ts:148:28)
● serializing › arrays should handle › a one element function array
expect(received).toEqual(expected) // deep equality
Expected: "[null]"
Received: "[undefined]"
232 | test('a one element function array', () => {
233 | let f = function foo() { }
> 234 | expect(canonify([f])).toEqual('[null]');
| ^
235 | });
236 |
237 | test('a nested array', () => {
at Object.<anonymous> (tests/basics.spec.ts:234:29)
● serializing › objects should handle › an object with a function value
expect(received).toEqual(expected) // deep equality
Expected: "{}"
Received: "{\"test\":undefined}"
291 | test('an object with a function value', () => {
292 | let f = function foo() { }
> 293 | expect(canonify({ test: f })).toEqual('{}');
| ^
294 | });
295 |
296 | test('an object with a toJSON serializer function value', () => {
at Object.<anonymous> (tests/basics.spec.ts:293:37)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 failed, 1 passed, 2 total
Tests: 6 failed, 47 passed, 53 total
Snapshots: 0 total
Time: 4.451 s
Ran all test suites.
Cheers.
Glenn
cyberphone/json-canonicalization#10
This is hardly a major problem but it might be worth looking into.
Hi, I have read through your package and source code, and I'm wondering in what situation the serialize
method will return undefined
. Based on the type definition that is shown in this link, it seems like the output for typescript is shown as string | undefined
.
However, when I compare this to the javascript implementation in this file, it appears this method can only return as a string.
Thank you for your contribution! :)
Hi,
I ran into an issue with the order of properties when serializing Objection.js model instances using canonicalize. All Objection.js model instances have a toJSON
method which exports the model as a simple JSON object after doing some mapping.
The problem occurs when JSON.stringify
is used to serialize the Objection.js records. The order of properties becomes dependent on the implementation of JSON.stringify
and not canonicalize
.
This is not a problem when you're always working with Objection models, but this is not always the case. Especially when the JSON string is used to sign and verify the data has not been tampered using JWS.
When the signed data is sent back to the client, it becomes a POJO, so when canoicalize
serializes it, it orders the properties as expected. This results in the properties being in a different order than when it was generated for signing.
I think the code needs to return serialize(object.toJSON()
of object.toJSON()
is defined.
Example of something that will cause problems after being converted to a POJO.
{
a: 123,
b: 456,
toJSON: function() {
return {
b: this.b,
a: this.a
}
}
}
Expected:
{"a":123,"":456}
Actual:
{"b":345,"a":123}
If I misunderstood the specification, then apologies. I just think the way it is now, creates uncertainties when a non-simple object is serialized by JSON.stringify
If it makes sense, I can create a PR to correct this behavior if that is preferred.
How can we use this without monkeypatching the global object?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.