"""ORM models for the internal system registry (2 tables). See ``server/alembic/versions/542_system_registry.py`` for the canonical schema and ``constants.py`` for the value enumerations. """ from __future__ import annotations import uuid from datetime import datetime from typing import Any from sqlalchemy import ( JSON, Boolean, DateTime, ForeignKey, Index, String, Text, UniqueConstraint, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from app.core.database import Base from app.core.sql_types import GUID as Uuid from app.domains.system_registry import constants as const class RegisteredSystem(Base): """One first-party system we build and run. ``owner_person_id`` is the point of the table. It is nullable only because an honest registry admits "we do not know yet" — but a live system with a NULL owner is a finding, surfaced by the ownerless read and by the coverage metric, not a quiet default. ``backup_owner_person_id`` exists so a single departure does not leave the system ownerless the moment the primary owner is offboarded: the offboarding blocker proposes the backup as the successor. """ __tablename__ = "registry_systems" __mapper_args__ = {"eager_defaults": True} id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4) #: Stable slug used in URLs, imports and the bounty picker. #: Lower-cased on write; the natural key. slug: Mapped[str] = mapped_column(String(64), nullable=False) name: Mapped[str] = mapped_column( String(const.MAX_NAME_CHARS), nullable=False, ) kind: Mapped[str] = mapped_column( String(24), nullable=False, default=const.KIND_OTHER, server_default=const.KIND_OTHER, ) status: Mapped[str] = mapped_column( String(16), nullable=False, default=const.STATUS_ACTIVE, server_default=const.STATUS_ACTIVE, ) exposure: Mapped[str] = mapped_column( String(16), nullable=False, default=const.EXPOSURE_UNKNOWN, server_default=const.EXPOSURE_UNKNOWN, ) #: Hostnames this system answers on. Matched case-insensitively against #: a bounty report's ``affected_system`` to suggest the link — the #: registry does not own DNS, it just knows its own front doors. hostnames: Mapped[list[str] | None] = mapped_column(JSON) description: Mapped[str | None] = mapped_column(Text) #: ``pingcap-inc/clinic`` — lets a duplicate-of-internal-work claim #: point at the repo where the fix landed. repo: Mapped[str | None] = mapped_column(String(255)) # ── Accountability ── owner_person_id: Mapped[uuid.UUID | None] = mapped_column( Uuid, ForeignKey("identity_persons.id", ondelete="SET NULL"), ) backup_owner_person_id: Mapped[uuid.UUID | None] = mapped_column( Uuid, ForeignKey("identity_persons.id", ondelete="SET NULL"), ) #: Snapshot of the owning org at the last ownership write. Kept for the #: same reason the bounty report keeps one: a transfer must not #: retroactively re-attribute last year's findings. owner_department: Mapped[str | None] = mapped_column(String(255)) owner_assigned_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), ) #: Which bounty pot a finding on this system is charged to. The #: registry is the natural home for the default; the report still #: carries its own value so a one-off can differ. budget_track: Mapped[str] = mapped_column( String(16), nullable=False, default="product", server_default="product", ) #: Whether the system is in the published bug-bounty scope. Separate #: from ``exposure``: something can be publicly reachable and still be #: out of scope (a third-party-hosted marketing page), and something #: internal can be in scope. Guessing either from the other is how a #: researcher gets told "out of scope" about a system that is not. in_bounty_scope: Mapped[bool] = mapped_column( Boolean, nullable=False, default=False, server_default="0", ) notes: Mapped[str | None] = mapped_column(Text) #: Free-form provenance for rows created by the seed/import, so a #: hand-curated row is distinguishable from a guessed one. source: Mapped[str] = mapped_column( String(24), nullable=False, default="manual", server_default="manual", ) created_by: Mapped[uuid.UUID | None] = mapped_column( Uuid, ForeignKey("identity_persons.id", ondelete="SET NULL"), ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now(), ) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) owner = relationship( "IdentityPerson", foreign_keys=[owner_person_id], lazy="raise", ) backup_owner = relationship( "IdentityPerson", foreign_keys=[backup_owner_person_id], lazy="raise", ) __table_args__ = ( UniqueConstraint("slug", name="uq_registry_systems_slug"), Index("ix_registry_systems_owner", "owner_person_id", "deleted_at"), Index("ix_registry_systems_status", "status", "deleted_at"), Index("ix_registry_systems_exposure", "exposure", "status"), Index("ix_registry_systems_scope", "in_bounty_scope", "status"), ) def __repr__(self) -> str: return f"" class RegisteredSystemEvent(Base): """Append-only ownership + lifecycle trail for one system. Separate from the audit log on purpose: the audit log answers "who did what across the platform", this answers "who has been responsible for THIS system, and since when" — the question an incident asks. """ __tablename__ = "registry_system_events" __mapper_args__ = {"eager_defaults": True} id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4) system_id: Mapped[uuid.UUID] = mapped_column( Uuid, ForeignKey("registry_systems.id", ondelete="CASCADE"), nullable=False, ) event_type: Mapped[str] = mapped_column(String(32), nullable=False) actor_person_id: Mapped[uuid.UUID | None] = mapped_column( Uuid, ForeignKey("identity_persons.id", ondelete="SET NULL"), ) #: Snapshot so the trail reads correctly after the actor leaves. actor_name: Mapped[str | None] = mapped_column(String(255)) body: Mapped[str | None] = mapped_column(Text) meta: Mapped[dict[str, Any] | None] = mapped_column(JSON) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) __table_args__ = ( Index("ix_registry_events_system", "system_id", "created_at"), ) def __repr__(self) -> str: return f""