Reputation: 291
I am writing a Firefox extension that needs to listen to communication between browser and server. For HTTP communication, I use the webRequest library in a background script. But according to this Mozilla bug report, I cannot use webRequest to intercept WebSocket messages. How can my extension listen in on WebSocket communication?
Update:
I've injected my WebSocket wrapper, but it's never called! The WebSocket I am trying to listen to is inside an iframe. Does that matter?
Upvotes: 3
Views: 4493
Reputation: 87
I don't know if you still need this, but I got it to work by editing something from trexinf14s's answer.
1, Add syringe.js to your manifest.json, "run_at": "document_end" if you want this extension run after webpage is fully loaded, change "web_accessible_resources" to new manifest format.
"content_scripts": [{
"css": ["styles.css"],
"run_at": "document_end",
"all_frames": true,
"js": ["lib/jquery-3.6.0.min.js","content/jquerycontent.js","content/syringe.js"],
"matches": ["https://example.com/*"]
}],
"web_accessible_resources": [{
"resources": ["lib/socket-sniffer.js"],
"matches": ["<all_urls>"]
}]
2, inject "s" variable at syringe.js (not at socket-sniffer.js), the result is:
var s = document.createElement('script');
s.src = chrome.runtime.getURL('lib/socket-sniffer.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
(function () {
var OrigWebSocket = window.WebSocket;
var callWebSocket = OrigWebSocket.apply.bind(OrigWebSocket);
var wsAddListener = OrigWebSocket.prototype.addEventListener;
wsAddListener = wsAddListener.call.bind(wsAddListener);
window.WebSocket = function WebSocket(url, protocols) {
var ws;
if (!(this instanceof WebSocket)) {
// Called without 'new' (browsers will throw an error).
ws = callWebSocket(this, arguments);
} else if (arguments.length === 1) {
ws = new OrigWebSocket(url);
} else if (arguments.length >= 2) {
ws = new OrigWebSocket(url, protocols);
} else { // No arguments (browsers will throw an error)
ws = new OrigWebSocket();
}
wsAddListener(ws, 'message', function (event) {
console.log("Received:", event);
});
return ws;
}.bind();
window.WebSocket.prototype = OrigWebSocket.prototype;
window.WebSocket.prototype.constructor = window.WebSocket;
var wsSend = OrigWebSocket.prototype.send;
wsSend = wsSend.apply.bind(wsSend);
OrigWebSocket.prototype.send = function (data) {
//console.log("Sent:", data);
return wsSend(this, arguments);
};
})();
console.log("nothing to show");
Upvotes: 0
Reputation: 291
The only way to listen to WebSocket communication is to inject a WebSocket wrapper into the site's code and have it relay the messages to you. You're code should look something like this:
manifest.json
{
...
"content_scripts": [
{
"matches": [
"<website-url>",
],
"js": ["content/syringe.js"]
}
],
"web_accessible_resources": ["lib/socket-sniffer.js"]
}
content/syringe.js
var s = document.createElement('script');
s.src = browser.runtime.getURL('lib/socket-sniffer.js');
s.onload = function() {
this.remove();
};
lib/socket-sniffer.js
(function() {
var OrigWebSocket = window.WebSocket;
var callWebSocket = OrigWebSocket.apply.bind(OrigWebSocket);
var wsAddListener = OrigWebSocket.prototype.addEventListener;
wsAddListener = wsAddListener.call.bind(wsAddListener);
window.WebSocket = function WebSocket(url, protocols) {
var ws;
if (!(this instanceof WebSocket)) {
// Called without 'new' (browsers will throw an error).
ws = callWebSocket(this, arguments);
} else if (arguments.length === 1) {
ws = new OrigWebSocket(url);
} else if (arguments.length >= 2) {
ws = new OrigWebSocket(url, protocols);
} else { // No arguments (browsers will throw an error)
ws = new OrigWebSocket();
}
wsAddListener(ws, 'message', function(event) {
console.log("Received:", event);
});
return ws;
}.bind();
window.WebSocket.prototype = OrigWebSocket.prototype;
window.WebSocket.prototype.constructor = window.WebSocket;
var wsSend = OrigWebSocket.prototype.send;
wsSend = wsSend.apply.bind(wsSend);
OrigWebSocket.prototype.send = function(data) {
console.log("Sent:", data);
return wsSend(this, arguments);
};
})();
(document.head || document.documentElement).appendChild(s);
All credit for the above code obviously goes to the posters linked above. I have only copied tt here for convenience' sake.
Update:
Yes, it does matter that the WebSocket is within an iframe! By default, extensions are only loaded in the top-most frame. For it to load into the iframe, you must add "all_frames": true
to your manifest.json
:
manifest.json
{
...
"content_scripts": [
{
"matches": [
"<website-url>",
],
"all_frames": true,
"js": ["content/syringe.js"]
}
],
"web_accessible_resources": ["lib/socket-sniffer.js"]
}
If you'd like to read more, here's the documentation for all_frames
.
Upvotes: 7