Add espaceJSON to String.sol (#5508)
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com>
This commit is contained in:
5
.changeset/nice-cherries-reply.md
Normal file
5
.changeset/nice-cherries-reply.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`Strings`: Add `espaceJSON` that escapes special characters in JSON strings.
|
||||
@ -15,6 +15,14 @@ library Strings {
|
||||
|
||||
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
|
||||
uint8 private constant ADDRESS_LENGTH = 20;
|
||||
uint256 private constant SPECIAL_CHARS_LOOKUP =
|
||||
(1 << 0x08) | // backspace
|
||||
(1 << 0x09) | // tab
|
||||
(1 << 0x0a) | // newline
|
||||
(1 << 0x0c) | // form feed
|
||||
(1 << 0x0d) | // carriage return
|
||||
(1 << 0x22) | // double quote
|
||||
(1 << 0x5c); // backslash
|
||||
|
||||
/**
|
||||
* @dev The `value` string doesn't fit in the specified `length`.
|
||||
@ -426,6 +434,43 @@ library Strings {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
|
||||
*
|
||||
* WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
|
||||
*/
|
||||
function escapeJSON(string memory input) internal pure returns (string memory) {
|
||||
bytes memory buffer = bytes(input);
|
||||
bytes memory output = new bytes(2 * buffer.length); // worst case scenario
|
||||
uint256 outputLength = 0;
|
||||
|
||||
for (uint256 i; i < buffer.length; ++i) {
|
||||
bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
|
||||
if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
|
||||
output[outputLength++] = "\\";
|
||||
if (char == 0x08) output[outputLength++] = "b";
|
||||
else if (char == 0x09) output[outputLength++] = "t";
|
||||
else if (char == 0x0a) output[outputLength++] = "n";
|
||||
else if (char == 0x0c) output[outputLength++] = "f";
|
||||
else if (char == 0x0d) output[outputLength++] = "r";
|
||||
else if (char == 0x5c) output[outputLength++] = "\\";
|
||||
else if (char == 0x22) {
|
||||
// solhint-disable-next-line quotes
|
||||
output[outputLength++] = '"';
|
||||
}
|
||||
} else {
|
||||
output[outputLength++] = char;
|
||||
}
|
||||
}
|
||||
// write the actual length and deallocate unused memory
|
||||
assembly ("memory-safe") {
|
||||
mstore(output, outputLength)
|
||||
mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
|
||||
}
|
||||
|
||||
return string(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Reads a bytes32 from a bytes array without bounds checking.
|
||||
*
|
||||
|
||||
@ -339,4 +339,11 @@ describe('Strings', function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Escape JSON string', function () {
|
||||
for (const input of ['', 'a', '{"a":"b/c"}', 'a\tb\nc\\d"e\rf/g\fh\bi'])
|
||||
it(`escape ${JSON.stringify(input)}`, async function () {
|
||||
await expect(this.mock.$escapeJSON(input)).to.eventually.equal(JSON.stringify(input).slice(1, -1));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user