Development - #159
Merged
Merged
Conversation
The former SimpleWebAPIDocumentation (detection) and SimpleWebAPITesting
(testing) were two separate registered CLI use-cases, even though detection's
entire output is exactly testing's input. Consolidate both into a single
usecases/web_api/ package exposing one registered use-case, WebAPITesting,
with modes document | test | auto (default auto):
* auto - detect the surface, then pentest it (or pentest --surface
directly when given)
* document - detect and write the OpenAPI spec only
* test - pentest a given --surface (an OpenAPI spec or a sitemap)
The two Simple* classes are de-registered (@use_case -> @DataClass) but kept
as internal phase engines the orchestrator and their unit tests construct
directly; WebAPITesting.run() drives each engine's perform_round with its own
per-phase turn budget and never calls their .run().
Integration details:
* Detection -> testing hand-off with no file round-trip:
OpenAPISpecificationHandler.to_openapi_document() +
SimpleWebAPIDocumentation.built_surface_document(), wrapped by
OpenAPISurface.from_dict. OpenAPISpecificationParser gains an api_data=
kwarg + from_dict() so specs load from memory.
* utils/web_api/target_surface.py normalises an OpenAPI spec OR a sitemap
(URL list / sitemap.xml / HTML) into one surface; a sitemap becomes a
synthetic OpenAPI api_data dict so the parser works unchanged. Sitemap XML
is parsed via a regex over <loc> to avoid XXE / billion-laughs; sitemap
surfaces are schema-less (is_sitemap=True) and degrade to path heuristics.
SimpleWebAPITesting._injected_surface bypasses the config oas/ loading.
* Fix a latent path bug in the handler: numeric path segments are now
replaced whole (/v1/users/1 -> /v1/users/{id}) instead of a bare substring
replace that corrupted /v1/users and /items/11.
Source files moved via git mv (history preserved); usecases/__init__.py, the
web-api test imports, the README command listing and .gitignore output
patterns updated accordingly. Adds tests/test_target_surface.py and
tests/test_web_api_merged.py (mode routing + surface injection). Full suite:
95 passed, 1 skipped.
The web/ use-case family (web-application pentesting) is unrelated and left
untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BRRj9t9uakmTyVQE9t4q6q
The Mac/Codespaces onboarding scripts provisioned a local ansible-ready-ubuntu container and ran `wintermute LinuxPrivesc` against it — a use-case name that no longer exists (now PrivEscLinux/ MinimalPrivEscLinux), so the demo was already broken. The current privesc benchmark (benchmark_privesc.py) drives its own privesc_* Docker fleet and never referenced these files. Delete the demo and everything that only supported it: - MAC.md, CODESPACES.md, .devcontainer/ - scripts/ (all 8 files, incl. Ansible tasks.yaml + hosts.ini) - .env.example.aws (stale AWS/ssh-key onboarding example) Clean up now-dangling references: - README.md: drop the "## Use Cases" section and the .env.example.aws step - .github/copilot-instructions.md: drop scripts//Ansible/Mac/Codespaces mentions; fix stale LinuxPrivesc example -> PrivEscLinux Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N6CzjpEU7X3Xty1ypJZB7m
The testing engine generated PythonTestCase objects and wrote test_cases.txt + python_test.py artifacts under web_api/tests/, but nothing ever read, imported, or ran them: they were a write-only byproduct disconnected from the testing loop, the report, and any pass/fail logic. Delete the PythonTestCase capability and the GenerationTestHandler, drop their wiring from SimpleWebAPITesting (imports, _test_handler, the python_test_case capability, and the generate_test_cases call in _handle_response), and remove the generated tests/ output dir. The write_analysis_to_report call in the same block is preserved, and the parse/ParsedInformation and record_note capabilities are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SLajve9XECKN3pFrgqmqdg
…tils Cleanup of utils/web_api + utils/prompt_generation after the WebAPITesting merge. Behaviour-preserving; full test suite green (94 passed, 1 skipped). Dead code removed: - response_analyzer_with_llm: broken __main__ demo, MagicMock import, and set_purpose / print_results / replace_account (unused). - pattern_matcher: __main__ demo. - openapi_parser: _print_api_details (called a non-existent get_paths), get_protected_endpoints, get_refresh_endpoints. - llm_handler: write-only _get_created_objects accessor (+ its test and the stale commented-out test_call_llm block). Redundant abstractions collapsed: - endpoint_categorizer.categorize_endpoints_with_query now backs both the parser's and the documentation engine's previously-identical categorize_endpoints. - New utils/web_api/http_response.extract_status_code_and_message replaces the status-line regex duplicated in the analyzer and the OpenAPI handler. Detection state machine moved into the use-case: - New usecases/web_api/detection_response_handler.DetectionResponseHandler holds the ExploreStep exploration FSM (handle_response, adjust_path*, get_next_path, check_if_successful, common_endpoints, ...). The base utils ResponseHandler is now phase-agnostic only (evaluate_result, extract_key_elements_of_response, parse_http_status_line, set_response_analyzer). The documentation engine builds the subclass; the testing engine keeps the slim base. - PromptGenerationHelper.get_hint now uses ExploreStep.INSTANCE/SUBRESOURCE/QUERY instead of the magic step-numbers 2/3/6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
…TTPRequest The testing phase drove its own tool-calling loop through LLMHandler so it could mutate the model's proposed request before executing it, which meant web-API captured no LLMResult (cost/tokens) and never used structured tool-call logging. Move that request mutation into a new ProposedHTTPRequest capability whose __call__ finalises the proposal against the current test step (path override, POST-on-setup, bearer-token resolution, empty-body fill, path guards), delegates the send to the real HTTPRequest, then captures response state (adjust_user / extract_ids). Because mutation now lives inside the capability, the testing round runs on the standard get_response + CapabilityManager.run_capability_json path: - get_response gains a tool_choice param (forwarded to raw_completion; ignored when no tools are offered) so "force exactly one capability" is preserved via tool_choice="required" over a single-capability set. - the round records the LLMResult (cost/tokens via log.call_response) and executes the tool call through run_capability_json (structured log.add_tool_call). - removed the now-dead bespoke path (adjust_action / execute_response / _handle_response) and code only it reached: save_resource / extract_resource_name (only the never-taken != "HTTPRequest" branch called them), the unreachable write_endpoint_to_report branch, and the uncalled extract_token_from_http_response. Scope: testing phase only. The detection engine and the response analyzer still use LLMHandler; removing LLMHandler and its context-window retry is a detection follow-up. Limits integration remains deferred (web-API uses manual turn loops). Tests: ProposedHTTPRequest mutation cases, get_response(tool_choice) plumbing, and test_perform_round re-mocked at the new boundary. Suite: 101 passed, 1 skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Finishes moving web-API off its bespoke LLMHandler onto the framework's standard LiteLLM.get_response path, so detection now captures LLMResult (cost/tokens) and structured tool-call logging like every other use-case. The detection FSM must mutate the request before executing it, but that split is expressible with framework pieces: get_response(tool_choice="required") for the forced call and capability.tool_call_to_action to rebuild the same un-executed HTTPRequest action LLMHandler produced. Because that action is exactly what the ExploreStep FSM, handle_response, document_response and the evaluator already consume, the detection loop converts with a boundary swap only — no FSM or consumer re-architecture (the returned is_good was already dead; run_documentation is steered by the handler's counters). - simple_openapi_documentation.run_documentation: get_response + call_response + tool_call_to_action in place of the LLMHandler call. - DetectionResponseHandler.handle_response/handle_http_response: take message/message_id/tool_call instead of completion; add log.add_tool_call; drop the unused categorized_endpoints param and the never-used llm_handler. - ResponseAnalyzerWithLLM: its one forced call converted the same way; constructor now takes llm + capabilities instead of llm_handler. - Removed dead plumbing: base ResponseHandler / DetectionResponseHandler stored an llm_handler they never used; OpenAPISpecificationHandler dropped its llm_handler param and 4 _add_created_object calls (no-ops into a reader-less store). - Deleted utils/web_api/llm_handler.py + tests/test_llm_handler.py; the context-window retry is removed with it. Tests updated to the get_response boundary. Suite: 99 passed, 1 skipped. git grep for LLMHandler/execute_prompt_with_specific_capability/llm_handler over src now returns no code hits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
The web-API use-cases run on SimpleStrategy (a max_turns loop) and never used the framework's Limits, so runs enforced no cost/token/duration budget and couldn't report why they stopped. Wire Limits through the whole flow: - WebAPITesting orchestrator: limits.start() in run(); shares one Limits with both phase engines (engine.limits = self.limits); both phase loops stop on limits.reached() and register_round() per turn; a reached limit ends the run as a failure with limits.reason (matching AutonomousUseCase), else success. - Detection and testing engines: their inner LLM loops also check limits.reached() (so one round can't overshoot a token/cost budget), and every get_response calls limits.register_message(llm_result) to accumulate tokens + cost. - ResponseAnalyzerWithLLM: takes a limits param and registers its analysis calls too. - Safe default: where limits is unset (direct construction / tests), each component defaults to a never-reached Limits(0,0,0,0), so existing behaviour is unchanged and bounded only by the per-phase turn caps; the CLI injects a real Limits that enforces. max_rounds now counts rounds across both phases as a global cap, alongside the existing per-phase detection_max_turns / max_turns caps (kept as secondary bounds). Tests: mocked-get_response tests give llm_result numeric total_tokens/cost; added tests that a cost limit exhausted in detection skips testing and is reported as the failure reason, and that the default limits is never-reached. Suite: 101 passed, 1 skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
A readability pass over web_api and its supporting capability/utils code: remove
dead code and degenerate/over-built constructs, behaviour-preserving throughout.
Removed dead components (deleted files):
- SubmitHTTPMethod capability: unreferenced repo-wide (old "find all HTTP methods"
flow), not exported from capabilities/__init__.
- ParsedInformation capability: constructed in the testing setup but never invoked
(the analyzer only forces http_request/record_note; the passed capacity was
write-only). Removed the whole chain: parse_capacity, the dead http_capability,
the _context test_cases/parsed entries, and the analyzer's capacity param.
- Evaluator: benchmark-only metrics (needs ground-truth config, wrote a .txt every
detection round, had a latent unbound-variable bug) + its two per-round calls.
Removed dead methods/aliases: found_all_endpoints, write_endpoint_to_report,
SitemapSurface.from_html, a dead nested substitute() in _pentesting_core, and the
no-op `Context = Any` alias (replaced by honest dict annotations).
Simplified PatternMatcher: its four regex patterns and matches_any_pattern/
replace_parameters machinery are provably equivalent to a single
re.sub(r"/\d+", "/{id}", path) in every reachable input (the query-value branch and
the "/1" fallback are unreachable). ~110 lines -> ~17, with a new characterization
test pinning the behaviour.
Deferred (needs characterisation tests first): the 5-way-duplicated prompt-class
transform methods, the side-effecting analyzer parser, and splitting the
mixed-purpose PromptGenerationHelper.
Net -172/+25 across 10 files + 3 deletions. Suite: 104 passed, 1 skipped;
git grep confirms the removed symbols are gone from src.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
…ders
Behaviour-preserving readability refactor of the prompt-generation code, guarded by
characterisation tests written first (seeded golden pentesting prompts for cot/tot/icl)
that stay byte-identical before and after.
Prompt-class dedup:
- Hoist the single _get_pentesting_steps into BasicPrompt, deleting the two ~55-line
identical copies in StatePlanningPrompt and TaskPlanningPrompt.
- Unify the transform method name: InContextLearningPrompt's
transform_into_prompt_structure_with_previous_examples -> transform_into_prompt_structure
(now one abstract on BasicPrompt + three overrides).
- Extract the shared _step_fields(test_case, counter) used by the CoT/ToT/ICL transforms,
removing ~18 duplicated lines from each (and a stray debug print in ToT).
PromptGenerationHelper cleanup:
- Move the three pure _get_{sub,related,multi_level}_resource_endpoint builders into a new
utils/web_api/endpoint_shapes.py as functions (dropping the unused `name` param); remove
them from the helper and update the detection-handler call sites.
Tests: tests/test_prompt_characterization.py (the golden safety net) and
tests/test_endpoint_shapes.py. Net -222 lines across 8 files + 3 new files.
Suite: 110 passed, 1 skipped. git grep confirms one _get_pentesting_steps definition and
no remaining duplicate transform/builder names.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
parse_http_response tangled HTTP parsing (status/headers/body, JSON decode, HTML stripping) with stateful account/token capture and three stray debug prints. Faithful, byte-identical factoring: the method is now a readable parse skeleton that calls four named, guarded side-effect helpers extracted from the inline branches: - _capture_token(body): token -> analyzer/current_user/matching account - _capture_ids(body): matching-value id -> account - _note_account_from_list(body): list-branch capture - _note_current_account(): fallback account registration Each returns early when its guard fails, so the skeleton calls them unconditionally, matching the old inline behaviour exactly. Both callers are unchanged and still fire the capture, so behaviour is byte-identical. Also removed the three debug prints, replaced body.__contains__(x) with x in body, and dropped the always-true (body != '' or body != "") tautology. Characterisation tests written first (previously the side effects had no coverage): token capture and id capture with a real prompt_helper, plus the existing parse-return tests. All stay green unchanged through the refactor. Suite: 112 passed, 1 skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
The generic OWASP web/API security knowledge (auth, authz, injection, session, rate-limiting, misconfig, logging, ...) was locked inside web-api's scripted, OpenAPI-bound scenario generators, while the separate `web` agents had no structured coverage at all. Extract that knowledge into an agent-agnostic form and expose it as a capability both agents can use. - utils/pentest_playbook.py: the generic knowledge as data (topic -> checklist of what to test / verify), no target or endpoint binding; case/separator-insensitive lookup. - capabilities/pentest_playbook.py: PentestPlaybook, an LLM-callable knowledge lookup modelled on RecordNote (describe() + async __call__(topic)); pure, no side effects. - Wire PentestPlaybook into the direct web agents (with_explanation, with_shell), which previously had no structured OWASP checklist to consult. For web-api the same module is usable as a data source (its testing round forces a single tool via tool_choice, so a callable capability is not reachable there). Tests: tests/test_pentest_playbook.py (topics, lookup, capability, describe). Suite: 117 passed, 1 skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
…tion tests
get_path_and_schema crashed on a login endpoint with no schema ("argument of type
'NoneType' is not iterable"), even though its callers already handle a None schema
(they skip, or pass it to helpers that guard None). Return (path, None) instead of
crashing. This is a genuine bug fix and it unblocks the SESSION_MANAGEMENT generator on
the test fixture.
Add tests/test_pentesting_scenarios.py: seeded (Faker/random + pinned secrets.token_hex)
golden md5s of explore_steps(purpose) for the purposes that produce non-empty
deterministic output on the fakeapi fixture (authorization, input-validation,
error-handling, session, xss, business-logic, misconfiguration) — the safety net for the
upcoming scenario-file cleanup. Suite: 117 passed + 7 subtests, 1 skipped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Introduce PenTestingCore._test_case(objective, *, steps, path, token, expected_response_code, security) which builds one scenario test case and documents in one place the parallel, index-aligned list contract the prompt transforms rely on (previously implicit in ~82 inline dicts). Convert _pentesting_session.py to use it and drop its redundant "# This prompt tests..." comments (234 -> 212 lines). Behaviour-preserving: the SESSION_MANAGEMENT scenario golden (tests/test_pentesting_scenarios.py) is unchanged. Suite: 118 passed + 7 subtests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Convert the 10 scenario dicts in _pentesting_misc.py (error-handling, misconfiguration, logging) to PenTestingCore._test_case(...) and drop the redundant per-dict comments (359 -> 316 lines). Behaviour-preserving: the ERROR_HANDLING / SECURITY_MISCONFIGURATIONS scenario goldens are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Replace the remaining inline test-case dict literals in _pentesting_authz.py with self._test_case(...) calls and drop the redundant duplicated comment block. Behaviour-preserving: the AUTHORIZATION golden (test_pentesting_scenarios) is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Replace the 26 inline test-case dict literals in _pentesting_injection.py with self._test_case(...) calls and drop the redundant per-dict '# This prompt/request tests...' comments. Behaviour-preserving: the INPUT_VALIDATION / CROSS_SITE_SCRIPTING / BUSINESS_LOGIC_VULNERABILITIES goldens (test_pentesting_scenarios) are unchanged. The pre-existing malformed 'security' set literal in test_css (dead on the fixture) is preserved verbatim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Replace the 5 inline test-case dict literals in _pentesting_ratelimit.py with self._test_case(...) calls and drop the redundant per-dict comments. This file's scenarios are not exercised by the fixture golden (RATE_LIMITING raises on fakeapi), so the conversion was verified byte-identical with an AST-equivalence check: each converted call reconstructs the same 6 keys from the same value expressions (ast.dump match) as the original dict literal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Replace the 16 inline test-case dict literals in _pentesting_auth.py with self._test_case(...) calls and drop the redundant per-dict explanatory comments. This file's scenarios are not exercised by the fixture golden (AUTHENTICATION yields no cases on fakeapi), so the conversion was verified byte-identical with an AST-equivalence check (each converted call reconstructs the same 6 keys from the same value expressions). Pre-existing latent bugs on dead paths (e.g. random.sample without a count) are preserved verbatim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Convert the remaining 8 inline test-case dict literals in _pentesting_core.py (verify_setup, resource_prompts, resource_endpoints, generate_user) to self._test_case(...) calls and drop the redundant explanatory comment block. The _test_case builder's own return dict is left untouched. With this all 82 scenario test-cases across the _pentesting_* mixins now go through the single builder (0 raw dict literals remain). Verified byte-identical: the golden suite is unchanged and an AST-equivalence check confirms each converted call reconstructs the original dict's keys/value-expressions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
…ication Pin the exact behaviour of the shared native-tool-calling round (assistant message then tool results appended in order, one register_message + register_round, execution via run_capability_json) that the web agents and MinimalToolCallPrivEscLinux rely on, so the upcoming CapabilityRegistry and run_tool_calling_turn refactors can prove they preserve it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Extract the duplicated add_capability / get_capability / run_capability_json / run_capability_simple_text / get_capability_block logic into a single CapabilityRegistry mixin (capability.py). Agent and CapabilityManager now both inherit it, so capability management has one implementation shared by every use-case family: the web agents and MinimalToolCallPrivEscLinux (Agent), and the CommandStrategy priv-esc use-cases and the SimpleStrategy web-api engines (CapabilityManager). No use-case files change - they keep calling the same methods. Also fixes CapabilityManager's class-level mutable-default (_capabilities/ _default_capability shared across instances) by storing them per instance, and gives Agent the run_capability_simple_text that TemplatedAgent already calls. Behaviour-preserving: the ChatAgent characterization and the full suite stay green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Add run_tool_calling_turn() and route both the ChatAgent loop and the web-api testing engine through it, removing the hand-rolled duplicate. The helper owns one LLM tool-calling turn: ask the model, log/register the result, append the assistant message, then execute each tool call via run_capability_json and append a tool message. Parameters cover the caller differences - offered capabilities, tool_choice, sequential vs concurrent execution, an optional result transform, and an optional per-call reporting hook. - ChatAgent.perform_round (web agents + MinimalToolCallPrivEscLinux) delegates to it; the old Agent.run_tool_calls is folded in and removed. Byte-identical: the ChatAgent characterization stays green, and the default call omits tool_choice exactly as before. - SimpleWebAPITesting._run_testing_step becomes one call with tool_choice= required, sequential=True, a response-condensing transform, and a new _analyse_and_report hook (per-call vulnerability + LLM analysis). The engine test stays green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
The scenario generators inlined the same generic security-check and expected-response-code sentences verbatim many times (an access-control note 7x, the brute-force block 3x, etc.). Move those 14 full generic strings into named constants in utils/pentest_playbook.py (a GENERIC_NOTES section beside the topic checklists) and reference them from the _pentesting_* scenario files, so the generic knowledge has one home and the copies are gone. Byte-identical: each constant was verified to equal the original literal exactly (AST-decoded), all occurrences were replaced (counts checked), and the scenario goldens stay green. The golden-unguarded files (auth) are covered by the exact-value proof. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
The only test exercising this loop (integration_minimal_test.py) is skipped - it imports a pre-PR-#141 module layout (usecases.examples.agent / usecases.privesc.linux) that no longer exists. Add a live characterization that drives the real PrivEscLinux and MinimalPrivEscLinux through a scripted enumerate -> escalate -> root sequence via a fake SSH connection + fake LLM, pinning that run({}) returns True on root and False when the turn budget is exhausted. Guards CommandStrategy.run before it is converged onto the shared AutonomousUseCase.run + Limits loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Neither has any live reference - the only importer of their conceptual ancestors is tests/integration_minimal_test.py, which is skipped (it targets a pre-PR-#141 module layout that no longer exists). Drop the now-unused mako Template and log_conversation imports with them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Collapse the duplicate run loops onto AutonomousUseCase.run so every leaf use-case shares one Limits-driven loop: - AutonomousUseCase.run now returns success (limits.reason is None) so orchestrators like call_usecase_from_usecase can consume the outcome; callers that ignore the return are unaffected. - CommandStrategy is based on AutonomousUseCase; its bespoke max_turns run loop is removed. perform_round() (no turn) runs the round, signals success via limits.complete() on check_success, and calls limits.register_round(); the capability block is exposed to the template in before_run(). init() seeds a Limits and folds max_turns into the round cap (strictest of max_turns and any injected --max_rounds wins); get_next_command registers the round's LLM call so the cost/token/duration caps now also bound strategy runs (uniform Limits). - SimpleStrategy's vestigial max_turns loop (never reached - WebAPITesting overrides run(); the engines are driven externally) is replaced by an explicit NotImplementedError stub; the dead _got_root is dropped. The linux/windows/minimal privesc use-cases inherit CommandStrategy.perform_round and need no changes. Behaviour-preserving: the strategy characterization (run-to-root True / budget-exhausted False) and the full suite stay green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLA8kcbLZvM3gwwvPCQEjW
Ports the attack tool from the cochise research prototype into hackingBuddyGPT as a new `AD` (Active Directory assumed-breach) use-case, reusing existing infrastructure and adding no new dependencies. The log replayer and analysis tools are intentionally not imported. New package src/hackingBuddyGPT/usecases/ad/: - ad.py: ADPlanner (persistent strategic agent) + ADUseCase (@use_case, CLI name `AD`). Seeds cochise's 4-message plan history, delegates via perform_task, ends early via a new objective_complete tool, optional history compaction. - executor.py: ADExecutor (ephemeral per-task worker) + PerformTaskCapability, modelled on SubAgentCapability: carves parent_limits.sub_limit, runs the worker with the SSH tool + a local Knowledge + a complete tool, and merges the worker's dirty knowledge back into the planner's global Knowledge. - knowledge.py: near-verbatim port of cochise's dirty-flag Knowledge. - templates/: scenario/planner prompts copied verbatim; the executor prompt is rendered with stdlib string.Template (Mako's `##` comment would eat markdown headers). Shared infra reused by the worker's SSH tool: - utils/connectors/async_ssh_connection.py: AsyncSSHConnection (asyncssh, one channel per command for genuine parallel execution, 600s timeout, lazy connect). - capabilities/ssh_execute_command.py: SSHExecuteCommand (tool name execute_command); registered in capabilities/__init__.py. Reused rather than re-implemented: the shared Limits run loop, ChatAgent / run_tool_calling_turn, LiteLLM.get_response, function_call_capability / Capability.to_model (so numpydoc is not needed), JsonlLogger, and the @configurable/parameter config system. The scenario is a swappable file via --scenario_path (bundled AD default) rather than hardcoded, keeping target scope config-driven. Tests: tests/test_ad.py (Knowledge merge/resolve, registration, perform_task happy-path + forced-summary fallback). Full suite 128 passed / 1 skipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XoAhn2jzM1Leq9X4MjSiet
…riv-esc Extract shared bases across the capability and use-case families, remove dead code, and make the Windows priv-esc use-case live and actually functional. capabilities: - add SSHCommandCapability base for the two execute_bash_command capabilities (shared fields, tool name, prefix-stripping, describe/banner tail) - add TestCredentialCapability base (shared test_credential name + auth-error helper) - fix PSExecRunCommand.describe (@Property -> method) and unify the base-import style - PSExecTestCredential now verifies the logged-in identity (whoami /groups) and only reports root success for a genuinely elevated token, instead of always claiming admin - remove the YAMLFile no-op capability (its __call__ body was commented out and it was only stored in a write-only handler attribute) usecases: - add WebTestingAgent base for the three web agents (flag params, shared prompt fragments, SubmitFlag/EndRun wiring); emitted system prompts stay byte-identical - add TemplatedCommandPrivEsc base + hoisted template for the minimal Linux/Windows strategy priv-esc use-cases - register PrivEscWindows (was decorated but never imported, so unreachable) and give it a working check_success utils: - shell_root_detection: add is_admin_from_whoami / check_windows_admin_success tests: - add Windows root-detection and PSExecTestCredential coverage - drop the permanently-skipped integration_minimal_test.py (pre-PR#141 layout) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViJRtekD3nmE6yqdTe77Bz
Move the privilege-escalation modules out of the flat usecases/ namespace into a dedicated priv_esc/ subpackage, matching the existing web/, web_api/ and ad/ convention. Pure relocation + import rewiring, no behavior change: use-cases register by class name via @use_case, so CLI names, benchmark aliases and packaging entry points are unaffected. - move linux_privesc, minimal_linux_privesc, minimal_linux_privesc_tool_calling and windows_privesc into priv_esc/ - rename the shared base _privesc_common.py -> priv_esc/_base.py (matches web/_base.py); subclasses import it relatively - add priv_esc/__init__.py re-exporting the four modules so their @use_case decorators still run on package import - collapse the four priv-esc star-imports in usecases/__init__.py into `from .priv_esc import *` - keep call_usecase_from_usecase.py in place, repoint its PrivEscLinux import - update the two priv-esc test modules to the new import paths Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RezLqdd2GNRMXJxv2jD2cy
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overall the lines-of-code stayed the same, but we improved readability a bit and added the ActiveDirectory use-case. More cleanups to come.