import util from 'util'; import cprog from 'child_process'; import { Plugin, Configurable, EventListener, DependencyLoad } from '@squeebot/core/lib/plugin'; import { IMessage, MessageResolver } from '@squeebot/core/lib/types'; import { ISqueebotCore, logger } from '@squeebot/core/lib/core'; function addCommands(plugin: UtilityPlugin, commands: any): void { const cmds = [{ plugin: plugin.manifest.name, name: 'evaljs', execute: async (msg: IMessage, msr: MessageResolver, spec: any, prefix: string, ...simplified: any[]): Promise => { if (!simplified[0]) { return true; } const script = simplified.join(' '); // Disallow child_process when shell is disallowed if ((script.indexOf('child_process') !== -1 || script.indexOf('cprog') !== -1 || script.indexOf('fork') !== -1) && !plugin.config.config.allowShell) { msg.resolve('Error: child_process is not allowed in evaljs due to security reasons.'); return true; } try { // tslint:disable-next-line: no-eval const mesh = eval(script); if (mesh === undefined) { return true; } msg.resolve(util.format(mesh)); } catch (e: any) { msg.resolve('Error: ' + e.message); } return true; }, usage: '', description: 'Execute JavaScript in a command context', permissions: ['system_execute'], hidden: true }]; if (plugin.config.config.allowShell) { logger.warn('WARNING! Shell command execution is enabled! Make absolutely sure that there is proper authentication!'); if (process.getuid && process.getuid() === 0) { logger.warn('NEVER run Squeebot as root! Run `useradd squeebot`! We are not responsible for possible security leaks!'); } cmds.push({ plugin: plugin.manifest.name, name: 'sh', execute: async (msg: IMessage, msr: MessageResolver, spec: any, prefix: string, ...simplified: any[]): Promise => { const stripnl = (simplified[0] !== '-n'); const cmd = simplified.slice(stripnl ? 0 : 1).join(' '); if (!cmd) { msg.resolve('Nothing to execute!'); return true; } cprog.exec(cmd, {shell: '/bin/bash'}, (error, stdout, stderr) => { if (stdout) { if (stripnl) { stdout = stdout.replace(/\n/g, ' ;; '); } return msg.resolve(stdout); } msg.resolve('Error executing command.'); logger.error(stderr || error); }); return true; }, description: 'Run raw shell command', usage: '', hidden: true, permissions: ['system_shell'], }); } commands.registerCommand(cmds); } @Configurable({ allowShell: false, }) class UtilityPlugin extends Plugin { public core: ISqueebotCore | null = null; @DependencyLoad('simplecommands') addCommands(cmd: any): void { addCommands(this, cmd); } @EventListener('pluginUnload') public unloadEventHandler(plugin: string | Plugin): void { if (plugin === this.name || plugin === this) { this.config.save().then(() => this.emit('pluginUnloaded', this)); } } initialize(): void { this.on('core', (c: ISqueebotCore) => this.core = c); this.emitTo('core', 'request-core', this.name); } } module.exports = UtilityPlugin;