Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**Vulnerability:** User-provided string fields (like project and connection names) lacked strict validation against control characters, only relying on length constraints.
**Learning:** This could potentially lead to Log Injection (CRLF injection), Null Byte Injection, or terminal escape injection if these strings are subsequently logged or rendered directly.
**Prevention:** Use explicit regex validation `pattern=r'^[^\x00-\x1F\x7F]+$'` on Pydantic string fields to strictly reject control characters.

## 2025-02-27 - Strict ASCII Control Character Validation in Pydantic Schemas
**Vulnerability:** Pydantic `str` fields intended for identifiers/names (e.g., `DiagramViewCreateIn.name`, `TableAnnotationUpsertIn.schema_name`, `ApiKeyCreateIn.key_name`) were previously lacking strict validation against ASCII control characters, exposing the system to potential log injection and terminal escape sequence vulnerabilities.
**Learning:** Standard length limits (`min_length`, `max_length`) do not prevent the ingestion of invisible control characters (`\x00-\x1F\x7F`).
**Prevention:** Enforce strict pattern matching using `pattern=r"^[^\x00-\x1F\x7F]+$"` for string fields that do not legitimately require multiline inputs or control characters, preventing malicious inputs from persisting into logs or execution environments.
24 changes: 20 additions & 4 deletions backend/app/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,11 @@ class IndexRedundancyOut(BaseModel):
class DiagramViewCreateIn(BaseModel):
"""Request body for saving an ERD canvas view."""

name: str = Field(min_length=1, max_length=200)
name: str = Field(
min_length=1,
max_length=200,
pattern=r"^[^\x00-\x1F\x7F]+$",
)
# Opaque client layout (node positions, hidden tables, viewport). The API
# bounds the serialized size in the endpoint to prevent abuse.
layout_json: dict
Expand All @@ -214,8 +218,16 @@ class DiagramViewDetailOut(DiagramViewOut):
class TableAnnotationUpsertIn(BaseModel):
"""Request body for creating/updating a table annotation."""

schema_name: str = Field(min_length=1, max_length=255)
relation_name: str = Field(min_length=1, max_length=255)
schema_name: str = Field(
min_length=1,
max_length=255,
pattern=r"^[^\x00-\x1F\x7F]+$",
)
relation_name: str = Field(
min_length=1,
max_length=255,
pattern=r"^[^\x00-\x1F\x7F]+$",
)
body: str = Field(min_length=1, max_length=10_000)


Expand Down Expand Up @@ -302,7 +314,11 @@ class DbmlConvertOut(BaseModel):
class ApiKeyCreateIn(BaseModel):
"""Request body for creating an API key."""

key_name: str = Field(min_length=1, max_length=128)
key_name: str = Field(
min_length=1,
max_length=128,
pattern=r"^[^\x00-\x1F\x7F]+$",
)


class ApiKeyOut(BaseModel):
Expand Down
Loading