编写标记网页CVE浏览器插件

  • 5 分钟阅读
  • 标签: 
  • cve

前言

  • 在查看文章的时候经常会遇到CVE编号,如果想查看详细信息还有手动复制到搜索引擎查询,非常的麻烦,所以打算开发一个浏览器插件,识别页面上的CVE编号,并且在这个CVE编号后面添加一个跳转按钮。
  • https://github.com/cn-kali-team/mark-cve 全部代码。

实现逻辑

  1. 要在页面上的可见文本上面找到CVE编号,这里使用正则表达式匹配找到完整的CVE编号,再通过浏览器自带的window.find搜索在页面上找到用户可见的CVE编号。
  2. 使用浏览器的设计模式对页面进行修改,window.find会返回一个用户选择的块,就像用户用鼠标划选的那块区域,使用代码可以读取这块的元素,然后在这个元素后面追加跳转按钮。

代码实现

  • 下面代码为使用正则表达式在innerText匹配CVE编号,再将获取到的完整CVE编号传给window.find在用户可见页面搜索完整的CVE编号获取选块。
function FindCVE() {
    GetBaseURL();
    if (DefaultBaseUrl.startsWith(location.hostname)){
        return;
    }
    const regex = new RegExp('\\bCVE-\\d{4}-\\d{4,7}\\b', 'gmi');
    document.designMode = "on";
    const sel = window.getSelection();
    sel.collapse(document.body, 0);
    let m;
    while (m = regex.exec(document.body.innerText)) {
        while (window.find(m)) {
            Mark();
        }
    }
    document.designMode = "off";
}
  • 下面为高亮CVE并且在后面添加一个 🐞图标,点击就会跳转到设置好的BaseURL和ID拼接后的链接。
function Mark() {
    const userSelection = window.getSelection();
    const id = userSelection.toString();
    let cve = userSelection.getRangeAt(0).startContainer.parentNode;
    if (cve.getElementsByClassName("Marked").length > 0) {
        return;
    }
    const spanElement = document.createElement("span");
    spanElement.setAttribute("class", "Marked");
    const selectedTextRange = userSelection.getRangeAt(0);
    selectedTextRange.surroundContents(spanElement);
    const icon = document.createElement("a");
    icon.href = DefaultBaseUrl + id;
    icon.target = "_blank";
    const svg = document.createElement('img');
    svg.setAttribute("style", "background-color: rgb(154, 205, 50);");
    svg.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpY29uIGljb24tdGFibGVyIGljb24tdGFibGVyLWJ1ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIGZpbGw9Im5vbmUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggc3Ryb2tlPSJub25lIiBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgOXYtMWEzIDMgMCAwIDEgNiAwdjEiIC8+PHBhdGggZD0iTTggOWg4YTYgNiAwIDAgMSAxIDN2M2E1IDUgMCAwIDEgLTEwIDB2LTNhNiA2IDAgMCAxIDEgLTMiIC8+PHBhdGggZD0iTTMgMTNsNCAwIiAvPjxwYXRoIGQ9Ik0xNyAxM2w0IDAiIC8+PHBhdGggZD0iTTEyIDIwbDAgLTYiIC8+PHBhdGggZD0iTTQgMTlsMy4zNSAtMiIgLz48cGF0aCBkPSJNMjAgMTlsLTMuMzUgLTIiIC8+PHBhdGggZD0iTTQgN2wzLjc1IDIuNCIgLz48cGF0aCBkPSJNMjAgN2wtMy43NSAyLjQiIC8+PC9zdmc+";
    icon.appendChild(svg);
    spanElement.appendChild(icon);
}

第一个插件

Untitled

  • 我并没有从头开始学习开发完整的浏览器插件,而是在https://github.com/mdn/webextensions-examples中翻找了几个功能相似的例子,并开始编写javascript代码。
  • 主要是**manifest.json**文件的标注需要一个一个看是实现了什么功能。

权限

  • 下面的权限分别为,activeTab获取当前激活页面的用户交互页面操作,和scripting执行注入用户代码功能,最后的storage的保存BaseURL配置的。
"permissions": [
    "activeTab",
    "scripting",
    "storage"
  ],

用户代码

  • 在这里的代码会在document_idle页面加载完成后将文件content-script.js注入到页面。
"content_scripts": [
    {
      "run_at": "document_idle",
      "all_frames": false,
      "matches": [
        "*://*/*"
      ],
      "js": [
        "content-script.js"
      ]
    }
  ],

配置

  • 添加配置页面,用于对插件的配置进行设置,这里只添加了一个自定义框,需要上面说到的storage权限。
"options_ui": {
    "page": "options/options.html"
  },
  • 可以使用browser.storage.local,对象进行getset方法操作。
const KeyName = 'base_url';
const DefaultBaseUrl = 'https://scap.kali-team.cn/cve/'
let Config = {
    base_url: DefaultBaseUrl,
};

async function updateUI() {
    await browser.storage.local.get(KeyName)
        .then((item) => {
            document.querySelector('#base_url').value = item.base_url || DefaultBaseUrl;
        })
}

async function updateBaseUrl() {
    Config.base_url = document.querySelector('#base_url').value;
    await browser.storage.local.set(Config);
    updateUI();
}

async function resetBaseUrl() {
    await browser.storage.local.set(Config);
    updateUI();
}

document.addEventListener('DOMContentLoaded', updateUI);

/**
 * Handle update and reset button clicks
 */
document.querySelector('#update').addEventListener('click', updateBaseUrl)
document.querySelector('#reset').addEventListener('click', resetBaseUrl)

option.png

调试

  • https://github.com/mozilla/web-ext安装好,直接在项目文件夹run,然后去浏览器的插件的管理页面点击调试插件,就可以在新建窗口看到调试控制台信息了。

Untitled

书签插件

  • Firefox浏览器插件还没审核通过,现在推荐使用书签插件执行javascript,创建一个书签将URL这框以javascript:开头,然后将**content-script.js文件里面的javascript代码复制进去就可以了,注意不要复制前面的Tampermonkey**注释。

Untitled

TamperMonkey

  • 在原来的代码基础上加上Tampermonkey的配置注释描述,下面的代码不变。
// ==UserScript==
// @name         Mark CVE
// @namespace    http://tampermonkey.net/
// @version      0.1.2
// @description  Mark the current page CVE
// @author       Kali-Team
// @match        *://*/*
// @exclude      https://scap.kali-team.cn/*
// @icon         https://avatars.githubusercontent.com/u/99640169?s=200&v=4
// @grant        none
// @run-at       document-idle
// @homepage     https://github.com/cn-kali-team/mark-cve
// @license      GPL-3.0-only
// ==/UserScript==

效果

Untitled

AttackerKB.png

参考