DEV Community

loading...
Cover image for Browser extension - Integrate your features securely

Browser extension - Integrate your features securely

qmenoret profile image Quentin Ménoret Updated on ・4 min read

I just published a new extension on Chrome and Firefox that allows anyone to run Code Tours from the Github UI. More information about Code Tours and the extension in this blog post.

I thought it would be nice to write a series about how you could do exactly the same, step by step.

This fifth blog post will focus on integrating your features securely into a website.

Short notice

For this part of the extension, we need to be able to import some external modules.

I won't explain how to setup webpack in this post, but if it's something you'd be interested in, drop a comment and I may write another post in the series about this.

What we are building

Here is a screenshot of what we'll have at the end of this post. We'll display safely the description of a Code Tour Step:

A basic tour description displayed on Github

The challenge

In order to display a Code Tour to the user, here are the different steps that we implemented:

  • Find the list of tours
  • Get the content of each code tour
  • Redirect to the right page

Now that we are at the right place, with the content of the Code Tour, we need to load it into the page.

The content of a Code Tour is written using the Markdown language. There are ways to generate html out of Markdown, but we need to make sure that it's safe.

But let's first build a basic version!

The innocent version

First, let's add the text directly to the UI. Here is the code we had so far:

function forwardRequest(message) {
  return new Promise((resolve, reject) => {
    chrome.runtime.sendMessage(message, (response) => {
      if (!response) return reject(chrome.runtime.lastError);
      return resolve(response);
    });
  });
}

document.addEventListener("DOMContentLoaded", async () => {
  const urlParams = new URLSearchParams(window.location.search);
  const title = urlParams.get("code-tour-title");
  if (!title) return;

  const tour = await forwardRequest({ title });

  const step = urlParams.get("step");
  console.log(tour.steps[step]);
});
Enter fullscreen mode Exit fullscreen mode

Now, instead of logging the state, let's add the description of the tour on the right line:

document.addEventListener("DOMContentLoaded", async () => {
  const urlParams = new URLSearchParams(window.location.search);
  const title = urlParams.get("code-tour-title");
  if (!title) return;

  const tour = await forwardRequest({ title });

  const step = urlParams.get("step");

  // We'll add the description on the right line
  const parent = document.querySelector(
    `#LC${tour.steps[step].line}.blob-code`
  );
  const section = document.createElement("div");

  const span = document.createElement("span");
  span.innerHTML = tour.steps[step].description;

  section.append(span);

  // A bit of style
  section.setAttribute(
    "style",
    `
    padding: 14px;
    margin: 14px;
    border: 1px lightgrey solid;
    background-color: white;
    border-radius: 1em;
    font-family: sans-serif;
    `
  );

  parent.append(section);
});
Enter fullscreen mode Exit fullscreen mode

Transform Markdown to HTML

In order to transform the Markdown to HTML, we can use a generator such as showdown. It's really easy to use:

const showdown = require('showdown')
const converter = new showdown.Converter()
const htmlString = converter.makeHtml(yourMarkdownString)
Enter fullscreen mode Exit fullscreen mode

Now we can use this as inner HTML for the section:

span.innerHTML = converter.makeHtml(tour.steps[step].description);
Enter fullscreen mode Exit fullscreen mode

XSS injections with Markdown

Since our Markdown generation can write html, we can probably generate dangerous code as well. Consider the following Markdown code:

[XSS injection](javascript:alert('xss'))
Enter fullscreen mode Exit fullscreen mode

Once you use a html generator (for instance showdown) with this code, you will get this html:

<p><a href="javascript:alert('xss')">XSS injection</a></p>
Enter fullscreen mode Exit fullscreen mode

Try this in your browser, if you click it, it executes the JavaScript. Of course this is a very basic example, but there are a lot of more complex ways to exploit this. And since the Code Tours we load are untrusted code, we better protect our users!

Protecting from the XSS

There are a few libraries you can use to protect from xss. For instance the xss library on npm.

Using this, we are able to escape the dangerous bits of our HTML. Just use it this way:

filterXSS(converter.makeHtml(rawText))
Enter fullscreen mode Exit fullscreen mode

Let's use it in our code:

span.innerHTML = filterXSS(converter.makeHtml(tour.steps[step].description));
Enter fullscreen mode Exit fullscreen mode

Now our users are protected.

Conclusion

If there is one thing to remember from this post, it's this:

don't trust external inputs, even if it's text

As soon as you apply any kind of transformation to something you have no control over, there is a risk for it to be exploited. And trust me, it’s way worse when the code is in an extension that can be loaded on any website on your browser.

In the next post, we'll see how to deploy this extension on the different stores. Feel free to follow me here if you want to check the post when it's out:

qmenoret image

Photo by Ricardo Gomez Angel on Unsplash

Discussion (0)

pic
Editor guide