Files
rengine/shared/models/scan_correlation.py
T

290 lines
8.3 KiB
Python

import uuid
from pydantic import BaseModel, Field
from shared.definitions.asset_query import MAX_QUERY_LENGTH
from shared.models.asset_query import QueryError
from shared.models.http_asset import HttpAssetRead
from shared.models.ip_address import IpAddressRead
from shared.models.port import PortRead
from shared.models.subdomain import Facet, SubdomainRelation
class SurfaceStat(BaseModel):
key: str
label: str
value: int
filter: str = ""
class AttentionItem(BaseModel):
key: str
label: str
count: int
filter: str
tone: str
class Bucket(BaseModel):
key: str
label: str
count: int
klass: str
class Tally(BaseModel):
name: str
count: int
class ClusterStat(BaseModel):
kind: str
reason: str
value: str
count: int
class SubdomainInsights(BaseModel):
surface: list[SurfaceStat] = Field(default_factory=list)
attention: list[AttentionItem] = Field(default_factory=list)
sources: list[Tally] = Field(default_factory=list)
single_source: int = 0
resolution: list[Bucket] = Field(default_factory=list)
status_reframe: list[Bucket] = Field(default_factory=list)
cert_buckets: list[Bucket] = Field(default_factory=list)
top_tech: list[Tally] = Field(default_factory=list)
tech_total: int = 0
top_asn: list[Tally] = Field(default_factory=list)
geography: list[Tally] = Field(default_factory=list)
geo_total: int = 0
clusters: list[ClusterStat] = Field(default_factory=list)
class SubdomainCorrelation(BaseModel):
primary_asset: HttpAssetRead | None = None
services: list[HttpAssetRead] = Field(default_factory=list)
ports: list[PortRead] = Field(default_factory=list)
ip_metas: list[IpAddressRead] = Field(default_factory=list)
related: list[SubdomainRelation] = Field(default_factory=list)
class IpGroupRead(BaseModel):
ip: str
version: int
asn: int | None = None
asn_org: str | None = None
country: str | None = None
prefix: str | None = None
is_cdn: bool = False
cdn_name: str | None = None
is_alive: bool | None = None
ptr_hostnames: list[str] = Field(default_factory=list)
ports: list[PortRead] = Field(default_factory=list)
host_count: int = 0
hosts: list[str] = Field(default_factory=list)
port_count: int = 0
has_sensitive: bool = False
asset_count: int = 0
class IpGroupPage(BaseModel):
items: list[IpGroupRead] = Field(default_factory=list)
total: int = 0
total_capped: bool = False
error: QueryError | None = None
class IpGroupFilter(BaseModel):
q: str | None = Field(default=None, max_length=MAX_QUERY_LENGTH)
exposure: list[str] = Field(default_factory=list, max_length=4)
asns: list[int] = Field(default_factory=list, max_length=100)
countries: list[str] = Field(default_factory=list, max_length=100)
ports: list[int] = Field(default_factory=list, max_length=200)
services: list[str] = Field(default_factory=list, max_length=200)
cdn: str = Field(default="any", max_length=10)
alive: str = Field(default="any", max_length=10)
version: int = Field(default=0, ge=0, le=6)
sensitive: bool = False
hosted: bool = False
open: bool = False
sort: str = Field(default="hosts", max_length=20)
order: str = Field(default="desc", max_length=4)
limit: int = Field(default=50, ge=1, le=200)
offset: int = Field(default=0, ge=0, le=100_000_000)
def has_facets(self) -> bool:
return bool(
self.exposure
or self.asns
or self.countries
or self.ports
or self.services
or self.cdn != "any"
or self.alive != "any"
or self.version
or self.sensitive
or self.hosted
or self.open
)
class IpFacets(BaseModel):
exposure: list[Facet] = Field(default_factory=list)
asn: list[Facet] = Field(default_factory=list)
country: list[Facet] = Field(default_factory=list)
port: list[Facet] = Field(default_factory=list)
service: list[Facet] = Field(default_factory=list)
class ServiceRead(BaseModel):
id: uuid.UUID
ip: str
port: int
protocol: str
state: str
service_name: str | None = None
service_class: str
description: str = ""
registered: bool = False
source: str
is_http: bool = False
tls: bool = False
product: str | None = None
version: str | None = None
banner: str | None = None
asn: int | None = None
asn_org: str | None = None
country: str | None = None
prefix: str | None = None
is_cdn: bool = False
cdn_name: str | None = None
scan_policy: str | None = None
host_count: int = 0
hosts: list[str] = Field(default_factory=list)
web_count: int = 0
status_code: int | None = None
url: str | None = None
title: str | None = None
screenshot_path: str | None = None
is_sensitive: bool = False
is_new: bool = False
class ServicePage(BaseModel):
items: list[ServiceRead] = Field(default_factory=list)
total: int = 0
total_capped: bool = False
error: QueryError | None = None
class ServiceFilter(BaseModel):
q: str | None = Field(default=None, max_length=MAX_QUERY_LENGTH)
classes: list[str] = Field(default_factory=list, max_length=10)
ports: list[int] = Field(default_factory=list, max_length=200)
services: list[str] = Field(default_factory=list, max_length=200)
sources: list[str] = Field(default_factory=list, max_length=10)
asns: list[int] = Field(default_factory=list, max_length=100)
countries: list[str] = Field(default_factory=list, max_length=100)
cdn: str = Field(default="any", max_length=10)
http: str = Field(default="any", max_length=10)
sensitive: bool = False
named: bool = False
new: bool = False
sort: str = Field(default="exposure", max_length=20)
order: str = Field(default="desc", max_length=4)
limit: int = Field(default=50, ge=1, le=200)
offset: int = Field(default=0, ge=0, le=100_000_000)
def has_facets(self) -> bool:
return bool(
self.classes
or self.ports
or self.services
or self.sources
or self.asns
or self.countries
or self.cdn != "any"
or self.http != "any"
or self.sensitive
or self.named
or self.new
)
class ServiceFacets(BaseModel):
klass: list[Facet] = Field(default_factory=list, serialization_alias="class")
port: list[Facet] = Field(default_factory=list)
service: list[Facet] = Field(default_factory=list)
source: list[Facet] = Field(default_factory=list)
asn: list[Facet] = Field(default_factory=list)
country: list[Facet] = Field(default_factory=list)
class ExposureBand(BaseModel):
key: str
label: str
count: int
addresses: int
query: str
class ExposureLine(BaseModel):
key: str
label: str
detail: str | None = None
count: int
query: str
class ScanExposure(BaseModel):
services: int = 0
addresses: int = 0
web_services: int = 0
non_web_services: int = 0
answering_http: int = 0
sensitive: int = 0
named: int = 0
passive_only: int = 0
nonstandard_web: int = 0
bands: list[ExposureBand] = Field(default_factory=list)
top_services: list[ExposureLine] = Field(default_factory=list)
coverage: list[ExposureLine] = Field(default_factory=list)
scanned: int = 0
class OriginEvidence(BaseModel):
kind: str
label: str
value: str
class OriginSample(BaseModel):
host: str
url: str
ip: str | None = None
port: int = 0
status_code: int | None = None
title: str | None = None
webserver: str | None = None
cdn_name: str | None = None
asn_org: str | None = None
screenshot_path: str | None = None
class OriginFinding(BaseModel):
kind: str
confidence: str
exposed: OriginSample
fronted: list[OriginSample] = Field(default_factory=list)
fronted_total: int = 0
evidence: list[OriginEvidence] = Field(default_factory=list)
open_ports: list[int] = Field(default_factory=list)
sensitive_ports: list[int] = Field(default_factory=list)
query: str = ""
class OriginExposure(BaseModel):
findings: list[OriginFinding] = Field(default_factory=list)
probed_addresses: int = 0
fronted_assets: int = 0