feat: initial public release
ConsentOS — a privacy-first cookie consent management platform. Self-hosted, source-available alternative to OneTrust, Cookiebot, and CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant architecture with role-based access, configuration cascade (system → org → group → site → region), dark-pattern detection in the scanner, and a tamper-evident consent record audit trail. This is the initial public release. Prior development history is retained internally. See README.md for the feature list, architecture overview, and quick-start instructions. Licensed under the Elastic Licence 2.0 — self-host freely; do not resell as a managed service.
This commit is contained in:
142
apps/api/src/schemas/scanner.py
Normal file
142
apps/api/src/schemas/scanner.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""Pydantic schemas for scanner and client-side cookie reports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from enum import StrEnum
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ScanStatus(StrEnum):
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class ScanTrigger(StrEnum):
|
||||
MANUAL = "manual"
|
||||
SCHEDULED = "scheduled"
|
||||
CLIENT_REPORT = "client_report"
|
||||
|
||||
|
||||
# ── Client-side cookie report ────────────────────────────────────────
|
||||
|
||||
|
||||
class ReportedCookie(BaseModel):
|
||||
"""A single cookie/storage item reported by the client-side reporter."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
domain: str = Field(..., min_length=1, max_length=255)
|
||||
storage_type: str = Field(default="cookie", max_length=30)
|
||||
value_length: int = Field(default=0, ge=0)
|
||||
path: str | None = None
|
||||
is_secure: bool | None = None
|
||||
same_site: str | None = None
|
||||
script_source: str | None = None
|
||||
|
||||
|
||||
class CookieReportRequest(BaseModel):
|
||||
"""Payload from the client-side cookie reporter."""
|
||||
|
||||
site_id: uuid.UUID
|
||||
page_url: str = Field(..., max_length=2000)
|
||||
cookies: list[ReportedCookie] = Field(..., max_length=500)
|
||||
collected_at: datetime
|
||||
user_agent: str = Field(default="", max_length=500)
|
||||
|
||||
|
||||
class CookieReportResponse(BaseModel):
|
||||
"""Acknowledgement response for a cookie report."""
|
||||
|
||||
accepted: bool = True
|
||||
cookies_received: int
|
||||
new_cookies: int = 0
|
||||
|
||||
|
||||
# ── Scan job schemas ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
class ScanResultResponse(BaseModel):
|
||||
"""A single scan result — a cookie found on a specific page."""
|
||||
|
||||
id: uuid.UUID
|
||||
scan_job_id: uuid.UUID
|
||||
page_url: str
|
||||
cookie_name: str
|
||||
cookie_domain: str
|
||||
storage_type: str
|
||||
attributes: dict | None = None
|
||||
script_source: str | None = None
|
||||
auto_category: str | None = None
|
||||
initiator_chain: list[str] | None = None
|
||||
found_at: datetime
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class ScanJobResponse(BaseModel):
|
||||
"""Response schema for a scan job."""
|
||||
|
||||
id: uuid.UUID
|
||||
site_id: uuid.UUID
|
||||
status: str
|
||||
trigger: str
|
||||
pages_scanned: int
|
||||
pages_total: int | None
|
||||
cookies_found: int
|
||||
error_message: str | None
|
||||
started_at: datetime | None
|
||||
completed_at: datetime | None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class ScanJobDetailResponse(ScanJobResponse):
|
||||
"""Scan job with results included."""
|
||||
|
||||
results: list[ScanResultResponse] = []
|
||||
|
||||
|
||||
class TriggerScanRequest(BaseModel):
|
||||
"""Request to trigger a new scan."""
|
||||
|
||||
site_id: uuid.UUID
|
||||
max_pages: int = Field(default=50, ge=1, le=500)
|
||||
|
||||
|
||||
# ── Diff engine schemas ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class DiffStatus(StrEnum):
|
||||
NEW = "new"
|
||||
REMOVED = "removed"
|
||||
CHANGED = "changed"
|
||||
|
||||
|
||||
class CookieDiffItem(BaseModel):
|
||||
"""A single cookie difference between two scans."""
|
||||
|
||||
name: str
|
||||
domain: str
|
||||
storage_type: str
|
||||
diff_status: DiffStatus
|
||||
details: str | None = None
|
||||
|
||||
|
||||
class ScanDiffResponse(BaseModel):
|
||||
"""Diff between two scans."""
|
||||
|
||||
current_scan_id: uuid.UUID
|
||||
previous_scan_id: uuid.UUID | None
|
||||
new_cookies: list[CookieDiffItem] = []
|
||||
removed_cookies: list[CookieDiffItem] = []
|
||||
changed_cookies: list[CookieDiffItem] = []
|
||||
total_new: int = 0
|
||||
total_removed: int = 0
|
||||
total_changed: int = 0
|
||||
Reference in New Issue
Block a user