socio
Version:
A WebSocket Real-Time Communication (RTC) API framework.
108 lines (107 loc) • 5.05 kB
JavaScript
;
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(); }