scan

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: GPL-3.0 Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ScannerNmap    = "nmap"
	ScannerMasscan = "masscan"
	ScannerZgrab2  = "zgrab2"
	// ScannerNuclei names nuclei (github.com/projectdiscovery/nuclei) — a JSON-emitting finding
	// scanner. It ties together the ingest.Ingestor (scan/ingest/nuclei.go, folding -jsonl findings
	// into severity-tagged NSE-style scripts via jsonscript.go's schemaless mapping), the
	// drive.Scanner (scan/drive/nuclei.go, streaming those findings live), the server driver dispatch
	// (server/scan/run.go scannerFor), and the CLI leaf + template/tag/severity/id completers
	// (cmd/scan/run_nuclei.go, cmd/completers/nuclei_templates.go).
	ScannerNuclei = "nuclei"
)

Scanner identity keys.

A scanner's name is the single string that ties its pieces together across packages: the server-side driver dispatch (server/scan/run.go), the ingestor Name() + the Scanner value it stamps on a Run (scan/ingest), the provenance Tool, and the completion guards (cmd/scan). A spelling drift (e.g. "zgrab2" vs "zgrab") silently breaks the driver↔ingestor lookup, so the identity lives here once rather than as literals scattered across those call sites.

View Source
const ExitInterrupted = "interrupted"

ExitInterrupted is the Finished.Exit value a deliberately-stopped scan carries (server-side `Stop` cancels the job, and consume stamps this on the partial run). It marks a *terminal* interrupted state — distinct from a scanner's own error exit ("failed") and from a heartbeat-stale orphan — so a stopped scan reads as interrupted immediately and deterministically (not after the 30s stale window), and stays resumable. A process killed outright cannot stamp it and still falls back to the heartbeat-stale path below; both converge on stateInterrupted.

View Source
const TargetDone = "done"

TargetDone marks a target that produced a result and so was actually scanned. It is AIMS's own, scanner-uniform record of per-target completion: because the fold sees every result stream in, a target the scan reached is one AIMS matched a result back to — a signal that survives a SIGKILL (which would lose a scanner's native checkpoint) and needs no per-tool support. It is deliberately target-granular, not port-granular: a target is "done" once ANY result matches it, so an interrupted host is re-scanned whole on resume (the fold makes that idempotent), while a host that was never reached is the remaining work. A down host counts as done — it was scanned, and deriving completion from targets-minus-observed-hosts would wrongly re-scan it forever.

Variables

View Source
var DisplayFields = map[string]func(r *scan.Run) string{
	"ID": func(r *scan.Run) string {
		id := display.FormatSmallID(r.GetId())
		switch stateOf(r) {
		case stateDone:
			return color.HiGreenString(id)
		case stateFailed:
			return color.HiRedString(id)
		case stateRunning:
			return color.HiYellowString(id)
		case stateInterrupted:
			return color.HiBlackString(id)
		default:
			return id
		}
	},
	"Scanner": func(r *scan.Run) string { return r.GetScanner() },
	"Name":    func(r *scan.Run) string { return r.GetProfileName() },
	"Status":  func(r *scan.Run) string { return stateToken(r) },
	"Info":    func(r *scan.Run) string { return scanInfo(r) },

	"Args": func(r *scan.Run) string { return fullCommand(r) },
	"Series": func(r *scan.Run) string {

		if n := r.GetFormerRuns(); n > 0 {
			return color.HiBlackString("+%d", n)
		}
		return ""
	},
	"When":  func(r *scan.Run) string { return whenLabel(r) },
	"Hosts": func(r *scan.Run) string { return hostsUpDown(r) },
	"Targets": func(r *scan.Run) string {
		if label := targetsLabel(r); label != "" {
			return color.HiCyanString("%s", label)
		}
		return ""
	},
	"Tasks": func(r *scan.Run) string { return tasksSummary(r) },
}

DisplayFields maps column names to per-run value generators — the single source of truth feeding the table and completions. Every accessor is nil-safe (a partially observed run must never panic the table), and state-dependent fields route through stateOf so the whole view agrees.

Functions

func AreScansIdentical

func AreScansIdentical(a, b *scan.RunORM) bool

AreScansIdentical reports whether two runs are the same scan re-imported.

RawXML is the scanner's verbatim output and thus a definitive fingerprint: when both runs carry it, equality alone decides identity and inequality alone rules it out. Only when a run lacks raw output does it fall back to a field-weighted comparison over the runs' identity fields.

The fallback counts *evidence*, not absence: a field contributes only when both runs actually populated it — agreement is positive evidence, a disagreement on a populated field is disqualifying, and a field left empty on either side is neutral. Two dataless runs therefore score no evidence and are not "identical" (the earlier scoring treated two empty task-lists as a match, which made every empty run collide with every other). Runs match when they agree on every populated identity field, disagree on none, and carry at least one field of real evidence.

func Completions

func Completions() []display.Options

Completions returns the columns combined into completion candidates and their descriptions.

func Detail

func Detail(r *scan.Run, all []*scan.Run, opt DetailOpts) display.Detail

Detail assembles the full `show` view for a single run: the state banner, the side-by-side info panes, derived insights (including the cross-run host-sharing count, which needs the whole set `all` to compute), and any flag-selected trailing sections. It hands these to the shared display.Detail renderer, so a run's detail view is laid out identically to every other domain's.

func DisplayDetails

func DisplayDetails() []display.Options

DisplayDetails is retained only for the c2 agent/channel `show` placeholders, which reuse this weighted header set against their own DisplayFields map. Scan's own `show` uses the richer Detail renderer (banner + panes + insights + sections); prefer that. Deprecated: do not build on this for the scan domain.

func DisplayHeaders

func DisplayHeaders() []display.Options

DisplayHeaders returns all weighted table headers for a table of scans. Weight-1 columns are the always-on signal (identity, scanner, live status, host outcome, recency); heavier columns shed first on narrow terminals.

func HeadOf added in v0.2.0

func HeadOf(all []*scan.Run, r *scan.Run) *scan.Run

HeadOf resolves the surviving head for any run: the run itself if visible, else the run its SupersededBy points to (following one level). Returns the input when nothing better is found.

func IsInterrupted added in v0.3.0

func IsInterrupted(r *scan.Run) bool

IsInterrupted reports whether a run is in the interrupted state (deliberately stopped, or an orphan whose heartbeat went stale) as opposed to failed — used to sub-categorize resumable runs.

func IsResumable added in v0.3.0

func IsResumable(r *scan.Run) bool

IsResumable reports whether a run can be resumed: it reached a terminal state that left work undone — interrupted (stopped or orphaned) or failed. A clean done run has nothing to resume, and a still-running one must be stopped first. `scan resume` guards on this.

func IsRunning

func IsRunning(r *scan.Run) bool

IsRunning reports whether the run is mid-flight — a non-final run with a fresh heartbeat. A killed scan goes stale and reads as interrupted (not running), so it no longer blocks destructive operations (`scan rm`) the way a perpetually-"running" orphan would.

func IsSuperseded added in v0.2.0

func IsSuperseded(r *scan.Run) bool

IsSuperseded reports whether a run has been tombstoned under a surviving head.

func MarkTargetsDone added in v0.3.0

func MarkTargetsDone(targets []*scan.Target, h *host.Host) int

MarkTargetsDone stamps TargetDone on every target the observed host satisfies, so a run's persisted Targets record which targets have been scanned. Returns the number newly marked (so a caller can persist only when the completion set actually advanced).

func RemainingTargets added in v0.3.0

func RemainingTargets(targets []*scan.Target) []*scan.Target

RemainingTargets returns the targets a run has not yet completed — those not marked TargetDone. This is the reforged target set a resume re-scans: only the work an interrupted run never reached, uniform across scanners and independent of any native checkpoint.

func SeriesOf added in v0.2.0

func SeriesOf(all []*scan.Run, head *scan.Run) []*scan.Run

SeriesOf returns a head run together with every run tombstoned under it (directly), ordered head-first then by recency — the browse set behind `scan history`.

func SortRuns

func SortRuns(runs []*scan.Run)

SortRuns orders runs for listing: running scans first (most actionable), then interrupted (orphaned, likely need attention), then freshly-created, then the rest — each group by most-recent activity. Stable, so equal keys keep their read order.

func TargetMatchesHost added in v0.3.0

func TargetMatchesHost(t *scan.Target, h *host.Host) bool

TargetMatchesHost reports whether an observed host is the result of scanning this target: the host carries the target's address among its addresses, or the target's domain among its hostnames. This is the same identity a resume uses to decide a target is finished.

func TargetSpecs

func TargetSpecs(targets []*scan.Target) []string

TargetSpecs renders targets as the address/host tokens a scanner takes on its command line — Address preferred, else Domain — preserving order and dropping blanks, so the result can be appended straight onto a scanner's arguments.

func TargetsFromHosts

func TargetsFromHosts(hosts ...*host.Host) []*scan.Target

TargetsFromHosts derives scan Targets from stored hosts. Each host contributes one Target per address (its identity anchor); a host with no address falls back to its hostnames as Domain targets. Duplicate endpoints (same address, or same domain) are collapsed so overlapping hosts never re-target the same thing. The result is deterministic in host/address order.

func VisibleRuns added in v0.2.0

func VisibleRuns(runs []*scan.Run) []*scan.Run

VisibleRuns drops tombstoned runs — the default view for `scan list` and completions. Callers that need the full set (history, diff, cleanup) use the unfiltered slice.

Types

type CleanupPlan added in v0.2.0

type CleanupPlan struct {
	Heads      []*scan.Run // survivors whose FormerRuns was (re)computed
	Tombstoned []*scan.Run // runs newly pointed at their head via SupersededBy
	Prunable   []*scan.Run // tombstoned runs whose output is byte-identical to the head (hard-deletable)
}

CleanupPlan is the set of field mutations a cleanup pass computes over the whole run set. The runs it references are mutated in place (SupersededBy / FormerRuns set) and ready to persist: Heads and Tombstoned via Upsert, Prunable via Delete. It carries no DB or RPC dependency.

func ComputeCleanup added in v0.2.0

func ComputeCleanup(all []*scan.Run) CleanupPlan

ComputeCleanup groups every run into its series-and-outcome-class and collapses each multi-run group onto a single head, mutating the affected runs in place and returning the plan. It is idempotent: a group already collapsed to one visible head yields no new tombstones, so re-running is a no-op.

Only currently-visible, coalescible runs are candidates to become or absorb a head (see coalesceClass): a live scan, an interrupted run, and a failure that found hosts are left untouched as their own rows; already-tombstoned runs are re-homed only if their head is itself absorbed (chains are flattened to one level). FormerRuns on each head is recomputed from the full set so it always equals the number of runs it supersedes.

func SupersedeFor added in v0.2.0

func SupersedeFor(all []*scan.Run, runID string) CleanupPlan

SupersedeFor computes a cleanup plan limited to the series containing runID — the auto-collapse a server runs when a new scan of the same definition finishes, so `scan list` self-collapses without a manual `scan cleanup`. It restricts the run set to that one series and reuses ComputeCleanup, so the collapse stays within outcome classes: a finished clean run heads the success line, a resultless failure coalesces with earlier resultless failures of the same definition (latest wins, with a count), and the two heads coexist. A still-running or interrupted sibling, and a failure that found hosts, are left alone. Returns an empty plan when the run is unknown or its series has nothing to collapse.

func (CleanupPlan) Empty added in v0.2.0

func (p CleanupPlan) Empty() bool

Empty reports whether the plan collapses nothing.

type DetailOpts

type DetailOpts struct {
	Tasks   bool // the running/done task tables (the live view)
	Targets bool // the full target list with per-target status/reason
	Hosts   bool // the scanned hosts rendered as a compact table
}

DetailOpts selects which trailing sections a detail view includes. They are flag-gated at the CLI because each is verbose (task streams, full target lists, the shared-host table) and off by default keeps `scan show` scannable.

type HostDelta

type HostDelta struct {
	Before    *host.Host
	After     *host.Host
	NewPorts  []*host.Port // in b, not in a
	GonePorts []*host.Port // in a, not in b
	Changed   []PortDelta  // same (proto, number), but service or state differs
}

HostDelta captures how one host's surface changed between the two runs.

type PortDelta

type PortDelta struct {
	Before *host.Port
	After  *host.Port
}

PortDelta is a port whose service identity or state changed between runs.

type PortStability added in v0.2.0

type PortStability struct {
	Addr     string
	Proto    string
	Port     uint32
	Service  string
	Presence []bool // per run, oldest -> newest: was this port open in that run
	Class    Stability
}

PortStability is one port's presence across the series.

func (PortStability) Ratio added in v0.2.0

func (p PortStability) Ratio() string

Ratio is the "present / total" fraction shown next to the sparkline.

func (PortStability) Sparkline added in v0.2.0

func (p PortStability) Sparkline() string

Sparkline renders a presence vector as full/empty blocks, oldest -> newest.

type Result

type Result scan.Result

Result - A type containing various objects that are outputs of a scan. It has only one .Target, which theoretically means that we must have n Results for n Results. This type is to be created from and used by a scan.Run type, which has various methods to set up, populate, curate and save the data from a complete Scan, sometimes concurrently. A Result is not meant to be saved in a database: it is only used as a feeder type for the scan.Run.

func (*Result) ToPB

func (r *Result) ToPB() *scan.Result

ToPB - Get the Protobuf object for the Result.

type Run

type Run scan.Run

Run - Represents a scan before, after or while being run. This run can be the one of any scanner: fields are not mandatorily used by all scanners for all scans, but this type gives a common tree in which to store hosts, ports, services, statistics and various other information.

The type provides many convenience methods to process all the output of the scan, either at once or continuously, or even to refine the objects based on/ with those already in a database. Therefore, all the methods of this type are meant to be used server-side, and not in an implant.

For having similar functionality from within an implant, use the Protobuf scan.Run type, which itself has some convenience methods that do NOT need any database or its related libraries.

func NewRun

func NewRun(scanner string, args ...string) *Run

NewRun - Create a new scan.Run based on a tool (scanner) name, and with an optional Options type holding various settings to be customized for your use.

func (*Run) AddHosts

func (r *Run) AddHosts(hosts ...*host.Host)

AddHosts folds one or more hosts into this Run, deduplicating and merging by natural key. It is the bulk entry point behind the import path: the Hosts of a freshly parsed scan.Run (e.g. from nmap.FromXML) are folded in one by one, so an import that overlaps hosts already in the Run enriches them instead of duplicating.

func (*Run) AddResult

func (r *Run) AddResult(res *Result) (err error)

AddResult folds one feeder Result into the Run's host tree. The Result is the universal adapter output (one {Host, Address, Port, Service, Data} tuple emitted by any scanner); AddResult assembles it into a single-host subtree and merges that in via the non-destructive fold (see fold.go / DEDUP.md). Calling it twice with the same observation is idempotent — the second call merges into the row the first created and changes nothing.

If the Result carries Data (a custom scanner's opaque payload), it is preserved as a script observation on the port (or host) so nothing is lost; mapping structured payloads into the recursive NSE Script/Table/Element tree (jsonToScript, SCAN.md §D) is the richer, philosophy-true follow-on.

func (*Run) AddTarget

func (r *Run) AddTarget(t *Target)

when they are needed by the service probing stack used by the scan.

func (*Run) InitResult

func (r *Run) InitResult() *Result

InitResult - Instantiate a new result that has the Run UUID in ref. The rest of the object can be populated by the user as he wishes.

func (*Run) ToPB

func (r *Run) ToPB() *scan.Run

ToPB - Get the Protobuf object for the Result.

type RunDiff

type RunDiff struct {
	NewHosts  []*host.Host // present in b, absent in a
	GoneHosts []*host.Host // present in a, absent in b
	Changed   []HostDelta  // present in both, but ports/services differ
}

RunDiff is the delta from Run a (earlier) to Run b (later).

func DiffRuns

func DiffRuns(a, b *scan.Run) *RunDiff

DiffRuns computes the drift from a (earlier) to b (later). Either may be nil (treated as an empty host set). The result is deterministic in b's then a's host order.

func (*RunDiff) Empty

func (d *RunDiff) Empty() bool

Empty reports whether the two runs are identical at the host/port/service level.

type SeriesHistory added in v0.2.0

type SeriesHistory struct {
	Runs     []*scan.Run     // the series ordered oldest -> newest
	Timeline []TimelineEntry // ordered newest -> oldest, unchanged runs collapsed
	Surface  []PortStability // one row per (addr, proto, port) ever seen open, sorted
	Span     int64           // seconds from first to last run
	Cadence  int64           // mean seconds between consecutive runs (0 if <2 runs)
}

SeriesHistory is the analysed evolution of one scan series.

func BuildHistory added in v0.2.0

func BuildHistory(runs []*scan.Run) SeriesHistory

BuildHistory analyses a series (any order) into its drift timeline and stability surface.

type Stability added in v0.2.0

type Stability int

Stability classifies a port's presence pattern across a series.

const (
	Stable   Stability = iota // open in every run — the persistent attack surface
	Emerging                  // opened partway through and still open — newly exposed
	Receding                  // was open early, now closed — surface that went away
	Flapping                  // intermittent (on/off) — noise, filtering, or a load-balanced pool
)

func (Stability) String added in v0.2.0

func (s Stability) String() string

type Target

type Target scan.Target

Target - This type can be used as an Input object to a scan, in which case only the Input fields matter to you

Represents how the target was specified when passed to nmap, its status and the reason of its status. Example: <target specification="domain.does.not.exist" status="skipped" reason="invalid"/>.

func (*Target) ToORM

func (t *Target) ToORM(ctx context.Context) (scan.TargetORM, error)

ToORM - Get the SQL object for the Target.

func (*Target) ToPB

func (t *Target) ToPB() *scan.Target

ToPB - Get the Protobuf object for the Target.

type TimelineEntry added in v0.2.0

type TimelineEntry struct {
	Run       *scan.Run // the run this row represents (the newest of a collapsed stretch)
	Delta     *RunDiff  // change vs the previous (older) run; nil for the baseline (oldest run)
	Unchanged int       // >1 means this row collapses that many consecutive no-change runs
	Summary   []string  // short per-change lines ("+ 443/tcp https", "~ 22/tcp ssh 8.9 → 9.0")
}

TimelineEntry is one row of the drift timeline: either a run (with its delta vs the previous run) or a collapsed marker standing in for a stretch of runs that changed nothing.

Directories

Path Synopsis
pb
rpc

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL