Skip to content

fix(prompts): stop exposing the sequential DB id on the prompts API - #1201

Closed
seonghobae wants to merge 2 commits into
developfrom
claude/cwlab-pr-audit-governance-1hdcp5
Closed

fix(prompts): stop exposing the sequential DB id on the prompts API#1201
seonghobae wants to merge 2 commits into
developfrom
claude/cwlab-pr-audit-governance-1hdcp5

Conversation

@seonghobae

Copy link
Copy Markdown
Contributor

Description

PromptResponse (backend/api/prompts.py) declared id: int with model_config = {"from_attributes": True}, so both GET /api/prompts and POST /api/prompts serialized the raw sequential PromptTemplate.id surrogate PK straight onto the API surface — e.g. {"id": 1, "prompt_uid": "prompt_ab12…", …} — leaking row ordinals/volume (enumeration / count inference) to the client.

This violates the repo convention (CLAUDE.md → Key conventions):

Never expose sequential database ids through APIs or UI — use opaque public ids (task_uid, source_uid, folder_uid, document_id).

It is a genuine violation, not design intent:

  • The model already carries the opaque prompt_uid right beside the surrogate.
  • No route in this router consumes a prompt integer id — identity is prompt_uid, and there is no /{id} update/delete path.
  • The sibling AI-hub renderer (backend/api/ai_hub.py) already keys off prompt.prompt_uid and hashes provider ids into opaque keys, so this field only leaked the surrogate.

Fix: drop id from PromptResponse; prompt_uid is the sole public identity. The frontend prompt-studio keys its UI off static template constants, not this response id, so nothing downstream breaks.

Fixes # (no tracked issue — surfaced by an internal contract audit)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Verification (CI parity)

DISABLE_BACKGROUND_WORKERS=1 PYTHONWARNINGS=error pytest tests/test_prompts_api.py -q   → 20 passed
ruff check api/prompts.py tests/test_prompts_api.py                                     → All checks passed

TDD: the added test_prompt_crud assertions ("id" not in data on both the create and list responses) were confirmed to fail on the pre-fix code (assert 'id' not in {'id': 1, ...}) and pass after the fix. No Timeout/Fatal/Warn/Denied in output.

Follow-up (same anti-pattern, intentionally out of scope for this atomic fix)

backend/api/llm_providers.py exposes id: int (a clean analogous drop); backend/api/emails.py and backend/api/search.py use the int as a path key and need an opaque-id migration before the surrogate can be removed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HdCssGnNMhKHNu3TXFstWH


Generated by Claude Code

`PromptResponse` declared `id: int` with `from_attributes=True`, so both
`GET /api/prompts` and `POST /api/prompts` serialized the raw sequential
`PromptTemplate.id` surrogate PK straight onto the API surface (e.g.
`{"id": 1, "prompt_uid": "prompt_ab12…", …}`), leaking row ordinals/volume to
the client. This violates the repo convention: "Never expose sequential
database ids through APIs or UI — use opaque public ids". The model already
carries the opaque `prompt_uid` beside it, no route in this router consumes a
prompt integer id (identity is `prompt_uid`; there is no `/{id}` path), and the
sibling AI-hub renderer already keys off `prompt_uid` — so the field only
leaked the surrogate.

Fix: drop `id` from `PromptResponse`; `prompt_uid` is the sole public identity.

TDD: added assertions to `test_prompt_crud` that the create and list responses
omit `id` and carry `prompt_uid` — confirmed they fail on the pre-fix code
(`assert 'id' not in {'id': 1, ...}`) and pass after. Verified with CI parity:
`DISABLE_BACKGROUND_WORKERS=1 PYTHONWARNINGS=error pytest tests/test_prompts_api.py`
→ 20 passed (no Timeout/Fatal/Warn/Denied); `ruff check` clean. The frontend
prompt-studio keys UI off static template constants, not this response `id`.

Follow-up (same anti-pattern, out of scope for this atomic fix): `llm_providers.py`
exposes `id: int` (a clean analogous drop), while `emails.py`/`search.py` use the
int as a path key and need an opaque-id migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HdCssGnNMhKHNu3TXFstWH
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next review available in: 5 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 24cab67e-8126-468d-8c9a-f54586dd200e

📥 Commits

Reviewing files that changed from the base of the PR and between eae74e2 and 76a3fe8.

📒 Files selected for processing (2)
  • backend/api/prompts.py
  • backend/tests/test_prompts_api.py

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

@seonghobae

Copy link
Copy Markdown
Contributor Author

Superseded by clean reimplementation on current develop (opaque prompt_uid only). See the Loop PR that also lands CardDAV encoded path traversal.

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