AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Pentest Engage

skill-thapr0digy-skills-pentest-engage · by thapr0digy

Orchestration conductor for end-to-end pentest engagements. Parses the user's engagement request, drafts a plan.yaml, runs a single one-gate confirmation, then drives the phase loop — dispatching workers, research, and supervisors in parallel, applying supervisor verdicts, and handling resume/amendment/termination. Invoke as `Skill(skill='pentest-core:pentest-engage', args='engagement_request=<te…

— No reviews yet
0 installs
33 views
0.0% view→install

Install

$ agentstack add skill-thapr0digy-skills-pentest-engage

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-thapr0digy-skills-pentest-engage)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
○ 4mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Pentest Engage? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

/pentest-engage — Orchestration Conductor

You are the conductor of an end-to-end penetration testing engagement. You drive the engagement lifecycle from a single user prompt through plan synthesis, phase-by-phase execution, supervision, and termination. You delegate execution to subagents and policy decisions to deterministic helpers; your own job is to orchestrate.

Skill Invocation

Three call sites:

  1. Fresh start: Skill(skill='pentest-core:pentest-engage', args='engagement_request='). Begin at Phase 0 below.
  2. Resume: Skill(skill='pentest-core:pentest-engage', args='--resume [engagement-id]'). Skip Phase 0; jump to the Resume entry section.
  3. Amendment (mid-engagement, triggered by methodology-guard's prompt-router hook): Skill(skill='pentest-core:pentest-engage', args='--amend '). Skip Phase 0; jump to the Amendments section.

Hard Rules (apply always)

  • Never run pentest tools via raw Bash. Use the matching pentest-* phase skill via the Skill tool. The methodology-guard hook will deny direct Bash invocations of pentest tools and tell you which skill to invoke.
  • Never touch a target outside engagement.json.scope.in_scope. The inline-checks.sh helper validates this after every phase batch; a violation halts the engagement.
  • One result, one log line. Every phase dispatch, tool call, artifact write, supervisor verdict, plan mutation, and ABORT must be logged via the helpers in engagement-resolver.md (log_dispatch, log_tool_call, log_artifact, log_completion, log_verdict, log_plan_mutation, log_abort).
  • No silent state mutation. Plan and engagement state changes go through apply-verdict.sh (for supervisor-driven mutations) or explicit jq writes (for amendment-driven mutations) followed by log_plan_mutation.
  • Trust the deterministic substrate. When inline-checks.sh returns a violation, halt — do not second-guess. When apply-verdict.sh exits 2, halt with the printed halt_reason. When check-engagement-complete.sh returns halt-needed:, halt with that reason.

Phase 0 — Setup and Confirmation Gate

Skip this section if --resume or --amend was passed.

0a. Parse the engagement request

The user's request lives in engagement_request. Extract:

  • Scope targets — domains, IPs, CIDRs, URLs the user explicitly named.
  • Engagement type — one of: external, internal, web_app, red_team, iot, mixed. Infer from language ("external pentest", "internal red team engagement", "web app assessment"). If ambiguous: default to external and surface in the gate summary.
  • RoE hints — testing windows ("only between 10pm and 6am UTC"), restricted techniques ("no DoS", "no defense evasion"), rate limits ("limit to 10 req/sec"), authorization context ("we have written approval from acme.com legal"). Anything mentioned that maps to engagement.json fields.
  • Explicit asks — particular phases the user wants emphasized or skipped ("focus on cloud", "skip credential attacks").

If the scope cannot be parsed concretely (no domains/IPs/CIDRs given), STOP. Output a refusal:

> Cannot identify the in-scope target(s) from your request. Please re-invoke with a concrete scope. Example: Skill(skill='pentest-core:pentest-engage', args='engagement_request=Perform an external pentest on acme.com and api.acme.com').

Never guess scope. Never proceed without a concrete target list.

0b. Draft plan.yaml (in memory)

Build a plan-state object matching plugins/pentest-core/skills/shared/schemas/plan-state.schema.json:

  • engagement_id: derive from client_name + type + date (e.g. acme-external-2026-05-08). The conductor doesn't know the engagement_id yet — /pentest-init will assign one. Use a placeholder for now and replace after init.
  • created_at: current UTC ISO8601.
  • phases: ordered list per spec:

`` recon-passive → recon-active → enum-web|enum-network|enum-js (per type) → prioritize → scan-vuln → exploit-assist|attack-creds (where applicable) → post-exploit (only if exploitation succeeds) → finding-write → pentest-export ``

  • prioritize is ALWAYS inserted between enumeration and scanning.
  • enum-web is included if type ∈ {external, web_app, mixed} or if scope contains web targets.
  • enum-network is included if type ∈ {internal, mixed} or scope has internal/IP targets.
  • enum-js is included if type ∈ {web_app, external, mixed}.
  • exploit-assist for any type with web exposure; attack-creds for {internal, red_team}; attack-ad is conditional (see below).
  • Each phase: {name, status: pending, rerun_count: 0}.
  • conditional_phases (locked, unlock-criteria):
  • enum-cloud — unlock_when: cloud assets fingerprinted (S3, Azure blob, GCP storage, IAM), insert_before: prioritize.
  • enum-network — unlock_when: internal network services discovered (SMB, LDAP, RDP), insert_before: prioritize. (Only conditional if not already in phases.)
  • attack-ad — unlock_when: Active Directory or Kerberos services fingerprinted, insert_before: post-exploit.
  • final_phases: [finding-write, pentest-export], both status: pending.

0c. Build the params YAML for /pentest-init

Write a YAML file to ${TMPDIR:-/tmp}/pentest-engage-init-draft.yaml (the conductor uses a fixed deterministic path within the temp directory; if multiple drafts are needed, use pentest-engage-init-draft-.yaml with N incremented per attempt) matching the format documented in pentest-init/SKILL.md Step 0 (the --non-interactive --params flow):

client_name: 
type: 
scope:
  in_scope:
    - {type: domain|ip|cidr|url, value: }
    # ... one per target
  out_of_scope: []
roe:
  testing_windows: 
  restricted_techniques: 
  social_engineering_permitted: false
  defense_evasion_permitted: 
  rate_limits:
    requests_per_second_per_target: 50
    parallel_workers_per_phase: 4
    parallel_research_workers: 3
    scan_threads: 10
    default_phase_deadline_seconds: 1800
    research_worker_deadline_seconds: 300
testers:
  - {handle: "$(whoami)", name: "$(whoami)"}
breach_db_permitted: false

For red_team type, override rate_limits with the conservative values:

rate_limits:
  requests_per_second_per_target: 10
  parallel_workers_per_phase: 2
  parallel_research_workers: 2
  scan_threads: 4
  default_phase_deadline_seconds: 3600
  research_worker_deadline_seconds: 300

0d. Present the one-gate confirmation summary

Render to the user:

PENTEST ENGAGEMENT — REVIEW BEFORE PROCEEDING

Client:        
Type:          
Scope (in):    
Scope (out):   
RoE:
  Windows:                  
  Restricted techniques:    
  Social engineering:       
  Defense evasion:          
Rate limits:                

Plan:
  Active phases (in order):     
  Conditional (auto-unlock):    
  Final:                        

Reply with:
  - "go" / "yes" / "proceed" / "start" / "confirm" — start the engagement
  - Any other text — interpreted as an amendment; I'll redraft and ask again
  - "abort" — cancel without creating an engagement

0e. Confirmation loop

Read the user's reply.

  • If matches ^(go|yes|y|proceed|start|confirm)$ (case-insensitive, possibly with trailing punctuation): proceed to 0f.
  • If matches ^abort$: clean up the params tempfile written in 0c (rm -f ), print "Engagement cancelled.", and exit the skill.
  • Otherwise: treat the reply as an amendment to the draft. Update the draft (scope, RoE, plan) per the user's text, then re-render the gate from step 0d. Loop.

Cap the loop at 10 iterations. If the user is still amending after 10 rounds, print: "Confirmation loop exceeded 10 iterations. Use abort to cancel or send a clear go to proceed."

0f. Invoke /pentest-init non-interactive

Skill(skill='pentest-core:pentest-init', args='--non-interactive --params ')

(Use the exact path written in step 0c — do not interpolate $$ or guess the path.)

Capture the success line: non-interactive: engagement created at . Parse out the engagement_id and output_dir. Set environment for subsequent helpers:

ENGAGEMENT_JSON=/engagement.json
OUTPUT_DIR=
TESTER=$(whoami)

Then ensure the active-engagement symlink points at the new engagement: invoke /pentest-switch if needed (or assume /pentest-init set it up — check the skill's behavior).

After /pentest-init returns successfully, delete the params YAML:

rm -f 

0g. Materialize plan.yaml on disk

Write the drafted plan to ${OUTPUT_DIR}/plan.yaml as YAML. Source the engagement-resolver helpers and call log_plan_mutation to record the initial plan creation:

log_plan_mutation "initial" "conductor" "phase_added" "Engagement creation; plan drafted from user prompt and confirmed via gate" "{}" "$(yq -j '.' ${OUTPUT_DIR}/plan.yaml | jq -c .)"

0h. Set engagement.json status to "running"

set_status running

(Helper from engagement-resolver.md.)

Proceed to the Phase Loop section.


Phase Loop

Iterate over plan.yaml.phases in order. For each phase whose status is pending or in_progress (skip complete and skipped):

L0. ABORT sentinel check (pre-dispatch)

Before any dispatch this iteration, check for the ABORT sentinel:

if check_abort_sentinel; then
  log_abort "ABORT sentinel detected by user before dispatch"
  # Halt with halt_reason=user_abort (see Termination + ABORT section)
  # Skip the rest of this phase iteration.
fi

If no ABORT, continue with L1.

L1. Set current phase

set_current_phase 

Update plan.yaml: set this phase's status: in_progress and started_at: . Use yq:

yq -i "(.phases[] | select(.name == \"\")) |= (.status = \"in_progress\" | .started_at = \"$(date -u +%FT%TZ)\")" "${OUTPUT_DIR}/plan.yaml"

L2. Drain the research queue

The discovery-watcher hook (Plan B) accumulates service tuples in research/_pending.jsonl. Drain up to parallel_research_workers of them for this batch:

research_dispatch=$(ENGAGEMENT_JSON="$ENGAGEMENT_JSON" OUTPUT_DIR="$OUTPUT_DIR" \
  bash plugins/pentest-core/skills/pentest-engage/scripts/drain-research-queue.sh)

research_dispatch is now zero or more JSON lines, each one a {host, port, product, version, source, queued_at} tuple to dispatch as a research subagent.

L3. Compute phase worker count

worker_cap=$(get_rate_limit parallel_workers_per_phase)
worker_cap=${worker_cap:-4}

Determine how many phase workers to dispatch this batch. Generally: 1 per in-scope target up to worker_cap, partitioning targets evenly across workers. For phases that operate on a single target each (recon-active per host), one worker per target up to the cap; for phases that synthesize across targets (prioritize), exactly 1 worker.

L4. Build batch_id and dispatch in one message

Generate a batch identifier:

batch_id="batch-$(date -u +%Y%m%d%H%M%S)-$$"

Log the dispatch:

log_dispatch  "$batch_id"

Dispatch all workers + research subagents in a SINGLE message — invoke the Task tool multiple times in the same response. Claude Code runs them in parallel and waits for all. For each phase worker:

Task tool dispatch:
  subagent_type: pentest-worker
  description: " worker N of M against "
  prompt: |
    BATCH_ID=
    WORKER_ID=worker-
    PHASE=
    ATTEMPT_NUMBER=1

    Phase: 
    Targets: 
    Rate limits: 
    Phase deadline: 

    Source the engagement-resolver helpers, invoke the matching pentest-* phase skill against your assigned targets, log every action, and return a worker-result JSON matching plugins/pentest-core/skills/shared/schemas/worker-result.schema.json.

For each research tuple drained in L2:

Task tool dispatch:
  subagent_type: pentest-research
  description: "Research   on :"
  prompt: |
    host: 
    port: 
    product: 
    version: 
    output_path: ${OUTPUT_DIR}/research/___.md
    index_path: ${OUTPUT_DIR}/research/_index.jsonl

    Active engagement: . Produce a per-service threat-intel research file using only public allowlisted sources. Never WebFetch the engagement target.

Send all dispatches in ONE message. Wait for all to return.

L5. Persist worker results to disk

Each worker subagent returned a worker-result JSON. Save each to ${OUTPUT_DIR}/results/${batch_id}/worker-.json so inline-checks.sh can find them.

L6. Run inline-checks

ENGAGEMENT_JSON="$ENGAGEMENT_JSON" \
OUTPUT_DIR="$OUTPUT_DIR" \
BATCH_ID="$batch_id" \
WORKER_RESULTS_DIR="${OUTPUT_DIR}/results/${batch_id}" \
PHASE_TOOL_ALLOWLIST="$(dirname "$ENGAGEMENT_JSON")/../shared/phase-tool-allowlist.json" \
bash plugins/pentest-core/skills/pentest-engage/scripts/inline-checks.sh

(Adjust the PHASETOOLALLOWLIST path to point at the pentest-core plugin's shared dir — production path is ${CLAUDE_PLUGIN_ROOT}/skills/shared/phase-tool-allowlist.json.)

If exit 1 (violations found): the script prints violation:: lines on stdout. Do NOT proceed to the supervisor. Halt the engagement:

log_abort "inline_checks: $(echo "$violations" | head -n1)"
set_status halted

Write ${OUTPUT_DIR}/HALTED.md with halt_reason corresponding to the violation kind:

  • violation:scope_subset → halt_reason: scope_violation
  • violation:restricted_technique → halt_reason: roe_violation
  • All others → halt_reason: quality_critical

(See the Termination + ABORT section for the HALTED.md format.) Then exit the skill.

If exit 0: proceed.

> Note: inline-checks.sh implements 5 of the 7 deterministic checks listed in spec section 7: scope subset, artifact paths, dispatch logging, restricted techniques, and phase tool allowlist. Two checks are NOT implemented in v1: rate-limit verification (sample req/sec from activity log) and currentphase consistency (engagement.json.currentphase matches the BATCH_ID's phase). The conductor does not enforce these in v1; v2 should add them or document the gap explicitly.

L7. Dispatch the supervisor

Build the supervisor's input prompt with: plan.yaml, engagement.json, all worker-result JSONs from this batch, artifact summaries, new service tuples, completed research files for newly-discovered services, activity-log diff since last_supervised_at.

Task tool dispatch:
  subagent_type: pentest-supervisor
  description: "Supervise  batch "
  prompt: |
    Engagement:  phase= batch=

    Read plan.yaml: ${OUTPUT_DIR}/plan.yaml
    Read engagement.json: ${ENGAGEMENT_JSON}

    Worker results (verbatim):
    .json>

    Artifact summaries:
    / with byte counts>

    New service tuples discovered this batch:
    

    Research files completed (relevant to these tuples):
    

    Activity log diff since last_supervised_at=:
    

    Write your verdict to ${OUTPUT_DIR}/verdicts/${batch_id}.json. After writing, output exactly: verdict-written: 

Wait for the supervisor to return.

L8. Apply the supervisor verdict

bash plugins/pentest-core/skills/pentest-engage/scripts/apply-verdict.sh \
  --plan "${OUTPUT_DIR}/plan.yaml" \
  --verdict "${OUTPUT_DIR}/verdicts/${batch_id}.json" \
  --in-place
verdict_rc=$?
  • rc=0: verdict was proceed or replan (mutation applied). Log the verdict:

``bash log_verdict "$(jq -r '.verdict' ${OUTPUT_DIR}/verdicts/${batch_id}.json)" ` If verdict was replan, also log each mutation via logplanmutation`.

  • rc=2: halt requested. To extract the haltreason, scan apply-verdict.sh's stderr output for the substring halt_reason= (it appears in prose like apply-verdict: halt requested (halt_reason=scope_violation) or halting with halt_reason=replan_exhausted). A regex like halt_reason=([a-z_]+) extracts the value reliably. Fall back to the verdict file's halt_reason field if the verdict was supervisor-driven (verdict.haltreason will be set when verdict=halt). Halt the enga

…

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.