@@ -15,6 +15,14 @@ library Strings {
1515
1616 bytes16 private constant HEX_DIGITS = "0123456789abcdef " ;
1717 uint8 private constant ADDRESS_LENGTH = 20 ;
18+ uint256 private constant SPECIAL_CHARS_LOOKUP =
19+ (1 << 0x08 ) | // backspace
20+ (1 << 0x09 ) | // tab
21+ (1 << 0x0a ) | // newline
22+ (1 << 0x0c ) | // form feed
23+ (1 << 0x0d ) | // carriage return
24+ (1 << 0x22 ) | // double quote
25+ (1 << 0x5c ); // backslash
1826
1927 /**
2028 * @dev The `value` string doesn't fit in the specified `length`.
@@ -426,6 +434,43 @@ library Strings {
426434 return value;
427435 }
428436
437+ /**
438+ * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
439+ *
440+ * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
441+ */
442+ function escapeJSON (string memory input ) internal pure returns (string memory ) {
443+ bytes memory buffer = bytes (input);
444+ bytes memory output = new bytes (2 * buffer.length ); // worst case scenario
445+ uint256 outputLength = 0 ;
446+
447+ for (uint256 i; i < buffer.length ; ++ i) {
448+ bytes1 char = bytes1 (_unsafeReadBytesOffset (buffer, i));
449+ if (((SPECIAL_CHARS_LOOKUP & (1 << uint8 (char))) != 0 )) {
450+ output[outputLength++ ] = "\ \" ;
451+ if (char == 0x08) output[outputLength++] = " b";
452+ else if (char == 0x09) output[outputLength++] = " t";
453+ else if (char == 0x0a) output[outputLength++] = " n";
454+ else if (char == 0x0c) output[outputLength++] = " f";
455+ else if (char == 0x0d) output[outputLength++] = " r";
456+ else if (char == 0x5c) output[outputLength++] = " \\";
457+ else if (char == 0x22 ) {
458+ // solhint-disable-next-line quotes
459+ output[outputLength++ ] = '" ' ;
460+ }
461+ } else {
462+ output[outputLength++ ] = char;
463+ }
464+ }
465+ // write the actual length and deallocate unused memory
466+ assembly ("memory-safe" ) {
467+ mstore (output, outputLength)
468+ mstore (0x40 , add (output, shl (5 , shr (5 , add (outputLength, 63 )))))
469+ }
470+
471+ return string (output);
472+ }
473+
429474 /**
430475 * @dev Reads a bytes32 from a bytes array without bounds checking.
431476 *
0 commit comments