Files
rengine/shared/models/scan_preview.py
T
Yogesh OjhaandClaude Opus 5 c41f0c860e feat(targets): store the assets you already know and seed every scan with them
A target carries a list of host names, addresses and URLs an operator already
holds. Every scan of that target starts from them, so a list earned elsewhere
no longer has to be re-pasted or re-discovered.

Each pasted line is classified by its own shape and scope-checked against the
target; a refusal names the line and the reason. Seeds enter a run through the
asset_seed stage, which resolves the names it has no answer for and materialises
what they resolve to. Stages the engine has switched off still do not run.

Seeds no longer force a focused run: scope reads the new seed_only, which only
rescan, watch and connector runs set, so a seeded census scan still covers its
target. Seed count is a material run facet, so Compare states it before any
delta it would otherwise explain.

Two stages share the subdomains table for a run: discovery's clear no longer
drops rows asset_seed wrote, and the run's seeded hosts join its merge so the
imported source survives a name discovery also finds.

Closes #1538

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYZfaNjq3Ym8GPvWYuHisS
2026-09-13 07:21:10 +05:30

63 lines
1.8 KiB
Python

import uuid
from enum import StrEnum
from pydantic import BaseModel, Field
class PreviewToolStatus(StrEnum):
WILL_RUN = "will_run"
SKIPPED_DISABLED = "skipped_disabled"
SKIPPED_NEEDS_KEY = "skipped_needs_key"
SKIPPED_NOT_APPLICABLE = "skipped_not_applicable"
SKIPPED_NO_INPUT = "skipped_no_input"
class PreviewTool(BaseModel):
capability: str
label: str
status: PreviewToolStatus
reason: str | None = None
rate: int | None = None
threads: int | None = None
timeout: int | None = None
class PreviewPhase(BaseModel):
phase: str
label: str
tools: list[PreviewTool] = Field(default_factory=list)
class PreviewSummary(BaseModel):
auth_summary: str
custom_header_names: list[str] = Field(default_factory=list)
rate_summary: str
thread_multiplier: float
timeout_multiplier: float
http_protocol: str
follow_redirects: bool | None = None
excluded_subdomains_count: int = 0
excluded_paths_count: int = 0
excluded_ips_count: int = 0
excluded_subdomains: list[str] = Field(default_factory=list)
excluded_paths: list[str] = Field(default_factory=list)
excluded_ips: list[str] = Field(default_factory=list)
included_subdomains: list[str] = Field(default_factory=list)
seed_count: int = 0
proxy_name: str | None = None
estimated_duration_seconds: int = 0
estimated_duration_human: str = ""
class ScanPreview(BaseModel):
target_id: uuid.UUID | None = None
target_value: str
target_type: str
engine_id: uuid.UUID | None = None
engine_name: str
context_id: uuid.UUID | None = None
context_name: str | None = None
phases: list[PreviewPhase] = Field(default_factory=list)
summary: PreviewSummary
warnings: list[str] = Field(default_factory=list)