fix(descriptor): bound LIMIT to prevent unbounded SELECT (v0.2.3) - #13
Merged
eleven-by-clodocapeo[bot] merged 3 commits intoAug 15, 2026
Merged
Conversation
A descriptor with limit=None emitted no LIMIT clause at all, letting any of the 6 consuming services run an unbounded SELECT against production through the nominal query path (mass exfiltration / DoS). limit now has an upper bound (DEFAULT_MAX_LIMIT=1000) enforced by pydantic (le=), and the compiler applies that same ceiling whenever limit is omitted instead of emitting no clause. Refs #6 Agent-Role: forge Agent-Thread: queryme-v023-limit-bound Work-Unit: QUERYME-V023-LIMIT-BOUND Issue: 6
Persist the current work unit before returning control to Eleven. Agent-Role: Forge Agent-Thread: queryme-v023-limit-bound Work-Unit: QUERYME-V023-LIMIT-BOUND Issue: 6 Evidence: checkpoint-pre-standby
Bastion review of the LIMIT-bound patch (PR #13) found the original fix still allowed silent truncation: limit=None fell back to a default ceiling with no error, the same failure mode ADR 001 SS3.3 already rejects for descriptors that exceed the plafond (cancel entirely, never return a partial/truncated result — a truncated read looks complete, e.g. a grants/revocation enumeration cut at the ceiling reads as 'this right doesn't exist'). limit is now a required field (was: optional with a silent default). DEFAULT_MAX_LIMIT renamed MAX_LIMIT since there is no default anymore. Every caller must state its own bound at v0.2.3 bump time ; mypy strict + pydantic make the omission a compile-time/validation error, not a runtime surprise. compiler.py also re-checks the bound directly (model_construct() bypasses field validators, so this is the last real gate before SQL is emitted). Removed 3 agent-protocol staging files (.checkpoint-comment.md, .pr-body.md, .report.md) that leaked into this public repo's history via an earlier auto-checkpoint — one of them named the 6 affected production services in a vulnerability write-up before any were patched. Added matching .gitignore entries to prevent recurrence. Refs #6 Agent-Role: forge Agent-Thread: queryme-v023-limit-bound Work-Unit: QUERYME-V023-LIMIT-BOUND Issue: 6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
limitonQueryDescriptorhad no upper bound and, when omitted, producedno
LIMITclause at all — a security fix for that is the point of this PR(v0.2.3,
v0.2.2untouched — RC-40 of ADR 001 is frozen).Per Bastion review,
limitis now a required field (not optional with asilent default) — every caller must state its own bound explicitly, capped
at
MAX_LIMIT = 1000. An out-of-boundslimitis rejected(
ValidationError), never silently clamped or defaulted, matching ADR 001§3.3 (a truncated result is worse than a refusal). The compiler re-checks
the bound as a defense-in-depth gate against
model_construct()bypass.An earlier revision of this PR briefly included agent-protocol staging
files that named affected consumers and exploitation detail — removed;
.gitignoreupdated to prevent recurrence.Tests
Ran inside the branch worktree (
uv sync && uv run ruff check . && uv run mypy src && uv run pytest):ruff check .→ All checks passed!mypy src(strict) → Success: no issues found in 5 source filespytest→ 46 passedÉcarts / hypothèses
MAX_LIMIT = 1000) is a judgment call, not a documentedQueryMe convention — Bastion clearance requested on this choice.
limitbecoming required is a breaking change for every call site;migrating consumers is separate follow-up work, out of scope here.
v0.2.3tag not created yet — left for Keeper/Vigil once this PR merges(
v0.2.2must never move).Refs #6