Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ Python 3.10+ on Windows, macOS, or Linux. Nothing to install.

Use `--logo path/to/logo.png` and `--brand "Your Company"` to brand it for your own organization. CSV and JSON remain available for spreadsheets and pipelines.

File outputs refuse reuse by default; `--force` explicitly permits atomic final replacement. Every completed row is fsynced into `OUTPUT.partial.json` before progress, so an interrupted or force-killed run can be rendered offline with `python wordpress_check.py --recover OUTPUT.partial.json --format html --output recovered.html`. Recovery never requests targets and labels incomplete evidence (`130`). Existing checkpoints are always protected, including with `--force`; choose fresh paths and protect both artifacts. Successful final files are atomically published. Stdout cannot recover after forced termination.

CLI JSON now has a run envelope with `complete`, completed/pending/total target counts and `results` (previously a top-level array); CSV appends corresponding run metadata and an empty summary, and HTML labels completeness. See [recovery and file handling](docs/REFERENCE.md#file-handling-and-exit-codes) for naming, limits and conservative retained checkpoints.

## What the results mean

| Status | Meaning |
Expand Down
28 changes: 20 additions & 8 deletions docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ are rejected. Input is a text list, **not a CSV or an Excel workbook**.
| --- | --- | --- |
| `urls` | None | Zero or more HTTP(S) URLs or bare domains. |
| `--input PATH` | None | UTF-8 text file containing one website per line. |
| `--output PATH` | stdout | Write results to this file. An existing output file is overwritten. |
| `--output PATH` | stdout | Atomically publish the final report; refuse an existing path by default. Keep a durable `PATH.partial.json` checkpoint. |
| `--force` | off | Explicitly replace an existing final output after successful rendering; never replace an existing checkpoint. |
| `--recover PATH` | none | Render a persisted checkpoint without targets, requests, or automatic retries. |
| `--format csv\|json\|html` | `csv` | Output format. `html` writes a self-contained, branded report. |
| `--logo PATH` | `assets/logo.png` | Logo inlined into the HTML report. A missing file is omitted. |
| `--brand NAME` | `CompleteTech LLC` | Organization name shown on the HTML report. |
Expand Down Expand Up @@ -203,6 +205,8 @@ the coarse `blocked` value as proof that the owner deliberately blocked you.
| `php2shell_notes` | Evidence limits and the reason for this interpretation. |
| `php2shell_recommendation` | Administrator review or patch-verification guidance. |

CLI JSON is now a run envelope: `complete`, `completed_targets`, `pending_targets`, `total_targets`, and `results`. Consumers of the previous top-level array must read `results`. The result dictionaries and their evidence arrays are unchanged. CSV appends `record_type` and these four run fields to the existing result columns; an empty report has a summary row. HTML labels run completeness and counts separately from assessment status.

JSON preserves arrays in the evidence, version-candidate, CVE, source, and note fields. CSV joins array values
with ` | ` and prefixes potentially dangerous spreadsheet-formula strings with
an apostrophe. Empty version strings mean "not disclosed", not version zero.
Expand Down Expand Up @@ -286,14 +290,22 @@ Do not use `request()` directly to bypass the policy checks in `check()`.

## File handling and exit codes

Results are flushed after each website. CSV retains completed rows after an
interruption; JSON can be left without its closing bracket. HTML is written when the run ends, including after an interruption. There is no automatic
resume or retry. Preserve any partial result file before rerunning with the same
`--output` path, because the CLI opens it for replacement.
With `--output report.html`, the CLI first creates `report.html.partial.json` exclusively. Each completed result is committed to a private temporary file, flushed and fsynced, and atomically replaces that checkpoint before progress is printed. Checkpoints contain completed result dictionaries and explicit conservative incomplete/completed/pending counts, not pending target URLs. They contain assessment evidence and must be protected like the report. Never commit real checkpoints.

The final JSON, CSV or HTML is rendered completely and fsynced before publication in the destination directory. Default publication uses an atomic hard link and fails closed if hard links are unavailable or another writer creates the destination first. `--force` explicitly allows atomic replacement of the final file; failures before publication preserve its prior contents. Existing checkpoint names are always refused, even with `--force`; choose a fresh output name. Directories and symbolic-link output paths are refused.

A handled Ctrl-C returns `130`, retains the incomplete checkpoint, and does not publish a final file or replace a previous report. A forced process termination cannot execute cleanup, but previously checkpointed results remain reconstructable. Recovery performs no requests and never reruns completed targets:

```sh
python wordpress_check.py --recover report.html.partial.json --format html --output recovered.html
python wordpress_check.py --recover report.html.partial.json --format json --output recovered.json
```

Recovery returns `130` for an incomplete checkpoint, even if all targets were checkpointed before final publication. The conservative checkpoint is intentionally retained after success and is not final-output attestation. Keep the final report as the completion receipt. Runs accept at most 100,000 targets; split larger lists explicitly. Checkpoint publication refuses content beyond the 64 MiB recovery bound and preserves the previous checkpoint, with an error rather than an unrecoverable larger artifact. Recovery requires a regular non-symlink checkpoint, validates its structure/counts/result types and reads at most 64 MiB (100,000 result rows). Recovery retains integer HTTP statuses 100–999 (or null), matching the fetcher, including nonstandard responses; this does not turn a failed fetch into a successful assessment. Boolean, string, fractional and out-of-domain statuses remain invalid, and exploitability must remain `not_tested`. Keep both the original checkpoint and any reconstructed artifact; continuation requires a separately authorized new run of pending targets only. The tool never resumes automatically.

Without `--output`, results accumulate in memory and stdout receives one well-formed report on completion or a handled interrupt. Stdout/pipes do not provide durable recovery after forced termination; use `--output` when preserving evidence matters. Final output/partial names and report/input paths must differ.

Exit code `0` means the run completed, not that every website was reachable or
identified. Inspect each result's status. Argument/input-file errors exit with
code `2`. Unexpected I/O errors may raise a Python exception.
Exit code `0` means the run completed, not that every website was reachable or patched. `130` means interruption or incomplete recovery; `2` means argument/input/recovery/publication error. Inspect operational and assessment status separately. Disk exhaustion or abrupt termination during a new checkpoint leaves the previous atomic checkpoint intact; the in-progress result may not have been committed. No file mechanism guarantees recovery from storage hardware failure.

## Tests and contributing

Expand Down
2 changes: 1 addition & 1 deletion docs/example-report.html

Large diffs are not rendered by default.

14 changes: 5 additions & 9 deletions skills/php2shell-check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ matching revision when known.
## Run and preserve the output

Choose a new output path outside version control; create its parent directory
first. `--output` overwrites an existing file. The following paths and domains
first. `--output` refuses existing files; `--force` explicitly replaces only the final artifact after rendering. Existing `.partial.json` checkpoints are never overwritten by a new run. The following paths and domains
are examples: replace them with the authorized inputs and chosen output paths,
and do not scan example domains as a test.

Expand Down Expand Up @@ -92,16 +92,12 @@ public contact value the user intends to disclose in requests.

Inspect each result even if the process exits with `0`: that means the run
completed, not that every fetch succeeded or every site is patched. Argument or
input-file errors exit with `2`; unexpected I/O errors can raise exceptions.
input/recovery/publication errors exit with `2`; interruption or incomplete recovery exits with `130`.
For blocked or failed targets, report the reason without silently retrying.

On interruption, preserve the output before any rerun. Completed CSV rows are
flushed incrementally; JSON may lack its closing bracket. HTML is rendered from
completed rows during normal finalization, including a handled interruption
path, but a force-killed process may leave it empty. There is no automatic
resume. Determine which targets completed, then run only authorized outstanding
targets into a **new** output file if continuation is requested or already in
scope; do not imply the original run completed.
Use `--output` for durable evidence. The CLI atomically checkpoints every completed row to `OUTPUT.partial.json` before printing progress; forced termination preserves prior checkpoints. Ctrl-C returns `130` and leaves an explicitly incomplete checkpoint without replacing any previous final report. Recover JSON/CSV/HTML offline with `python wordpress_check.py --recover OUTPUT.partial.json --format html --output NEW.html`; do not supply targets. Recovery never makes requests and returns `130` for incomplete evidence. JSON now wraps rows under `results` with completeness and completed/pending/total counts; CSV appends run fields and an empty summary, HTML labels run state. Protect checkpoints like reports. A conservative incomplete checkpoint remains even after final success, so preserve the final artifact as the completion receipt.

Stdout has no forced-termination durability. Recovery is bounded to regular non-symlink files up to 64 MiB / 100,000 rows; never claim uncommitted in-progress results survived. There is no automatic resume: use completed/pending counts and saved rows to prepare only authorized outstanding targets into a fresh output if continuation is authorized. Never imply the original run completed.

## Interpret and deliver

Expand Down
Loading
Loading