Service validation playbook
Service entry-point validation starts from a discovery record whose probe, endpoint, scope, and protocol evidence have already been reconciled. Preparing a handoff remains offline. Execution occurs only after an operator selects one exact action and supplies a fresh budget attempt and an approval receipt bound to the exact plan, endpoint, and action.
The current executable provider is a disposable loopback laboratory fixture used
by the acceptance suite. COPS does not register a native provider for external
targets. If either a provider or an independent evidence verifier is absent, the
dispatcher returns provider_unavailable before reserving a budget, requesting
approval, or opening a connection.
Routed service families
The discovery boundary conservatively routes independently observed protocols to the following specialist skills. A route identifies who can assess the service; it does not prove a service, vulnerability, or successful use.
| Observed service | Specialist skill | Current executable evidence |
|---|---|---|
| HTTP | web-api-inventory-and-replay |
GET /health and an HTTP status line in the loopback acceptance lab |
| SSH | network-remote-and-file-services |
SSH identification-string exchange in the loopback acceptance lab |
| Redis | network-data-services |
Redis PING; separately authorized proof also uses ECHO authorized-use |
| SMTP | network-messaging-services |
SMTP greeting plus EHLO response in the loopback acceptance lab |
| Docker API | network-developer-interfaces |
Docker Engine GET /version and JSON API version in the loopback acceptance lab |
| DNS | network-infrastructure-services |
Length-prefixed DNS-over-TCP query and response in the loopback acceptance lab |
| SOCKS | network-legacy-and-proxy-services |
SOCKS5 method negotiation in the loopback acceptance lab |
These seven adapters are registered only by the disposable laboratory. They exercise the public selection, authorization, dispatcher, verifier, budget, reporting, and cleanup boundaries without claiming production coverage for an external target. The provider opens one loopback connection, and the verifier reads the fixture-owned passive audit after the server closes that connection. Even discovery requests and server greetings require a fixture-issued one-use audit reference before the fixture responds; an unaudited connection receives no service response. The verifier disregards provider-authored output. Here, “independent evidence” means independent of provider-authored output only. The fixture and verifier share one Python process, administrative authority, and parser, so this laboratory does not establish independently administered observation or native protocol support.
Unknown responses, port-only guesses, endpoints outside the validated scope, and evidence without matching active-probe provenance produce no execution plan.
Evidence and proof gates
Low-impact validation requires the verifier to read a fixture-owned passive audit
record that binds the approval receipt, plan, attempt, endpoint, selected action,
evidence digest, random dispatch correlation identifier, opaque one-use audit
reference, and observation time within the dispatch interval. The provider returns
only the opaque audit reference; provider-authored evidence claims do not satisfy
this gate. The verifier waits for the exact issued audit reference up to the bounded
transaction deadline; a missing closure audit still produces dispatch_uncertain.
The result records
protocol evidence plus all five assessment dimensions: exposure, authentication,
configuration, version, and trust. Complete evidence may establish an observed
exposure or a versioned candidate; it cannot establish a verified finding or
successful authorized use.
The owner-controlled ledger consumes the one-use approval receipt when dispatch is
durably recorded. A successful low-impact candidate produces a durable prerequisite
receipt. Controlled proof requires a fresh approval receipt and atomically consumes
the exact prerequisite receipt bound to the candidate plan, endpoint, source
action, evidence digest, trusted issue #143 reproduction digest, affected
version, and candidate-verification state. A finding becomes verified only when
the fixture-owned audit records the reproduction’s exact expected effect and
cleanup evidence.
Receipt uniqueness in the SQLite ledger is limited to that database. The injected
authorization authority must reject receipt reuse across every ledger database for
the engagement. The laboratory gate keeps this authority state only in the fixture
process, so restarting it resets that additional replay defense.
Successful authorized use is a separate state and requires distinct authorized
effect evidence; verified finding evidence alone never sets it.
Every action uses its own attempt identifier, budget reservation, and approval
receipt. A terminal or reused attempt fails before approval and provider dispatch.
The provider’s retained-output byte count is an upper bound, not the charged
usage: the dispatcher charges the canonical serialized size of the retained
result and marks an over-bound result uncertain. Before reservation, the
dispatcher rejects a request whose retained-output budget cannot hold its
canonical control record. After dispatch, an impossible provider-declared bound
leaves the attempt uncertain and raises RetainedResultBudgetExceeded instead
of returning an oversized record. This limit also applies when the dispatcher
must retain an uncertain effect or missing-cleanup result; a generic uncertainty
record is returned only when it fits both the request reservation and the
provider declaration.
Authorization and dispatch-intent failures release an undispatched reservation.
Once dispatch is durably recorded, provider, verifier, or settlement ambiguity
becomes an uncertain attempt and is not retried automatically. Incomplete
correlated evidence may return insufficient_evidence only when the dispatcher
can establish that no uncertain effect remains. Either outcome blocks progression.
An invalid candidate claim retains that insufficient state instead of being
silently reported as a completed non-candidate.
Results retain bounded ATT&CK mappings and explicit coverage. T1046 records
only that a complete correlated protocol response exists; an incomplete
response receives no technique mapping. T1210 is recorded only for a
version-bound #143 candidate and remains a candidate until controlled proof
records the exact effect and cleanup. Each assessment and finding field is marked
tested, documented, untested, or unverified.
A completed recorded probe from a cancelled discovery session remains eligible
for handoff. A session in requires_reconciliation remains quarantined until its
uncertain scanner state is resolved.
Run the disposable laboratory
Install the repository test dependencies, then run the public operator journey:
.venv/bin/python scripts/service_validation_loopback_example.py
The command creates eight services on random nonstandard ports bound to
127.0.0.1, discovers and routes all seven families, and runs the public
selection, authorization, provider, dispatcher, fixture-owned verifier, budget,
reporting, and cleanup path. Its JSON report includes benign SSH, a versioned
Redis #143 candidate and separately authorized proof, remediated Redis, and an
HTTP insufficient-evidence outcome. It stops every service and confirms that all
fixture transport connections have closed. This does not prove rollback, effect
reversal, external process cleanup, or target-side reconciliation. To retain the
state artifacts for inspection, select a fresh path that does not yet exist:
.venv/bin/python scripts/service_validation_loopback_example.py \
--state-dir /tmp/cops-service-validation-lab
Each journey uses fixed fixture attempt identifiers, so a retained state directory cannot be reused. The command exclusively creates the final directory before starting a fixture, rejects symbolic-link ancestors, and pins the directory identity for later ledger opens. The identity checks reject persistent replacement before and after each ledger open. Python’s SQLite interface still opens the database by pathname, so the checks do not eliminate a same-account pathname ABA during that internal open. Run the lab where no untrusted process under the same account can rename or replace the state path or SQLite entry, or alter the database contents, during the journey. A cleanup failure exits unsuccessfully instead of reporting a completed journey.
Run the focused executable acceptance suite with:
.venv/bin/python -m pytest -q \
tests/test_service_entry_validation.py \
tests/test_service_validation_execution.py \
tests/test_service_validation_lab.py \
tests/step_defs/test_service_entry_validation_steps.py
The suite checks every loopback adapter through the public subprocess entry point and separately exercises fabricated provider claims, ledger-reopen replay, approval and prerequisite consumption, correlation/reference/time and receipt/plan/attempt/endpoint/action/digest mismatches, exact endpoint/version/effect binding, provider availability, budget release before dispatch, post-dispatch uncertainty, fresh attempts, and cleanup reporting.
These commands authorize loopback fixtures only. They do not grant scope for an external endpoint or register a production provider.