mirror of
https://github.com/hack-chat/main.git
synced 2024-03-22 13:20:33 +08:00
stabilized modules and server cmd field
This commit is contained in:
parent
584813fb23
commit
7d8220d838
|
@ -79,4 +79,6 @@ This project is licensed under the WTFPL License - see the [http://www.wtfpl.net
|
|||
|
||||
## Acknowledgments
|
||||
|
||||
* Andrew Belt [https://github.com/AndrewBelt/hack.chat](https://github.com/AndrewBelt/hack.chat)
|
||||
* Andrew Belt, [https://github.com/AndrewBelt/hack.chat](https://github.com/AndrewBelt/hack.chat), for original base work
|
||||
|
||||
* wwAndrew [https://github.com/sendMeYourGitOrSomething](https://youtu.be/oHg5SJYRHA0), for finding server flaws including attack vectors
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
"version": "2.0.0",
|
||||
"description": "a minimal distraction free chat application",
|
||||
"main": "main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/hack-chat/main.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0",
|
||||
"npm": ">= 5.7.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js",
|
||||
"config": "node src/scripts/configure.js",
|
||||
"debug": "node src/scripts/debug.js",
|
||||
"dev": "node src/scripts/debug.js"
|
||||
|
|
|
@ -36,6 +36,5 @@ exports.run = async (core, server, socket, data) => {
|
|||
|
||||
exports.info = {
|
||||
name: 'listusers',
|
||||
usage: 'listusers',
|
||||
description: 'Outputs all current channels and sockets in those channels'
|
||||
};
|
||||
|
|
|
@ -13,8 +13,9 @@ exports.run = async (core, server, socket, data) => {
|
|||
let loadResult = core.managers.dynamicImports.reloadDirCache('src/commands');
|
||||
loadResult += core.commands.loadCommands();
|
||||
|
||||
if (loadResult == '')
|
||||
if (loadResult == '') {
|
||||
loadResult = 'Commands reloaded without errors!';
|
||||
}
|
||||
|
||||
server.reply({
|
||||
cmd: 'info',
|
||||
|
@ -29,6 +30,5 @@ exports.run = async (core, server, socket, data) => {
|
|||
|
||||
exports.info = {
|
||||
name: 'reload',
|
||||
usage: 'reload',
|
||||
description: '(Re)loads any new commands into memory, outputs errors if any'
|
||||
};
|
||||
|
|
|
@ -21,6 +21,11 @@ exports.run = async (core, server, socket, data) => {
|
|||
return;
|
||||
}
|
||||
|
||||
server.reply({
|
||||
cmd: 'info',
|
||||
text: 'Config saved!'
|
||||
}, socket);
|
||||
|
||||
server.broadcast({
|
||||
cmd: 'info',
|
||||
text: 'Config saved!'
|
||||
|
@ -29,6 +34,5 @@ exports.run = async (core, server, socket, data) => {
|
|||
|
||||
exports.info = {
|
||||
name: 'saveconfig',
|
||||
usage: 'saveconfig',
|
||||
description: 'Saves current config'
|
||||
};
|
||||
|
|
|
@ -4,15 +4,23 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
exports.run = async (core, server, socket, data) => {
|
||||
// process text
|
||||
let text = String(data.text);
|
||||
function parseText(text) {
|
||||
if (typeof text !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// strip newlines from beginning and end
|
||||
text = text.replace(/^\s*\n|^\s+$|\n\s*$/g, '');
|
||||
// replace 3+ newlines with just 2 newlines
|
||||
text = text.replace(/\n{3,}/g, "\n\n");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
exports.run = async (core, server, socket, data) => {
|
||||
let text = parseText(data.text);
|
||||
if (!text) {
|
||||
// lets not send empty text?
|
||||
// lets not send objects or empty text, yea?
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,19 +4,38 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
exports.run = async (core, server, socket, data) => {
|
||||
let reply = `Help usage: { cmd: 'help', type: 'categories'} or { cmd: 'help', type: 'commandname'}`;
|
||||
const stripIndents = require('common-tags').stripIndents;
|
||||
|
||||
if (typeof data.type === 'undefined') {
|
||||
//
|
||||
} else {
|
||||
if (data.type == 'categories') {
|
||||
let categories = core.commands.categories();
|
||||
// TODO: bad output, fix this
|
||||
reply = `Command Categories:\n${categories}`;
|
||||
} else {
|
||||
// TODO: finish this module later
|
||||
}
|
||||
exports.run = async (core, server, socket, data) => {
|
||||
// verify passed arguments
|
||||
let typeDt = typeof data.type;
|
||||
let catDt = typeof data.category;
|
||||
let cmdDt = typeof data.command;
|
||||
if (typeDt !== 'undefined' && typeDt !== 'string' ) {
|
||||
return;
|
||||
} else if (catDt !== 'undefined' && catDt !== 'string' ) {
|
||||
return;
|
||||
} else if (cmdDt !== 'undefined' && cmdDt !== 'string' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set default reply
|
||||
let reply = stripIndents`Help usage:
|
||||
Show all categories -> { cmd: 'help', type: 'categories' }
|
||||
Show all commands in category -> { cmd: 'help', category: '<category name>' }
|
||||
Show specific command -> { cmd: 'help', command: '<command name>' }`;
|
||||
|
||||
if (typeDt !== 'undefined') {
|
||||
let categories = core.commands.categories().sort();
|
||||
reply = `Command Categories:\n${categories.map(c => `- ${c.replace('../src/commands/', '')}`).join('\n')}`;
|
||||
} else if (catDt !== 'undefined') {
|
||||
let catCommands = core.commands.all('../src/commands/' + data.category).sort((a, b) => a.info.name.localeCompare(b.info.name));
|
||||
reply = `${data.category} commands:\n${catCommands.map(c => `- ${c.info.name}`).join('\n')}`;
|
||||
} else if (cmdDt !== 'undefined') {
|
||||
let command = core.commands.get(data.command);
|
||||
reply = stripIndents`
|
||||
Usage: ${command.info.usage || command.info.name}
|
||||
Description: ${command.info.description || '¯\_(ツ)_/¯'}`;
|
||||
}
|
||||
|
||||
server.reply({
|
||||
|
@ -28,6 +47,6 @@ exports.run = async (core, server, socket, data) => {
|
|||
// optional parameters are marked, all others are required
|
||||
exports.info = {
|
||||
name: 'help', // actual command name
|
||||
usage: 'help ([type:categories] | [type:command])',
|
||||
usage: 'help ([ type:categories] | [category:<category name> | command:<command name> ])',
|
||||
description: 'Outputs information about the servers current protocol'
|
||||
};
|
||||
|
|
|
@ -9,14 +9,16 @@ function verifyNickname(nick) {
|
|||
}
|
||||
|
||||
exports.run = async (core, server, socket, data) => {
|
||||
let targetNick = String(data.nick);
|
||||
if (typeof data.nick !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!verifyNickname(targetNick)) {
|
||||
if (!verifyNickname(data.nick)) {
|
||||
// Not a valid nickname? Chances are we won't find them
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetNick == socket.nick) {
|
||||
if (data.nick == socket.nick) {
|
||||
// TODO: reply with something witty? They invited themself
|
||||
return;
|
||||
}
|
||||
|
@ -36,7 +38,7 @@ exports.run = async (core, server, socket, data) => {
|
|||
cmd: 'info',
|
||||
text: `${socket.nick} invited you to ?${channel}`
|
||||
};
|
||||
let inviteSent = server.broadcast( payload, { channel: socket.channel, nick: targetNick });
|
||||
let inviteSent = server.broadcast( payload, { channel: socket.channel, nick: data.nick });
|
||||
|
||||
if (!inviteSent) {
|
||||
server.reply({
|
||||
|
@ -49,7 +51,7 @@ exports.run = async (core, server, socket, data) => {
|
|||
|
||||
server.reply({
|
||||
cmd: 'info',
|
||||
text: `You invited ${targetNick} to ?${channel}`
|
||||
text: `You invited ${data.nick} to ?${channel}`
|
||||
}, socket);
|
||||
|
||||
core.managers.stats.increment('invites-sent');
|
||||
|
|
|
@ -32,14 +32,18 @@ exports.run = async (core, server, socket, data) => {
|
|||
return;
|
||||
}
|
||||
|
||||
let channel = String(data.channel).trim();
|
||||
if (typeof data.channel !== 'string' || typeof data.nick !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
let channel = data.channel.trim();
|
||||
if (!channel) {
|
||||
// Must join a non-blank channel
|
||||
return;
|
||||
}
|
||||
|
||||
// Process nickname
|
||||
let nick = String(data.nick);
|
||||
let nick = data.nick;
|
||||
let nickArray = nick.split('#', 2);
|
||||
nick = nickArray[0].trim();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ const createReply = (echoInput) => {
|
|||
|
||||
// `exports.run()` is required and will always be passed (core, server, socket, data)
|
||||
// be sure it's asyn too
|
||||
// this is the main function
|
||||
exports.run = async (core, server, socket, data) => {
|
||||
|
||||
server.reply({
|
||||
|
@ -35,12 +36,13 @@ exports.init = (core) => {
|
|||
}
|
||||
|
||||
// optional, if `data.echo` is missing `exports.run()` will never be called & the user will be alerted
|
||||
// remember; this will only verify that the data is not undefined, not the type of data
|
||||
exports.requiredData = ['echo'];
|
||||
|
||||
// optional parameters are marked, all others are required
|
||||
exports.info = {
|
||||
name: 'showcase', // actual command name
|
||||
aliases: ['templateModule'], // optional, an array of other names this module can be executed by
|
||||
usage: 'showcase {echo}', // used for help output
|
||||
usage: 'showcase {echo}', // used for help output, can be ommited if no parameters are required
|
||||
description: 'Simple command module template & info' // used for help output
|
||||
};
|
||||
|
|
|
@ -50,6 +50,5 @@ exports.run = async (core, server, socket, data) => {
|
|||
|
||||
exports.info = {
|
||||
name: 'stats',
|
||||
usage: 'stats',
|
||||
description: 'Sends back current server stats to the calling client'
|
||||
};
|
||||
|
|
|
@ -10,7 +10,11 @@ exports.run = async (core, server, socket, data) => {
|
|||
return;
|
||||
}
|
||||
|
||||
let targetNick = String(data.nick);
|
||||
if (typeof data.nick !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetNick = data.nick;
|
||||
let badClient = null;
|
||||
for (let client of server.clients) {
|
||||
// Find badClient's socket
|
||||
|
@ -38,11 +42,12 @@ exports.run = async (core, server, socket, data) => {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: ratelimiting here
|
||||
// TODO: add reference to banned users nick or unban by nick cmd
|
||||
//POLICE.arrest(getAddress(badClient))
|
||||
server._police.arrest(badClient.remoteAddress);
|
||||
// TODO: add event to log?
|
||||
|
||||
console.log(`${socket.nick} [${socket.trip}] banned ${targetNick} in ${socket.channel}`);
|
||||
|
||||
server.broadcast({
|
||||
cmd: 'info',
|
||||
text: `Banned ${targetNick}`
|
||||
|
|
|
@ -10,7 +10,11 @@ exports.run = async (core, server, socket, data) => {
|
|||
return;
|
||||
}
|
||||
|
||||
let targetNick = String(data.nick);
|
||||
if (typeof data.nick !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetNick = data.nick;
|
||||
let badClient = null;
|
||||
for (let client of server.clients) {
|
||||
// Find badClient's socket
|
||||
|
|
|
@ -10,11 +10,15 @@ exports.run = async (core, server, socket, data) => {
|
|||
return;
|
||||
}
|
||||
|
||||
let ip = String(data.ip);
|
||||
let nick = String(data.nick); // for future upgrade
|
||||
if (typeof data.ip !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: remove ip from ratelimiter
|
||||
// POLICE.pardon(ip)
|
||||
let ip = data.ip;
|
||||
let nick = data.nick; // for future upgrade
|
||||
|
||||
// TODO: support remove by nick future upgrade
|
||||
server._police.pardon(badClient.remoteAddress);
|
||||
console.log(`${socket.nick} [${socket.trip}] unbanned ${/*nick || */ip} in ${socket.channel}`);
|
||||
|
||||
server.reply({
|
||||
|
|
|
@ -63,7 +63,6 @@ class server extends wsServer {
|
|||
* @param {String} data Message sent from client
|
||||
*/
|
||||
handleData (socket, data) {
|
||||
// TODO: Rate limit here
|
||||
// Don't penalize yet, but check whether IP is rate-limited
|
||||
if (this._police.frisk(socket.remoteAddress, 0)) {
|
||||
this.reply({ cmd: 'warn', text: "Your IP is being rate-limited or blocked." }, socket);
|
||||
|
@ -87,18 +86,23 @@ class server extends wsServer {
|
|||
socket.close();
|
||||
}
|
||||
|
||||
if (args === null)
|
||||
if (args === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof args.cmd === 'undefined' || args.cmd == 'ping')
|
||||
if (typeof args.cmd === 'undefined' || args.cmd == 'ping') {
|
||||
return;
|
||||
}
|
||||
|
||||
var cmd = args.cmd;
|
||||
|
||||
if (typeof socket.channel === 'undefined' && cmd !== 'join')
|
||||
if (typeof args.cmd !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this._cmdBlacklist[cmd] === 'function') {
|
||||
if (typeof socket.channel === 'undefined' && args.cmd !== 'join') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this._cmdBlacklist[args.cmd] === 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user