scan

package
v0.2.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

This section is empty.

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 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 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 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 collapses each multi-run series onto a single head, mutating the affected runs in place and returning the plan. It is idempotent: a series that is already collapsed to one visible head yields no new tombstones, so re-running is a no-op.

Only currently-visible, non-running runs are candidates to become or absorb a head — a live scan is left untouched (it collapses on a later pass once finished), and 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 newest clean run becomes the head and older completed siblings are tombstoned under it; a still-running sibling is left alone (ComputeCleanup skips running runs). 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