Replaces the opaque Bounty.bounty_type='credential' path with a
dedicated `credentials` table whose schema is forward-compatible
across every auth-bearing service in the fleet. Hoisted indexed
columns (secret_sha256, principal, service, attacker_ip) carry the
universal reuse-analytics signal; service-specific JSON keys ride
in `fields`. Cross-service reuse queries become an indexed lookup
on secret_sha256 instead of JSON_EXTRACT scans.
Schema decisions baked in (per ANTI):
- New `Credential` table, not extension to Bounty
- Hoisted `principal` column for cross-service principal-reuse
- Standardized JSON keys: every payload carries secret_b64 +
secret_printable + principal universally; service-specific extras
(user, domain, dn, mech, …) ride alongside
The auth-helper SD-block emits the new shape natively. The ingester
forks at _extract_bounty:
- Native shape (SSH/Telnet, future emitters): secret_b64 present →
direct upsert_credential
- Legacy shape (FTP/POP3/IMAP/SMTP today): username + password →
adapter synthesizes secret_{b64,sha256,printable} on the fly,
upserts into the same Credential table. Tracked as DEBT-039;
one-shot bridge until those service templates migrate.
Defense-in-depth across five layers (input validation):
- C helper: bytes outside [0x20, 0x7f) collapse to '?', RFC 5424
escape rules for \\, ", ]; b64 preserves exact bytes
- Ingester native branch: rejects malformed secret_b64 (regex), drops
the credential row but keeps the underlying Log
- Ingester legacy adapter: same printable-ASCII filter as the C
code; sha256 + b64 over the original utf-8 bytes (lossless, even
when secret_printable is sanitized)
- DB column caps with truncation warning; sha256 always over the
full pre-truncation bytes so reuse queries match across truncation
- JSON serialized with ensure_ascii=True so utf8mb4 columns stay
safe even with non-ASCII service-specific keys
Bounty.bounty_type='credential' is no longer written. Pre-v1: no
historical backfill; existing rows stay untouched but unused.
595 tests pass; new tests cover the model + repo (upsert dedup,
null-principal independence, cross-service reuse, filters), both
ingester branches, b64 validation, sanitization preserving the
fingerprinting signal in b64.
249 lines
5.2 KiB
Python
249 lines
5.2 KiB
Python
"""
|
|
Database tables (SQLModel) and HTTP request/response shapes (Pydantic).
|
|
|
|
Split into topical modules for readability, but every symbol is re-exported
|
|
from this package so ``from decnet.web.db.models import X`` keeps working
|
|
everywhere — no importer needs to know which submodule a class lives in.
|
|
"""
|
|
from ._base import (
|
|
NullableDatetime,
|
|
NullableString,
|
|
_BIG_TEXT,
|
|
_normalize_null,
|
|
)
|
|
from .common import (
|
|
MessageResponse,
|
|
)
|
|
from .auth import (
|
|
AdminConfigResponse,
|
|
ChangePasswordRequest,
|
|
ConfigResponse,
|
|
CreateUserRequest,
|
|
DeploymentLimitRequest,
|
|
GlobalMutationIntervalRequest,
|
|
LoginRequest,
|
|
ResetUserPasswordRequest,
|
|
Token,
|
|
UpdateUserRoleRequest,
|
|
User,
|
|
UserResponse,
|
|
)
|
|
from .attackers import (
|
|
Attacker,
|
|
AttackerBehavior,
|
|
AttackersResponse,
|
|
SessionProfile,
|
|
SmtpTarget,
|
|
)
|
|
from .deploy import (
|
|
DeployIniRequest,
|
|
DeployResponse,
|
|
MutateIntervalRequest,
|
|
PurgeResponse,
|
|
)
|
|
from .health import (
|
|
ComponentHealth,
|
|
HealthResponse,
|
|
)
|
|
from .logs import (
|
|
Bounty,
|
|
BountyResponse,
|
|
Credential,
|
|
CredentialsResponse,
|
|
Log,
|
|
LogsResponse,
|
|
State,
|
|
StatsResponse,
|
|
)
|
|
from .swarm import (
|
|
DeckyShard,
|
|
DeckyShardView,
|
|
SwarmCheckResponse,
|
|
SwarmDeployRequest,
|
|
SwarmDeployResponse,
|
|
SwarmEnrolledBundle,
|
|
SwarmEnrollRequest,
|
|
SwarmHost,
|
|
SwarmHostHealth,
|
|
SwarmHostResult,
|
|
SwarmHostView,
|
|
SwarmTeardownRequest,
|
|
SwarmUpdaterBundle,
|
|
)
|
|
from .topology import (
|
|
LAN,
|
|
ArchetypeCatalogResponse,
|
|
ArchetypeEntry,
|
|
DeckyCreateRequest,
|
|
DeckyRow,
|
|
DeckyUpdateRequest,
|
|
DeployAcceptedResponse,
|
|
EdgeCreateRequest,
|
|
EdgeRow,
|
|
LANCreateRequest,
|
|
LANRow,
|
|
LANUpdateRequest,
|
|
MutationEnqueueRequest,
|
|
MutationEnqueueResponse,
|
|
MutationRow,
|
|
NextIPResponse,
|
|
NextSubnetResponse,
|
|
NotEditableResponse,
|
|
ReapReportResponse,
|
|
ServiceCatalogResponse,
|
|
Topology,
|
|
TopologyDecky,
|
|
TopologyDetail,
|
|
TopologyEdge,
|
|
TopologyGenerateRequest,
|
|
TopologyListResponse,
|
|
TopologyMutation,
|
|
TopologyStatusEvent,
|
|
TopologyStatusEventRow,
|
|
TopologySummary,
|
|
ValidationErrorResponse,
|
|
ValidationIssueResponse,
|
|
VersionConflictResponse,
|
|
)
|
|
from .updater import (
|
|
HostReleaseInfo,
|
|
HostReleasesResponse,
|
|
PushUpdateRequest,
|
|
PushUpdateResponse,
|
|
PushUpdateResult,
|
|
RollbackRequest,
|
|
RollbackResponse,
|
|
)
|
|
from .webhooks import (
|
|
SimpleEvent,
|
|
WebhookCreateRequest,
|
|
WebhookCreateResponse,
|
|
WebhookResponse,
|
|
WebhookSubscription,
|
|
WebhookTestResponse,
|
|
WebhookUpdateRequest,
|
|
)
|
|
from .workers import (
|
|
StartAllResponse,
|
|
StartFailure,
|
|
WorkerControlResponse,
|
|
WorkersResponse,
|
|
WorkerStatus,
|
|
)
|
|
|
|
__all__ = [
|
|
# _base
|
|
"NullableDatetime",
|
|
"NullableString",
|
|
"_BIG_TEXT",
|
|
"_normalize_null",
|
|
# common
|
|
"MessageResponse",
|
|
# auth
|
|
"AdminConfigResponse",
|
|
"ChangePasswordRequest",
|
|
"ConfigResponse",
|
|
"CreateUserRequest",
|
|
"DeploymentLimitRequest",
|
|
"GlobalMutationIntervalRequest",
|
|
"LoginRequest",
|
|
"ResetUserPasswordRequest",
|
|
"Token",
|
|
"UpdateUserRoleRequest",
|
|
"User",
|
|
"UserResponse",
|
|
# attackers
|
|
"Attacker",
|
|
"AttackerBehavior",
|
|
"AttackersResponse",
|
|
"SessionProfile",
|
|
"SmtpTarget",
|
|
# deploy
|
|
"DeployIniRequest",
|
|
"DeployResponse",
|
|
"MutateIntervalRequest",
|
|
"PurgeResponse",
|
|
# health
|
|
"ComponentHealth",
|
|
"HealthResponse",
|
|
# logs
|
|
"Bounty",
|
|
"BountyResponse",
|
|
"Credential",
|
|
"CredentialsResponse",
|
|
"Log",
|
|
"LogsResponse",
|
|
"State",
|
|
"StatsResponse",
|
|
# swarm
|
|
"DeckyShard",
|
|
"DeckyShardView",
|
|
"SwarmCheckResponse",
|
|
"SwarmDeployRequest",
|
|
"SwarmDeployResponse",
|
|
"SwarmEnrolledBundle",
|
|
"SwarmEnrollRequest",
|
|
"SwarmHost",
|
|
"SwarmHostHealth",
|
|
"SwarmHostResult",
|
|
"SwarmHostView",
|
|
"SwarmTeardownRequest",
|
|
"SwarmUpdaterBundle",
|
|
# topology
|
|
"LAN",
|
|
"ArchetypeCatalogResponse",
|
|
"ArchetypeEntry",
|
|
"DeckyCreateRequest",
|
|
"DeckyRow",
|
|
"DeckyUpdateRequest",
|
|
"DeployAcceptedResponse",
|
|
"EdgeCreateRequest",
|
|
"EdgeRow",
|
|
"LANCreateRequest",
|
|
"LANRow",
|
|
"LANUpdateRequest",
|
|
"MutationEnqueueRequest",
|
|
"MutationEnqueueResponse",
|
|
"MutationRow",
|
|
"NextIPResponse",
|
|
"NextSubnetResponse",
|
|
"NotEditableResponse",
|
|
"ReapReportResponse",
|
|
"ServiceCatalogResponse",
|
|
"Topology",
|
|
"TopologyDecky",
|
|
"TopologyDetail",
|
|
"TopologyEdge",
|
|
"TopologyGenerateRequest",
|
|
"TopologyListResponse",
|
|
"TopologyMutation",
|
|
"TopologyStatusEvent",
|
|
"TopologyStatusEventRow",
|
|
"TopologySummary",
|
|
"ValidationErrorResponse",
|
|
"ValidationIssueResponse",
|
|
"VersionConflictResponse",
|
|
# updater
|
|
"HostReleaseInfo",
|
|
"HostReleasesResponse",
|
|
"PushUpdateRequest",
|
|
"PushUpdateResponse",
|
|
"PushUpdateResult",
|
|
"RollbackRequest",
|
|
"RollbackResponse",
|
|
# webhooks
|
|
"SimpleEvent",
|
|
"WebhookCreateRequest",
|
|
"WebhookCreateResponse",
|
|
"WebhookResponse",
|
|
"WebhookSubscription",
|
|
"WebhookTestResponse",
|
|
"WebhookUpdateRequest",
|
|
# workers
|
|
"StartAllResponse",
|
|
"StartFailure",
|
|
"WorkerControlResponse",
|
|
"WorkersResponse",
|
|
"WorkerStatus",
|
|
]
|