Compare commits

...

2 commits

Author SHA1 Message Date
5ae62595b4
idk 2024-09-24 20:29:39 -04:00
7db24166a0
refactor(tree-wide): i restarted 2024-09-15 16:43:51 -04:00
8 changed files with 790 additions and 17 deletions

4
.gitignore vendored
View file

@ -173,4 +173,6 @@ dist
# Finder (MacOS) folder config
.DS_Store
*.asc
*.asc
*.key
ssh_keys

BIN
bun.lockb

Binary file not shown.

1
host.key.pub Normal file
View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOrBXBiOtqX7YEOyLCMag2XFc8unduCPDI3q/pIYBqMD neon@leon

View file

@ -1,15 +1,20 @@
{
"name": "nest-pgp-chat",
"module": "src/index.ts",
"type": "module",
"devDependencies": {
"@types/bun": "latest"
"@types/bun": "latest",
"ts-node": "^10.9.2",
"typescript": "^5.6.2"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"blessed": "^0.1.81",
"dotenv": "^16.4.5",
"openpgp": "^5.11.2"
"nodemailer": "^6.9.15",
"nodemailer-direct-transport": "^3.3.2",
"openpgp": "^5.11.2",
"ssh2": "^1.15.0"
}
}
}

View file

@ -6,7 +6,7 @@ import openpgp from 'openpgp'
const { privateKey, publicKey } = await openpgp.generateKey({
type: 'rsa', // Type of the key
rsaBits: 2048, // RSA key size (defaults to 4096 bits)
userIDs: [{ name: 'Jon Smith', email: 'jon@example.com' }], // you can pass multiple user IDs
userIDs: [{ name: 'Server', email: 'neon+pgp_server__temp@hackclub.app' }], // you can pass multiple user IDs
format: 'object',
passphrase: 'super long and hard to guess secret' // protects the private key
});
@ -68,6 +68,7 @@ const Config = {
"server": {
// nests ram COULD not handle more then 20 sadly
"maxConnections": 20,
"noInputTimeout": 55_000 ,
"host": "127.0.0.1",
"port": process.env.PORT
},
@ -75,7 +76,7 @@ const Config = {
"messageLength": 150,
"messages": {
"welcome": "Welcome to chat server. Press CTRL+C to leave.\n(note: pgp key name will be used in server. extra metadata such as [email, pgp key id] can be found via whois cmd)",
"nickname": "Enter your public PGP key (in base64, in one line): ",
"nickname": "Enter your public PGP key (in base64, in one line once done write in a new line 'ENDKEY'): ",
"logout": "%s has left the chat",
"login": "%s has joined the chat",
"not_allowed": "You may only connect from the nest server. This is not a public server."
@ -85,9 +86,20 @@ const Config = {
}
const Messages = Config.chat.messages;
// to prevent my ram from being disposed
const all_connections = [];
const clients = [];
setInterval(() => {
all_connections.forEach(client => {
try {
if(Date.now() - client.lastInput >= 0) {
client.end(`Timed out`)
}
} catch (e) {
console.error(e.message, 'e')
}
})
}, Config.server.noInputTimeout)
const messageListener = fn => data => {
console.log('Received data', data, data.toString());
@ -152,11 +164,16 @@ function askNickname(socket) {
};
const onData = messageListener(data => {
str += data.toString()
if (!data.length) return ask();
if(!data.toString().includes('END PGP PUBLIC KEY')) return ask()
socket.off('data', onData)
resolve(str);
if(data.toString().includes('ENDKEY')) {
socket.off('data', onData)
resolve(str);
} else {
str += data.toString()
if (!data.length) return ask();
if(!data.toString().includes('ENDKEY')) return ask()
}
});
ask();
@ -165,6 +182,7 @@ if(!data.toString().includes('END PGP PUBLIC KEY')) return ask()
function createServer() {
const server = net.createServer(async socket => {
try {
console.log(socket.remoteAddress)
if(![`::1`].includes(socket.remoteAddress.trim())) {
@ -172,6 +190,13 @@ function createServer() {
socket.end(`Bye`)
return;
}
socket.on('data', () => {
//@ts-ignore
all_connections[all_connections.indexOf(socket)].lastInput = Date.now()
})
//@ts-ignore
socket.lastInput = Date.now()
all_connections.push(socket)
console.log(`Connection from ${socket.remoteAddress}`);
sendMessageLn(socket, null, Messages.welcome);
@ -182,10 +207,10 @@ function createServer() {
console.debug(`$afternic`)
let pgp_key=null, nickname= null;
try {
console.log(raw_pgp_data)
// console.log(Buffer.from(raw_pgp_data as string, 'base64').toString())
// console.log(raw_pgp_data)
console.log(Buffer.from(raw_pgp_data as string, 'base64').toString(), Buffer.from(raw_pgp_data as string, 'base64').toString().length)
pgp_key = await openpgp.readKey({
armoredKey: raw_pgp_data as string,
armoredKey: Buffer.from(raw_pgp_data as string, 'base64').toString(),
})
// .then(d=>d.users[0])
console.log(pgp_key.users[0])

396
src/index.js Normal file
View file

@ -0,0 +1,396 @@
const fs = require('fs');
const openpgp = require('openpgp')
const path = require('path')
const { readFileSync } = fs
const blessed = require('blessed');
const { utils: { parseKey }, Server } = require('ssh2');
const RE_SPECIAL =
// eslint-disable-next-line no-control-regex
/[\x00-\x1F\x7F]+|(?:\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K])/g;
const MAX_MSG_LEN = 128;
const MAX_NAME_LEN = 10;
const PROMPT_NAME = `Enter a nickname to use (max ${MAX_NAME_LEN} chars): `;
const users = [];
function formatMessage(msg, output) {
output.parseTags = true;
msg = output._parseTags(msg);
output.parseTags = false;
return msg;
}
function userBroadcast(msg, source) {
const sourceMsg = `> ${msg}`;
const name = `{cyan-fg}{bold}${source.name}{/}`;
msg = `: ${msg}`;
for (const user of users) {
const output = user.output;
if (source === user)
output.add(sourceMsg);
else
output.add(formatMessage(name, output) + msg);
}
}
function localMessage(msg, source) {
const output = source.output;
output.add(formatMessage(msg, output));
}
function noop(v) {}
let known_keys = require('fs').readdirSync('./ssh_keys').map(dir => {
const files = fs.readdirSync(path.join(__dirname, 'ssh_keys', dir))
return {
parsedKey: parseKey(fs.readFileSync(files.find(f => f.endsWith('.sshkey')))),
armoredGPGKey: fs.readFileSync(files.find(f => f.endsWith('.asc')))
}
})
new Server({
hostKeys: [readFileSync('host.key')],
}, (client) => {
let stream;
let name;
// client.on('authentication', (ctx) => {
// console.log(ctx, ctx.key, ctx.method)
// let nick = ctx.username;
// let prompt = PROMPT_NAME;
// let lowered;
// // Try to use username as nickname
// if (nick.length > 0 && nick.length <= MAX_NAME_LEN) {
// lowered = nick.toLowerCase();
// let ok = true;
// for (const user of users) {
// if (user.name.toLowerCase() === lowered) {
// ok = false;
// prompt = `That nickname is already in use.\n${PROMPT_NAME}`;
// break;
// }
// }
// if (ok) {
// name = nick;
// return ctx.accept();
// }
// } else if (nick.length === 0) {
// prompt = 'A nickname is required.\n' + PROMPT_NAME;
// } else {
// prompt = 'That nickname is too long.\n' + PROMPT_NAME;
// }
// if (ctx.method !== 'keyboard-interactive')
// return ctx.reject(['keyboard-interactive']);
// ctx.prompt(prompt, function retryPrompt(answers) {
// if (answers.length === 0)
// return ctx.reject(['keyboard-interactive']);
// nick = answers[0];
// if (nick.length > MAX_NAME_LEN) {
// return ctx.prompt(`That nickname is too long.\n${PROMPT_NAME}`,
// retryPrompt);
// } else if (nick.length === 0) {
// return ctx.prompt(`A nickname is required.\n${PROMPT_NAME}`,
// retryPrompt);
// }
// lowered = nick.toLowerCase();
// for (const user of users) {
// if (user.name.toLowerCase() === lowered) {
// return ctx.prompt(`That nickname is already in use.\n${PROMPT_NAME}`,
// retryPrompt);
// }
// }
// name = nick;
// ctx.accept();
// });
// })
let username = null;
let reject_on_connect = false;
client.on('authentication', (ctx) => {
let allowed = false;
username = ctx.username;
if (ctx.username == "gimmie-key") {
allowed = true;
ctx.accept();
return;
}
switch (ctx.method) {
case 'password':
// console.log('Not passwor')
ctx.reject();
break;
case 'publickey':
console.log(`pubkey`)
// let is_allowed =
// console.log(ctx.key.data.toString())
known_keys.forEach((info) => {
let allowedPubKey = info.parsedKey
if (ctx.key.algo !== allowedPubKey.type
|| !checkValue(ctx.key.data, allowedPubKey.getPublicSSH())
|| (ctx.signature && allowedPubKey.verify(ctx.blob, ctx.signature, ctx.hashAlgo) !== true)) {
// do nothing;
} else {
allowed = true;
}
})
break;
default:
return ctx.reject();
}
if (allowed)
ctx.accept();
else {
console.error(`no`, ctx.prompt)
// ctx.prompt(`Your ssh key was not found/provided please add by sshing with the username gimmie-key`, () => {
// ctx.reject();
// })
reject_on_connect = true;
ctx.accept()
}
})
.on('ready', () => {
let rows;
let cols;
let term;
client.once('session', (accept, reject) => {
accept().once('pty', (accept, reject, info) => {
rows = info.rows;
cols = info.cols;
term = info.term;
accept && accept();
}).on('window-change', (accept, reject, info) => {
rows = info.rows;
cols = info.cols;
if (stream) {
stream.rows = rows;
stream.columns = cols;
stream.emit('resize');
}
accept && accept();
}).once('shell', (accept, reject) => {
stream = accept();
stream.name = name;
stream.rows = rows || 24;
stream.columns = cols || 80;
stream.isTTY = true;
stream.setRawMode = noop;
stream.on('error', noop);
if(reject_on_connect) {
stream.write("Your ssh key was not found/provided please add by sshing with the username gimmie-key`\n")
stream.end()
}
if(username == 'gimmie-key') {
const screen = new blessed.screen({
autoPadding: true,
smartCSR: true,
program: new blessed.program({
input: stream,
output: stream
}),
terminal: term || 'ansi'
});
const output = stream.output = new blessed.log({
screen: screen,
top: 0,
left: 0,
width: '100%',
bottom: 2,
scrollOnInput: true
});
screen.append(output);
screen.append(new blessed.box({
screen: screen,
height: 1,
bottom: 3,
left: 0,
width: '100%',
type: 'line',
ch: '='
}));
const input = new blessed.textarea({
screen: screen,
bottom: 0,
height: 3,
width: '100%',
inputOnFocus: true
});
const btn = new blessed.button({
screen,
bottom: 5,
height: 2,
mouse: true,
keys: true,
shrink: true,
padding: {
left: 1,
right: 1
},
style: {
bg: 'blue',
focus: {
bg: 'red'
},
hover: {
bg: 'red'
}
},
content: "submit",
width: '10%',
})
screen.append(input);
screen.append(btn)
input.focus();
output.add('Please send your public GPG key here! ')
output.add(`Once you have pasted it click the red submit button`)
output.add('You will then recive an email shortly pgp-signed with your ssh pirv key')
// console.log(input)
btn.on('press', async () => {
console.debug(`#click`)
let line = input.getValue()
stream.lastInput = Date.now()
// input.clearValue();
// screen.render();
if (!input.focused)
input.focus();
try {
const key = await openpgp.readKey({ armoredKey: line })
output.add(`PGP key added.`)
console.log( key.getFingerprint(), key.users[0].userID.name)
sendMail(key)
stream.end()
} catch (e) {
output.add(`Error! Your key is an invalid pgp key`)
output.add(`> ${e.message}`)
}
// line = line.replace(RE_SPECIAL, '').trim();
// if (line.length > MAX_MSG_LEN)
// line = line.substring(0, MAX_MSG_LEN);
// if (line.length > 0) {
// if (line === '/quit' || line === '/exit')
// stream.end();
// else
// userBroadcast(line, stream);
// }
});
// stream.end()
} else {
users.push(stream);
stream.lastInput = Date.now()
const screen = new blessed.screen({
autoPadding: true,
smartCSR: true,
program: new blessed.program({
input: stream,
output: stream
}),
terminal: term || 'ansi'
});
screen.title = 'SSH Chatting as ' + name;
// Disable local echo
screen.program.attr('invisible', true);
const output = stream.output = new blessed.log({
screen: screen,
top: 0,
left: 0,
width: '100%',
bottom: 2,
scrollOnInput: true
});
screen.append(output);
screen.append(new blessed.box({
screen: screen,
height: 1,
bottom: 1,
left: 0,
width: '100%',
type: 'line',
ch: '='
}));
const input = new blessed.textbox({
screen: screen,
bottom: 0,
height: 1,
width: '100%',
inputOnFocus: true
});
screen.append(input);
input.focus();
// Local greetings
localMessage('{blue-bg}{white-fg}{bold}Welcome to SSH Chat!{/}\n'
+ 'There are {bold}'
+ (users.length - 1)
+ '{/} other user(s) connected.\n'
+ 'Type /quit or /exit to exit the chat.',
stream);
// Let everyone else know that this user just joined
for (const user of users) {
const output = user.output;
if (user === stream)
continue;
output.add(formatMessage('{green-fg}*** {bold}', output)
+ name
+ formatMessage('{/bold} has joined the chat{/}', output));
}
screen.render();
// XXX This fake resize event is needed for some terminals in order to
// have everything display correctly
screen.program.emit('resize');
setInterval(() => {
if(Date.now() - stream.lastInput >= 60_000) {
return stream.end();
}
},1000)
// Read a line of input from the user
input.on('submit', (line) => {
stream.lastInput = Date.now()
input.clearValue();
screen.render();
if (!input.focused)
input.focus();
line = line.replace(RE_SPECIAL, '').trim();
if (line.length > MAX_MSG_LEN)
line = line.substring(0, MAX_MSG_LEN);
if (line.length > 0) {
if (line === '/quit' || line === '/exit')
stream.end();
else
userBroadcast(line, stream);
}
});
}
});
});
}).on('close', () => {
if (stream !== undefined) {
users.splice(users.indexOf(stream), 1);
// Let everyone else know that this user just left
for (const user of users) {
const output = user.output;
output.add(formatMessage('{magenta-fg}*** {bold}', output)
+ name
+ formatMessage('{/bold} has left the chat{/}', output));
}
}
}).on('error', (err) => {
// Ignore errors
});
}).listen(3001, function() {
console.log('Listening on port ' + this.address().port);
});

38
src/test_mail.js Normal file
View file

@ -0,0 +1,38 @@
const nodemailer = require('nodemailer')
const openpgp = require('openpgp')
var directTransport = require('nodemailer-direct-transport');
var transporter = nodemailer.createTransport(directTransport())
const { utils: { generateKeyPair, generateKeyPairSync } } = require('ssh2');
// transporter.sendMail({
// to:"neon@saahild.com",
// from: "neon@localhost",
// html: "hello world"
// })
;(async () => {
// create this sessions PGP key
const { privateKey, publicKey } = await openpgp.generateKey({
type: 'rsa', // Type of the key
rsaBits: 2048, // RSA key size (defaults to 4096 bits)
userIDs: [{ name: 'Server', email: 'neon+pgp_server__temp@hackclub.app' }], // you can pass multiple user IDs
format: 'object',
passphrase: 'super long and hard to guess secret' // protects the private key
});
function sendMail(key) {
const usersSSHKey = generateKeyPairSync('rsa', { bits: 2048, comment: 'Priv key for nest pgp chat - GPG FINGERPRINT: '+ key.getFingerprint() });
transporter.sendMail({
from: publicKey.users[0].userID.email,
to: key.users[0].userID.email,
attachments: [{
filename: `privkey.asc`,
}, {
filename: `pubkey.pub.asc`
}]
// html: await
})
}
const clientKey = await openpgp.readKey({ armoredKey: require('fs').readSync('./key.asc').toString() })
sendMail(clientKey)
})()

306
yarn.lock Normal file
View file

@ -0,0 +1,306 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@jridgewell/resolve-uri@^3.0.3":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@tsconfig/node10@^1.0.7":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==
"@tsconfig/node12@^1.0.7":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
"@types/bun@latest":
version "1.1.9"
resolved "https://registry.yarnpkg.com/@types/bun/-/bun-1.1.9.tgz#2a10783816f178538be72f78e93e2f4f4d73825d"
integrity sha512-SXJRejXpmAc3qxyN/YS4/JGWEzLf4dDBa5fLtRDipQXHqNccuMU4EUYCooXNTsylG0DmwFQsGgEDHxZF+3DqRw==
dependencies:
bun-types "1.1.27"
"@types/node@*":
version "22.5.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.5.tgz#52f939dd0f65fc552a4ad0b392f3c466cc5d7a44"
integrity sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==
dependencies:
undici-types "~6.19.2"
"@types/node@~20.12.8":
version "20.12.14"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.14.tgz#0c5cf7ef26aedfd64b0539bba9380ed1f57dcc77"
integrity sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==
dependencies:
undici-types "~5.26.4"
"@types/ws@~8.5.10":
version "8.5.12"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e"
integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==
dependencies:
"@types/node" "*"
acorn-walk@^8.1.1:
version "8.3.4"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7"
integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==
dependencies:
acorn "^8.11.0"
acorn@^8.11.0, acorn@^8.4.1:
version "8.12.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
asn1.js@^5.0.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
safer-buffer "^2.1.0"
asn1@^0.2.6:
version "0.2.6"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
dependencies:
safer-buffer "~2.1.0"
bcrypt-pbkdf@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==
dependencies:
tweetnacl "^0.14.3"
blessed@^0.1.81:
version "0.1.81"
resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129"
integrity sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==
bn.js@^4.0.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
buildcheck@~0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238"
integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==
bun-types@1.1.27:
version "1.1.27"
resolved "https://registry.yarnpkg.com/bun-types/-/bun-types-1.1.27.tgz#53cbc56d412157fbaf0ba30328208494a6f52cc6"
integrity sha512-rHXAiIDefeMS/fleNM1rRDYqolJGNRdch3+AuCRwcZWaqTa1vjGBNsahH/HVV7Y82frllYhJomCVSEiHzLzkgg==
dependencies:
"@types/node" "~20.12.8"
"@types/ws" "~8.5.10"
cpu-features@~0.0.9:
version "0.0.10"
resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5"
integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==
dependencies:
buildcheck "~0.0.6"
nan "^2.19.0"
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dotenv@^16.4.5:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
httpntlm@1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2"
integrity sha512-Tcz3Ct9efvNqw3QdTl3h6IgRRlIQxwKkJELN/aAIGnzi2xvb3pDHdnMs8BrxWLV6OoT4DlVyhzSVhFt/tk0lIw==
dependencies:
httpreq ">=0.4.22"
underscore "~1.7.0"
httpreq@>=0.4.22:
version "1.1.1"
resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-1.1.1.tgz#b8818316cdfd6b1bfb0f68b822fa1306cd24be68"
integrity sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==
inherits@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
minimalistic-assert@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
nan@^2.18.0, nan@^2.19.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3"
integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==
nodemailer-direct-transport@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86"
integrity sha512-vEMLWdUZP9NpbeabM8VTiB3Ar1R0ixASp/6DdKX372LK4USKB4Lq12/WCp69k/+kWk4RiCWWEGo57CcsXOs/bw==
dependencies:
nodemailer-shared "1.1.0"
smtp-connection "2.12.0"
nodemailer-fetch@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4"
integrity sha512-P7S5CEVGAmDrrpn351aXOLYs1R/7fD5NamfMCHyi6WIkbjS2eeZUB/TkuvpOQr0bvRZicVqo59+8wbhR3yrJbQ==
nodemailer-shared@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0"
integrity sha512-68xW5LSyPWv8R0GLm6veAvm7E+XFXkVgvE3FW0FGxNMMZqMkPFeGDVALfR1DPdSfcoO36PnW7q5AAOgFImEZGg==
dependencies:
nodemailer-fetch "1.6.0"
nodemailer@^6.9.15:
version "6.9.15"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.15.tgz#57b79dc522be27e0e47ac16cc860aa0673e62e04"
integrity sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==
openpgp@^5.11.2:
version "5.11.2"
resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-5.11.2.tgz#2c035a26b13feb3b0bb5180718ec91c8e65cc686"
integrity sha512-f8dJFVLwdkvPvW3VPFs6q9Vs2+HNhdvwls7a/MIFcQUB+XiQzRe7alfa3RtwfGJU7oUDDMAWPZ0nYsHa23Az+A==
dependencies:
asn1.js "^5.0.0"
safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
smtp-connection@2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1"
integrity sha512-UP5jK4s5SGcUcqPN4U9ingqKt9mXYSKa52YhqxPuMecAnUOsVJpOmtgGaOm1urUBJZlzDt1M9WhZZkgbhxQlvg==
dependencies:
httpntlm "1.6.1"
nodemailer-shared "1.1.0"
ssh2@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.15.0.tgz#2f998455036a7f89e0df5847efb5421748d9871b"
integrity sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==
dependencies:
asn1 "^0.2.6"
bcrypt-pbkdf "^1.0.2"
optionalDependencies:
cpu-features "~0.0.9"
nan "^2.18.0"
ts-node@^10.9.2:
version "10.9.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tweetnacl@^0.14.3:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==
typescript@^5.6.2:
version "5.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==
underscore@~1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
integrity sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==
undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==