garrytan / garrytan/gstack

community-pulse / security dashboard fail open: backend error or missing jq reports "0 attacks" instead of "unknown"

Open
#1,947 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## Summary

The community **security** dashboard can display a reassuring "0 attacks / all clear" when it is actually erroring — a fail-open on a security-signaling surface. Found during a security review of v1.57.7.0.

### 1. `community-pulse` returns all-zeros (HTTP 200) on any error

`supabase/functions/community-pulse/index.ts:194-211`:
```ts
} catch {
return new Response(
JSON.stringify({
weekly_active: 0, change_pct: 0, top_skills: [], crashes: [], versions: [],
security: {
attacks_last_7_days: 0,
top_attack_domains: [],
top_attack_layers: [],
verdict_distribution: [],
},
}),
{ status: 200, ... }
);
}
```
Any internal failure (DB outage, malformed cache, query error) is masked as a healthy "0 attacks" response with HTTP 200 — indistinguishable from a genuine zero.

### 2. `gstack-security-dashboard` reports 0 when `jq` is missing

`bin/gstack-security-dashboard:51-64` — when `jq` is unavailable it falls back to a lossy `grep -o '"security":{[^}]*}'` that breaks on the nested arrays (`top_attack_domains`, etc.), and the documented behavior is to "degrade gracefully to '0 attacks'" (the machine-readable fallback emits `attacks_last_7_days: 0`). So a host without `jq` silently under-reports attacks as zero.

### 3. (related, latent) telemetry `error_message` is transmitted unredacted

`bin/gstack-telemetry-sync:83-84` strips only `_repo_slug`/`_branch` before upload, while `bin/gstack-telemetry-log:227` includes a raw `error_message` field in the synced record. The current edge function's `TelemetryEvent` interface (`supabase/functions/telemetry-ingest/index.ts:7-18`) omits `error_message` and persists only `error_class` (sliced to 100 chars) — so it isn't stored in the DB today — but the raw string still leaves the machine in the POST body and would be persisted if that field is ever added server-side. Free-text telemetry fields should run through the redaction engine before upload.

## Impact

A user glancing at the shield icon / dashboard sees "0 attacks" and concludes there's no prompt-injection activity, when the real state is "backend errored" or "`jq` not installed." For a security indicator, the safe default is "unknown / degraded," not "0 / clear." (This is the same fail-open pattern the project's own `9cc41b71` commit — "4 security guards failing open" — was correcting elsewhere.)

## Suggested fix

- `community-pulse`: on `catch`, return a non-200 status or include an explicit `{ "error": true }` / `{ "stale": true }` flag instead of all-zero aggregates.
- Dashboards: render "data unavailable" distinctly from "0 attacks"; when `jq` is missing, say so rather than reporting 0.
- Redact (or drop) `error_message` before it is written to the synced payload.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.