先上效果图:
功能简介:
选择被测试的 tab,
1.然后点击打开我们的 plugin,然后会有如上述截图的一个 POPUP 展示出来,
2.然后点击开始捕获请求,
3.然后在当前的页面进行页面操作,插件会开始捕获用户操作期间中的Document
、Fetch
、XHR
、JavaScript
、CSS
等的返回值,
4.然后 service-worker 中使用正则表达式,来统一匹配,是否会有命中,然后将结果写入到storage中
5.然后打开插件 POPUP 界面,点击加载检查结果,即可以看到相关敏感词结果.
下面我先把核心代码罗列一下,后面找个时间系统的介绍下 plugin dev 的方法和我的经验
1.开始捕获请求,即 attach chrome.debugger 到被测的 tab 下,这里会涉及到 popup 和 service-worker 的通信,后续文章会讲到,如果想提前了解,可以参考https://developer.chrome.com/docs/extensions/mv3/messaging/
popup.js
这里通过 chrome.runtime.sendMessage 告诉 service-worker, 参数有 action,值可以自己定义和处理,然后 tab 是哪一个
async function showBg() {
let [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
console.log("homepage tab id " + tab.id)
chrome.runtime.sendMessage({"action":"start", "tab": tab}, (response) => {
console.log("popup send message success")
console.log("response:" + response)
});
}
service-worker.js
通过监听事件chrome.runtime.onMessage来获取 popup 发来的消息,然后通过chrome.debugger.attachattach debugger, 并且开启Network Domain,为后续 network 请求做分析
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
const {action, tab} = request;
if (action === "start") {
if (tab.url.startsWith('http')) {
currentPageUrl = tab.url;
chrome.debugger.attach({tabId: tab.id}, '1.2', function () {
chrome.debugger.sendCommand(
{tabId: tab.id},
'Network.enable',
{},
function () {
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
}
}
);
});
} else {
console.log('Debugger can only be attached to HTTP/HTTPS pages.');
}
}
sendResponse("content got!")
})
然后再介绍下如何获取不同资源的响应值
service-worker.js
要点是
1.通过chrome.debugger.onEvent监听 Network 相关事件
2.通过 method 为Network.responseReceived来获取 responseReceived 通知
3.通过chrome.debugger.sendCommand发送 method 为Network.getResponseBody来获取实际响应值
4.通过正则表达式完成敏感词检测
5.将结果保存到storage中,供 popup 使用
let checkTypes = ['xhr', 'fetch', 'stylesheet', 'document', 'script'];
chrome.debugger.onEvent.addListener(async function (source, method, params) {
if (method === 'Network.responseReceived') {
// console.log('Network.responseReceived', source, method, params)
if (checkTypes.includes(params.type.toLowerCase())) {
pendingRequests[params.requestId] = { status: true, type: params.type, url: params.response.url };
}
} else if (
method === 'Network.loadingFinished' &&
pendingRequests[params.requestId] !== undefined
) {
// console.log('Network.loadingFinished', source, method, params);
getResponseBody(
source.tabId,
params.requestId,
pendingRequests[params.requestId]
);
delete pendingRequests[params.requestId];
}
});
function getResponseBody(tabId, requestId, requestInfo) {
chrome.debugger.sendCommand(
{ tabId: tabId },
'Network.getResponseBody',
{ requestId: requestId },
async function (response) {
// console.log(requestInfo.type + ' response', response.body);
let tab = await getCurrentTab();
currentPageUrl = tab.url;
// 首先检查是否已经检查过当前的response
let requestUrl = requestInfo.url;
let newKeyValue = {}
let checkResult = {};
let isExits = undefined;
// 感觉这个处理有点问题,这里用chrome tabAPI来获取当前tab的url
let result = await chrome.storage.local.get(["key"]);
isExits = getNestedValue(result.key, currentPageUrl + splitChar + requestUrl)
console.log("isExits:", isExits)
if (isExits !== undefined && oldSensitiveWordList === sensitiveWordList) {
console.log("requestUrl:", requestUrl, "type:", requestInfo.type, "already has return")
return;
} else {
console.log("result.key:", result.key)
newKeyValue = result.key === undefined ? {} : result.key;
}
console.log("newKeyValue start:", newKeyValue)
// 进行敏感词的校验
let checkResponse = check(response.body, sensitiveWordList);
if (checkResponse === undefined) {
// 标记处理过了,并且没有敏感词
checkResult = {
"hasSensitiveWordList": false,
"result": {}
}
} else {
checkResult = {
"hasSensitiveWordList": true,
"result": checkResponse
}
}
checkResult["page"] = currentPageUrl;
checkResult["url"] = requestUrl;
checkResult["type"] = requestInfo.type;
// 保存到storage中
setNestedValue(newKeyValue, currentPageUrl + splitChar + requestUrl, checkResult, splitChar);
console.log("setNestedValue:", newKeyValue)
await chrome.storage.local.set({"key": newKeyValue});
}
);
}
以上便是核心的逻辑代码,然后还有很多优化点,我还需要慢慢修改,有兴趣的小伙伴们可以一起讨论哦
我贴上我自己的 repo 地址给大家
https://github.com/MRJC92/react-crx-2023