Skip to content

fix(loki): scope namespace-lockdown check to the LogQL stream selector - #532

Merged
billzhuang merged 4 commits into
mainfrom
claude/eloquent-bell-l5jecm
Jul 11, 2026
Merged

fix(loki): scope namespace-lockdown check to the LogQL stream selector#532
billzhuang merged 4 commits into
mainfrom
claude/eloquent-bell-l5jecm

Conversation

@billzhuang

@billzhuang billzhuang commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

The Linear Heimdall project's Backlog/Todo/In Progress were all empty and the open-PR queue was empty too, so per the autonomous-shipping routine's fallback I audited the codebase for a genuine bug rather than another cosmetic refactor (the repo already merged a long streak of those — #521-#531).

Problem

validateNamespaceLockdown (src/lib/loki.ts) is supposed to reject any LogQL query that doesn't target the operator-configured lockedNamespace — a multi-tenant isolation control. It worked by running a regex for namespace="<locked>" (or namespace=~"<locked>") against the entire raw query string, not just the stream selector.

LogQL supports backtick-delimited raw string literals in line filters (no quote-escaping needed), so a query can put the literal text namespace="prod" in an unrelated line filter while the real stream selector targets a completely different namespace:

{namespace="evil"} |= `namespace="prod"`

With lockedNamespace: "prod" configured, this query passed validateNamespaceLockdown (confirmed via a standalone repro before the fix) and was forwarded to Loki verbatim — Loki executes the real selector {namespace="evil"}, silently bypassing the lockdown. This is the same class of bug as PR #526 (kubectl/aws quoted-value bypass of the read-only policy), just in the Loki namespace-isolation control instead.

I checked the other tools with namespace-lockdown-style logic (kubecost.ts, helm.ts via resolveNamespaceLockdown in tool-config.ts) — those compare a structured requestedNamespace field, not a free-text regex over a query string, so they aren't affected. Datadog/Jaeger/Prometheus don't implement a namespace lockdown at all. Loki is the only tool with this exposure.

Changes

  • src/lib/loki.ts: added extractStreamSelector, which locates the leading {...} brace group of a LogQL query (quote-aware, so braces inside quoted label values like {namespace=~"prod-[0-9]{3}"} don't break the boundary). validateNamespaceLockdown now runs its regex against only that selector substring, and fails safe (false) if no selector is found at all.
  • src/lib/__tests__/loki.test.ts: added regression tests — confirmed 2 of the 4 new cases fail on pre-fix code ({namespace="evil"} |= \namespace="prod"`and a barenamespace="prod"` with no selector) and all pass with the fix, plus coverage for the brace-inside-quotes selector-boundary case.

Validation

  • Confirmed the bug live before the fix via a standalone repro (validateNamespaceLockdown returned true for the malicious query above).
  • New regression tests confirmed to fail on pre-fix code (git stash/manual revert) and pass with the fix.
  • npm run typecheck — clean.
  • npm test — full suite: 108 files / 3049 tests passing.

Not auto-verified: no live Loki/Grafana Tempo server was used — this is a pure query-string-parsing fix validated at the unit level, consistent with this repo's testing policy (mock HTTP clients, never spawn real external services in tests).


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved namespace restrictions for Loki queries by accurately interpreting selectors and matcher values.
    • Blocked bypass attempts using misleading text, quoted values, negated matchers, or malformed selectors.
    • Continued allowing valid queries with properly matched locked namespaces, including quoted values and regular-expression matchers.

validateNamespaceLockdown regex-matched the *entire* raw query string, so a
query like `{namespace="evil"} |= `namespace="prod"`` satisfied the lockdown
via the backtick line-filter text while the real stream selector targeted a
different namespace — a silent bypass of the multi-tenant namespace
isolation the lockdown exists to enforce. Now the check is scoped to the
leading `{...}` stream selector only, mirroring the argv-tokenization fix
already applied to kubectl/aws safety (#526).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyBW9iLrDdjDxndKvAr8TD
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@billzhuang, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8168f55d-65b7-44b0-bc08-c33785169cd9

📥 Commits

Reviewing files that changed from the base of the PR and between 076cc76 and cfaf425.

📒 Files selected for processing (2)
  • src/lib/__tests__/loki.test.ts
  • src/lib/loki.ts
📝 Walkthrough

Walkthrough

Namespace lockdown now uses selector-aware LogQL parsing. It skips quoted and raw-string contents while extracting selectors, parses label matchers, validates exact positive namespace matches, and adds coverage for spoofed, malformed, negated, quoted, and valid selectors.

Changes

Namespace Lockdown Validation

Layer / File(s) Summary
LogQL selector extraction and matcher parsing
src/lib/loki.ts
Selectors are extracted while ignoring quoted and backtick-delimited braces, then parsed into structured label matchers.
Structured namespace lockdown enforcement
src/lib/loki.ts, src/lib/__tests__/loki.test.ts
Lockdown accepts exact namespace values with = or =~; tests cover spoofing, negation, malformed selectors, quoting, and valid queries.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LokiQuery
  participant NamespaceLockdown
  participant SelectorParser
  LokiQuery->>NamespaceLockdown: validate query and locked namespace
  NamespaceLockdown->>SelectorParser: extract and parse stream selector
  SelectorParser-->>NamespaceLockdown: return structured matchers
  NamespaceLockdown-->>LokiQuery: allow or reject query
Loading

Possibly related PRs

  • billzhuang/heimdall#320: Refactored the regular-expression utility previously used by namespace lockdown validation.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: scoping Loki namespace-lockdown validation to the LogQL stream selector.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/eloquent-bell-l5jecm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves LogQL namespace lockdown validation by extracting and validating only the stream selector portion of the query, preventing bypasses via line filters. However, a critical security vulnerability was identified in the extractStreamSelector parser, which fails to track backtick-quoted raw string literals. This omission allows attackers to bypass the namespace lockdown by embedding double quotes and closing braces inside backticks. A code suggestion was provided to track both double quotes and backticks correctly.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/loki.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22c058bad6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/loki.ts Outdated
…elector text

Review from gemini-code-assist and codex both found real gaps in the prior
fix: extractStreamSelector only tracked double quotes, so a backtick raw
string (valid LogQL syntax) confused brace-depth counting — letting an
attacker's decoy `namespace="prod"` text inside another matcher's backtick
value spoof the lockdown regex (real bypass), while a legitimate backtick
value containing a brace could get a valid query wrongly rejected
(false-negative).

Root cause was deeper than quote-tracking: even with correct selector
boundaries, running a regex over the raw selector text is still spoofable by
decoy text inside an unrelated matcher's unescaped backtick value. Replaced
that regex check with a real parse of the selector into label/operator/value
matchers (handling both quote styles), so only the actual "namespace" label's
parsed value is compared — decoy text elsewhere can no longer masquerade as
the real matcher, and malformed selectors fail closed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyBW9iLrDdjDxndKvAr8TD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 076cc76921

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/loki.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/__tests__/loki.test.ts (1)

97-144: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add a regression test for multi-selector queries.

The new cases thoroughly cover single-selector spoofing, quoting, negation, and malformed selectors. They don't cover a query with more than one stream selector (see the bypass flagged in loki.ts). Recommend adding a case such as sum(rate({namespace="prod"}[5m])) / sum(rate({namespace="evil"}[5m])) asserting false for 'prod', so the fix is locked in.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/__tests__/loki.test.ts` around lines 97 - 144, The tests are missing
coverage for queries containing multiple stream selectors. In the Loki
validation test suite around validateNamespaceLockdown, add a regression case
using a query such as sum(rate({namespace="prod"}[5m])) /
sum(rate({namespace="evil"}[5m])) and assert it returns false for the locked
namespace "prod".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/loki.ts`:
- Around line 147-154: Update validateNamespaceLockdown to extract and validate
every stream selector in the query rather than only the first one. Require each
selector to parse successfully and contain exactly a namespace matcher using '='
or '=~' with value lockedNamespace; return false for any missing, invalid, or
mismatched selector, and only return true when all selectors pass.

---

Nitpick comments:
In `@src/lib/__tests__/loki.test.ts`:
- Around line 97-144: The tests are missing coverage for queries containing
multiple stream selectors. In the Loki validation test suite around
validateNamespaceLockdown, add a regression case using a query such as
sum(rate({namespace="prod"}[5m])) / sum(rate({namespace="evil"}[5m])) and assert
it returns false for the locked namespace "prod".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a54b065e-0b2e-44a9-a61d-f1f7b3d82252

📥 Commits

Reviewing files that changed from the base of the PR and between f9d4032 and 076cc76.

📒 Files selected for processing (2)
  • src/lib/__tests__/loki.test.ts
  • src/lib/loki.ts

Comment thread src/lib/loki.ts Outdated
…ockdown check

Two more review findings (gemini-code-assist/codex/coderabbit) on the same
namespace-lockdown check, both real:

- A decoy selector hidden in a leading `#`-comment (which Loki itself ignores
  per the LogQL spec) was picked up by indexOf('{') and validated in place
  of the real, executed selector.
- Only the *first* stream selector was checked. LogQL metric queries can
  combine multiple selectors via binary operators (e.g.
  `sum(rate({a}[5m])) / sum(rate({b}[5m]))`), so a second selector could
  target any namespace while the whole query is forwarded to Loki unchanged.

Fixed by stripping quote-aware `#` comments before extraction, and by
extracting *every* top-level `{...}` selector and requiring all of them to
carry the locked namespace matcher, not just the first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyBW9iLrDdjDxndKvAr8TD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e1efe241d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/loki.ts
codex flagged that the matcher-value decoder only understood escapes as
"drop the backslash, keep the next char" — correct for \" and \\, but wrong
for anything else. prod would naively decode to the literal text
"u0070rod" instead of the "prod" Loki actually parses it as (a real LogQL
unicode escape), so a namespace value written with an escape sequence could
compare against the wrong string in either direction.

We don't implement full Go/LogQL string-literal decoding (octal, \xHH,
\uHHHH, \UHHHHHHHH, \n/\t/...); K8s namespace names are simple DNS-1123
labels that never need escaping in practice, so instead of guessing wrong,
any escape other than \" or \\ now fails the whole selector parse closed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyBW9iLrDdjDxndKvAr8TD
@billzhuang
billzhuang merged commit 6f4398e into main Jul 11, 2026
5 checks passed
@billzhuang
billzhuang deleted the claude/eloquent-bell-l5jecm branch July 11, 2026 04:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants