UNPKG

@zbo14/mongomap

Version:

A CLI to store/fetch bug bounty information in mongodb

217 lines (161 loc) 5.2 kB
#!/usr/bin/env node 'use strict' const commander = require('commander') const fs = require('fs') const { MongoClient } = require('mongodb') const path = require('path') const banner = fs.readFileSync(path.join(__dirname, 'banner'), 'utf8') const error = msg => console.error('\x1b[31m%s\x1b[0m', msg) const warn = msg => console.warn('\x1b[33m%s\x1b[0m', msg) const checkCollection = collection => { if (!['domains', 'ips', 'ranges', 'urls'].includes(collection)) { error('[!] Expected <collection> to be one of ["domains","ips","ranges","urls"]') process.exit(1) } } const init = async () => { const client = await MongoClient.connect('mongodb://127.0.0.1', { useNewUrlParser: true, useUnifiedTopology: true }) const db = client.db('mongomap') return { client, db } } const getCollection = async (db, collection) => { let coll if (collection === 'domains') { coll = await db.createCollection('domains') await coll.createIndex('name', { background: true, unique: true }) } else if (collection === 'ips') { coll = await db.createCollection('ips') await coll.createIndex({ address: 1, program: 1 }, { background: true, unique: true }) } else if (collection === 'ranges') { coll = await db.createCollection('ranges') await coll.createIndex('cidr', { background: true, unique: true }) } else { coll = await db.createCollection('urls') await coll.createIndex('href', { background: true, unique: true }) } return coll } const generateOps = (program, collection, batch) => { const docs = [] const created = new Date() if (collection === 'domains') { batch.forEach(name => { const parts = name.split('.') let parent = null if (parts.length > 2) { parent = parts.slice(1).join('.') } docs.push({ created, name, parent, program }) }) } else if (collection === 'ips') { batch.forEach(address => docs.push({ address, created, program })) } else if (collection === 'ranges') { batch.forEach(({ asn, cidr }) => docs.push({ asn, cidr, created, program })) } else { batch.forEach(href => { try { const url = new URL(href) const domain = url.hostname const secure = url.protocol === 'https:' docs.push({ created, domain, href, program, secure }) } catch {} }) } return docs.map(document => ({ insertOne: { document } })) } const generateOpts = collection => { const opts = { projection: { _id: 0 } } if (collection === 'domains') { opts.projection.name = 1 } else if (collection === 'ips') { opts.projection.address = 1 } else if (collection === 'ranges') { opts.projection.cidr = 1 } else { opts.projection.href = 1 } return opts } const push = async (program, collection, file) => { checkCollection(collection) let asn if (collection === 'ranges') { asn = path.parse(path.basename(file)).name if (!/^AS[0-9]+$/.test(asn)) { error('[!] Filename is not a valid ASN') process.exit(1) } asn = +asn.slice(2) } let data try { data = await fs.promises.readFile(file, 'utf8') } catch { error('[!] Couldn\'t read file: ' + file) process.exit(1) } error(banner) warn('[-] Loaded file of ' + collection) const lines = data .split('\n') .filter(Boolean) .map(line => line.trim()) const { client, db } = await init() const coll = await getCollection(db, collection) warn('[-] Connected to database') for (let i = 0; i < lines.length; i += 1e3) { let batch = lines.slice(i, i + 1e3) if (collection === 'ranges') { batch = batch.map(cidr => ({ asn, cidr })) } const ops = generateOps(program, collection, batch, file) await coll.bulkWrite(ops, { ordered: false }) .catch(err => err.message.includes('duplicate key error') || error('[!] ' + err.message)) } warn('[-] Wrote ' + collection) warn('[-] Closing connection') client.close() warn('[-] Done!') } const pull = async (program, collection, opts) => { checkCollection(collection) error(banner) const { client, db } = await init() const coll = await getCollection(db, collection) warn('[-] Connected to database') const today = new Date() today.setHours(0) today.setMinutes(0) today.setSeconds(0) today.setMilliseconds(0) const created = { $gte: today } const query = { created, program } opts = generateOpts(collection) warn('[-] Finding ' + collection) const cursor = coll.find(query, opts) const results = await cursor.toArray() results.forEach(result => { const [key] = Object.keys(result) console.log(result[key]) }) warn('[-] Closing connection') client.close() warn('[-] Done!') } const program = new commander.Command() program.version('0.0.0') program .command('push <program> <collection> <file>') .description('store the contents of a file in the database') .action(push) program .command('pull <program> <collection>') .description('pull documents that were added to the database today') .action(pull) program .parseAsync(process.argv) .catch(err => error(err) || 1) .then(process.exit)