Skip to content

[Security] HITL audit: empty TypeSafe response is treated as approval and executes tools #393

Open

Description

@EddieMurphy89

Affected versions / environment

CyberStrikeAI v1.7.24 (commit 4ca8b7d33663cd4ba8213f4afa8194ae1928ef16), and the same decision code remains on the default branch. The deployment must enable HITL approval with default_reviewer: audit_agent and audit_backend: typesafe.

Summary

An HTTP 200 response from the TypeSafe audit backend with an empty or partial answers object is accepted as a successful evaluation. The client turns nil/missing answers into an empty map, the decision code reads the absent answers as zero, and the audit agent applies its default “approve”. The HITL middleware then runs the tool, and the approval record attributes the decision to the audit agent. A provider incident, a truncated response, or a malformed 200 body is enough to run a tool that was supposed to be gated.

A complete destructive answer (availability.noul = 0.97) and an HTTP 500 both reject correctly; the empty/partial 200 path is the one that approves.

Details / root cause

  • internal/typesafe/client.go (SystemOne) only checks the HTTP status and JSON decoding. Nil answers is normalized to an empty map; Noul returns 0 for missing, nil or non-numeric answers.
  • internal/hitl/jev.go (DecideJev) treats the absent evaluation as “no destructive signal” and follows its “uncertain → approve” default. It distinguishes result == nil, but not “HTTP 200 with no usable answers”.
  • internal/handler/hitl_audit_agent.go passes the result to DecideJev; the HITL middleware then proceeds and executes the tool.

Reproduction

Control (complete destructive answer):

  1. Enable HITL approval with default_reviewer: audit_agent and audit_backend: typesafe.
  2. Point the TypeSafe endpoint at a test server returning a complete destructive answer set.
  3. Trigger an exec tool call through the normal conversation/approval flow. The audit agent rejects, the tool does not run, and no marker is created.

Fault (empty or partial answer):

  1. Same setup, but TypeSafe returns HTTP 200 with {"answers":{}}, or only one answer key.
  2. Trigger the same exec call.
  3. Baseline: the audit agent approves, the tool executes, and the command output/marker is observable.
  4. Expected after fix: the audit agent rejects the incomplete evaluation and the tool does not execute.

Expected behavior

An empty or incomplete audit response means the evaluation did not complete, so it must be rejected or retried, never treated as “no destructive signal”.

Actual behavior

The tool executes while the deployment believes the audit agent approved it; the only changed variable is the TypeSafe response body.

Impact

Any HITL-gated tool can run during a TypeSafe incident or with a malformed 200 response, including the exec tool. The operator gets no error, retry, or conservative rejection, and the approval record makes the failure look legitimate.

Affected deployments

Deployments with HITL approval enabled, default_reviewer: audit_agent, and audit_backend: typesafe. HITL is off by default, so stock deployments without HITL are not affected; tools in hitl.tool_whitelist are exempt by configuration.

Suggested fix

Validate that every requested audit question has a usable typed answer before applying DecideJev; treat an empty/partial result as an evaluation failure (reject/retry), not as a low-risk result. Preserve the complete destructive and HTTP-error rejection paths.

Fix

A fix is open in #394: SystemOne rejects empty/partial/malformed answer sets, and DecideJev re-validates the built-in questions so a directly constructed incomplete result also rejects. The existing destructive, HTTP-error and HITL-off paths are unchanged.

Activity

  1. changed the title [-]HITL audit: empty TypeSafe response is treated as approval and executes tools[/-] [+][Security] HITL audit: empty TypeSafe response is treated as approval and executes tools[/+] on Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions