fix: normalize and validate signature_header in webhook schemas
Strip whitespace (blank → None) and reject non-ASCII-token characters to prevent impossible header lookups that would fail all signed requests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
Abhimanyu Saharan
parent
fe310b50dc
commit
ac69c6b7b8
@@ -6,6 +6,8 @@ from datetime import datetime
|
|||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from pydantic import BeforeValidator
|
from pydantic import BeforeValidator
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
@@ -13,6 +15,9 @@ from app.schemas.common import NonEmptyStr
|
|||||||
|
|
||||||
RUNTIME_ANNOTATION_TYPES = (datetime, UUID, NonEmptyStr)
|
RUNTIME_ANNOTATION_TYPES = (datetime, UUID, NonEmptyStr)
|
||||||
|
|
||||||
|
# RFC 7230 token characters: visible ASCII except delimiters.
|
||||||
|
_HTTP_TOKEN_RE = re.compile(r"^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$")
|
||||||
|
|
||||||
|
|
||||||
def _normalize_secret(v: str | None) -> str | None:
|
def _normalize_secret(v: str | None) -> str | None:
|
||||||
"""Normalize blank/whitespace-only secrets to None."""
|
"""Normalize blank/whitespace-only secrets to None."""
|
||||||
@@ -22,7 +27,21 @@ def _normalize_secret(v: str | None) -> str | None:
|
|||||||
return stripped or None
|
return stripped or None
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_signature_header(v: str | None) -> str | None:
|
||||||
|
"""Normalize and validate signature_header as a valid HTTP header name."""
|
||||||
|
if v is None:
|
||||||
|
return None
|
||||||
|
stripped = v.strip()
|
||||||
|
if not stripped:
|
||||||
|
return None
|
||||||
|
if not _HTTP_TOKEN_RE.match(stripped):
|
||||||
|
msg = "signature_header must be a valid HTTP header name (ASCII token characters only)"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return stripped
|
||||||
|
|
||||||
|
|
||||||
NormalizedSecret = Annotated[str | None, BeforeValidator(_normalize_secret)]
|
NormalizedSecret = Annotated[str | None, BeforeValidator(_normalize_secret)]
|
||||||
|
NormalizedSignatureHeader = Annotated[str | None, BeforeValidator(_normalize_signature_header)]
|
||||||
|
|
||||||
|
|
||||||
class BoardWebhookCreate(SQLModel):
|
class BoardWebhookCreate(SQLModel):
|
||||||
@@ -32,7 +51,7 @@ class BoardWebhookCreate(SQLModel):
|
|||||||
enabled: bool = True
|
enabled: bool = True
|
||||||
agent_id: UUID | None = None
|
agent_id: UUID | None = None
|
||||||
secret: NormalizedSecret = None
|
secret: NormalizedSecret = None
|
||||||
signature_header: str | None = None
|
signature_header: NormalizedSignatureHeader = None
|
||||||
|
|
||||||
|
|
||||||
class BoardWebhookUpdate(SQLModel):
|
class BoardWebhookUpdate(SQLModel):
|
||||||
@@ -42,7 +61,7 @@ class BoardWebhookUpdate(SQLModel):
|
|||||||
enabled: bool | None = None
|
enabled: bool | None = None
|
||||||
agent_id: UUID | None = None
|
agent_id: UUID | None = None
|
||||||
secret: NormalizedSecret = None
|
secret: NormalizedSecret = None
|
||||||
signature_header: str | None = None
|
signature_header: NormalizedSignatureHeader = None
|
||||||
|
|
||||||
|
|
||||||
class BoardWebhookRead(SQLModel):
|
class BoardWebhookRead(SQLModel):
|
||||||
|
|||||||
Reference in New Issue
Block a user