Skip to content

Instantly share code, notes, and snippets.

@SuperOleg39
Created July 21, 2023 08:50
Show Gist options
  • Select an option

  • Save SuperOleg39/54474af9f9b22b34439b2abb41c74975 to your computer and use it in GitHub Desktop.

Select an option

Save SuperOleg39/54474af9f9b22b34439b2abb41c74975 to your computer and use it in GitHub Desktop.
Old version of safe-strings
/**
* High performance encoding of the specified characters in the string
*
* source https://github.com/preactjs/preact-render-to-string/blob/60075a5a7389d638d535c85f3706739e9ba932bc/src/util.js
* perf https://esbench.com/bench/5f88af6cb4632100a7dcd414
*/
export function encode(str: string, entities: RegExp, encodeMap: Record<number, string>) {
// Skip all work for strings with no entities needing encoding:
if (str.length === 0 || entities.test(str) === false) {
return str;
}
let last = 0;
let i = 0;
let out = '';
let ch = '';
// Seek forward in str until the next entity char:
for (; i < str.length; i++) {
const charCode = str.charCodeAt(i);
if (charCode in encodeMap) {
ch = encodeMap[charCode];
} else {
continue;
}
// Append skipped/buffered characters and the encoded entity:
if (i !== last) {
out += str.slice(last, i);
}
out += ch;
// Start the next seek/buffer after the entity's offset:
last = i + 1;
}
if (i !== last) {
out += str.slice(last, i);
}
return out;
}
import { encode } from './encode';
const ENTITIES = /[&<>"']/;
// references:
// - https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-for-html-contexts
// - https://github.com/preactjs/preact-render-to-string/blob/master/src/util.js#L6
// - https://github.com/OWASP/owasp-java-encoder/blob/main/core/src/main/java/org/owasp/encoder/HTMLEncoder.java#L53
const ENCODE_MAP = {
38: '&amp;', // &
60: '&lt;', // <
62: '&gt;', // >
34: '&quot;', // "
39: '&#039;', // '
};
/**
* Encode possible XSS in string for insertion into HTML
*/
export function encodeForHTMLContext(str = ''): string {
return encode(str, ENTITIES, ENCODE_MAP);
}
import { encode } from './encode';
const ENTITIES = /[<\u2028\u2029]/;
// references:
// - https://github.com/yahoo/serialize-javascript/blob/main/index.js#L25
// - https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/server/page/serialize_data.js#L22
// - https://github.com/vercel/next.js/blob/canary/packages/next/src/server/htmlescape.ts#L4
// - https://github.com/OWASP/owasp-java-encoder/blob/main/core/src/main/java/org/owasp/encoder/JavaScriptEncoder.java#L128
const ENCODE_MAP = {
60: '\\u003C', // <
8232: '\\u2028', // line separator
8233: '\\u2029', // paragraph separator
};
/**
* Encode possible XSS and breaking code symbols in string for insertion into script tag
*/
export function encodeForJSContext(str = ''): string {
return encode(str, ENTITIES, ENCODE_MAP);
}
import { encodeForJSContext } from './encodeForJSContext';
/**
* Stringify object and encode possible XSS and breaking code symbols for insertion result into script tag
*/
export const safeStringify = (json: Record<string, any>): string => {
return encodeForJSContext(JSON.stringify(json));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment