Skip to content

feat(serve): accept an explicit target list in POST /run - #2

Merged
JoshuaMart merged 1 commit into
mainfrom
feat/serve-targets
Oct 23, 2026
Merged

JoshuaMart merged 1 commit into
mainfrom
feat/serve-targets

Conversation

@JoshuaMart

Copy link
Copy Markdown
Owner

Summary

POST /run gains a targets field: the same "scan a known list" mode the CLI has with --targets / --targets-file / --targets-url. This is what Recon needs to periodically verify known hosts through fastrecon serve.

Example request body:

{
  "targets": ["api.example.com", "www.example.com", "shop.example.net"],
  "exclude": ["staging.example.com"],
  "stages": "resolve",
  "ports": "80,443,8080,8443",
  "timeout": "30m"
}
  • With targets, domain is an optional report label (like -d in CLI targets mode). run.input is targets, sources is empty, and stats.enumerated counts the list. Without targets, domain stays required.
  • The list goes through enumerate.LoadTargets, the CLI's own loader, in runConfig — so it is checked before the run takes the instance. A host with a scheme, path or port, a single label, or an empty list ("targets": []) answers 400 and nothing is scanned. No second validator.
  • exclude still only adds to the deployment's exclusions, and both apply to the list.
  • Unchanged: unknown fields rejected, one run per instance with 429, no caller-supplied webhook, credentials from the environment only.

Behaviour changes to note

  • Body cap 64 KiB → 1 MiB, so a list of a few hundred (or a few thousand) hosts fits. Past it the request is a 400, never truncated.
  • A target list configured on the deployment (FASTRECON_TARGETS*) is now cleared per request: the request says what to scan. Before, a domain request on such a deployment would have scanned the deployment's list instead.

Tests

  • A real app.App behind the handler: a targets request returns run.input == "targets", no sources, the list normalized, and both the deployment's and the request's exclusions remove matching targets.
  • Malformed entries (scheme, path, port, single label), an empty list, a non-list and an oversized body each answer 400 without reaching the pipeline.
  • Request targets replace a deployment-level list and don't mutate the process config.

make lint test passes.

Docs: docs/04-deployment.md (field table and example), docs/01-usage.md, docs/05-design.md.

🤖 Generated with Claude Code

A request may now carry `targets`, the CLI's --targets: the run scans
exactly those hosts, `domain` becomes an optional label, and the report
says `run.input: targets` with no source queried.

The list goes through enumerate.LoadTargets, the loader the CLI uses,
before the run takes the instance: a host with a scheme, path or port,
or an empty list, is a 400 and nothing is scanned. Exclusions still add
to the deployment's and apply to the list. A list configured on the
deployment no longer runs in place of, or merged into, the request's.

The body cap goes from 64 KiB to 1 MiB so a list fits; past it the
request is refused, never truncated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JoshuaMart
JoshuaMart merged commit 6d6d0dc into main Oct 23, 2026
8 checks passed
@JoshuaMart
JoshuaMart deleted the feat/serve-targets branch October 23, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant