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:
- NaN: `NaN > X` is always `false` in IEEE 754. An invocation with `estimated_cost_usd: NaN` passes any cost limit.
- Negative values: `-999999.0 > 100.0` is false. Negative costs pass any positive limit.
- 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.
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:
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.