Showing the security status in all external link using Google Safe Browsing Lookup API

A while ago, I was looking for a strategy to protect my website from external links provided by forum members. I’m trying to create my own database of harmful URLs. But it’s harder than you think. The sheer volume of links is tough to sort through. So I looked for an API service that could fit my demands and discovered the Google Safe Browsing API, a free service API provided by Google, and attempted to add it to the Discourse forum.

Result in my forum
*when mouse hover

Here’s how.
To add an icon after all external links in Discourse and display tooltips showing the security status of the links using Google Safe Browsing Lookup API (v4), follow these steps:

  1. Create an API Key for Google Safe Browsing:

    • Go to the Google Cloud Console
    • Create a new project or use an existing one
    • Enable the Safe Browsing API
    • Create an API key for this project
      *It’s all free.
  2. Log in to Discourse Admin:

    • Log in to Discourse with your admin account
  3. Access Theme Customization:

    • Go to “Admin” → “Customize” → “Themes”
    • Select the theme you want to edit or create a new theme or theme component
  4. Add CSS:

    • Click “Edit CSS/HTML” for the desired theme

    • In the “CSS” tab, add the following code:

      a[target="_blank"]:after {
          content: url('https://example.com/icon.png'); /* Replace with the URL of your desired icon */
          margin-left: 5px; /* Spacing between the link and the icon */
          display: inline-block;
      }
      
      .tooltip {
          position: relative;
          display: inline-block;
      }
      
      .tooltip .tooltiptext {
          visibility: hidden;
          width: 200px;
          background-color: #555;
          color: #fff;
          text-align: center;
          border-radius: 6px;
          padding: 5px;
          position: absolute;
          z-index: 1;
          bottom: 125%; /* Position of the tooltip */
          left: 50%;
          margin-left: -100px;
          opacity: 0;
          transition: opacity 0.3s;
      }
      
      .tooltip:hover .tooltiptext {
          visibility: visible;
          opacity: 1;
      }
      
  5. Add JavaScript:

    • In the “Header” tab of the theme, add the following code:

      <script type="text/discourse-plugin" version="0.8">
        api.onPageChange(() => {
          const externalLinks = document.querySelectorAll('a[href^="http"]:not([href*="' + window.location.hostname + '"])');
          const apiKey = 'YOUR_GOOGLE_API_KEY'; // Replace with your API key
          const lookupUrl = 'https://safebrowsing.googleapis.com/v4/threatMatches:find?key=' + apiKey;
      
          externalLinks.forEach(link => {
            link.setAttribute('target', '_blank');
            link.classList.add('tooltip');
      
            const tooltipText = document.createElement('span');
            tooltipText.className = 'tooltiptext';
            tooltipText.innerText = 'Checking link safety...';
            link.appendChild(tooltipText);
      
            fetch(lookupUrl, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                client: {
                  clientId: 'yourcompany',
                  clientVersion: '1.5.2'
                },
                threatInfo: {
                  threatTypes: ['MALWARE', 'SOCIAL_ENGINEERING'],
                  platformTypes: ['ANY_PLATFORM'],
                  threatEntryTypes: ['URL'],
                  threatEntries: [
                    { url: link.href }
                  ]
                }
              })
            })
            .then(response => response.json())
            .then(data => {
              if (data.matches && data.matches.length > 0) {
                tooltipText.innerText = 'Warning: This link may be unsafe!';
                tooltipText.style.backgroundColor = '#ff0000';
              } else {
                tooltipText.innerText = 'This link is safe.';
              }
            })
            .catch(error => {
              tooltipText.innerText = 'Error checking link safety.';
            });
          });
        });
      </script>
      

      This JavaScript will:

      • Find all external links and set them to open in a new tab
      • Add an icon after the link
      • Check the link using the Google Safe Browsing API and display the result in a tooltip
  6. Save and Apply the Theme:

    • Click “Save” to save your changes
    • If you created a new theme, set it as the default theme or assign it to the desired user groups

These steps will ensure that all external links in your Discourse instance will have an icon after them, and tooltips will show the security status of the links using the Google Safe Browsing API.

Edit for security
You should add your website to the Website limits section for Google API usage.

Option

Use Font Awesome icon

To use an icon from Font Awesome. Here is the updated CSS:

Updated CSS with Font Awesome Icons

/* Ensure Font Awesome is included */
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css');

/* Add Font Awesome icon after external links */
a[target="_blank"]:after {
    content: "\f35d"; /* Unicode for Font Awesome external link icon */
    font-family: 'Font Awesome 5 Free'; /* Font Awesome family */
    font-weight: 900; /* Font Awesome solid weight */
    margin-left: 5px; /* Spacing between the link and the icon */
    display: inline-block;
}

Explanation:

  1. Font Awesome Import:

    • The CSS starts by importing the Font Awesome CSS from a CDN.
  2. Icon Addition:

    • The content property in the a[target="_blank"]:after selector is set to "\f35d", which is the Unicode for the Font Awesome external link icon.
    • The font-family is set to ‘Font Awesome 5 Free’ and font-weight to 900 to use the solid version of the icon.

I hope this is helpful to those who are looking for the same thing as me.

3 Likes

This is a very cool idea!!

:warning: Note that by including your Google Cloud API key in a theme component, you’re exposing it to the world. When people have access to your API key then can potentially put you up with a very large bill. :warning:

You should either make sure that your API key is sufficiently scoped (e.g. it can only access the Safe Browsing API) or you should add a server side proxy so you can keep the API key safe on your server.

3 Likes

Or … turn this into a plugin and keep the API conversation happening behind the scenes where no-one can see.

1 Like

That’s what I tried to say :wink:

2 Likes

Well a proxy is a special kind of middleman and also a good idea.

It was definitely an OR.

1 Like

Thank you for the great advice. :blush: I believe there is a mechanism to block API access in Google Cloud Console; add your website there.

Because I lack the necessary skills to write plugins. I believe this strategy is basic and straightforward. But if there are people who wish to create, I believe it is superior to my way. And I will be quite thrilled.

1 Like

And meanwhile I would even propose this could be worth of a feature request.

(If that service is liable, that I don’t know)

3 Likes

I’m not making any promises but when I have some spare time in the upcoming months then I will definitely consider this.

2 Likes

I’m glad I grabbed your attention, and I appreciate the interest you showed in making this plugin.

I hope and am pleased to see this in the feature, too.

Does anyone know if this works with the self hosted version of Discourse? Don’t appear to have an option in Admin to “Customize” the theme settings.

There are no Theme Settings on that theme, the settings file is empty:

Additionally, in order to change a Theme downloaded from GitHub, you need to fork it, edit your fork and use your fork.

2 Likes

You can create a new theme component and add that to your theme

2 Likes