Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule proposal: .replace or .replaceAll with non-literal replacement #2309

Open
andersk opened this issue Apr 5, 2024 · 3 comments
Open

Rule proposal: .replace or .replaceAll with non-literal replacement #2309

andersk opened this issue Apr 5, 2024 · 3 comments

Comments

@andersk
Copy link
Contributor

andersk commented Apr 5, 2024

Description

One might think that a function like this generates safe HTML because the argument is HTML-escaped.

function imageLink(url) {
  const IMAGE_TEMPLATE = '<img src="{url}">';
  return IMAGE_TEMPLATE.replace('{url}', htmlEscape(url));
}

But in fact there’s a very obscure cross-site scripting vulnerability here, abusing the $` replacement sequence interpreted by String.prototype.replace and .replaceAll!

imageLink("$` onerror=alert(1) ")
//=> '<img src="<img src=" onerror=alert(1) ">'

To protect against this mistake, it would be nice to have an ESLint rule that forbids use of .replace and .replaceAll where the second argument isn’t a string literal or a function.

Fail

IMAGE_TEMPLATE.replace('{url}', htmlEscape(url))

Pass

IMAGE_TEMPLATE.replace('{url}', () => htmlEscape(url))
IMAGE_TEMPLATE.replace('{url}', "https://example.com")

Additional Info

No response

@sindresorhus
Copy link
Owner

What should happen to IMAGE_TEMPLATE.replace('{url}', foo) if we cannot infer to it be a string literal?

@andersk
Copy link
Contributor Author

andersk commented Apr 5, 2024

It would be disallowed.

If foo is really a string, I’d expect most people who write an invocation like that are expecting the behavior of IMAGE_TEMPLATE.replace('{url}', () => foo). If it’s really a function, the invocation can be rewritten IMAGE_TEMPLATE.replace('{url}', (...match) => foo(...match)) (a la no-array-callback-reference).

@sindresorhus
Copy link
Owner

Accepted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants