Repository navigation
Description
Implementation completed: merged in php2shell-check #7 and complete.tech #13, with passing PR CI. Production rollout remains separate; its comparison, backup and enablement prerequisites are intentionally not marked complete below.
Problem and intended behavior
The free-scan form at https://complete.tech/services/website-security/#promo-form currently saves a private request, records the visitor's electronic acceptance of the scan terms, and sends an administrator notification. It does not enqueue or run php2shell-check.
After a valid request and complete authorization record are durably saved, start an asynchronous, bounded PHP2Shell assessment of the explicitly authorized website. Keep the request, authorization, execution state, evidence, and report-delivery state linked. A form submission must return promptly and must never run a subprocess in the web request.
This issue scopes future implementation. No scanner, WordPress code, production configuration, or customer scan was changed/run during scoping. The trigger is the free-scan authorization, not the separate 12-month hosting agreement.
Verified starting point
Read-only investigation on 2026-10-21 (America/New_York), with checker baseline 0da54dd1c55f25a9d0d3444ccf8516393c6da0be:
- Website integration lives in CompleteTech-LLC/complete.tech, which is a production WordPress control/context repository, not a normal local build/deploy application. Read its
AGENTS.mdandDOMAIN_CONTEXT.mdbefore changes. theme/spintech-child/functions.php::ct_promo_request_handler()validates the request, saves a privatect_promo_request, records referral attribution, then callsct_promo_send_notification().mu-plugins/ct-promo-terms.phpvalidatesct_agreeandct_terms_version; itswp_insert_postcallback stores_ct_terms_*acceptance metadata and the filled-in contract. Local copies of this file and the theme handler matched production by SHA-256. No scan event/worker is present in this path.- Current published terms authorize non-exploit external checks for the submitted website only. The promotion closes November 20, 2026; the current authorization ends by December 20, 2026 or report delivery, whichever comes first. Preserve each request's accepted terms rather than applying later terms retroactively.
docs/php2shell-contract-prompt.mdcalls for owner confirmation before scanning because callers may submit requests. That confirmation flow is not implemented in the inspected handler and is not included in the current published terms.- The checker is a dependency-free Python 3.10+ CLI/library; production's default
python3is 3.6.8. An execution environment must be selected and provisioned before enablement. - Checker reference:
Checker.check()reads robots policy and a public page, with bounded responses/redirects, TLS verification, and pacing. It allows arbitrary HTTP(S) hosts, including local addresses, and is explicitly not ready to expose to arbitrary web submissions without network controls. - The checker reports
potentially_affected,fixed_version_reported, orunknown; exploitability is alwaysnot_tested. Its rules are an embedded dated snapshot, not a live vulnerability feed.
Implementation scope
1. Durable authorization and a single submission event
- Add an explicit event only after the saved request, structured target/contact/referral fields, and complete acceptance record are verified. Do not trigger from an unqualified
save_post, acceptance checkbox alone, administrator email success, or later editing of the request. - Store the normalized authorized target and scope immutably alongside the accepted terms version/hash and timestamp. Do not extract future execution targets from editable free-text request content.
- Reconcile the documented owner-confirmation requirement before enablement. Recommended flow: signed submission → pending confirmation → explicit confirmation → eligible for queueing. Bind a short-lived, single-use token to the request and scope; store only its hash. Opening a link with GET must not authorize a scan, because email security products prefetch links; require an explicit POST action. Email confirmation establishes control of that mailbox, not independently proven ownership of the domain.
- Preserve original acceptance records. Changed targets require fresh authorization. Do not automatically scan historical requests.
- Add authorized operator cancellation/revocation, and recheck validity/expiry before every execution or retry. A request ends eligibility on revocation, authorization expiry, or report delivery.
2. Asynchronous queue and isolated execution
- Prefer a dedicated MU plugin for orchestration, with the smallest necessary changes to the existing handler/terms integration.
- Persist job identity, request/scope/terms binding, state, attempts, timestamps, worker lease, outcome, and safe error category. Use an atomic claim and a stable idempotency key so duplicate events and competing workers cannot duplicate a scan.
- Distinguish awaiting confirmation, queued, running, completed, failed, cancelled, and expired. Reconcile interrupted/expired leases explicitly; retries must be bounded and authorization-aware. Do not automatically retry access denial, robots denial, or rate-limit responses.
- Run the pinned checker in an isolated Python 3.10+ environment with least privilege, no WordPress/database/mail secrets, a total wall-clock deadline, bounded output, and concurrency/rate limits. Per-socket
--timeoutalone is not a total job deadline. - Choose and document the actual runner and reliable scheduling mechanism. Do not assume shared hosting supports the runtime or rely on visitor-triggered WP-Cron as the only delivery mechanism.
- If the runner is separate, authenticate and replay-protect claims/results, bind responses to the job/target/attempt, and reject unexpected or oversized result payloads. Do not add an unauthenticated scan endpoint.
- Run exactly one authorized target per job; clients must not supply checker arguments, batch files, output paths, or arbitrary configuration.
- Record the selected distribution's provenance and applicable licensing for deployment, and preserve its copyright/license notices. The inspected revision offers AGPL-3.0 or a CompleteTech commercial license; do not assume older MIT release terms apply.
- Reuse
Checker.check()and existing result/report code. Do not fork a competing PHP vulnerability classifier or interpolate visitor input into shell commands. Record the pinned checker revision andphp2shell_rules_as_of.
3. Network scope and abuse controls
- Accept only normalized HTTP(S) targets on explicitly approved ports. Reject credentials, malformed/multiple inputs, and local, private, loopback, link-local, metadata, reserved, multicast, and otherwise non-public destinations for IPv4 and IPv6.
- Enforce checks at the connection boundary, including DNS resolution changes/rebinding, every page redirect, and every robots redirect. Initial string validation or one DNS lookup is insufficient. Prevent proxy/environment configuration from bypassing those controls.
- Keep every request within the exact authorized hostname and allowed scheme/port scope. A redirect to another domain or a
www/other subdomain must stop unless that hostname was explicitly authorized; do not silently expand scope. - Retain robots policy, TLS verification, response limits, identification/contact information, and pacing. Add submission quotas and per-target/global limits that remain effective across jobs.
- No exploit probes, credentials, login attempts, authentication bypass, plugin enumeration, broad crawling, or internal-network access.
4. Evidence, operator visibility, and reporting
- Store results privately with access controls; expose status and safe summaries in Promo Requests. Retain exact scanner states, observed evidence, timestamps, rule date, checker revision, and limitations.
- Treat returned titles, headers, notes, and URLs as untrusted input. Escape them in WordPress/admin/report views and prevent script execution or unsafe links in delivered artifacts.
- Validate both process outcome and result schema/content. Exit code 0 is not proof of a successful inspection; partial/malformed JSON, timeouts, blocked pages, or DNS/TLS failures must not become a clean bill of health.
- Preserve the distinction between potentially affected, a fixed version being advertised, and unknown. Hidden/conflicting/vendor-qualified versions stay unknown. Do not automatically assert confirmed exploitation, confirmed vulnerability, or verified safety.
- Generate the PHP2Shell report section from the stored result without scanning a second time. Keep customer delivery restricted to the authorized request contact. Track report notification/transport acceptance separately from confirmed delivery, following the existing mail-status pattern.
- Align customer-facing copy and completion status with actual coverage: this checker does not perform the broader TLS audit, plugin vulnerability inventory, exposed-file checks, malware/blocklist checks, or server configuration review promised in current terms. Either retain those as separately tracked manual work before the full report is complete, or update future terms and the form version together before launch. Do not silently narrow existing accepted contracts.
- Apply the accepted retention policy: request/acceptance records three years and scan results twelve months under the current terms. Keep customer targets, contacts, tokens, and results out of Git/public issue logs.
Acceptance criteria
- Valid owner-authorized submissions produce one durable job bound to the exact accepted scope and terms; malformed submissions, stale/missing terms, failed persistence, honeypots, and incomplete confirmation produce no runnable job.
- Confirmation-link prefetch, replay, changed scope, duplicate submission events, and competing workers cannot authorize or duplicate scans.
- Cancellation, expiry, and report completion prevent further execution/retry; old requests are not swept into the queue.
- Local fixture tests prove no unauthorized network request occurs for private IPv4/IPv6, mixed DNS answers, rebinding, cross-scope redirects, robots redirects, credentials, or disallowed ports.
- Worker crash/restart, timeout, queue outage, partial output, schema mismatch, and rejected/late callbacks preserve recoverable state and never produce false success.
- Hidden/conflicting/vendor-qualified versions, fixed-version advertisements, potential exposure, robots/access denial, DNS/TLS failure, and malformed output retain correct uncertainty and evidence. UI and reports never promote these to confirmed vulnerability or verified safety; unsupported versions remain a manual-review item.
- Malicious titles, headers, notes, and URLs cannot execute script or inject unsafe content in WordPress views, reports, or email.
- Existing request storage, referral attribution, acceptance record, and administrator email continue working; failed/unknown email outcomes neither lose requests nor duplicate scans/reports.
- End-to-end verification uses controlled fixtures or an explicitly authorized owned target. No customer/prospect targets are scanned as development tests.
- Independent review covers authorization, network boundaries, concurrency/recovery, and report semantics; relevant checker and WordPress tests pass.
- Before a separately authorized production rollout: document the chosen runner, runtime, network controls, pinned revision, scheduler, rollback/disable procedure, monitoring, and retention cleanup; compare/back up production files and preserve unrelated work.
Coordination and boundaries
- Coordinate WordPress changes with complete.tech #9, which changes the same form and handler. Its description of callers submitting for prospects must not be treated as the prospect's signature/authorization.
- Preserve the authenticated mail and status behavior tracked in complete.tech #6.
- Coordinate checker documentation/interfaces with php2shell-check #4; do not duplicate that skill/documentation work.
- Outside this issue: the paid hosting agreement/remediation workflow, active exploitation, authenticated server inspection, and implementation of the full security-analysis suite.
Activity
CompleteDotTech commentedon Oct 22, 2026
Triage (2026-10-21)
The scope is accurate. I checked each "verified starting point" claim against the code: the handler flow, the terms gate and
_ct_terms_*record, the missing owner confirmation (docs/php2shell-contract-prompt.mdrequires it, but terms §1 only has the submitter confirm their own authority), the Dec 20 / report-delivery expiry, the coverage gap with terms §3, retention, andREFERENCE.md's local-address warning. All of them hold.Labels:
enhancement,security,blockedBlockers
The WordPress code this issue cites (CompleteTech-LLC/complete.tech#10 now tracks it, verified identical to production, with mail-status tests passing 53/53. Merge it before starting.mu-plugins/ct-promo-terms.php,ct-promo-mail.php, the contract prompt and the terms) was not in GitHub; it existed only on production and in a local checkout.Corrections to the issue body
0da54ddis now one commit behindmain(27a251d, which only adds the skill and docs). Pin whatever revision the runner actually ships.Recommended sequencing
CompleteDotTech commentedon Oct 22, 2026
Implementation is in progress on
codex/issue-6-wordpress-worker, with the site wiring in an isolatedcomplete.techbranch. The source-baseline prerequisite CompleteTech-LLC/complete.tech#10 is merged after local PHP mail-status verification. The implementation follows the issue's recommended explicit POST owner-confirmation flow and preserves manual review for the broader promised checks. Validation uses an internal Docker WordPress/MariaDB fixture and mocked scanner transports; no prospect/customer scans or production changes are being made. Production rollout remains separate from this implementation.CompleteDotTech commentedon Oct 22, 2026
Implementation is now in php2shell-check PR #7 and companion complete.tech PR #13. Signed commits are pushed; the site vendors the exact plugin revision and verifies its SHA-256. Local validation passed: 65 Python tests (three Linux-only skips on Windows), all 22 worker tests under restricted Linux Docker privileges, WordPress/MariaDB integration and seven actual site-handler cases, synthetic HTTP confirmation/claim/start/result/replay/concurrency, browser GET/POST confirmation, and network-disabled packaged isolation. Independent review findings are resolved. Hosted CI is running; I will merge both after it passes. Production remains disabled/separately scoped.
CompleteDotTech commentedon Oct 22, 2026
Implemented and merged in php2shell-check #7 (
b4f8b4f) and complete.tech #13 (6a5b756). Signed implementation commits were pushed; the WordPress copy is pinned by source revision and SHA-256.All final PR CI passed: scanner matrix + WordPress/container integration and site PHP/mail checks. The matrix covers Python 3.10/3.14 on Windows/Linux. Locally, the 65-test suite, all 22 restricted Linux worker tests, WordPress database/report/recovery checks, seven actual form-handler cases, synthetic HTTP concurrent claims/receipts and browser confirmation flow passed. Independent review findings were resolved.
Production activation is deliberately separate: no customer/prospect scan, real email, production configuration change or production worker was run by this implementation. The runbook documents runtime, scheduling, network boundaries, revision pinning, retention, monitoring, rollback and the required live comparison/backups. The pre-rollout item below remains a deployment prerequisite, not an unimplemented code path. Site deployment issue #12 has the terms/plugin coordination handoff.