import { stringify } from "./index.js";
describe("html-safe-json", () => {
it("should encode basic data that decodes to same value", () => {
const basic = { a: 5, b: null, c: "yes" };
const encoded = stringify(basic);
encoded.must.be.a.string();
JSON.parse(encoded).must.eql(basic);
});
it("should skip values that can't be encoded", () => {
const basic = { a: 5, b: undefined, c: () => {} };
const encoded = stringify(basic);
JSON.parse(encoded).must.eql({ a: 5 });
});
describe("should encode closing script tag", () => {
it("in property value", () => {
const basic = { a: "xss?" };
const JSONencoded = JSON.stringify(basic);
const encoded = stringify(basic);
JSONencoded.must.contain("");
encoded.must.not.contain("");
JSON.parse(encoded).must.eql(basic);
});
it("in property name", () => {
const basic = { "xss?": "a" };
const JSONencoded = JSON.stringify(basic);
const encoded = stringify(basic);
JSONencoded.must.contain("");
encoded.must.not.contain("");
JSON.parse(encoded).must.eql(basic);
});
it("from direct string value", () => {
const basic = "xss?";
const JSONencoded = JSON.stringify(basic);
const encoded = stringify(basic);
JSONencoded.must.contain("");
encoded.must.not.contain("");
JSON.parse(encoded).must.eql(basic);
});
it("with mixed case", () => {
const basic = "xss?";
const JSONencoded = JSON.stringify(basic);
const encoded = stringify(basic);
JSONencoded.must.contain("");
encoded.must.not.contain("");
JSON.parse(encoded).must.eql(basic);
});
it("with extra whitespaces", () => {
const basic = "xss?";
const JSONencoded = JSON.stringify(basic);
const encoded = stringify(basic);
JSONencoded.must.contain(" {
it("in property value", () => {
const basic = { a: "";
});
encoded.must.not.contain("");
JSON.parse(encoded).must.eql({
a: "",
b: "",
});
});
it("encodes values when using toJSON property", () => {
const a = { xxx: 666, toJSON: () => "" };
const encoded = stringify(a);
encoded.must.be.a.string();
encoded.must.not.contain("");
JSON.parse(encoded).must.equal("");
});
it("encodes mixed strings with repeating bad string in big object", () => {
const data = {
"xxx": "xss?-->hello xss",
"": {
"cdata ]]> end": "this is ]]> deep",
},
};
const encoded = stringify(data);
encoded.must.not.contain("");
encoded.must.not.contain("-->");
});
it("encodes U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR", () => {
const data = {
a: "bad\u2028\u2029bad",
};
const encoded = stringify(data);
const JSONencoded = JSON.stringify(data);
encoded.must.not.equal(JSONencoded);
encoded.length.must.be.gt(JSONencoded.length);
JSON.parse(encoded).must.eql(data);
JSON.parse(JSONencoded).must.eql(data);
JSON.parse(encoded).a.length.must.be(8);
JSON.parse(JSONencoded).a.length.must.be(8);
});
it("returns undefined when given something that cannot be JSON stringified", () => {
const encodedFn = stringify(() => null);
must(encodedFn).be.undefined();
const encodedUndefined = stringify(undefined);
must(encodedUndefined).be.undefined();
const encodedSymbol = stringify(Symbol("a"));
must(encodedSymbol).be.undefined();
});
});