Migrate to string-based escaping implementation
This commit is contained in:
parent
a7b8710d10
commit
2c4e9178f9
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Random } from "@cosmjs/crypto";
|
||||
import { fromUtf8, toBech32, toUtf8 } from "@cosmjs/encoding";
|
||||
import { toBech32 } from "@cosmjs/encoding";
|
||||
|
||||
import { AminoMsg, escapeCharacters, makeSignDoc, sortedJsonStringify } from "./signdoc";
|
||||
|
||||
@ -133,13 +133,42 @@ describe("encoding", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("escape characters after utf8 encoding", () => {
|
||||
describe("escapeCharacters", () => {
|
||||
it("works", () => {
|
||||
const test = JSON.stringify({ memo: "ampersand:&,lt:<,gt:>" });
|
||||
const utf8Encoding = toUtf8(test);
|
||||
const escapedEncoding = escapeCharacters(utf8Encoding);
|
||||
// Unchanged originals
|
||||
expect(escapeCharacters(`""`)).toEqual(`""`);
|
||||
expect(escapeCharacters(`{}`)).toEqual(`{}`);
|
||||
expect(escapeCharacters(`[]`)).toEqual(`[]`);
|
||||
expect(escapeCharacters(`[123,null,"foo",[{}]]`)).toEqual(`[123,null,"foo",[{}]]`);
|
||||
expect(escapeCharacters(`{"num":123}`)).toEqual(`{"num":123}`);
|
||||
expect(escapeCharacters(`{"memo":"123"}`)).toEqual(`{"memo":"123"}`);
|
||||
expect(escapeCharacters(`{"memo":"\\u0026"}`)).toEqual(`{"memo":"\\u0026"}`);
|
||||
|
||||
expect(JSON.parse(fromUtf8(utf8Encoding))).toEqual(JSON.parse(fromUtf8(escapedEncoding)));
|
||||
// Escapes one
|
||||
expect(escapeCharacters(`{"m":"with amp: &"}`)).toEqual(`{"m":"with amp: \\u0026"}`);
|
||||
expect(escapeCharacters(`{"m":"with lt: <"}`)).toEqual(`{"m":"with lt: \\u003c"}`);
|
||||
expect(escapeCharacters(`{"m":"with gt: >"}`)).toEqual(`{"m":"with gt: \\u003e"}`);
|
||||
// Escapes multiple
|
||||
expect(escapeCharacters(`{"m":"with amp: &&"}`)).toEqual(`{"m":"with amp: \\u0026\\u0026"}`);
|
||||
expect(escapeCharacters(`{"m":"with lt: <<"}`)).toEqual(`{"m":"with lt: \\u003c\\u003c"}`);
|
||||
expect(escapeCharacters(`{"m":"with gt: >>"}`)).toEqual(`{"m":"with gt: \\u003e\\u003e"}`);
|
||||
expect(escapeCharacters(`{"m":"with all: &<>"}`)).toEqual(`{"m":"with all: \\u0026\\u003c\\u003e"}`);
|
||||
});
|
||||
|
||||
it("escaped encoding can be decoded to the same document", () => {
|
||||
const docs = [
|
||||
{ memo: "ampersand:&,lt:<,gt:>", value: 123.421 },
|
||||
"",
|
||||
123,
|
||||
["foo", "ampersand:&,lt:<,gt:>"],
|
||||
];
|
||||
|
||||
for (const doc of docs) {
|
||||
const normalEncoding = JSON.stringify(doc);
|
||||
const escapedEncoding = escapeCharacters(normalEncoding);
|
||||
expect(JSON.parse(escapedEncoding)).toEqual(JSON.parse(normalEncoding));
|
||||
expect(JSON.parse(escapedEncoding)).toEqual(doc);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -72,35 +72,28 @@ export function makeSignDoc(
|
||||
};
|
||||
}
|
||||
|
||||
export function escapeCharacters(encodedArray: Uint8Array): Uint8Array {
|
||||
const AmpersandUnicode = new Uint8Array([92, 117, 48, 48, 50, 54]);
|
||||
const LtSignUnicode = new Uint8Array([92, 117, 48, 48, 51, 99]);
|
||||
const GtSignUnicode = new Uint8Array([92, 117, 48, 48, 51, 101]);
|
||||
|
||||
const AmpersandAscii = 38;
|
||||
const LtSign = 60; // <
|
||||
const GtSign = 62; // >
|
||||
|
||||
const filteredIndex: number[] = [];
|
||||
encodedArray.forEach((value, index) => {
|
||||
if (value === AmpersandAscii || value === LtSign || value === GtSign) filteredIndex.push(index);
|
||||
});
|
||||
|
||||
let result = new Uint8Array([...encodedArray]);
|
||||
const reversedFilteredIndex = filteredIndex.reverse();
|
||||
reversedFilteredIndex.forEach((value) => {
|
||||
let unicode = AmpersandUnicode;
|
||||
if (result[value] === LtSign) {
|
||||
unicode = LtSignUnicode;
|
||||
} else if (result[value] === GtSign) {
|
||||
unicode = GtSignUnicode;
|
||||
}
|
||||
result = new Uint8Array([...result.slice(0, value), ...unicode, ...result.slice(value + 1)]);
|
||||
});
|
||||
|
||||
return result;
|
||||
/**
|
||||
* Takes a valid JSON document and performs the following escapings in string values:
|
||||
*
|
||||
* `&` -> `\u0026`
|
||||
* `<` -> `\u003c`
|
||||
* `>` -> `\u003e`
|
||||
*
|
||||
* Since those characters do not occur in other places of the JSON document, only
|
||||
* string values are affected.
|
||||
*
|
||||
* If the input is invalid JSON, the behaviour is undefined.
|
||||
*/
|
||||
export function escapeCharacters(input: string): string {
|
||||
// When we migrate to target es2021 or above, we can use replaceAll instead of global patterns.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll
|
||||
const amp = /&/g;
|
||||
const lt = /</g;
|
||||
const gt = />/g;
|
||||
return input.replace(amp, "\\u0026").replace(lt, "\\u003c").replace(gt, "\\u003e");
|
||||
}
|
||||
|
||||
export function serializeSignDoc(signDoc: StdSignDoc): Uint8Array {
|
||||
return escapeCharacters(toUtf8(sortedJsonStringify(signDoc)));
|
||||
const serialized = escapeCharacters(sortedJsonStringify(signDoc));
|
||||
return toUtf8(serialized);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user