From 6a978aa025752f366050a911446363dea61d0c86 Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 17 May 2021 20:24:00 +0100 Subject: [PATCH] Settings validation --- jsconfig.json | 5 ++ package.json | 1 + pnpm-lock.yaml | 6 ++ src/commands/settings.js | 113 ++++++++++++++++++++++++++++++++- src/locales/en-GB.json | 1 + src/modules/plugins/manager.js | 2 +- 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 jsconfig.json diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..2670155 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "include": [ + "src/**/*.js" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 9e5feb3..63510b7 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "cryptr": "^6.0.2", "discord.js": "^12.5.1", "dotenv": "^8.2.0", + "jsonschema": "^1.4.0", "keyv": "^4.0.3", "leeks.js": "^0.2.2", "leekslazylogger-fastify": "^0.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a47cce9..2652f22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,7 @@ specifiers: discord.js: ^12.5.1 dotenv: ^8.2.0 eslint: ^7.21.0 + jsonschema: ^1.4.0 keyv: ^4.0.3 leeks.js: ^0.2.2 leekslazylogger-fastify: ^0.1.1 @@ -33,6 +34,7 @@ dependencies: cryptr: 6.0.2 discord.js: 12.5.1 dotenv: 8.2.0 + jsonschema: 1.4.0 keyv: 4.0.3 leeks.js: 0.2.2 leekslazylogger-fastify: 0.1.1 @@ -1422,6 +1424,10 @@ packages: /json-stringify-safe/5.0.1: resolution: {integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=} + /jsonschema/1.4.0: + resolution: {integrity: sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==} + dev: false + /jsprim/1.4.1: resolution: {integrity: sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=} engines: {'0': node >=0.6.0} diff --git a/src/commands/settings.js b/src/commands/settings.js index 156bdbc..f3f11a4 100644 --- a/src/commands/settings.js +++ b/src/commands/settings.js @@ -1,6 +1,7 @@ const Command = require('../modules/commands/command'); const fetch = require('node-fetch'); const { MessageAttachment } = require('discord.js'); +const { Validator } = require('jsonschema'); module.exports = class SettingsCommand extends Command { constructor(client) { @@ -16,6 +17,108 @@ module.exports = class SettingsCommand extends Command { args: [], permissions: ['MANAGE_GUILD'] }); + + this.schema = { + type: 'object', + properties: { + categories: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string' + }, + claiming: { + type: 'boolean' + }, + image: { + type: ['string', 'null'] + }, + max_per_member: { + type: 'number' + }, + name: { + type: 'string' + }, + name_format: { + type: 'string' + }, + opening_message: { + type: 'string' + }, + opening_questions: { + type: ['array', 'null'], + items: { + type: 'string' + } + }, + ping: { + type: ['array', 'null'], + items: { + type: 'string' + } + }, + require_topic: { + type: 'boolean' + }, + roles: { + type: 'array', + items: { + type: 'string' + } + }, + survey: { + type: ['string', 'null'] + } + }, + required: [ + 'name', + 'name_format', + 'opening_message', + 'roles' + ] + } + }, + colour: { + type: 'string' + }, + command_prefix: { + type: 'string' + }, + error_colour: { + type: 'string' + }, + footer: { + type: 'string' + }, + locale: { + type: 'string' + }, + log_messages: { + type: 'boolean' + }, + success_colour: { + type: 'string' + }, + surveys: { + type: 'object' + } + }, + required: [ + 'categories', + 'colour', + 'command_prefix', + 'error_colour', + 'footer', + 'locale', + 'log_messages', + 'success_colour', + 'surveys' + ] + }; + + this.v = new Validator(); } async execute(message) { @@ -29,6 +132,14 @@ module.exports = class SettingsCommand extends Command { // load settings from json this.client.log.info(`Downloading settings for "${message.guild.name}"`); let data = await (await fetch(attachments[0].url)).json(); + + const { valid, errors } = this.v.validate(data, this.schema); + + if (!valid) { + this.client.log.warn('Settings validation error'); + return await message.channel.send(i18n('commands.settings.response.invalid', errors.map(error => `\`${error.stack}\``).join(',\n'))); + } + settings.colour = data.colour; settings.command_prefix = data.command_prefix; settings.error_colour = data.error_colour; @@ -137,7 +248,7 @@ module.exports = class SettingsCommand extends Command { } this.client.log.success(`Updated guild settings for "${message.guild.name}"`); - message.channel.send(i18n('commands.settings.response.updated')); + return await message.channel.send(i18n('commands.settings.response.updated')); } else { diff --git a/src/locales/en-GB.json b/src/locales/en-GB.json index 1c80250..b608d42 100644 --- a/src/locales/en-GB.json +++ b/src/locales/en-GB.json @@ -323,6 +323,7 @@ "description": "Configure Discord Tickets", "name": "settings", "response": { + "invalid": "❌ Settings data is invalid; please refer to the documentation.\n%s", "updated": "✅ Settings have been updated." } }, diff --git a/src/modules/plugins/manager.js b/src/modules/plugins/manager.js index d53a433..ab2953d 100644 --- a/src/modules/plugins/manager.js +++ b/src/modules/plugins/manager.js @@ -1,5 +1,5 @@ // eslint-disable-next-line no-unused-vars -const { Collection, Client } = require('discord.js'); +const { Collection } = require('discord.js'); // eslint-disable-next-line no-unused-vars const Plugin = require('./plugin');