Skip to content

feat: support custom prompt#1441

Open
earayu wants to merge 8 commits intomainfrom
feature/support_customize_prompt
Open

feat: support custom prompt#1441
earayu wants to merge 8 commits intomainfrom
feature/support_customize_prompt

Conversation

@earayu
Copy link
Collaborator

@earayu earayu commented Feb 4, 2026

Note

Medium Risk
Introduces new DB-backed prompt resolution used in the agent chat flow plus new public endpoints; incorrect resolution/validation could change agent behavior or break chats, and includes a schema migration.

Overview
Adds first-class prompt customization backed by a new prompt_template table (model, repository mixin, Alembic migration) and a PromptTemplateService that resolves prompts by priority (bot config → user default → system default → hardcoded) and supports preview/validation.

Exposes new REST endpoints under /api/v1/prompts/* (user CRUD/reset, system defaults, preview, validate) and updates OpenAPI schemas/clients accordingly; also extends collection config with index_prompts for per-collection index prompt overrides.

Integrates the resolver into agent_chat_service so WebSocket chats use resolved system/query prompts, bumps mcp-agent to 0.2.6 with corresponding logger setting changes, and includes small compatibility fixes (FastAPI Query(..., pattern=...), relax OTel span recording check).

Written by Cursor Bugbot for commit 637cc04. This will update automatically on new commits. Configure here.

@earayu earayu requested a review from sailwebs February 4, 2026 13:14
@apecloud-bot apecloud-bot added the size/XXL Denotes a PR that changes 1000+ lines. label Feb 4, 2026
sa.Column('gmt_updated', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('NOW()')),
sa.Column('gmt_deleted', sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint('id', name=op.f('pk_prompt_template'))
)
Copy link

Choose a reason for hiding this comment

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

Missing unique constraint creates race condition for duplicates

Medium Severity

The prompt_template table lacks a unique constraint on (prompt_type, scope, user_id, language). The create_or_update_prompt_template method uses a read-check-write pattern (query existing, then create if not found) which is susceptible to race conditions. If two concurrent requests try to create the same prompt configuration, both could pass the existence check and create duplicate records, leading to data integrity issues and unpredictable behavior when querying.

Additional Locations (1)

Fix in Cursor Fix in Web

TemplateSyntaxError: If template has syntax errors
"""
jinja_template = Template(template)
return jinja_template.render(**variables)
Copy link

Choose a reason for hiding this comment

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

Template preview allows server-side template injection

High Severity

The preview_prompt method uses Jinja2's basic Template class to render user-provided templates without sandboxing. This allows authenticated users to execute arbitrary Python code on the server through Server-Side Template Injection (SSTI) payloads. Jinja2's SandboxedEnvironment from jinja2.sandbox should be used instead to restrict access to dangerous attributes and methods.

Additional Locations (1)

Fix in Cursor Fix in Web

except TemplateSyntaxError as e:
raise HTTPException(status_code=400, detail=f"Template syntax error: {str(e)}")
except Exception as e:
raise HTTPException(status_code=400, detail=f"Template rendering error: {str(e)}")
Copy link

Choose a reason for hiding this comment

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

Preview endpoint vulnerable to server-side template injection

High Severity

The /prompts/preview endpoint accepts user-provided Jinja2 templates and renders them using an unsandboxed Template(). This enables Server-Side Template Injection (SSTI). An attacker can craft a malicious template like {{ ''.__class__.__mro__[2].__subclasses__() }} to access Python internals, potentially leading to arbitrary code execution. Jinja2's SandboxedEnvironment is needed to restrict access to dangerous attributes and methods.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

description = Column(Text, nullable=True)
gmt_created = Column(DateTime(timezone=True), default=utc_now, nullable=False)
gmt_updated = Column(DateTime(timezone=True), default=utc_now, nullable=False)
gmt_deleted = Column(DateTime(timezone=True), nullable=True, index=True)
Copy link

Choose a reason for hiding this comment

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

Race condition allows duplicate prompt records without unique constraint

Medium Severity

The PromptTemplate model and migration lack a unique constraint on the logical key (prompt_type, scope, user_id, language) for non-deleted records. The create_or_update_prompt_template method uses a check-then-act pattern (SELECT then INSERT) which creates a race condition. Concurrent requests could both find no existing record and both insert, creating duplicates. Subsequent queries use .first() without ORDER BY, causing non-deterministic results where updates might affect only one duplicate while stale data gets returned.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XXL Denotes a PR that changes 1000+ lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants