Projects#
JavaScript projects build when the target is browser-based, when the recon stack is Node, or when scraping demands a real headless browser instead of an HTTP client.
Headless scraper#
Drive Chrome via Playwright to render a target page (including its JavaScript) and capture HTML, console output, and network traffic.
// src/scrape.js
import { chromium } from "playwright";
async function scrape(url) {
const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext();
const page = await ctx.newPage();
const requests = [];
page.on("request", r => requests.push({ url: r.url(), method: r.method() }));
await page.goto(url, { waitUntil: "networkidle" });
const html = await page.content();
await browser.close();
return { html, requests };
}
scrape(process.argv[2]).then(r => console.log(JSON.stringify(r, null, 2)));
$ pnpm add playwright
$ pnpm exec playwright install chromium
$ node src/scrape.js https://target.example.com/
Browser extension#
A WebExtension (Chrome / Firefox compatible) that adds an operator-facing toolbar action, reads page DOM, and posts back to a local listener.
// manifest.json
{
"manifest_version": 3,
"name": "operator-toolbox",
"version": "0.1.0",
"action": { "default_popup": "popup.html" },
"permissions": ["activeTab", "scripting"],
"host_permissions": ["http://localhost:9000/*"]
}
// popup.js
document.querySelector("#scan").addEventListener("click", async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const [{ result }] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => ({ title: document.title, links: document.links.length }),
});
fetch("http://localhost:9000/collect", {
method: "POST",
body: JSON.stringify(result),
});
});
Node CLI#
A small CLI tool drop on a workstation. Uses
commander for arg parsing and writes to stdout.
// src/cli.js
#!/usr/bin/env node
import { Command } from "commander";
const program = new Command()
.name("op-dns")
.description("Resolve A/AAAA/MX for a list of domains")
.argument("<file>", "file with one domain per line")
.option("-c, --concurrency <n>", "parallel queries", parseInt, 20);
program.action(async (file, opts) => {
const dns = await import("node:dns/promises");
const domains = (await import("node:fs/promises"))
.readFile(file, "utf8").then(t => t.split("\n").filter(Boolean));
for (const d of await domains) {
try {
const a = await dns.resolve(d, "A");
console.log(d, a.join(","));
} catch (e) {
console.error(d, e.code);
}
}
});
program.parse();
Live DOM analysis server#
A small Express service run locally, accepts URLs from a browser extension, runs Playwright, and stores findings in SQLite.
import express from "express";
import Database from "better-sqlite3";
import { chromium } from "playwright";
const db = new Database("findings.db");
db.exec("CREATE TABLE IF NOT EXISTS findings (ts INTEGER, url TEXT, json TEXT)");
const app = express();
app.use(express.json());
app.post("/collect", async (req, res) => {
db.prepare("INSERT INTO findings VALUES (?, ?, ?)")
.run(Date.now(), req.body.url, JSON.stringify(req.body));
res.status(204).end();
});
app.listen(9000);
References#
Setup for the toolchain.
Frameworks for Playwright, Express, and front-end frameworks.
Networking for the HTTP / WebSocket surface.