TrustScanner

Warn users about potentially malicious websites by checking the domain against the FishFish API and displaying a prominent alert if flagged as malware or phishing.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         TrustScanner
// @namespace    http://tampermonkey.net/
// @version      v1.0.0
// @description  Warn users about potentially malicious websites by checking the domain against the FishFish API and displaying a prominent alert if flagged as malware or phishing.
// @match        *://*/*
// @tag          security
// @tag          privacy
// @tag          safety
// @author       Suchti18
// @copyright    /
// @supportURL   https://github.com/Suchti18/TrustScanner
// @homepageURL  https://github.com/Suchti18/TrustScanner
// @homepage     https://github.com/Suchti18/TrustScanner
// @icon         https://raw.githubusercontent.com/Suchti18/TrustScanner/main/.github/logo.png
// @icon64       https://raw.githubusercontent.com/Suchti18/TrustScanner/main/.github/logo.png
// @license      Unlicense
// @grant        GM_xmlhttpRequest
// ==/UserScript==

// This ensures we check the main domain only, ignoring subdomains because FishFish only accepts the main domain
function getCurrentDomain() {
    // Get the current host
    const hostname = window.location.host

    // Return the domain. The levels concatenated by a dot. Return null if no match is found
    return hostname ? hostname : null;
}

async function checkWebsite(domain) {
    // Null check for the parameter
    if(domain == null) {
        return ""
    }
    
    // API URL for FishFish
    const apiURL = "https://api.fishfish.gg/v1/domains/"
    const completeURL = apiURL + domain   
    
    // API Request to FishFish
    // Docs: https://api.fishfish.gg/docs/
    // Using GM_xmlhttpRequest to bypass CORS restrictions imposed by browsers
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: completeURL,
            // Response 404: Domain wasnt found in the FishFish database.
            // Response 200: FishFish got an entry for the domain
            // Every other response is an error
            onload: function(response) {
                if (response.status === 404) {
                    resolve("Not found");
                } else if (response.status === 200) {
                    try {
                        const data = JSON.parse(response.responseText);
                        resolve(data.category);
                    } catch(e) {
                        reject(e);
                    }
                } else {
                    reject(new Error("HTTP Status: " + response.status));
                }
            },
            onerror: function(err) {
                reject(new Error("An error occurred during the api request"));
            }
        });
    });
}

// Display a prominent banner warning users about the site's potential risk.
function showBanner(result) {
    // Create a div with a red background and the text "This site was flagged as [result]. Proceed with caution!"
    const div = document.createElement('div');
    div.style.display = 'flex';
    div.style.justifyContent = 'center';
    div.style.alignItems = 'center';
    div.style.minHeight = '20vh';
    div.style.padding = '2vh 1vw';
    div.style.backgroundColor = 'red';
    div.style.fontSize = '3.5vw';
    div.style.color = 'white';
    div.style.textAlign = 'center';
    div.style.boxSizing = 'border-box';

    // Adds the text mentioned earlier to the div
    div.innerHTML = `This site was flagged as\u00A0<strong>${result}</strong>. Proceed with caution!`

    // Create a shadow DOM to encapsulate the styles and prevent conflicts with the page's styles
    // This is useful to ensure the banner looks consistent across different sites
    const host = document.createElement('div');
    const shadow = host.attachShadow({ mode: 'open' });

    shadow.appendChild(div);

    // Add banner to the top of the document
    document.body.prepend(host);
}

(async function() {
    'use strict';

    // Check the current domain against the FishFish API
    // If the domain is flagged as malware or phishing, show a warning banner
    const result = await checkWebsite(getCurrentDomain())

    // Decide whether to show the banner or not
    // Log the result to the console for debugging purposes

    console.log("Website checked: " + getCurrentDomain())

    if(result === "safe") {
        console.log("Website is marked as safe")
    } else if(result === "malware" || result === "phishing") {
        showBanner(result)
        console.log("Website was flagged as " + result + ". A warning banner was displayed.")
    } else if(result === "Not found") {
        console.log("Website was not found in the FishFish Database. Do you think its dangerous? Visit https://fishfish.gg/")
    } else {
        console.error("Oops, something went wrong while checking the website.")
        console.error("Returned result: " + result)
    }
})();