UNPKG

socio

Version:

A WebSocket Real-Time Communication (RTC) API framework.

108 lines (107 loc) 5.05 kB
"use strict"; import MagicString from 'magic-string'; import { randomUUID, createCipheriv, createDecipheriv, getCiphers, randomBytes, createHash } from 'crypto'; import { socio_string_regex } from './sql-parsing.js'; import { LogHandler, E } from './logging.js'; import { extname } from 'path'; const cipher_algorithm_bits = 256; const cipher_algorithm = `aes-${cipher_algorithm_bits}-gcm`; export function SocioSecurityVitePlugin(SocioSecurityOptions, { include_file_types = ['js', 'svelte', 'vue', 'jsx', 'ts', 'tsx'], exclude_file_types = [], exclude_svelte_server_files = true, exclude_regex } = {}) { const ss = new SocioSecurity(SocioSecurityOptions); return { name: 'vite-socio-security', enforce: 'pre', transform(code, id) { if (/.*\/(node_modules|socio\/core|socio\/dist)\//.test(id)) return undefined; if (exclude_svelte_server_files && /.*\.server\.(js|ts)$/.test(id)) return undefined; const ext = extname(id).slice(1); if (exclude_file_types.includes(ext) || (exclude_regex && exclude_regex.test(id))) return undefined; if (!(include_file_types.includes(ext))) return undefined; const s = ss.SecureSouceCode(code, id); return { code: s.toString(), map: s.generateMap({ source: id, includeContent: true }) }; }, }; } export class SocioSecurity extends LogHandler { #key; #rand_int_gen; static iv_counter = 1; verbose = false; constructor({ secure_private_key = '', rand_int_gen = undefined, logging = { verbose: false, hard_crash: false } }) { super({ ...logging, prefix: 'SocioSecurity' }); if (!secure_private_key) throw new E(`Missing secure_private_key constructor argument!`); if (typeof secure_private_key == 'string') secure_private_key = StringToByteBuffer(secure_private_key); const cipher_algorithm_bytes = cipher_algorithm_bits / 8; if (secure_private_key.byteLength < cipher_algorithm_bytes) throw new E(`secure_private_key has to be at least ${cipher_algorithm_bytes} bytes length! Got ${secure_private_key.byteLength}`); this.#key = createHash('sha256').update(secure_private_key).digest().subarray(0, cipher_algorithm_bytes); this.verbose = logging.verbose || false; this.#rand_int_gen = rand_int_gen; if (this.verbose) this.done('Initialized SocioSecurity object succesfully!'); } SecureSouceCode(source_code = '', file_path = '') { const s = new MagicString(source_code); for (const m of source_code.matchAll(socio_string_regex)) { if (m.index) s.update(m.index, m.index + m[0].length, this.EncryptSocioString(m.groups?.sql)); } return s; } EncryptString(str = '') { const iv = this.get_next_iv(); const cipher = createCipheriv(cipher_algorithm, this.#key, iv); const cipher_text = cipher.update(str, 'utf-8', 'base64') + cipher.final('base64'); const auth_tag = cipher.getAuthTag().toString('base64'); return [iv.toString('base64'), cipher_text, auth_tag].join(' '); } DecryptString(iv_base64, cipher_text, auth_tag_base64) { try { const iv = Buffer.from(iv_base64, 'base64'); const auth_tag = Buffer.from(auth_tag_base64, 'base64'); const decipher = createDecipheriv(cipher_algorithm, this.#key, iv); decipher.setAuthTag(auth_tag); return decipher.update(cipher_text, 'base64', 'utf-8') + decipher.final('utf-8'); } catch (e) { throw new E('SocioSecurity.DecryptString() error. Perhaps secret keys mismatch.', e); } } EncryptSocioString(sql = '') { let randint_sql = ''; for (const l of sql) { if (l == ' ') randint_sql += `-;¦${this.GenRandInt(100, 999)}`; else randint_sql += l; } randint_sql = `-;¦${this.GenRandInt(100, 999)}${randint_sql}-;¦${this.GenRandInt(100, 999)}`; return `\`${this.EncryptString(randint_sql)}\``; } RemoveRandInts(randint_sql = '') { return randint_sql.replace(/-;¦\d{3}/gi, ' '); } GenRandInt(min = 10_000, max = 100_000_000) { return this.#rand_int_gen ? this.#rand_int_gen(min, max) : Math.floor((Math.random() * (max - min)) + min); } get supportedCiphers() { return getCiphers(); } get defaultCipher() { return cipher_algorithm; } get_next_iv() { const iv = Buffer.alloc(8); SocioSecurity.iv_counter += 1; iv.writeUInt32LE(SocioSecurity.iv_counter); return Buffer.concat([iv, randomBytes(8)]); } } export function StringToByteBuffer(str) { return Buffer.from(str, 'utf8'); } export function GenRandomBytes(size) { return randomBytes(size); } export function UUID() { return randomUUID(); }