Disable physical control through legacy HTTP/M30 producers, typed admission, queue dispatch and upgraded Windows executors. Restrict diagnostic transports to exact read-only operations, with ordinal PowerShell validation before sockets. Retire unsupported pending/claimed work with atomic, idempotent security audits. Preserve prior delivery evidence and report unknown outcomes after a prior claim, including explicitly repaired pending rows. Include containment, cancellation, concurrency and malformed-payload regressions and the old-Agent cutover runbook. Validation: independent final application-security revalidation PASS. Two unchanged baseline source-assertion failures are documented as exceptions. Production release still requires native Windows validation and verified fencing/stop/drain/upgrade; server cancellation alone cannot revoke a cached command held by an old Agent.
66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""Agent command transport coordination and result validation."""
|
|
|
|
import re
|
|
|
|
from app.agent_commands.exceptions import (
|
|
AgentCommandConflict,
|
|
AgentCommandNotFound,
|
|
)
|
|
from app.agent_commands.models import (
|
|
AgentCommand,
|
|
AgentCommandResultRequest,
|
|
)
|
|
from app.agent_commands.repository import AgentCommandRepository
|
|
from app.agent_commands.policy import rejection_reason
|
|
|
|
|
|
class AgentCommandService:
|
|
def __init__(self, repository: AgentCommandRepository):
|
|
self.repository = repository
|
|
|
|
def claim(self, agent_id: str) -> list[AgentCommand]:
|
|
return [
|
|
AgentCommand.model_validate(row)
|
|
for row in self.repository.claim_pending(agent_id)
|
|
if rejection_reason(row["command_type"]) is None
|
|
]
|
|
|
|
@staticmethod
|
|
def _legacy_error(value: str | None) -> str | None:
|
|
if not value:
|
|
return None
|
|
sanitized = re.sub(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", "[address]", value)
|
|
return sanitized[:255]
|
|
|
|
def submit_result(
|
|
self, agent_id: str, request: AgentCommandResultRequest
|
|
) -> None:
|
|
command = self.repository.get_command(request.command_id)
|
|
if not command:
|
|
raise AgentCommandNotFound("Agent command not found")
|
|
if command["agent_id"] != agent_id:
|
|
raise AgentCommandConflict("Agent command belongs to another Agent")
|
|
reason = rejection_reason(command["command_type"])
|
|
if reason:
|
|
# Repository repeats this check under a row lock and records the
|
|
# cancellation atomically; never accept an old physical result.
|
|
self.repository.finish_command(
|
|
request.command_id, agent_id, "cancelled", {}, reason
|
|
)
|
|
raise AgentCommandConflict(reason)
|
|
if command["command_status"] != "claimed":
|
|
raise AgentCommandConflict(
|
|
f"Agent command is {command['command_status']}, not claimed"
|
|
)
|
|
|
|
result_payload = request.result_payload
|
|
error_message = self._legacy_error(request.error_message)
|
|
|
|
self.repository.finish_command(
|
|
request.command_id,
|
|
agent_id,
|
|
request.status,
|
|
result_payload,
|
|
error_message,
|
|
)
|