Skip to content

consumption command returns zero when scan-payload app_name differs from SCM application name #240

Description

@scthornton

Summary

airs runtime customer-apps consumption returns zero-valued token_stats for apps whose scan-payload app_name differs from the SCM application name registered in customer_apps. This happens with integrations (e.g. LiteLLM's panw_prisma_airs guardrail) that send a metadata.app_name value that overrides what's registered in SCM.

Root cause: two identifier spaces

The dashboard endpoint and the customer-apps endpoint use different (id, name) tuples for the same logical application:

Source id name Populated by
GET /aisec/v1/mgmt/customerapp/tsg/<TSG> (what the CLI lists) customer_appId app_name SCM application registration
GET /aisec/v1/mgmt/dashboard/v2/apps/application (where token_stats live) dashboard id dashboard name Whatever app_name the scan payload actually sent

The dashboard endpoint requires both appid AND appname to match a bucket it knows about. Empirically (verified live 2026-05-29): any mismatched (appid, appname) tuple returns a 200 with an all-null body, not a 4xx, so the CLI's per-app loop silently produces zeros instead of erroring.

Today's getCustomerAppConsumption(appName) in src/airs/management.ts does:

  1. List customer_apps
  2. Find entry where a.app_name === appName
  3. Use that entry's customer_appId as appid and its app_name as appname when calling dashboard.application(...)

When the dashboard's tuple is (85ddfdbb-..., "LiteLLM") (scan-payload name) but customer_apps only has (some-other-uuid, "airs-app-litellm") (SCM application name), the dashboard call hits the wrong bucket and returns nulls.

Evidence

Sample response from dashboard/v2/apps/application for a real LiteLLM-fronted app where this is happening (UUIDs masked, structure preserved):

{
  "cloud": "gcp",
  "id": "<dashboard-uuid-A>",
  "name": "LiteLLM",
  "profiles": ["ep-airs-api-litellm"],
  "source": "api",
  "session_stats": {
    "most_recent_session_time": "2026-05-29T16:39:36Z",
    "total": 14431,
    "violating": 8372
  },
  "token_stats": {
    "average_daily_tokens": 155.504,
    "average_daily_tokens_scale": "K",
    "monthly_total_tokens": 2.985758,
    "monthly_total_tokens_scale": "M"
  }
}

That's the bucket the dashboard UI displays correctly. But the customer-apps list for the same tenant has:

  • app_name: "airs-app-litellm" (the SCM application name)
  • customer_appId: <customer-apps-uuid-B> (a UUID different from <dashboard-uuid-A>)

So airs runtime customer-apps consumption airs-app-litellm calls the dashboard with the wrong tuple (<customer-apps-uuid-B>, "airs-app-litellm") and returns zeros. And there is no way for the user to surface the ("LiteLLM", <dashboard-uuid-A>) bucket through the current CLI.

Reproduction

Any tenant where an integration sends a metadata.app_name (or equivalent) in the scan request that differs from the SCM application name. The LiteLLM panw_prisma_airs guardrail is one example: it sets app_name from its own app_name config option, prefixed with "LiteLLM-" by default. Customers who register a friendlier SCM application name (airs-app-litellm, prod-genai, etc.) hit this immediately.

Proposed fix

Add two override flags to runtime customer-apps consumption that bypass the customer_apps lookup:

airs runtime customer-apps consumption --app-id <uuid> --app-name <literal-name>

When both are provided, skip the customer_apps.list() lookup and pass them directly to dashboard.application() / dashboard.applicationViolationBreakdown(). The user obtains the values from the SCM dashboard UI (AI Security > Runtime > API Applications > pick the app > details panel), where the dashboard-side (id, name) tuple is what's displayed.

Both flags should be required together (passing one without the other should error with guidance), because the dashboard endpoint requires both.

Behavior matrix

Invocation Behavior
consumption (no args) Unchanged: loop customer_apps, query dashboard for each (still useful when names match)
consumption <appName> Unchanged: look up customer_apps, call dashboard
consumption --app-id <uuid> --app-name <name> New: skip lookup, call dashboard directly
consumption <appName> --app-id <uuid> Error: mutually exclusive with positional
consumption --app-id <uuid> (no name) Error: both --app-id and --app-name required together
consumption --app-name <name> (no id) Error: same

Additional improvement: warn on all-zero all-apps mode

When the no-args "all apps" mode produces a result where every app's token_stats.monthly_total_tokens is zero/null, emit a diagnostic note pointing to this issue. Distinguishing "no apps known" from "apps known but the dashboard doesn't have data under those names" saves customers from wondering why they see zero.

Suggested message:

All apps returned zero token consumption. This can happen when the app_name your integration sends in scan requests does not match the SCM application name. Try airs runtime customer-apps consumption --app-id <uuid> --app-name <scan-payload-name> using the values shown in the SCM dashboard UI.

Out of scope (future)

It would be cleaner to enumerate apps from the dashboard's own backing store rather than from customer_apps. The SCM UI does this somewhere, but the endpoint isn't documented. Probes against likely paths under /dashboard/v2/apps/ return HTTP 403 (which per prior findings means "wrong path" for this surface, not "no permission"). If anyone has visibility into the UI's network calls, locating that endpoint would let a future enhancement auto-list dashboard apps and remove the need for users to look up (id, name) in the UI. Not blocking for this fix.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions