Skip to content

Policy cost check bypassed by NaN, Infinity, and negative values #14

@OkeyAmy

Description

@OkeyAmy

The Problem

The policy evaluator compares `estimated_cost_usd` from invocation args against `max_cost_usd` from the delegation policy using a bare floating-point comparison:

```go
// drs-verify/pkg/policy/evaluate.go:29
if cost > *pol.MaxCostUSD {
return fmt.Errorf("cost limit exceeded...")
}
```

Three classes of input bypass this check:

  1. NaN: `NaN > X` is always `false` in IEEE 754. An invocation with `estimated_cost_usd: NaN` passes any cost limit.
  2. Negative values: `-999999.0 > 100.0` is false. Negative costs pass any positive limit.
  3. Negative Infinity: `-Inf > X` is always false.

Go's `encoding/json` does not produce NaN or Infinity from standard JSON (JSON has no NaN literal). But if the args map is constructed programmatically (SDK path, not HTTP JSON path), or if a custom unmarshaler is involved, these values can appear.

More importantly, `toFloat64()` at line 238 accepts `float32` inputs, and a `float32` NaN promoted to `float64` is still NaN.

What Must Change

In `toFloat64()`, reject NaN and Infinity:

```go
func toFloat64(v interface{}) (float64, bool) {
// ... existing switch ...
if math.IsNaN(f) || math.IsInf(f, 0) {
return 0, false
}
return f, true
}
```

In `Evaluate()`, reject negative costs:

```go
if cost < 0 {
return fmt.Errorf("estimated_cost_usd must be non-negative, got %v", cost)
}
```

Both changes. Both are one-liners. Both prevent silent policy bypass.

Severity

HIGH. Cost limits are a policy enforcement mechanism. Silent bypass means an agent can invoke arbitrarily expensive operations while the delegation chain claims to restrict cost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    highHigh severitysecuritySecurity vulnerability or hardening

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions