UNPKG

@bsv/overlay-express

Version:
118 lines 4.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BanService = void 0; /** * BanService provides a persistent ban list stored in MongoDB. * * When the Janitor or an admin removes a SHIP/SLAP token, the associated * domain or outpoint can be added to the ban list. The BanAwareLookupWrapper * checks this list before admitting new outputs, preventing GASP from * re-syncing previously removed stale tokens. */ class BanService { constructor(db) { this.bans = db.collection('bannedRecords'); } /** * Sanitizes a value for use in MongoDB queries, preventing NoSQL injection. * Ensures the value is a plain string and rejects objects/arrays that could * contain MongoDB operators like $ne, $gt, etc. */ sanitize(value) { if (typeof value !== 'string') { throw new TypeError('Invalid input: expected a string value'); } return value; } /** * Creates indexes for efficient ban lookups. */ async ensureIndexes() { await this.bans.createIndex({ type: 1, value: 1 }, { unique: true }); await this.bans.createIndex({ bannedAt: -1 }); } /** * Bans a domain, preventing any SHIP/SLAP tokens referencing it from being stored. */ async banDomain(domain, reason, bannedBy) { const safeDomain = this.sanitize(domain); await this.bans.updateOne({ type: 'domain', value: safeDomain }, { $set: { type: 'domain', value: safeDomain, reason: reason !== null && reason !== void 0 ? reason : 'Manually banned', bannedAt: new Date(), bannedBy } }, { upsert: true }); } /** * Removes a domain ban. */ async unbanDomain(domain) { await this.bans.deleteOne({ type: 'domain', value: this.sanitize(domain) }); } /** * Checks if a domain is banned. */ async isDomainBanned(domain) { const record = await this.bans.findOne({ type: 'domain', value: this.sanitize(domain) }); return record !== null; } /** * Bans a specific outpoint (txid.outputIndex), preventing it from being re-admitted. */ async banOutpoint(txid, outputIndex, reason, domain, bannedBy) { const value = `${this.sanitize(txid)}.${Number(outputIndex)}`; await this.bans.updateOne({ type: 'outpoint', value }, { $set: { type: 'outpoint', value, domain: domain != null ? this.sanitize(domain) : undefined, reason: reason !== null && reason !== void 0 ? reason : 'Manually banned', bannedAt: new Date(), bannedBy } }, { upsert: true }); } /** * Removes an outpoint ban. */ async unbanOutpoint(txid, outputIndex) { const value = `${this.sanitize(txid)}.${Number(outputIndex)}`; await this.bans.deleteOne({ type: 'outpoint', value }); } /** * Checks if a specific outpoint is banned. */ async isOutpointBanned(txid, outputIndex) { const value = `${this.sanitize(txid)}.${Number(outputIndex)}`; const record = await this.bans.findOne({ type: 'outpoint', value }); return record !== null; } /** * Lists all bans, optionally filtered by type. */ async listBans(type) { const query = typeof type === 'string' ? { type } : {}; return await this.bans.find(query).sort({ bannedAt: -1 }).toArray(); } /** * Removes a ban by type and value. */ async removeBan(type, value) { await this.bans.deleteOne({ type: this.sanitize(type), value: this.sanitize(value) }); } /** * Returns ban statistics. */ async getStats() { const [domainBans, outpointBans] = await Promise.all([ this.bans.countDocuments({ type: 'domain' }), this.bans.countDocuments({ type: 'outpoint' }) ]); return { domainBans, outpointBans, totalBans: domainBans + outpointBans }; } } exports.BanService = BanService; //# sourceMappingURL=BanService.js.map