我正在创建一个仅供个人使用的 Chrome 扩展程序。我需要它来监视对 https://www.instagram.com/api/graphql 发出的任何请求并记录响应(即将其转发到另一个执行此操作的脚本)。我对 Chrome 扩展程序开发非常陌生,所以请原谅我任何菜鸟错误。
目前看来我能够获取响应; console.log 还打印出响应良好。但是,我陷入了 CSP 错误(内容安全策略),并且它不会将收集到的响应转发到我的脚本(当前位于本地主机上)。
到目前为止,我所拥有的都是来自各地的复制粘贴内容的混合物,但我明白它的作用。我只是不明白我必须做什么才能让它做我想做的事情😊😊:
manifest.json
{
"manifest_version": 3,
"name": "BytE IG",
"description": "BytE Instagram checker",
"version": "1.3",
"content_security_policy": {
"extension_pages": "script-src 'self' http://localhost; object-src 'self' script-src-elem 'self'"
},
"background": {
"service_worker": "background.js"
},
"host_permissions": [
"*://*/*"
],
"permissions": [
"activeTab",
"tabs",
"webRequest"
],
"content_scripts": [
{
"matches": ["*://*.instagram.com/direct/*"],
"run_at": "document_start",
"js": ["inject.js"]
}
],
"web_accessible_resources": [
{
"resources": ["injected.js"],
"matches": ["*://*.instagram.com/*"]
}
]
}
背景.js
chrome.webRequest.onCompleted.addListener(
(details) => {
const url = new URL(details.url);
console.log('caught request' + url);
},
{
urls: "https://*.instagram.com/api/graphql*"
}
);
注入.js
var s = document.createElement('script');
s.src = chrome.runtime.getURL('injected.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
注入.js
(function(xhr) {
var XHR = XMLHttpRequest.prototype;
var open = XHR.open;
var send = XHR.send;
var setRequestHeader = XHR.setRequestHeader;
XHR.open = function(method, url) {
this._method = method;
this._url = url;
this._requestHeaders = {};
this._startTime = (new Date()).toISOString();
return open.apply(this, arguments);
};
XHR.setRequestHeader = function(header, value) {
this._requestHeaders[header] = value;
return setRequestHeader.apply(this, arguments);
};
XHR.send = function(postData) {
this.addEventListener('load', function() {
var endTime = (new Date()).toISOString();
var myUrl = this._url ? this._url.toLowerCase() : this._url;
if(myUrl) {
if (postData) {
if (typeof postData === 'string') {
try {
// here you get the REQUEST HEADERS, in JSON format, so you can also use JSON.parse
this._requestHeaders = postData;
} catch(err) {
console.log('Request Header JSON decode failed, transfer_encoding field could be base64');
console.log(err);
}
} else if (typeof postData === 'object' || typeof postData === 'array' || typeof postData === 'number' || typeof postData === 'boolean') {
// do something if you need
}
}
// here you get the RESPONSE HEADERS
var responseHeaders = this.getAllResponseHeaders();
if ( this.responseType != 'blob' && this.responseText) {
// responseText is string or null
try {
// here you get RESPONSE TEXT (BODY), in JSON format, so you can use JSON.parse
var arr = this.responseText;
// printing url, request headers, response headers, response body, to console
// building your form values
var data = new URLSearchParams();
data.set('ig', arr);
// send to the endpoint
fetch("http://localhost:1608/ig.php", {
method: 'POST',
mode: 'no-cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: data
}).then(function(response) {
// check the response object for result
// ...
});
} catch(err) {
console.log("Error in responseType try catch");
console.log(err);
}
}
}
});
return send.apply(this, arguments);
};
})(XMLHttpRequest);
我没有得到一个漂亮的小 JSON 到 ig.php,而是得到:
injected.js:62 拒绝连接到“http://localhost:1608/ig.php”,因为它违反了文档的内容安全策略。 (匿名)@injected.js:62 Injected.js:62 拒绝连接到“http://localhost:1608/ig.php”,因为它违反了文档的内容安全策略。 (匿名)@injected.js:62 Injected.js:62 拒绝连接到“http://localhost:1608/ig.php”,因为它违反了文档的内容安全策略。 (匿名)@injected.js:62 Injected.js:62 拒绝连接到“http://localhost:1608/ig.php”,因为它违反了文档的内容安全策略。 (匿名)@injected.js:62 Injected.js:62 拒绝连接到“http://localhost:1608/ig.php”,因为它违反了文档的内容安全策略。
编辑:
我还尝试使用 FQDN 而不是 localhost,并使用 SSL 证书。没有运气。
我把这件事搁置了一段时间,然后又回来了。感谢 wOxxOm 的评论 我设法使其可用,并将其转换为 v3。
这超出了我的专业领域(系统管理、DevOps),所以它肯定有很多缺陷,但我只需要它在我的机器上工作。它可能拥有不需要的权限,但这只是我需要尽快进行的快速集成。
这是最终起作用的新代码:
manifest.json:
{
"manifest_version": 3,
"name": "BytE IG",
"description": "BytE Instagram checker",
"version": "2.0",
"background": {
"service_worker": "background.js"
},
"host_permissions": [
"*://*/*"
],
"permissions": [
"activeTab",
"tabs",
"webRequest"
],
"externally_connectable": {
"matches": [
"*://*.instagram.com/*"
]
},
"content_scripts": [
{
"matches": [
"*://*.instagram.com/direct/*"
],
"run_at": "document_start",
"js": [
"inject.js"
]
}
],
"web_accessible_resources": [
{
"resources": ["injected.js"],
"matches": ["*://*.instagram.com/*"]
}
]
}
注入.js
var s = document.createElement('script');
s.src = chrome.runtime.getURL('injected.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);json
注入.js
(function (xhr) {
var XHR = XMLHttpRequest.prototype;
var open = XHR.open;
var send = XHR.send;
var setRequestHeader = XHR.setRequestHeader;
XHR.open = function (method, url) {
this._method = method;
this._url = url;
this._requestHeaders = {};
return open.apply(this, arguments);
};
XHR.setRequestHeader = function (header, value) {
this._requestHeaders[header] = value;
return setRequestHeader.apply(this, arguments);
};
XHR.send = function (postData) {
this.addEventListener('load', function () {
var extensionId = "your-extension-id";
var myUrl = this._url ? this._url.toLowerCase() : this._url;
if (myUrl) {
if (postData) {
if (typeof postData === 'string') {
try {
this._requestHeaders = postData;
} catch (err) {
console.log(err);
}
}
}
var responseHeaders = this.getAllResponseHeaders();
if (this.responseType != 'blob' && this.responseText) {
// responseText is string or null
try {
var arr = this.responseText;
chrome.runtime.sendMessage(extensionId, {arr: arr});
} catch (err) {
console.log(err);
}
}
}
});
return send.apply(this, arguments);
};
})(XMLHttpRequest);
background.js(服务工作者)
chrome.runtime.onMessageExternal.addListener(
function (request, sender, sendResponse) {
const url = new URL(sender.url);
console.log('caught request' + url);
var data = new URLSearchParams();
data.set('ig', request.arr);
// send to the endpoint
fetch("http://localhost:1609/ig.php", {
method: 'POST',
mode: 'no-cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: data
}).then(function (response) {
// check the response object for result
// ...
});
});
inject.js ... 将 ... injected.js 注入到页面上。但是,使用 CSP,您无法直接与网站交互,除非网站所有者批准的网站。我试图绕过这个问题,因为我只需要它在我的浏览器上工作,但没有运气。
但是,您可以从批准的网站向您自己的分机发送消息,然后网站可以将它们发送到需要的地方。
很好。 :)