Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Armur-Ai/Pentest-Swarm-AI/llms.txt

Use this file to discover all available pages before exploring further.

Attack Surface Monitoring (ASM) mode turns the swarm into a persistent sentinel. Rather than running a one-off campaign, ASM watches your scopes on a configurable schedule, snapshots each run’s attack surface, diffs it against the previous snapshot, and automatically launches new campaigns when significant changes appear — new subdomains, freshly-opened ports, updated technology stacks, or expiring TLS certificates.

How ASM works

The ScopeWatcher runs a goroutine per watched scope. On each tick it:
  1. Calls the recon function against the target (same tool chain as a manual scan)
  2. Saves the resulting AttackSurface snapshot to the snapshot store
  3. Diffs the current snapshot against the previous one using scope.Compare
  4. If diff.IsSignificant() — new domains, new CIDRs, new open ports — calls TriggerEngine.OnAssetChange
The TriggerEngine rate-limits automatic campaign creation: it tracks campaign creation timestamps per scope and blocks new triggers once max_auto_campaigns campaigns have been created in the last 24 hours (default: 3). This prevents runaway scans when a target changes rapidly.
ScopeWatcher (goroutine per scope)

  ├─ recon run (schedule tick)
  │     │
  │     └─ SnapshotStore.Save()

  ├─ scope.Compare(prev, current)
  │     │
  │     └─ AssetDiff.IsSignificant()?
  │           │
  │           YES ──► TriggerEngine.OnAssetChange()
  │                         │
  │                         ├─ rate limit check (max_auto_campaigns / 24h)
  │                         │
  │                         └─ createCampaignFunc() ──► new swarm campaign

  └─ time.Sleep(schedule)

Enable ASM in config.yaml

Copy config.example.yaml to config.yaml and configure the asm section:
# --- Continuous ASM ---
asm:
  enabled: false           # Enable continuous attack surface monitoring
  default_schedule: "24h"  # How often to re-scan watched scopes
  max_auto_campaigns: 3    # Max auto-triggered campaigns per 24h per scope
  notification_slack: ""   # Slack webhook URL for ASM alerts
  notification_email: ""   # Email address for ASM alerts
Set enabled: true to activate the watcher on startup. The default_schedule accepts Go duration strings: "6h", "24h", "168h" (weekly), etc. For notification-only setups, populate notification_slack with an incoming webhook URL or notification_email with an address — ASM alerts fire on every significant diff regardless of whether auto-campaigns are enabled.

ASM scan flags

Run a single ASM-mode scan directly:
pentestswarm scan example.com --mode asm
Combine with --scope to enforce the imported scope file:
pentestswarm scan example.com --scope scope.yaml --mode asm --swarm
Watch a campaign as the swarm works through the attack surface:
pentestswarm campaign watch <campaign-id>
Browse the discovered attack surface interactively after a campaign finishes:
pentestswarm campaign explore <campaign-id>

External ASM playbook

The built-in external-asm.yaml playbook is designed for cron-driven execution. It runs passive OSINT first (no packets to target infrastructure), resolves candidates, fingerprints ports and services, and only emits critical and high severity findings to keep ASM signal-to-noise high:
name: External Attack Surface Monitoring
description: >
  Scheduled external ASM playbook. Discovers and fingerprints the perimeter
  (subdomains, ports, services, tech), takes screenshots, and diffs against
  the previous run so the swarm only surfaces NEW exposures. Meant for cron.
author:
  name: Armur AI
  github: Armur-Ai
version: 1.0.0
tags: [asm, external, monitoring, passive-first]

variables:
  target_domain:
    type: string
    required: true
  diff_against_run:
    type: string
    required: false
    description: Campaign ID of a prior run to diff against (default = most recent)

phases:
  - name: passive_osint
    tools:
      - name: subfinder
        options: { recursive: true }
      - name: gau
    post_analysis: |
      Passive collection first — no packets to target infrastructure.
      Pull historical URLs and passive DNS to enrich the picture.

  - name: resolution
    tools:
      - name: dnsx
    post_analysis: |
      Resolve every candidate to A/AAAA/CNAME. Flag cloud-ranges
      (AWS, GCP, Azure) separately — owner attribution changes by range.

  - name: port_and_service
    tools:
      - name: naabu
        options: { ports: top-1000, rate: 1000 }
      - name: nmap
        options: { scan_type: "-sV", top_ports: 1000, timing: "-T4" }
    post_analysis: |
      Concentrate on unexpected-open ports on production assets.
      Cross-reference product + version against CVE database for
      known-bad versions.

  - name: web_fingerprint
    tools:
      - name: httpx
        options: { follow_redirects: true, threads: 100 }
    post_analysis: |
      Note tech stack, TLS certs (flag expiring < 30d), HTTP headers
      (missing HSTS, CSP, X-Frame-Options).

  - name: quick_vuln_sweep
    tools:
      - name: nuclei
        options:
          severity: [critical, high]
          templates: ["cves/", "exposures/", "misconfiguration/"]
    post_analysis: |
      Critical + high severity only — ASM noise at lower severities
      swamps the signal. Everything else becomes follow-up campaigns.
Run it on a schedule:
pentestswarm playbook run external-asm --target example.com

Scope diffing

After re-importing a program scope or running a new ASM cycle, compare two scope files to see exactly what changed:
pentestswarm scope diff yesterday.yaml today.yaml
  + api.new-service.example.com
  + 203.0.113.0/28
  - legacy.example.com

  14 unchanged, 2 added, 1 removed
Exit codes:
  • 0 — scopes are identical
  • 1 — changes found (new domains, CIDRs added or removed)
This makes the command composable in shell pipelines and cron jobs:
# Re-import scope daily; scan only if the scope changed
pentestswarm scope import h1 shopify --out today.yaml
if ! pentestswarm scope diff yesterday.yaml today.yaml; then
  pentestswarm scan shopify.com --scope today.yaml --mode asm --swarm
  cp today.yaml yesterday.yaml
fi
Emit the diff as JSON for downstream processing:
pentestswarm scope diff yesterday.yaml today.yaml --json

Notification configuration

ASM alerts are sent whenever diff.IsSignificant() is true and a campaign is triggered. Configure Slack or email in config.yaml:
asm:
  notification_slack: "https://hooks.slack.com/services/T.../B.../..."
  notification_email: "security-team@example.com"
Both can be set simultaneously. Alerts include the scope ID, target, number of new domains and CIDRs, and a link to the triggered campaign.
The max_auto_campaigns limit (default 3) is enforced per scope per 24-hour window. Once the limit is hit, further diffs are recorded and notifications are sent, but no new campaign is created until the window resets. Raise this value carefully — each campaign consumes API credits and tool rate limits.

Bug Bounty

Import program scope from HackerOne, Bugcrowd, or Intigriti before starting ASM.

Playbooks

Author custom ASM playbooks tuned to your environment’s tech stack.

GitHub Actions

Schedule weekly ASM scans with SARIF output for the GitHub Security tab.

MCP Integration

Trigger on-demand ASM recon interactively from Claude Desktop or Cursor.

Build docs developers (and LLMs) love