200.Land

zap-alerts.ts

This Node.js script uses Puppeteer and OWASP ZAP API to scan a list of URLs (from "urls.json") for security vulnerabilities. It visits each URL, triggers ZAP scans, and appends alerts along with the URL found to a newline-delimited JSON file ("zap-report.jsonl").

import express from "express";
import { launch } from "puppeteer";
const proxyChain = require("proxy-chain");
import { URLSearchParams } from "node:url";
import fetch from "node-fetch";
import { appendFile } from "fs/promises";
import urls from "./urls.json";
import { ScanResults } from "./zap.type";

express().listen(process.env.PORT || 4000);

const zapApiUrl = "http://127.0.0.1:8080";
const zapApiKey = "xxxxxxxxxxxxxxxxxxxxxxx";

async function getZapResults(url: string): Promise<ScanResults> {
  await clearAllAlerts();
  const anonymizedProxy = await proxyChain.anonymizeProxy(zapApiUrl);
  const browser = await launch({
    args: [`--proxy-server=${anonymizedProxy}`],
    ignoreHTTPSErrors: true,
    headless: "new",
  });
  const page = await browser.newPage();
  await page.goto(url);
  await page.close();
  await browser.close();
  await poll(fetchScans, hasRecordsLeft, 1000);
  const params = new URLSearchParams({ apikey: zapApiKey });
  const res = await fetch(`${zapApiUrl}/JSON/alert/view/alerts/?${params}`);
  return res.json();
}

const clearAllAlerts = async () => {
  const params = new URLSearchParams({ apikey: zapApiKey });
  await fetch(`${zapApiUrl}/JSON/alert/action/deleteAllAlerts?${params}`);
};

const fetchScans = async () => {
  const params = new URLSearchParams({ apikey: zapApiKey });
  const res = await fetch(`${zapApiUrl}/JSON/pscan/view/recordsToScan?${params}`);
  return res.json();
};

const hasRecordsLeft = (results: any) => results.recordsToScan !== "0";

const poll = async function (fn: Function, fnCondition: Function, ms: number) {
  let result = await fn();
  while (fnCondition(result)) {
    await wait(ms);
    result = await fn();
  }
  return result;
};

const wait = (ms = 1000) => new Promise(res => setTimeout(res, ms));

(async () => {
  for await (const url of urls) {
    const { alerts } = await getZapResults(url);
    const withFoundAt = alerts.map(alert => ({ ...alert, foundAt: url }));
    await appendFile("zap-report.jsonl", `${withFoundAt.map(v => JSON.stringify(v)).join("\n")}\n`);
  }
})();