1-SEC contributors · Reviewed · Module: llm_firewall

LLM Token Budget Exhaustion: Hourly Usage Alerts and Enforcement Limits

The token-usage handler accumulates positive reported usage per user and alerts when the total exceeds its hourly limit.

What 1-SEC checks

The token-usage handler accumulates positive reported usage per user and alerts when the total exceeds its hourly limit. A new user budget reads token_budget_per_hour, defaulting to one hundred thousand. The fixed one-hour window starts when that budget is created and resets on a later event after expiry. Usage equal to the limit does not exceed it.

Read the implementation: checkTokenBudget

Required telemetry and context

Relevant input events: llm_token_usage. Use a trusted producer at the real application boundary.

  • user_id
  • source_ip (fallback)
  • tokens_used (positive integer)
  • modules.llm_firewall.settings.token_budget_per_hour

Field names above describe relevant context, not a complete API request. Consult the current event contract for envelope and detail requirements. Preserve booleans, numbers, and strings as the receiving handler expects them. Agent-provided text cannot independently verify identity or authorization.

Deployment, prevention, and response

Emit actual per-request usage from the trusted model integration after reconciling its units and retries. The fixtures compare a reported 150 tokens with a configured limit of 100 and a within-budget case. Check both attribution and alert delivery. Apply request admission, cancellation, provider spending controls, and durable usage accounting separately when a hard budget is required.

Record the source event, affected agent/host, actual target, policy decision, and response result. Review enforcement configuration separately from detection. A recommendation, selected action, dry-run record, and completed action are different outcomes.

Reproduce the source fixtures

From a checkout of the linked 1-SEC source revision, run the selected fixtures with the repository's Go toolchain. These commands run tests; they do not execute a real attack against your systems.

go test ./internal/modules/llmfirewall -run '^(TestCheckTokenBudget_ExceedLimit|TestCheckTokenBudget_WithinLimit_NoAlert|TestCheckTokenBudget_NoUserID_Skipped|TestCheckTokenBudget_ZeroTokens_Skipped)$' -count=1

Selected package fixtures passed locally. Helper fixtures establish only their explicit assertions; any accompanying handler fixture may check only error-free dispatch. Synthetic source tests do not establish production prevention, media coverage, or framework compatibility.

TestCheckTokenBudget_ExceedLimit

Full fixture and assertions — run in its original package with its helpers.

func TestCheckTokenBudget_ExceedLimit(t *testing.T) {
	pipeline := makeCapturingPipeline()
	f := New()
	cfg := core.DefaultConfig()
	cfg.Modules[ModuleName] = core.ModuleConfig{
		Enabled:  true,
		Settings: map[string]interface{}{"token_budget_per_hour": 100},
	}
	f.Start(context.Background(), nil, pipeline.pipeline, cfg)

	userID := "test_user_budget"
	// Exceed budget in one shot
	ev := makeTokenEvent(userID, 150)
	f.HandleEvent(ev)

	if pipeline.count() == 0 {
		t.Error("expected token budget exceeded alert")
	}
}
TestCheckTokenBudget_WithinLimit_NoAlert

Full fixture and assertions — run in its original package with its helpers.

func TestCheckTokenBudget_WithinLimit_NoAlert(t *testing.T) {
	pipeline := makeCapturingPipeline()
	f := New()
	cfg := core.DefaultConfig()
	cfg.Modules[ModuleName] = core.ModuleConfig{
		Enabled:  true,
		Settings: map[string]interface{}{"token_budget_per_hour": 10000},
	}
	f.Start(context.Background(), nil, pipeline.pipeline, cfg)

	ev := makeTokenEvent("user_within_budget", 50)
	f.HandleEvent(ev)

	if pipeline.count() > 0 {
		t.Error("expected no alert when within token budget")
	}
}
TestCheckTokenBudget_NoUserID_Skipped

Full fixture and assertions — run in its original package with its helpers.

func TestCheckTokenBudget_NoUserID_Skipped(t *testing.T) {
	pipeline := makeCapturingPipeline()
	f := startedFirewallWithPipeline(t, pipeline)
	ev := core.NewSecurityEvent("test", "llm_token_usage", core.SeverityInfo, "tokens")
	ev.Details["tokens_used"] = 99999
	// No user_id, no source_ip — should be a no-op
	f.HandleEvent(ev)

	// Shouldn't panic or alert
	_ = pipeline.count()
}
TestCheckTokenBudget_ZeroTokens_Skipped

Full fixture and assertions — run in its original package with its helpers.

func TestCheckTokenBudget_ZeroTokens_Skipped(t *testing.T) {
	pipeline := makeCapturingPipeline()
	f := startedFirewallWithPipeline(t, pipeline)
	ev := makeTokenEvent("user_zero", 0)
	f.HandleEvent(ev)
	if pipeline.count() > 0 {
		t.Error("no alert expected for 0 tokens used")
	}
}

// ─── Multi-Turn Tracker ───────────────────────────────────────────────────────

Tuning, evaluation, and coverage limits

This is post-reported usage monitoring, not a prepaid quota, provider billing meter, or hard request/token cap. Missing identity or nonpositive usage skips counting. Source-IP fallback can merge users behind a shared address, and duplicate events can double-count. Budgets are bounded in-memory state; restart/eviction can lose the running total. A setting change does not necessarily replace an already-created budget.

Evaluate this behavior with representative authorized activity and a controlled known-positive case. Investigate the exact rule, inputs, and state before suppressing an alert. Do not assume a hard-coded heuristic is a configurable setting. Measure your own deployment; the source fixtures do not establish a benchmark, detection rate, compliance certification, or incident-prevention claim.

Related implemented controls

Implementation reviewed at source revision 8884bf4fbfb069094380006a783ae51d6b1ebf60. Recheck the actual release and integration before deployment. Sources and corrections policy.