Thanks for sending a PR. Three things before you do.
We track open work on the Pentest Swarm AI Roadmap project. Three views worth knowing:
- Board — kanban (Backlog / In Progress / Review / Done) for the active sprint
- Roadmap — timeline across waves
- By priority —
P0/P1/P2/P3columns
If you're a first-time contributor, filter by the good first issue label — those are scoped to one file, one PR, with clear acceptance criteria. Examples in the current backlog include tool adapters (area-tools, ~50 LOC each, follow the existing nuclei.go / sqlmap.go shape) and OSINT integrations (area-distribution).
- Pick an unchecked
[ ]item from IMPLEMENTATION_PLAN.md or a card from the project board. - Open a "Pick up a roadmap task" issue (template at .github/ISSUE_TEMPLATE/pick_up_roadmap_task.yml). This prevents two people implementing the same thing in parallel.
- A maintainer will assign the issue to you, usually same-day. Confirmation is mostly a chance to flag if anything has changed since the plan was written.
- Fork, branch off
main, implement (with tests), open a PR. The PR template will walk you through the rest.
We run 2-week sprints. Items in the current sprint are the working set; items in the backlog are fair game to claim but expect a slower review turnaround. If you pick up something outside the current sprint and ship it quickly, we'll prioritise the review.
A researcher must be able to find a real bug with one command.
pentestswarm scan example.comThat's the only command they should need to know. Everything else
(scope import, program inspect, submit, report polish, fp share)
is optional polish that pays for itself only when the user asks for it.
This is enforced at review time, not by tooling. If your PR adds a
required step to the scan flow — a new flag the user MUST set, a new
file the user MUST author, a new CLI command the user MUST run before
scan works — the PR will be rejected unless you can show that the
default behavior still works without it.
Concrete examples:
- ✅ A new optional flag
--foothat improves things when set - ✅ A new file
~/.pentestswarm/foo.yamlthat's auto-created with sensible defaults if missing - ✅ A new command
pentestswarm barthat augments the workflow - ❌ Making
--scoperequired again (we removed it intentionally; the default infers scope from the target — see 4.8.5) - ❌ A migration that breaks existing
~/.pentestswarm/config.yaml - ❌ A new "you must run
pentestswarm setup-thingfirst" step
When in doubt: if a researcher who installed the tool yesterday can't follow the Quick Start in the README verbatim after your PR, the PR isn't done.
pentestswarm --help must fit in 30 lines or fewer so it stays on
one laptop screen.
Adding a new top-level command is fine, but if it pushes the help output past 30 lines, you have to either:
- Hide it with
Hidden: trueon the cobra command (still callable viapentestswarm <command> --help, just not in the index), or - Combine it under an existing parent command (e.g.,
pentestswarm scope <thing>instead of a new top-level)
Verify with:
pentestswarm --help | wc -lCI doesn't enforce this yet — reviewer call.
Every error path the user can hit needs to tell them what to do next.
// ✅ Good — actionable
return fmt.Errorf("no API key configured.\n Fix: run %s",
colorCyan("pentestswarm init"))
// ❌ Bad — leaves the user guessing
return fmt.Errorf("no API key")The litmus test: if a reasonable researcher encounters this error, does the message tell them what command to run, what file to edit, or what docs page to read? If not, fix the message before merging.
Wrapping a new security tool is the easiest way in, and it's how most of the toolchain got built. Every adapter follows one small, consistent pattern — you don't touch the swarm internals.
Copy internal/tools/nikto.go as your template and adapt it:
XyzToolstruct +NewXyzTool()+Name()returning the tool's id.IsAvailable()→IsCommandAvailable("xyz").Run(ctx, target, opts)— scope-guard viagetScopeFromContext, run the tool with a JSON / temp-file output flag, and parse the output into[]map[string]anyfindings (each with at leasttool,title,severity). Treat a non-zero exit as expected when the tool signals findings that way; the parsed output on disk is the source of truth, not the exit code.- Register
NewXyzTool()ininternal/tools/coordinator.go. - Add the install line to the
Dockerfile. - Ship tests mirroring
internal/tools/nikto_test.go(Name, IsAvailable, parse-with-findings, parse-empty, parse-malformed), and tick the adapter in IMPLEMENTATION_PLAN.md.
Done when: go build ./..., go vet ./..., and go test ./internal/tools/...
are green, and the adapter shows up in pentestswarm doctor.
Starter adapters are labeled good first issue
— e.g. whatweb, wafw00f, feroxbuster. Stuck? Ask in Discord. 🐝
-
Conventional commit prefixes are used:
feat:,fix:,docs:,test:,chore:,refactor:. Scope in parens is optional but helpful:feat(scheduler): .... -
Tests come with the code. New packages ship with a
_test.gofile, even if it only has one happy-path test. CI fails without one. -
No new external deps without a reason.
go.modshould be small. We declinedgolang.org/x/time/ratefor the rate limiter and rolled ~50 lines instead — that's the bar. -
Plan check-offs. If your PR completes a phase task in IMPLEMENTATION_PLAN.md, tick the box and rewrite the bullet to describe what actually shipped (paths, function names, behavior). That keeps the plan honest as a record of what exists, not just what was promised.
Don't open a public issue. Email the security inbox listed in SECURITY.md, or open a private GitHub Security Advisory. Same applies to vulnerabilities in dependencies.