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

Improving the urls to not break protocols and adding tests #3995

Merged

Conversation

iifawzi
Copy link
Contributor

@iifawzi iifawzi commented Aug 19, 2022

Hi, This's my first contribution to this great project, and hopefully not the last one.
This PR should fix #3983
I've also added a bunch of tests, testing the urls utils.

Signed-off-by: iifawzi iifawzie@gmail.com

…he utils

Signed-off-by: iifawzi <iifawzie@gmail.com>
@auto-assign auto-assign bot requested a review from tommoor August 19, 2022 22:59
@CLAassistant
Copy link

CLAassistant commented Aug 19, 2022

CLA assistant check
All committers have signed the CLA.

shared/utils/urls.ts Outdated Show resolved Hide resolved
shared/utils/urls.ts Outdated Show resolved Hide resolved
@iifawzi
Copy link
Contributor Author

iifawzi commented Aug 19, 2022

This's my first contribution, I might not be aware of the folder structure decisions that was taken while ago, but I think it would make the code more organized if all the tests are under a separate directory, test for example. Even if not all the tests under a single main test directory, a sub-folder test in each folder would work too, like having all the urls tests in test folder under the utils directory.

Signed-off-by: iifawzi <iifawzie@gmail.com>
shared/utils/urls.ts Outdated Show resolved Hide resolved
Signed-off-by: iifawzi <iifawzie@gmail.com>
shared/utils/urls.ts Outdated Show resolved Hide resolved
iifawzi and others added 2 commits August 22, 2022 10:40
Javascript pseudo protocol does not require the //

Co-authored-by: Tom Moor <tom.moor@gmail.com>
Signed-off-by: iifawzi <iifawzie@gmail.com>
shared/utils/urls.test.ts Outdated Show resolved Hide resolved
shared/utils/urls.test.ts Outdated Show resolved Hide resolved
shared/utils/urls.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@apoorv-mishra apoorv-mishra left a comment

Choose a reason for hiding this comment

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

Just a few code style nitpicks!

shared/utils/urls.ts Fixed Show resolved Hide resolved
iifawzi and others added 5 commits August 24, 2022 02:43
Co-authored-by: Apoorv Mishra <apoorvmishra101092@gmail.com>
Co-authored-by: Apoorv Mishra <apoorvmishra101092@gmail.com>
Signed-off-by: iifawzi <iifawzie@gmail.com>
Signed-off-by: iifawzi <iifawzie@gmail.com>
Signed-off-by: iifawzi <iifawzie@gmail.com>
@iifawzi iifawzi requested review from tommoor and apoorv-mishra and removed request for tommoor and apoorv-mishra August 24, 2022 00:57
@vn-ncvinh
Copy link

@tommoor , I'm aware of a few possible XSS vulnerabilities here.

In the file urls.ts contains 2 code snippets with vulnerabilities.

export function isUrl(text: string) {
  if (text.match(/\n/)) {
    return false;
  }

  try {
    const url = new URL(text);
    return url.hostname !== "";
  } catch (err) {
    return false;
  }
}
export function sanitizeUrl(url: string | null | undefined) {
  if (!url) {
    return undefined;
  }

  if (
    (!isUrl(url) &&
      !url.startsWith("/") &&
      !url.startsWith("#") &&
      !url.startsWith("mailto:")) ||
    url.startsWith("javascript:") ||
    url.startsWith("file:") ||
    url.startsWith("vbscript:") ||
    url.startsWith("data:")
  ) {
    return `https://${url}`;
  }
  return url;
}

An attacker can easily bypass the filter to generate a url containing the XSS attack payload.

jAvascript://%0Aalert(origin)

jAvascript://: will fool and pass 2 test functions.

%0A%0Dalert(origin): Execute command alert origin.

Solution

Use filter by the URL protocol

var blacklist_protocol = ['javascript:', 'file:', 'vbscript:', 'data:']
export function isUrl(text: string) {
  if (text.match(/\n/)) {
    return false;
  }
  
  try {
    const url = new URL(text);
    return url.hostname !== "" && !blacklist_protocol.includes(url.protocol);
  } catch (err) {
    return false;
  }
}
export function sanitizeUrl(url: string | null | undefined) {
  if (!url) {
    return undefined;
  }

  if (
    (!isUrl(url) &&
      !url.startsWith("/") &&
      !url.startsWith("#") &&
      !url.startsWith("mailto:"))
  ) {
    return `https://${url}`;
  }
  return url;
}

@iifawzi
Copy link
Contributor Author

iifawzi commented Aug 25, 2022

Nice catch @vn-ncvinh, it's way better this way. Anyway, if we want to stick with the startsWith way, we can lowercase the urls before checking them.

@vn-ncvinh
Copy link

vn-ncvinh commented Aug 25, 2022

Nice catch @vn-ncvinh, it's way better this way. Anyway, if we want to stick with the startsWith way, we can lowercase the urls before checking them.

If using lowercase, there is still a way to bypass it by using special characters.
image

@apoorv-mishra
Copy link
Collaborator

Interesting. Thanks for pointing this out 🙏

Maybe we should replace these with the existing validator module that we already use on server-side? But I wonder why isn't it used already 🤔 To keep the bundle size low @tommoor ?

@iifawzi
Copy link
Contributor Author

iifawzi commented Aug 25, 2022

Ahh I see, thank you @vn-ncvinh for clarifying this, my bad.

@tommoor
Copy link
Member

tommoor commented Aug 25, 2022

I don't think the validator module is doing anything too special, in fact it may also be vulnerable to the use of special characters as a bypass based on this…

https://github.com/validatorjs/validator.js/blob/master/src/lib/isURL.js#L84

@apoorv-mishra
Copy link
Collaborator

Plug: Seems like mermaid uses this.

@vn-ncvinh
Copy link

I think the URL of javascript works quite well, we just need to add a blacklist of protocols.
For urls containing special characters, it will be automatically removed.
But maybe in the future there will be protocol spoofing vulnerability, but that's javascript problem, and we don't need to care about it right now.
image

@tommoor
Copy link
Member

tommoor commented Aug 25, 2022

Isn't this fun 😆

@apoorv-mishra
Copy link
Collaborator

I'm actually surprised why none of the third party validators leverage new URL() too as part of the validation flow 🤔

@vn-ncvinh
Copy link

I don't know about that either.
But maybe when they build a new validator function, they will avoid using the built-in validator functions.

@vn-ncvinh
Copy link

I think should add tel:, fax:, sms:... or some other alternative link

@tommoor
Copy link
Member

tommoor commented Aug 25, 2022

I think should add tel:, fax:, sms:... or some other alternative link

Not sure what you mean here?

@vn-ncvinh
Copy link

vn-ncvinh commented Aug 25, 2022

I think should add tel:, fax:, sms:... or some other alternative link

Not sure what you mean here?

These will make it easier to the contact methods. It is similar to the mailto: that was added earlier.

  • tel:0123456789 - Make a call to the phone number 0123456789
  • fax:0123456789 - Make a fax to 0123456789
  • sms:0123456789 - Make new SMS to phone number 0123456789

@apoorv-mishra
Copy link
Collaborator

I'm thinking, maybe instead of having a blacklist, we should have allowed_protocols? Because, the blacklist above might still be bypassed by something like new URL("somerandomprotocol://malicious.something"). We're more confident about what we want to allow than what we don't.

@iifawzi
Copy link
Contributor Author

iifawzi commented Aug 25, 2022

Although this will be more restrictive it would make more sense to be implemented this way, and legitimate protocols can be added by the time, when people notice it, through the issues/PRs.

instead of only sanitizing the forbidden protocols and forgetting some.

@vn-ncvinh
Copy link

whether to use blacklist or whitelist depends on each specific case. We will prioritize using what is supposed to be the minority, and here is the blacklist. Of the multitude of protocols available, only a few are considered dangerous, and it is easier to list them than to list the rest.

@iifawzi
Copy link
Contributor Author

iifawzi commented Aug 25, 2022

I think the subsequences of forgetting any dangerous protocol are more dangerous than forgetting any allowed protocol, in the later case, people will notice them easily and will be added accordingly

@vn-ncvinh
Copy link

So far, only 4 protocols have been deemed dangerous, which can lead to xss.

@tommoor
Copy link
Member

tommoor commented Aug 25, 2022

@iifawzi in the aim of maintainability let's keep going with the blocklist approach, I don't want to be fielding PR's for eternity for every desktop app possible.

Signed-off-by: iifawzi <iifawzie@gmail.com>
shared/utils/urls.ts Outdated Show resolved Hide resolved
Signed-off-by: iifawzi <iifawzie@gmail.com>
shared/utils/urls.ts Outdated Show resolved Hide resolved
iifawzi and others added 2 commits August 31, 2022 13:08
inlining the protocols in the same file

Co-authored-by: Tom Moor <tom.moor@gmail.com>
Signed-off-by: iifawzi <iifawzie@gmail.com>
@apoorv-mishra apoorv-mishra merged commit eb51263 into outline:main Sep 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do not add HTTPS to links if protocol is declared
5 participants