Skip to main content

Hairdye

This is a helper library which facilitates escaping strings.

Before using this library

Make sure you know the alternatives. For example, if you are dealing with SQL queries, the most appropriate ways are using parametrized queries or prepared statements, instead of manually inserting escape functions into every param you have.

However, we know that from time to time, we cannot do this easily. We end up escaping every param which can cause a lot of mistakes. There are two major types of flaws: forgetting to escape params, and escaping them more than once.

Example

Suppose we have an escape-html utility, and we want to escape the params, we can do something like converting

const text = `<span style="color: red; ">${email}, ${name}, ${count}</span>`;

to

const text = `<span style="color: red; ">${escapeHtml(email)}, ${
  escapeHtml(name)
}, ${count}</span>`;

. But then we have to insert escapeHtml to every param that we need to escape…and how about count? If it is a number, then most likely we don’t have to escape it. However, when the string complexity grows, sooner or later we may mix things up or forget to escape the params.

Example

Suppose we have something like

const content = `<span style="color: red; ">{escapeHtml(email)}</span>`;
const text = `<div style="color: blue">${content}</span>`; // oh, do we have to escape here?

We have such a question here because the escape operation is not idempotent. If we do not put the escape operation in content, we may end up forgetting to ecape the email. However, if we put the escape in text, we are still stuck because we will incorrectly escape the span.

Some people may mistakenly think that we can unescape the string repeatedly until it does not change, and then re-escape it. It actually does not work, because if the param contains escaped character, we still have to show it by escaping it, instead of just keeping that copy.

Usage

We can simply use

const { escape, escaped } = createEscapeHelper(escapeHtml);
const email = "your.email@provider.com";
const text = escape`<span style="color: red; ">${email}</span>`;
const content = escape`<div style="color: blue; ">${text} ${"injectedName"} ${
  escaped(3)
}</div>`;

console.log(`${content}`);

Then both email and "injectedName" will be escaped once, except 3. We can pass content around until we need to convert it to a string.