Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Don't use dangerous HTML in Linkify #10

Merged
merged 7 commits into from
Dec 7, 2018
Merged

Conversation

erikshestopal
Copy link
Contributor

@erikshestopal erikshestopal commented Dec 5, 2018

Summary

Fixed an issue with the Linkify component allowing execution of arbitrary HTML/JS that could be used for XSS attacks.

Props to @kylealwyn for the actual implementation.

> ### Summary
Fixed an issue with the `Linkify` component allowing execution
of arbitrary HTML/JS that could be used for XSS attacks.
@codecov-io
Copy link

codecov-io commented Dec 5, 2018

Codecov Report

Merging #10 into master will increase coverage by 3.49%.
The diff coverage is 95%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #10      +/-   ##
==========================================
+ Coverage   69.29%   72.78%   +3.49%     
==========================================
  Files          10       11       +1     
  Lines         127      147      +20     
  Branches       20       24       +4     
==========================================
+ Hits           88      107      +19     
- Misses         28       29       +1     
  Partials       11       11
Impacted Files Coverage Δ
src/Linkify/Linkify.js 95% <95%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fca4234...ab1637c. Read the comment docs.

escapeHtmlChars(string).replace(
REGEX,
url => `<a target="_blank" rel="noopener noreferrer" href="${url}">${url}</a>`
);

const Linkify = props => (
<Container style={props.style || {}} dangerouslySetInnerHTML={{ __html: parse(props.children) }} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you can do this without dangerouslySetInnerHTML if you iterate through regex matches and return a [React.Component]. see https://github.com/tasti/react-linkify/blob/master/src/components/Linkify.jsx#L75 (could also use that library directly, up to you)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I think using a library like this would be ideal, our use case for this component usually resolves parsing strings as opposed to React elements. So, for example, we currently use the component like this:

<Linkify>
  {message.body} //=> string
</Linkify>

as opposed to like this.

<Linkify>
  <span>Hello this a tag</span>
  <span>https://link.com</span>
</Linkify>

While I definitely do agree about with the general notion of making code as general and extensible as possible, I think in this case I would hold off on addressing edge cases until and if we get to them.

What do you think? @choochootrain 😄

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed offline-but-actually-online-because-slack - i think if we defer html escaping to react then we can be p confident that future bugs will have a lot more eyeballs on them :)

@erikshestopal erikshestopal changed the title fix(Linkify): escape HTML characters Don't use dangerous HTML in Linkify Dec 7, 2018
@erikshestopal
Copy link
Contributor Author

@kylealwyn ready for final pass.

const component = renderWithTheme(<Linkify>{`<span onmouseover=alert('XSS')></span>`}</Linkify>);
const component = renderWithTheme(
<Linkify>
<img src="fake.jpg" onError={() => {}} alt="hacker" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this all be in a template string to test escaping?

if (typeof child === 'string') {
const words = child.split(' ');
return words.map((word, index) => {
const isLastWord = words.length === index - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy pasta from my example but this should be words.length - 1 === index

@erikshestopal
Copy link
Contributor Author

@kylealwyn @choochootrain ready for final pass.

text-decoration: underline;
}
`;
const URL_REGEX = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/gi;
Copy link
Contributor Author

@erikshestopal erikshestopal Dec 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add some rules around domain extensions so that URLs like duckduckgo.com in the playground below will also be converted into links?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, probably yes. We can add rules to our doctors for now :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kylealwyn cool. LGTM 🚢

Copy link

@choochootrain choochootrain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@kylealwyn kylealwyn merged commit 0f2da65 into master Dec 7, 2018
@choochootrain choochootrain deleted the fix/escape-html-in-linkify branch August 13, 2019 21:16
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants