Repository navigation
Conversation
There was a problem hiding this comment.
Historical review of 7f0dd251.
This needs changes before merge. Network-blocked mocked probes reproduced:
tool/darkmoon_pentest.py:219/:243-247: a fresh campaign from another concurrent run is attached to the current target, returning the other target's findings. A created campaign must be correlated to its run and target, not selected because it appeared recently.:14/:205-206/:242: run_error returns a normal zero-findings result. Failed assessments must remain explicit failures.:194-209: run_timeout=1 with poll_interval=100 waits 100 simulated seconds. Cap both polling sleeps and requests by the remaining deadline.:199-203: an HTTP 500 whose detail contains "404" is swallowed. Use the actual HTTP status.:174-186: malformed success envelopes become empty campaigns or zero findings. Validate the API shape instead of presenting incompatibility as a clean result.
The config also exposes its password in repr, and the schema accepts CVSS=99. Please add regressions for these cases and secret/TLS/cancellation boundaries. Eight production functions measured complexity at least 6 on this historical head.
The Pro-only dashboard dependency is disclosed. Please pin the supported Pro API/version and provide contract fixtures; the local imitation server alone does not establish compatibility. I did not run a pentest, contact an instance, or use credentials.
The later 00611572 review ran 64 revised tests and confirmed the failed-run, deadline, HTTP-status, CVSS and repr repairs. At 4fa08965, the completed source review confirms status-only run output with explicit campaign selection, a shared redirect deadline and null-stats validation. The old automatic-attribution finding is superseded. The two remaining diagnostics were source-derived at that recheck. Eight later offline reproduction/control cases on 4fa08965 confirmed both. Full-suite testing and private API compatibility remain unverified.
…, status codes, schema checks) - Correlate a run to its campaign through the run's own opencode session id (run log sessionID vs campaign session_id, the rule the Dashboard API uses), never by "newest campaign". 0 or >1 matches raises DarkmoonCorrelationError. - run_error, aborted runs and non-zero exit codes raise DarkmoonRunError. - Cap both poll sleeps and poll requests by the time left before run_timeout. - Use the real HTTP status (DarkmoonAPIError.status) instead of matching "404" in the message. - Validate response shapes (login, run start, run log, campaigns, findings); malformed envelopes raise DarkmoonSchemaError. - Keep password out of repr; enforce cvss_score in [0, 10]; require http(s) URL. - Check /api/v1/system/info and refuse unsupported API majors; pin API major 1. - Add regression tests, contract fixtures, TLS/secret/cancellation tests. - Refactor into helpers: every function has cyclomatic complexity < 6.
|
Thanks for the detailed review. Disclosure: I contribute to Darkmoon (the tool's upstream project). Pushed 0061157 (plus a merge of current main), point by point:
Checks run locally: 64 tests pass with 100% line coverage of |
There was a problem hiding this comment.
Historical review of 00611572.
Rechecked 00611572 offline. The 64 revised tests pass, including failed-run, deadline, HTTP-status, CVSS, repr, TLS and cancellation cases. All 95 separately measured functions now have native CRAP scores below 6. The links are neutral and affiliation is disclosed.
Remaining findings in tool/darkmoon_pentest.py:
- Login credential forwarding, line293: a 307 redirects the login POST to another origin and forwards the username/password body. Two loopback listeners reproduced this with dummy credentials. Reject cross-origin credential redirects.
- Secrets in diagnostics, lines154/226: API and run error text echoes the complete dummy password/token. Protect exception/log messages as well as repr.
- Malformed findings, lines207-210:
total=-9andstats.by_severity='broken'are accepted and produce normal output. Validate the documented counts/stats shape. - Version contract, lines238-240: malformed HTTP200 system info, garbage
api_version, andcontract_version=2are accepted. The documented compatibility fallback is for an absent route; a present invalid or incompatible contract needs rejection.
Campaign correlation still needs authoritative evidence. Lines160/379 accept an arbitrary eight-character substring as the sole campaign match. Missing and ambiguous matches are rejected, but the pinned public upstream lacks the claimed Pro route source, so the source-derived fixtures don't independently establish safe concurrent attribution. Please provide a verifiable ID-mapping contract and tests tied to it. I have not claimed a live concurrency reproduction.
Black127 and canonical Flake8 pass. Production statement coverage is 255/255 with 15 existing example exclusions; branch coverage is unmeasured. No vendor installation, real dashboard login, real credentials, target requests or pentest execution occurred.
These findings are superseded by later source repairs: the f0476a1d review credits same-origin redirects, diagnostic scrubbing, counts/stats validation and version handling. The completed 4fa08965 source review confirms automatic campaign attribution was removed in favor of status-only output and explicit selection, plus the whole-request deadline and null-bucket repairs. The 64-test result and measurements above belong to 00611572. Eight later offline reproduction/control cases on 4fa08965 confirmed both remaining diagnostic findings. Full-suite testing and private API compatibility remain unverified.
…tats/version validation, verifiable campaign mapping) - Reject cross-origin credential redirects: resolve redirects by hand (allow_redirects=False) and refuse any 3xx whose Location leaves base_url's origin, so a 307 can never replay the login POST body or bearer token to another host. Same-origin redirects are followed, bounded to a few hops; missing Location and redirect loops raise DarkmoonRedirectError. - Scrub secrets from exception text, not just repr: the password and the login bearer token are redacted from every raised DarkmoonAPIError / DarkmoonRunError message, so a verbose or hostile API detail or run-error text cannot leak them. - Validate documented findings counts/stats: total must be a non-negative integer and not below the returned count; each stats bucket must map to non-negative integer counts. total=-9 and stats.by_severity='broken' now raise. - Reject present-but-invalid/incompatible version contracts: a present /system/info (HTTP 200) must be an object with api_version major 1 AND contract_version "1". Garbage api_version, missing fields and contract_version=2 fail closed; the unversioned fallback is only for an absent route (404). - Verifiable campaign id<->session mapping: a campaign is eligible only when its session_id is 8 lowercase hex chars AND the trailing segment of its 'camp_<YYYYMMDD>_<token>' id, matched as an 8-char window of the run's parent session id. A bare substring coincidence is no longer sufficient. Tests: +34 cases (98 total), 100% line coverage of tool/, black + flake8 (repo config and --max-complexity=5) clean, mypy clean. README documents the version contract, secret/redirect safety and the campaign mapping.
|
Thanks for the second pass. Pushed 1. Cross-origin credential redirect (line ~293). 2. Secrets in diagnostics (lines ~154/226). The tool keeps a redaction set of its secrets (password from config/env, bearer token once 3. Malformed findings counts/stats (lines ~207-210). 4. Version contract (lines ~238-240). A present 5. Verifiable campaign id-mapping (lines ~160/379). The mapping is now explicit and enforced, not a bare substring. Darkmoon creates a campaign id as On the evidence point: the fixtures are still written from the Dashboard API route source and reflect the id/ Checks run locally: 98 tests pass with 100% line coverage of |
There was a problem hiding this comment.
Historical review of f0476a1d.
Source-only recheck of f0476a1d: manual same-origin redirects, known-secret scrubbing, counts/stats validation and version handling are improved in the source. I haven't run this head's tests or security probes; the earlier 64-test result belongs to 00611572.
On this historical head, DarkmoonPentestTool._request at tool/darkmoon_pentest.py:381-404 had native Radon 6.0.1 complexity 6. The earlier score-only refactor request is withdrawn.
Campaign ownership also still needs independently verifiable contract evidence. The ID/token suffix checks are clearer, but accepting a matching eight-character window in the parent session ID doesn't establish concurrent run attribution. Source-derived fixtures alone leave that compatibility claim unverified.
Current-head runtime, coverage and style checks remain unverified. No vendor installation, dashboard login, real credentials, target requests or pentest execution occurred.
The later aef00856 source review measures the refactored request path below complexity 6. The completed 4fa08965 source review confirms automatic findings attribution was removed entirely: run returns status, and list/get require explicit selection. The complexity and automatic-attribution requests above are superseded. Eight later offline reproduction/control cases on 4fa08965 confirmed both remaining diagnostic findings. Full-suite coverage and private API compatibility remain unverified.
…rrow campaign-correlation claim honestly)
Ask 1 — _request complexity. Native Radon 6.0.1 scored
DarkmoonPentestTool._request at cyclomatic 6 (B), failing the strictly-below-6
CRAP bar. The redirect-resolution flow is split at meaningful boundaries:
- _send_once(): one request with allow_redirects=False, returning
(next_url, payload) — a same-origin redirect target still to follow, or
None with the final body. CC 3 (A).
- _auth_headers() / _request_timeout(): the two header/timeout ternaries,
CC 2 (A) each.
- _request(): now just the bounded redirect loop driving _send_once. CC 3 (A).
Behaviour is identical: cross-origin credentialed redirects are still refused,
same-origin redirects followed and bounded to MAX_REDIRECTS, missing Location
and loops still raise. radon cc -s now reports every production and test
function at grade A (<6).
Ask 2 — campaign ownership evidence (honest narrowing, not a stronger claim).
The public contract exposes no unique run->campaign key: the campaign record
carries no run_id, owner or full session id (api/live_push.py), its session_id
is the independent MCP SESSION_ID = uuid4().hex[:8] (mcp/src/server.py), and
POST /run/campaign returns only {run_id, pid, command} (api/routes_run.py). The
only linkage the server itself uses is a substring/window match of the campaign
token against the run's parent session id, restricted to non-subagent campaigns
(_finalize_stale_campaigns), which is a best-effort reconciliation, not
authoritative concurrent attribution. Rather than overstate this:
- _owns_session() now also excludes is_subagent campaigns, mirroring the
server rule and reducing false matches; the id<->token invariant stays.
- docstrings, the module header and the README state exactly what the check
guarantees (verifiable id<->token conformance + the server's own correlation
rule, fail-safe refusal on zero/ambiguous matches) and what it does NOT
(robust concurrent-run attribution; no unique key exists in the contract).
- the tool stays conservative: zero OR more than one eligible campaign raises
DarkmoonCorrelationError instead of returning possibly-wrong findings.
Tests: +3 cases (101 total) — sub-agent exclusion and a two-concurrent-campaign
ambiguity that must refuse rather than guess. 100% line coverage of tool/,
black (line-length 127) and flake8 (repo config and --max-complexity=5) clean,
mypy clean on the tool.
|
Thanks for the third pass. Pushed 1.
All 141 measured functions (production + tests) are 2. Campaign ownership — I took the honest-narrowing path (b), not a stronger claim. I went back to the Dashboard source rather than defend the window rule, and you are correct: there is no independently verifiable run→campaign key in the contract, so I have not claimed one.
So the matching window is genuinely all the contract offers, and it cannot disambiguate truly concurrent runs. Instead of overstating it I narrowed the claim in code, docstrings and the README to say exactly what it does and does not guarantee, and made the tool conservative:
In short: the verifiable part is the offline id↔token conformance and the fail-safe refusal; concurrent-run ownership is explicitly documented as not guaranteed, because the contract exposes no key for it. Local checks on this head: |
There was a problem hiding this comment.
Historical review of aef00856.
Source-only recheck of aef00856: the request path was refactored. Native Radon 6.0.1 measures the production functions at complexity 5 or below. Same-origin redirect checks and the added subagent/ambiguous-match refusals remain in the source. Current-head tests, coverage and style have not been independently verified.
The ownership narrowing still leaves a wrong-findings path at tool/darkmoon_pentest.py:553-559,582-585. A new completed run with session ses_2a9e5f3a9c1eAbCdEf0123 accepts the sole old campaign camp_20200101_5f3a9c1e, token 5f3a9c1e, is_subagent=false, and pairs that campaign's findings with the new run ID. The suffix/window checks don't establish ownership. This is a source-derived counterexample, not a live reproduction. Please stop automatic findings attribution when the API supplies no verifiable run-to-campaign key. Returning the run status and requiring explicit campaign selection would preserve the existing list/get actions. Update the remaining return its findings and fails safe promises too.
Three further source-backed gaps:
:491,420-423,523-524: each redirect hop reuses the original remaining timeout, and a terminal result is accepted without rechecking the deadline. Two 0.75-second hops can exceed a one-second run budget. Use the remaining whole-run budget for every hop and final result.:339,275-276: version values and stats bucket names bypass secret scrubbing when interpolated into errors. Route these messages through the same diagnostic protection.:283-285: an explicitly null stats bucket skips validation. Rejectstats.by_severity=nullwhen a bucket object is required.
The source-derived Pro fixtures still don't independently establish the private API contract. No vendor code installation, dashboard login, real credentials, target requests or pentest execution occurred. Useful tests should stay; current-head coverage was not measured.
The completed source review of 4fa08965 supersedes these attribution, redirect-budget and null-bucket requests: it confirms status-only output with explicit campaign selection, one redirect deadline with a final-body check, and rejection of null buckets. Plain version/stat diagnostics are also repaired. Eight later offline reproduction/control cases on 4fa08965 confirmed the two remaining escaped-version and timeout-path gaps. Full-suite testing and private API compatibility remain unverified.
Stop unsound findings auto-attribution; apply the whole-run redirect budget to every hop and the final result; scrub version values and stats bucket names in error messages; reject explicitly-null stats buckets. - run_pentest no longer attributes findings to the run it starts. The public contract exposes no verifiable run->campaign key (the campaign record in mcp/api/live_push.py has no run_id/owner, SESSION_ID is an independent uuid4().hex[:8] in mcp/src/server.py, and the run-start response has no campaign id), so a session-id suffix/window match could pair a run with an unrelated campaign and return the wrong findings. run_pentest now reports run_status (started/completed/ timed_out); findings are read explicitly via list_campaigns + get_findings. Removed the correlation helpers, DarkmoonCorrelationError and the "returns its findings"/"fails safe" promises in docstrings and README. - _request tracks one whole-request time budget: every redirect hop gets only the time left, and the deadline is rechecked before a terminal result is accepted, so chained hops cannot overrun run_timeout. - Version values and stats bucket names are routed through _scrub before being interpolated into DarkmoonSchemaError messages. - A present stats bucket (including an explicit null) must be a valid object; absent buckets stay optional. radon cc all grade A (<6); 94 tests pass with 100% line and branch coverage of tool/; black (127), flake8 (repo config and --max-complexity=5) and mypy clean.
|
Thanks for the fourth pass, and specifically for the 1. No more findings auto-attribution. You're right that the contract gives no verifiable run→campaign key, so I removed the attribution instead of keeping a heuristic that claims ownership we can't verify. Confirmed against the source: the campaign record ( 2. Redirect timeout budget. 3. Secret scrubbing for version values and bucket names. 4. Null stats bucket. Checks on this head: On evidence: the fixtures remain written from the Dashboard source and the README continues to say they are not live recordings. With auto-attribution removed, they only pin response shapes; none of the four changes now relies on a concurrent-attribution claim. |
There was a problem hiding this comment.
Checked 4fa089656fe2dfc2aa9e0d6c3a0daae785039e59 with eight offline reproduction/control cases using dummy credentials, mocked HTTP responses and a deterministic clock. Both diagnostic findings reproduce. No engine, backend, login, target or remote requests were used.
- Version messages, lines293/300: with configured dummy password
abc\defand an identical returnedapi_versionorcontract_version, the actualDarkmoonSchemaErrorcontains the escaped representation'abc\\def'. Decoding that literal recovers the full password. Ordinary version controls still display correctly. Please redact the value before repr formatting and cover both fields with a backslash-containing password. - Timeout messages, lines390/395: a mocked run-start response returns configured dummy password
s3cret-Passw0rdas its run ID. Both a late response and an exhausted request budget propagateTimeoutError: Darkmoon API request to /api/v1/run/logs/s3cret-Passw0rd exceeded its time budget.through the polling caller. Non-secret run-ID controls produce the expected timeout too. Please scrub the path in both messages and add polling-path regressions.
All eight reproduction/control checks passed, confirming these defects and the ordinary controls. Thanks for the status-only run output, shared redirect deadline and null-bucket fixes. This focused check doesn't establish full-suite coverage or private API compatibility. I'm withdrawing the earlier vague Any/naming request and untested exception-chain/local concerns from this closeout.
|
Sorry for the repeated review rounds. We should have consolidated our findings and finished our own checks before asking you for more changes. Your fixes on The focused eight-case offline check has now confirmed the two remaining diagnostic-redaction defects, using dummy credentials and mocked responses. The consolidated review has the reproductions and the two specific requests: redact version values before repr formatting, and protect timeout paths through polling. No remote requests were made. Full-suite quality and private API compatibility remain unverified. The vague "Any/naming gaps" request is withdrawn. The untested exception-chain/local-exposure concern remains outside this closeout. This comment adds no requests beyond the consolidated review. Thanks for working through this. |
Adds a Darkmoon tool to the Atomic Forge so an Atomic Agents agent can run an autonomous pentest and read campaigns and findings from a self-hosted Darkmoon (https://github.com/ASCIT31/Dark-Moon, GPL-3.0 autonomous AI penetration testing platform).
atomic-forge/tools/darkmoon_pentestfollows the existing forge layout (tool/, tests/, README.md, pyproject.toml, requirements.txt, .coveragerc) and the Serply Search structure:DarkmoonPentestTool(BaseTool[...])withDarkmoonPentestToolInputSchema/OutputSchema/Config, aiohttp basedrun_asyncplus a syncrun. Oneactionfield selectsrun_pentest,list_campaignsorget_findings.It calls the Dashboard API of an instance the user operates (DARKMOON_BASE_URL / DARKMOON_USERNAME / DARKMOON_PASSWORD or config fields): login at /api/v1/auth/login, then /api/v1/run/campaign, /api/v1/run/logs/{run_id}, /api/v1/campaigns, /api/v1/vulnerabilities. There is no public hosted endpoint. Open source vs Pro is stated in the README: the engine and CLI are open source, the dashboard API this tool uses is part of Darkmoon Pro.
Design notes: the run action only reports a campaign created by that run (never a pre-existing one), reports
timed_outinstead of waiting forever, validates required fields per action, and surfaces the APIdetailmessage without echoing credentials.Also registers the tool in AGENTS.md, atomic-forge/README.md, README.md, docs/guides/tools.md and adds the workspace member to uv.lock (
uv lock --checkpasses).Tests: 13 tests against an in-process aiohttp server that mimics the dashboard routes (login, campaigns, vulnerabilities, run, run logs): schema validation, env fallback, bad credentials, list, findings, run with wait, run without wait, timeout, stale campaign guard, sync wrapper.
pytest atomic-forge/tools/darkmoon_pentest/testspasses,black --checkandflake8clean per the CI workflow.🤖 Generated with Claude Code
https://claude.ai/code/session_014HBQNzAf5C2E3MiJC48HQw