diff --git a/package.json b/package.json index f6707f3..d7ea6c5 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "eris-pagination": "git+https://github.com/bsian03/eris-pagination#c0f77b118e98309e89e6522ef545f9d121601f21", "express": "^4.17.1", "fs-extra": "^8.1.0", + "hastebin-gen": "^2.0.5", "helmet": "^3.21.2", "ioredis": "^4.14.1", "jsonwebtoken": "^8.5.1", @@ -26,8 +27,7 @@ "mongoose": "^5.7.4", "nodemailer": "^6.3.1", "signale": "^1.4.0", - "uuid": "^3.3.3", - "x509": "bsian03/node-x509" + "uuid": "^3.3.3" }, "devDependencies": { "@types/express": "^4.17.6", diff --git a/src/class/Util.ts b/src/class/Util.ts index d732137..45be5fe 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-unresolved */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-param-reassign */ import axios from 'axios'; @@ -8,9 +9,11 @@ import { Message, PrivateChannel, GroupChannel, Member, User } from 'eris'; import uuid from 'uuid/v4'; import moment from 'moment'; import fs from 'fs'; +import hastebin from 'hastebin-gen'; import { getUserByUid } from '../functions'; import { AccountUtil, Client, Command, RichEmbed } from '.'; import { ModerationInterface, AccountInterface, Account } from '../models'; +import { Certificate } from '../../types/x509'; export default class Util { public client: Client; @@ -310,4 +313,15 @@ export default class Util { return null; } } + + public parseCertificate(pem: string) { + return axios.post('https://certapi.libraryofcode.org/parse', pem); + } + + public upload(text: string, extension = 'txt') { + return hastebin(text, { + url: 'https://snippets.cloud.libraryofcode.org', + extension, + }); + } } diff --git a/src/commands/cwg_data.ts b/src/commands/cwg_data.ts index ab3a514..0668b6c 100644 --- a/src/commands/cwg_data.ts +++ b/src/commands/cwg_data.ts @@ -1,6 +1,5 @@ import fs from 'fs'; import moment from 'moment'; -import x509 from 'x509'; import { createPaginationEmbed } from 'eris-pagination'; import { Message } from 'eris'; import { Client, Command, RichEmbed } from '../class'; @@ -30,21 +29,22 @@ export default class CWG_Data extends Command { } return this.error(message.channel, 'The domain or port you provided could not be found.'); } - const embeds = dom.map((domain) => { - const cert = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' }); + const embeds = await Promise.all(dom.map(async (domain) => { + const pem = fs.readFileSync(domain.x509.cert, { encoding: 'utf8' }); + const cert = await this.client.util.parseCertificate(pem); const embed = new RichEmbed(); embed.setTitle('Domain Information'); embed.addField('Account Username', domain.account.username, true); embed.addField('Account ID', domain.account.userID, true); embed.addField('Domain', domain.domain, true); embed.addField('Port', String(domain.port), true); - embed.addField('Certificate Issuer', x509.getIssuer(cert).organizationName, true); - embed.addField('Certificate Subject', x509.getSubject(cert).commonName, true); - embed.addField('Certificate Expiration Date', moment(x509.parseCert(cert).notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); + embed.addField('Certificate Issuer', cert.data.issuer.organization[0], true); + embed.addField('Certificate Subject', cert.data.issuer.commonName, true); + embed.addField('Certificate Expiration Date', moment(cert.data.notAfter).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); embed.setFooter(this.client.user.username, this.client.user.avatarURL); embed.setTimestamp(); return embed; - }); + })); this.client.signale.log(embeds); if (embeds.length === 1) return message.channel.createMessage({ embed: embeds[0] }); return createPaginationEmbed(message, embeds); diff --git a/src/commands/modlogs.ts b/src/commands/modlogs.ts index 1f80e1d..b54d1d2 100644 --- a/src/commands/modlogs.ts +++ b/src/commands/modlogs.ts @@ -19,8 +19,8 @@ export default class Modlogs extends Command { const query = await this.client.db.Moderation.find({ $or: [{ username: args.join(' ') }, { userID: args[0] }] }); if (!query.length) return msg.edit(`***${this.client.stores.emojis.error} Cannot locate modlogs for ${args.join(' ')}***`); - const formatted = query.sort((a, b) => a.date.getTime() - b.date.getTime()).map((log) => { - const { username, moderatorID, reason, type, date, logID } = log; + const formatted = await Promise.all(query.sort((a, b) => a.date.getTime() - b.date.getTime()).map(async (log) => { + const { username, moderatorID, type, date, reason, logID } = log; let name: string; switch (type) { default: name = 'Generic'; break; @@ -30,10 +30,11 @@ export default class Modlogs extends Command { case 3: name = 'Unlock'; break; case 4: name = 'Delete'; break; } - const value = `**ID:** ${logID}\n**Account name:** ${username}\n**Moderator:** <@${moderatorID}>\n**Reason:** ${reason || 'Not supplied'}\n**Date:** ${date.toLocaleString('en-us')} EST`; + let value = `**ID:** ${logID}\n**Account name:** ${username}\n**Moderator:** <@${moderatorID}>\n**Reason:** ${reason || 'Not supplied'}\n**Date:** ${date.toLocaleString('en-us')} EST`; + if (value.length > 1024) value = value.replace(reason, await this.client.util.upload(reason)); const inline = true; return { name, value, inline }; - }); + })); const users = [...new Set(query.map((log) => log.userID))].map((u) => `<@${u}>`); const logs = this.client.util.splitFields(formatted); diff --git a/tsconfig.json b/tsconfig.json index febdc18..7d691ff 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,7 +44,7 @@ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ + "typeRoots": ["./types"], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ diff --git a/types/x509.d.ts b/types/x509.d.ts index 3327af0..5df2035 100644 --- a/types/x509.d.ts +++ b/types/x509.d.ts @@ -1,54 +1,51 @@ -declare module 'x509' { - namespace Certificate { - interface Issuer { - countryName: string, - stateOrProvinceName: string, - localityName: string, - organizationName: string, - organizationalUnitName: string, - commonName: string, - emailAddress: string - } - interface Subject { - countryName: string, - postalCode: string, - stateOrProvinceName: string, - localityName: string, - streetAddress: string, - organizationName: string, - organizationalUnitName: string, - commonName: string, - emailAddress: string - } - interface Extensions { - keyUsage: string, - authorityInformationAccess: string, - certificatePolicies: string, - basicConstraints: string, - cRLDistributionPoints: string, - subjectAlternativeName: string, - extendedKeyUsage: string, - authorityKeyIdentifier: string, - subjectKeyIdentifier: string, - cTPrecertificateSCTs: string - } - } - interface FullCertificate { - version: number, - subject: Certificate.Subject, - issuer: Certificate.Issuer, - fingerPrint: string, - serial: string, - notBefore: Date, - notAfter: Date, - subjectHash: string, - signatureAlgorithm: string, - publicKey: { algorithm: string }; - altNames: string[] - extensions: Certificate.Extensions - } - function getAltNames(cert: string): string[]; - function getIssuer(cert: string): Certificate.Issuer; - function getSubject(cert: string): Certificate.Subject; - function parseCert(cert: string): FullCertificate +export interface Certificate { + status: true | false, + subject: { + commonName: string, + organization: string[], + organizationalUnit: string[], + locality: string[], + country: string[], + }, + issuer: { + commonName: string, + organization: string[], + organizationalUnit: string[], + locality: string[], + country: string[], + }, + aia: { + issuingCertificateURL: string, + ocspServer: string, + }, + validationType: 'DV' | 'OV' | 'EV', + signatureAlgorithm: string, + publicKeyAlgorithm: string, + serialNumber: number, + notAfter: Date, + /** + - 0: KeyUsageCRLSign + - 1: KeyUsageCertificateSign + - 2: KeyUsageContentCommitment + - 3: KeyUsageDataEncipherment + - 4: KeyUsageDecipherOnly + - 5: KeyUsageDigitalSignature + - 6: KeyUsageEncipherOnly + - 7: KeyUsageKeyAgreement + - 8: KeyUsageKeyEncipherment + */ + keyUsage: number[], + keyUsageAsText: ['CRL Signing', 'Certificate Signing', 'Content Commitment', 'Data Encipherment', 'Decipher Only', 'Digital Signature', 'Encipher Only', 'Key Agreement', 'Key Encipherment'], + /** + - 0: Any/All Usage + - 1: TLS Web Server Auth + - 2: TLS Web Client Auth + - 3: Code Signing + - 4: Email Protection (S/MIME) + */ + extendedKeyUsage: number[], + extendedKeyUsageAsText: ['All/Any Usages', 'TLS Web Server Authentication', 'TLS Web Client Authentication', 'Code Signing', 'E-mail Protection (S/MIME)'], + san: string, + emailAddresses: string, + fingerprint: string, } diff --git a/yarn.lock b/yarn.lock index 04b587c..8ea7cd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1324,6 +1324,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hastebin-gen@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/hastebin-gen/-/hastebin-gen-2.0.5.tgz#8f4f11d5c4890280e2dbd34217e6ff06d053fe68" + integrity sha512-At1LaKtcqh2jiP8xfE2sDGT9IshIki6FqsgLwn2y7FzAvlFJRtpUsSPh3yWjWIQIvxi/GPF07IBqSI8WhPL/gQ== + dependencies: + node-fetch "^2.6.0" + helmet-crossdomain@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz#5f1fe5a836d0325f1da0a78eaa5fd8429078894e" @@ -1965,11 +1972,6 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@2.14.1: - version "2.14.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" - integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -1990,6 +1992,11 @@ nocache@2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +node-fetch@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-source-walk@^4.0.0, node-source-walk@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.2.0.tgz#c2efe731ea8ba9c03c562aa0a9d984e54f27bc2c" @@ -2978,9 +2985,3 @@ x-xss-protection@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/x-xss-protection/-/x-xss-protection-1.3.0.tgz#3e3a8dd638da80421b0e9fff11a2dbe168f6d52c" integrity sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg== - -x509@bsian03/node-x509: - version "0.3.4" - resolved "https://codeload.github.com/bsian03/node-x509/tar.gz/cc32d7f590ec43cb856effbb4202921f3fa9aef3" - dependencies: - nan "2.14.1"