tool

package
v0.18.0 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2026 License: GPL-3.0 Imports: 26 Imported by: 0

Documentation

Overview

Package tool is the one contract for every workload a sensor runs: a target scanner, a connector to a vendor API, a parser of a file or an enricher of findings.

Stability: Stable (docs/STABILITY.md).

A tool is a manifest plus a run function:

  • the Manifest (a tool.yaml file, apiVersion openctem.io/tool/v1) declares what the tool is: its execution class, its intrusiveness tier, the CTIS types it consumes and produces, a typed configuration schema, the permissions it needs (network, filesystem, credentials), its resources and its self-test fixtures;
  • Run(ctx, task) does the work and reports through ctx: records through an Emitter that checks each one as it is emitted, progress, logs, artifacts and the outcome of each target, and returns a categorized Error when it fails.

The runtime (pkg/sensorkit) never calls Run in its own process. A Go tool compiled into a sensor runs in a separate, sandboxed process (the sensor re-executes itself), speaking the same adapter protocol a tool in any other language speaks (pkg/tool/adapter). Only pkg/testkit calls Run in-process, applying the same rules the runtime applies, so a tool that would be stopped in production fails its unit test.

The runtime owns every guarantee: it admits targets against the policy, validates the configuration against the manifest's schema, delivers only the credentials the manifest declares, checks every record again on its side of the process boundary (CTIS validity, declared output types, size and count limits, control characters) and stamps the provenance. Nothing in this package lets tool code skip any of it.

The design is docs/rfcs/sensor-sdk-v2.md.

Index

Constants

View Source
const (
	// ProfileAdapter: the program speaks adapter protocol v1.
	ProfileAdapter = "adapter"
	// ProfileExec: a CLI that writes CTIS or SARIF, with no adapter code.
	ProfileExec = "exec"
)

Run profiles.

View Source
const (
	OutputCTIS      = "ctis"
	OutputSARIF     = "sarif"
	OutputJSONLCTIS = "jsonl-ctis"
)

Output formats of the exec profile.

View Source
const (
	DefaultTimeout        = 60 * time.Minute
	DefaultIdleTimeout    = 10 * time.Minute
	DefaultMaxOutputBytes = 64 << 20
	DefaultMaxRecords     = 200000
)

Defaults of Resources.

View Source
const (
	KindAsset      = "asset"
	KindFinding    = "finding"
	KindDependency = "dependency"
)

Produce kinds.

View Source
const (
	RetestFinding = "finding"
	RetestAsset   = "asset"
)

Retest item kinds.

View Source
const APIVersion = "openctem.io/tool/v1"

APIVersion is the manifest format this package reads and writes.

View Source
const MaxErrorDetail = 256

MaxErrorDetail bounds Error.Detail on the wire.

View Source
const MaxManifestBytes = 256 << 10

MaxManifestBytes bounds a tool.yaml file.

View Source
const MaxRetestItems = 1000

MaxRetestItems bounds the items of one retest task.

View Source
const ProtocolVersion = 1

ProtocolVersion is the adapter protocol version this SDK speaks.

View Source
const Redacted = "[REDACTED]"

Redacted is what a Secret prints.

Variables

View Source
var (
	// ErrUndeclaredOutput: the record's kind is not in Manifest.Produces.
	// The record is quarantined (not delivered).
	ErrUndeclaredOutput = errors.New("undeclared output type")
	// ErrOutputLimit: the task reached Resources.MaxRecords or
	// MaxOutputBytes. The tool should stop; the task ends partial.
	ErrOutputLimit = errors.New("output limit reached")
	// ErrInvalidRecord: the record is not valid CTIS.
	ErrInvalidRecord = errors.New("invalid CTIS record")
	// ErrUndeclaredCredential: the tool asked for a credential its manifest
	// does not declare, or one the operator did not store.
	ErrUndeclaredCredential = errors.New("credential not declared or not granted")
)

Errors the emitter returns.

View Source
var ErrRetestRecord = errors.New("tool: a retest task reports verdicts, not records")

ErrRetestRecord is the error of a record emitted in a retest task.

Functions

func AdapterProtocolJSONSchema

func AdapterProtocolJSONSchema() []byte

AdapterProtocolJSONSchema returns the JSON Schema of one adapter protocol v1 message, for implementations in other languages.

func AuthFailure

func AuthFailure(err error) error

AuthFailure reports refused credentials.

func CapDetail

func CapDetail(s string) string

CapDetail cuts s to MaxErrorDetail bytes on a rune boundary.

func DecodeConfig

func DecodeConfig[C any](schema *core.SettingsSchema, raw json.RawMessage) (C, error)

DecodeConfig validates raw against schema (nil: no configuration), fills the defaults and decodes it into C, refusing unknown keys.

func Failed

func Failed(err error) error

Failed reports that the tool itself failed (not retried by default).

func Invalid

func Invalid(format string, args ...any) error

Invalid reports invalid configuration or targets (not retried).

func ManifestJSONSchema

func ManifestJSONSchema() []byte

ManifestJSONSchema returns the JSON Schema of tool.yaml (draft 2020-12), for editors and for implementations in other languages. Validate checks more than the schema can express (class against permissions, placeholders against the config schema, no secrets in config).

func RateLimit

func RateLimit(after time.Duration, err error) error

RateLimit reports that the far side asked to slow down.

func Refused

func Refused(reason string) error

Refused reports a refusal by scope or policy.

func Retry

func Retry(err error) error

Retry reports a transient failure (retried with back-off).

func SchemaFor

func SchemaFor[C any]() (json.RawMessage, error)

SchemaFor derives a configuration schema (the settings schema subset) from C's struct tags; nil when C has no fields. The output is deterministic.

func Unreachable

func Unreachable(err error) error

Unreachable reports that a target did not answer (retried next run).

Types

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder declares a simple tool without writing a manifest by hand:

var Dotenv = tool.Define("dotenv", "1.0.0").
	Describe("Finds .env files a web server serves").
	Targets("http_service", "domain").
	Produces("finding:misconfiguration").
	Params(
		tool.StringParam("path").Label("Path").Default("/.env").Pattern("^/").MaxLength(256),
		tool.IntParam("timeout").Label("Timeout (s)").Default(10).Range(1, 60),
	).
	Handle(func(ctx tool.Context, job *tool.Job, emit tool.Emit) error {
		for _, t := range job.Targets() {
			// ... probe t.URL(job.Param("path").String()) ...
			_ = emit.Misconfiguration(t, tool.Issue{RuleID: "dotenv-exposed", Title: ".env is readable", Severity: "high"})
			ctx.TargetDone(t)
		}
		return nil
	}).
	MustBuild()

The result is an ordinary Tool: the same manifest checks, the same runtime guarantees (out-of-process execution, admission, output checks, provenance) and the same tests (pkg/testkit) as a tool written with New. Fields the builder has no method for are set with Manifest.

func Define

func Define(name, version string) *Builder

Define starts a tool declaration. The defaults are the target-scan class and tier T1 (active, non-intrusive); Class and Tier change them.

func (*Builder) Build

func (b *Builder) Build() (Tool, error)

Build checks the declaration and returns the tool.

func (*Builder) Capabilities

func (b *Builder) Capabilities(ids ...string) *Builder

Capabilities declares the platform capability ids the tool provides.

func (*Builder) Class

func (b *Builder) Class(c Class) *Builder

Class sets the execution class.

func (*Builder) Describe

func (b *Builder) Describe(s string) *Builder

Describe sets the tool's description.

func (*Builder) Handle

func (b *Builder) Handle(h Handler) *Builder

Handle sets the function that runs a task.

func (*Builder) Manifest

func (b *Builder) Manifest(edit func(m *Manifest)) *Builder

Manifest edits the manifest directly, for the fields the builder has no method for (permissions, credentials, resources, self-tests, modes).

func (*Builder) MustBuild

func (b *Builder) MustBuild() Tool

MustBuild is Build for a package-level declaration: an invalid tool compiled into a sensor must stop it at start, not run.

func (*Builder) Params

func (b *Builder) Params(ps ...*Param) *Builder

Params declares the tool's parameters: they become the manifest's configuration schema, which the platform renders as a form and the runtime enforces.

func (*Builder) Produces

func (b *Builder) Produces(kinds ...string) *Builder

Produces declares the output types ("finding:<type>", "asset:<type>", "dependency"). A record of any other type is refused.

func (*Builder) Targets

func (b *Builder) Targets(types ...string) *Builder

Targets declares the target types the tool works on (CTIS asset types, the manifest's consumes).

func (*Builder) Tier

func (b *Builder) Tier(t Tier) *Builder

Tier sets the intrusiveness tier.

func (*Builder) Timeout

func (b *Builder) Timeout(d time.Duration) *Builder

Timeout bounds one task of the tool.

type ByteSize

type ByteSize int64

ByteSize is a size in bytes; in a manifest a number or a string with a binary or decimal suffix ("512Mi", "1Gi", "64MB").

func ParseByteSize

func ParseByteSize(s string) (ByteSize, error)

ParseByteSize reads "1024", "512Ki", "512Mi", "1Gi", "64MB", "1G".

func (*ByteSize) UnmarshalJSON

func (b *ByteSize) UnmarshalJSON(data []byte) error

UnmarshalJSON reads a number or a size string.

type Class

type Class string

Class is a tool's execution class: what it touches and how the runtime dispatches it.

const (
	// TargetScan touches customer targets (hosts, URLs, repositories).
	TargetScan Class = "target-scan"
	// Connector reads a vendor API with credentials.
	Connector Class = "connector"
	// Parser reads a file and produces records; no network.
	Parser Class = "parser"
	// Enricher annotates findings; it never creates assets.
	Enricher Class = "enricher"
)

Execution classes.

type Context

type Context interface {
	context.Context

	// Log is a structured logger. Values are redacted (Secret prints
	// [REDACTED]) and the runtime bounds the rate and size of what it
	// keeps.
	Log() *slog.Logger
	// Progress reports done of total units (throttled by the runtime).
	Progress(done, total int, msg string)
	// Emit is the record channel. Each record is checked when it is
	// emitted.
	Emit() Emitter
	// TargetDone reports that the tool finished a target.
	TargetDone(t Target)
	// TargetError reports that a target failed (its scan coverage); err
	// is categorized like a Run error.
	TargetError(t Target, err error)
	// TargetSkipped reports that the tool did not work on a target.
	TargetSkipped(t Target, reason string)
	// Artifact creates a size-capped artifact (a raw log, a screenshot),
	// delivered separately from the records.
	Artifact(name, mediaType string) (io.WriteCloser, error)
	// Workdir is the task's private directory, removed after the task.
	Workdir() string
	// Secret returns a credential the manifest declares and the operator
	// stored; anything else is ErrUndeclaredCredential.
	Secret(name string) (Secret, error)
	// HTTP is a client that reaches only what the manifest's network
	// permission allows: the task's targets (NetTargets), the vendor
	// hosts (NetVendor) or nothing (NetNone); it never reaches cloud
	// metadata endpoints. It is defense in depth: the sandbox backend's
	// network class is the boundary.
	HTTP() *http.Client
}

Context is what a running tool gets from the runtime. It is canceled on cancel, on the task's deadline and on the sensor's kill switch.

type CredentialReq

type CredentialReq struct {
	// Name is how the tool asks for it (Context.Secret).
	Name        string `json:"name"`
	Kind        string `json:"kind"`
	Required    bool   `json:"required,omitempty"`
	Description string `json:"description,omitempty"`
}

CredentialReq is a credential a tool declares.

type Duration

type Duration time.Duration

Duration is a time.Duration; in a manifest a string such as "20m" or a number of seconds.

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON writes the duration as a string ("20m0s").

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(data []byte) error

UnmarshalJSON reads "20m", "1h30m" or a number of seconds.

type Emit

type Emit struct {
	// contains filtered or unexported fields
}

Emit builds CTIS records for the common cases and sends them through the task's emitter, which checks each one (CTIS validity, declared output types, limits). CTIS returns the emitter for anything else.

func (Emit) Asset

func (e Emit) Asset(typ, value string) error

Asset emits a discovered asset of a CTIS type ("domain", "ip_address", "http_service", ...).

func (Emit) CTIS

func (e Emit) CTIS() Emitter

CTIS returns the task's emitter, for records the helpers do not cover.

func (Emit) Misconfiguration

func (e Emit) Misconfiguration(t Target, i Issue) error

Misconfiguration emits a misconfiguration finding on target t.

func (Emit) Vulnerability

func (e Emit) Vulnerability(t Target, i Issue) error

Vulnerability emits a vulnerability finding on target t.

type Emitter

type Emitter interface {
	// Asset emits an asset.
	Asset(a ctis.Asset) error
	// Finding emits a finding of target t. A finding without an asset
	// reference is filed on t's asset.
	Finding(t Target, f ctis.Finding) error
	// Dependency emits a dependency (SBOM component) found on t.
	Dependency(t Target, d ctis.Dependency) error
	// Report emits every record of a report (the output of a converter),
	// plus its tool, metadata and properties (ReportInfo).
	Report(r *ctis.Report) error
}

Emitter is the record channel. Every call checks the record at once, so a tool learns about a bad record at the line that produced it:

  • it must be valid CTIS (required fields, enums, ranges);
  • its kind must be in Manifest.Produces, else ErrUndeclaredOutput (the record is quarantined, the task ends partial);
  • the task's record count and bytes must stay within the resources, else ErrOutputLimit (the tool should stop);
  • control and bidirectional-override characters are removed and long strings cut;
  • provenance (which tool, version, sandbox, sensor, task) is stamped by the runtime, never taken from the tool.

The runtime repeats every check on its side of the process boundary.

type Error

type Error struct {
	Class ErrorClass
	// Retryable defaults from Class; a tool may only turn it off.
	Retryable  bool
	RetryAfter time.Duration
	// Detail is a short, human description. The runtime redacts it and
	// caps it at MaxErrorDetail bytes before it leaves the sensor.
	Detail string
	// Err is the cause, for local logs only; it is never sent.
	Err error
}

Error is a categorized tool error.

func AsError

func AsError(err error) *Error

AsError returns err as an *Error: an *Error in its chain as it is (a runtime-only class claimed by a tool becomes ToolError), a context error as Canceled or Timeout, anything else as ToolError. nil stays nil.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

type ErrorClass

type ErrorClass string

ErrorClass categorizes an error, so the platform can tell a retry from a refusal from a broken tool.

const (
	// InvalidInput: the configuration or targets are wrong; not retried.
	InvalidInput ErrorClass = "invalid_input"
	// TargetUnreachable: a target did not answer; retried on the next run.
	TargetUnreachable ErrorClass = "target_unreachable"
	// RefusedByPolicy: the scope or the local policy refused it; the job
	// goes back to the queue for another sensor.
	RefusedByPolicy ErrorClass = "refused_by_policy"
	// Transient: retry with back-off.
	Transient ErrorClass = "transient"
	// RateLimited: retry after RetryAfter.
	RateLimited ErrorClass = "rate_limited"
	// AuthFailed: a connector's credentials were refused.
	AuthFailed ErrorClass = "auth_failed"
	// PermissionDenied: the credentials lack a permission.
	PermissionDenied ErrorClass = "permission_denied"
	// NotFound: the resource does not exist.
	NotFound ErrorClass = "not_found"
	// ToolError: the tool failed; not retried by default. A plain error
	// returned from Run is a ToolError.
	ToolError ErrorClass = "tool_error"
)

Error classes a tool may return.

const (
	Timeout           ErrorClass = "timeout"
	Canceled          ErrorClass = "canceled"
	ResourceExhausted ErrorClass = "resource_exhausted"
	OutputRejected    ErrorClass = "output_rejected"
	ToolCrashed       ErrorClass = "tool_crashed"
)

Error classes set by the runtime only; a tool that returns one gets ToolError instead.

func ClassOf

func ClassOf(err error) ErrorClass

ClassOf is AsError(err).Class ("" for nil).

func (ErrorClass) Retryable

func (c ErrorClass) Retryable() bool

Retryable is the class's default retry verdict.

func (ErrorClass) RuntimeOnly

func (c ErrorClass) RuntimeOnly() bool

RuntimeOnly reports whether c is set by the runtime only.

func (ErrorClass) Valid

func (c ErrorClass) Valid() bool

Valid reports whether c is a class a tool may return.

type Filesystem

type Filesystem string

Filesystem is what a tool may read besides its private directory.

const (
	// FSWorkdir: the task's private directory only (the default).
	FSWorkdir Filesystem = "workdir"
	// FSScanRootsReadOnly: also the sensor's scan roots, read-only.
	FSScanRootsReadOnly Filesystem = "scan-roots-read-only"
)

Filesystem permissions.

type Fixture

type Fixture struct {
	Name   string `json:"name"`
	Task   string `json:"task"`
	Expect string `json:"expect"`
}

Fixture is one self-test: a task file and the CTIS it must produce, both relative to the manifest's directory.

type Handler

type Handler func(ctx Context, job *Job, emit Emit) error

Handler does the work of one task of a tool declared with Define. job holds the task and its parameters (validated, defaults filled); emit builds CTIS records.

type Input

type Input struct {
	Name      string `json:"name"`
	MediaType string `json:"media_type"`
	// Path is relative to the task's directory.
	Path   string `json:"path"`
	Size   int64  `json:"size,omitempty"`
	SHA256 string `json:"sha256,omitempty"`
}

Input is a file given to the task (a parser's input).

type Issue

type Issue struct {
	RuleID      string
	Title       string
	Severity    ctis.Severity
	Description string
	// Evidence is the proof (a response excerpt, a banner); never a
	// secret value.
	Evidence string
	// CVE is the vulnerability's CVE id (Vulnerability only).
	CVE string
}

Issue is a finding in a few fields. Severity is critical, high, medium, low or info.

type Job

type Job struct {
	Task
	// contains filtered or unexported fields
}

Job is one task of a tool declared with Define, with typed access to its targets and parameters.

func (*Job) Param

func (j *Job) Param(key string) Value

Param returns a parameter's value (its default when the task did not set it).

func (*Job) Targets

func (j *Job) Targets(types ...string) []Target

Targets returns the task's targets, only those of the given types when any are given.

type Manifest

type Manifest struct {
	// APIVersion is APIVersion ("" in Go code means APIVersion).
	APIVersion string `json:"apiVersion"`
	// Name is unique per sensor: ^[a-z][a-z0-9-]{1,62}$.
	Name string `json:"name"`
	// Version is the semantic version of the tool adapter (not of the
	// engine it runs).
	Version     string `json:"version"`
	Description string `json:"description,omitempty"`
	Class       Class  `json:"class"`
	// Modes the tool runs in; empty means both.
	Modes []Mode `json:"modes,omitempty"`
	Tier  Tier   `json:"tier"`
	// Capabilities are the platform's capability ids the tool provides
	// ("web.probe", "vuln.templates", ...).
	Capabilities []string `json:"capabilities,omitempty"`
	// Consumes are CTIS asset types, "file:<media type>" for parsers, or
	// "finding:<type>" for enrichers.
	Consumes []string `json:"consumes,omitempty"`
	// Produces are "asset:<type>", "finding:<type>" or "dependency". A
	// record of any other kind is refused (ErrUndeclaredOutput).
	Produces []string `json:"produces"`
	// Config is the tool's configuration schema: the closed JSON Schema
	// subset of api RFC-038 (pkg/core settings schemas), with no secret
	// fields (secrets are credentials, never configuration). nil: no
	// configuration (New derives one from the config type when it has
	// fields).
	Config      json.RawMessage `json:"config,omitempty"`
	Permissions Permissions     `json:"permissions"`
	Resources   Resources       `json:"resources"`
	// Selftest are fixtures: a task and the CTIS it must produce.
	Selftest []Fixture `json:"selftest,omitempty"`
	// Retest says the tool can check again what it reported (a retest
	// task, see Retester). Target-scan tools only.
	Retest bool `json:"retest,omitempty"`
	// Protocol is the range of adapter protocol versions the tool speaks.
	Protocol Range `json:"protocol"`
	// Run says how to start a tool that is not compiled into the sensor.
	Run *RunSpec `json:"run,omitempty"`
}

Manifest describes a tool. Its file form is tool.yaml; LoadManifest reads it, strictly (an unknown key is an error: the manifest is a security document). The runtime trusts the manifest file or the one compiled into the sensor, never a tool's description of itself.

func LoadManifest

func LoadManifest(data []byte) (Manifest, error)

LoadManifest reads a manifest (YAML or JSON) strictly: unknown keys, duplicate keys and more than one document are errors. The result is validated (Validate) and has its defaults filled.

func LoadManifestFile

func LoadManifestFile(path string) (Manifest, error)

LoadManifestFile reads and validates a tool.yaml file.

func (Manifest) Canonical

func (m Manifest) Canonical() ([]byte, error)

Canonical is the manifest's canonical JSON.

func (Manifest) ConfigSchema

func (m Manifest) ConfigSchema() (*core.SettingsSchema, error)

ConfigSchema parses the configuration schema (nil when there is none).

func (Manifest) Contract

func (m Manifest) Contract() *core.ToolContract

Contract names the manifest in the sensor manifest (core.ToolContract): its digest and the fields the platform plans with.

func (Manifest) Declares

func (m Manifest) Declares(kind, typ string) bool

Declares reports whether the manifest produces records of kind ("asset", "finding", "dependency") and CTIS type typ ("" for dependencies).

func (Manifest) Digest

func (m Manifest) Digest() string

Digest is "sha256:" + the hex SHA-256 of the manifest's canonical JSON (defaults filled, object members sorted, no insignificant whitespace). The runtime compares a tool's self-description with its file by digest, and the sensor registers manifests with the platform by digest.

func (Manifest) Normalized

func (m Manifest) Normalized() Manifest

Normalized returns the manifest with its defaults filled, as the runtime sees it.

func (Manifest) Validate

func (m Manifest) Validate() error

Validate checks the manifest: identity, class against permissions and tier, consumes/produces from the CTIS vocabulary, a configuration schema in the settings subset with no secret fields, resources within bounds, and a run specification with a closed set of placeholders and no shell. The error is ManifestErrors with every problem.

type ManifestError

type ManifestError struct {
	Path    string
	Message string
}

ManifestError is one problem in a manifest. Path is a JSON pointer.

func (ManifestError) Error

func (e ManifestError) Error() string

type ManifestErrors

type ManifestErrors []ManifestError

ManifestErrors are every problem found in a manifest.

func (ManifestErrors) Error

func (e ManifestErrors) Error() string

type Mode

type Mode string

Mode is a sensor mode a tool can run in.

const (
	// Daemon is the long-lived sensor that receives jobs.
	Daemon Mode = "daemon"
	// Runner is the ephemeral CI mode.
	Runner Mode = "runner"
)

Modes.

type Network

type Network string

Network is the network a tool needs.

const (
	// NetNone: no network at all.
	NetNone Network = "none"
	// NetTargets: the task's targets only, through the zone's egress.
	NetTargets Network = "targets"
	// NetEgressProxy: through the sensor's egress proxy only.
	NetEgressProxy Network = "egress-proxy"
	// NetVendor: the declared vendor hosts only.
	NetVendor Network = "vendor"
)

Network permissions.

type NoConfig

type NoConfig struct{}

NoConfig is the configuration type of a tool without configuration.

type OutputSpec

type OutputSpec struct {
	// Format is OutputCTIS, OutputSARIF or OutputJSONLCTIS.
	Format string `json:"format"`
	// From is "stdout" or "file" (the {{task.output}} path).
	From string `json:"from"`
}

OutputSpec is an exec-profile tool's output.

type Param

type Param struct {
	// contains filtered or unexported fields
}

Param is one parameter of a tool declared with Define. Its methods set what the platform shows (Label, Help) and what the runtime enforces (Default, Range, OneOf, Pattern, MaxLength, MaxItems, Required).

func BoolParam

func BoolParam(key string) *Param

BoolParam declares a boolean parameter.

func IntParam

func IntParam(key string) *Param

IntParam declares an integer parameter.

func ListParam

func ListParam(key string) *Param

ListParam declares a list of strings.

func NumberParam

func NumberParam(key string) *Param

NumberParam declares a number parameter.

func StringParam

func StringParam(key string) *Param

StringParam declares a string parameter.

func (*Param) Default

func (p *Param) Default(v any) *Param

Default is the value used when the task does not set one.

func (*Param) Help

func (p *Param) Help(s string) *Param

Help is the longer explanation the platform shows.

func (*Param) Label

func (p *Param) Label(s string) *Param

Label is the short name the platform shows.

func (*Param) MaxItems

func (p *Param) MaxItems(n int) *Param

MaxItems bounds a list parameter.

func (*Param) MaxLength

func (p *Param) MaxLength(n int) *Param

MaxLength bounds a string parameter.

func (*Param) OneOf

func (p *Param) OneOf(vals ...any) *Param

OneOf limits the parameter to these values.

func (*Param) Pattern

func (p *Param) Pattern(re string) *Param

Pattern is a regular expression a string parameter must match.

func (*Param) PerScan

func (p *Param) PerScan() *Param

PerScan lets a scan set the parameter (otherwise only the tool's settings on the sensor or platform do).

func (*Param) Range

func (p *Param) Range(lo, hi float64) *Param

Range bounds a number or integer parameter (inclusive).

func (*Param) Required

func (p *Param) Required() *Param

Required makes the task fail validation when the parameter is not set.

type Permissions

type Permissions struct {
	// Network defaults by class: none for parsers and enrichers, targets
	// for target scans, vendor for connectors.
	Network Network `json:"network,omitempty"`
	// VendorHosts are the hosts a vendor-network tool may reach
	// ("api.example.com", "api.example.com:8443", or "${config.<key>}" for
	// a string configuration key holding a URL or host).
	VendorHosts []string   `json:"vendor_hosts,omitempty"`
	Filesystem  Filesystem `json:"filesystem,omitempty"`
	// Credentials the tool may receive. Only these are ever delivered,
	// and only when the sensor's operator stored them.
	Credentials []CredentialReq `json:"credentials,omitempty"`
	// LinuxCaps the tool needs (NET_RAW for SYN scans); granted only when
	// the local policy allows.
	LinuxCaps []string `json:"linux_caps,omitempty"`
}

Permissions are what a tool needs. The effective permission is the manifest's intersected with the sensor's policy and the job.

type Range

type Range struct {
	Min int `json:"min,omitempty"`
	Max int `json:"max,omitempty"`
}

Range is a range of protocol versions (Max 0: no upper bound).

type Repo

type Repo struct {
	URL           string `json:"url,omitempty"`
	Branch        string `json:"branch,omitempty"`
	Commit        string `json:"commit,omitempty"`
	DefaultBranch bool   `json:"default_branch,omitempty"`
	PullRequest   int    `json:"pull_request,omitempty"`
}

Repo is a CI task's repository.

type ReportInfo

type ReportInfo struct {
	Tool       *ctis.Tool          `json:"tool,omitempty"`
	Metadata   *ReportInfoMetadata `json:"metadata,omitempty"`
	Properties ctis.Properties     `json:"properties,omitempty"`
}

ReportInfo is what a report carries besides its records: the tool's vendor and capabilities, metadata such as the coverage type and branch, and properties. The runtime keeps the tool's name as the manifest's.

func InfoOf

func InfoOf(r *ctis.Report) *ReportInfo

InfoOf returns the ReportInfo of r (nil when r carries none).

type ReportInfoMetadata

type ReportInfoMetadata struct {
	ID           string           `json:"id,omitempty"`
	DurationMs   int              `json:"duration_ms,omitempty"`
	SourceRef    string           `json:"source_ref,omitempty"`
	SourceType   string           `json:"source_type,omitempty"`
	CoverageType string           `json:"coverage_type,omitempty"`
	Branch       *ctis.BranchInfo `json:"branch,omitempty"`
	Scope        *ctis.Scope      `json:"scope,omitempty"`
	Properties   ctis.Properties  `json:"properties,omitempty"`
}

ReportInfoMetadata is the part of a report's metadata a tool may set.

type Resources

type Resources struct {
	CPU            float64  `json:"cpu,omitempty"`
	Memory         ByteSize `json:"memory,omitempty"`
	Timeout        Duration `json:"timeout,omitempty"`
	IdleTimeout    Duration `json:"idle_timeout,omitempty"`
	MaxOutputBytes ByteSize `json:"max_output_bytes,omitempty"`
	MaxRecords     int      `json:"max_records,omitempty"`
	// Cost is the slot cost for the sensor's capacity model.
	Cost int `json:"cost,omitempty"`
}

Resources bound one task of the tool. Zero fields take the runtime's defaults.

func (Resources) Limits

func (r Resources) Limits() (timeout, idle time.Duration, maxBytes int64, maxRecords int)

Limits are the effective bounds of one task.

type RetestContext

type RetestContext interface {
	Context
	// Verdict reports the verdict on item. The first verdict of an item
	// counts; a verdict on an item not in the task is ignored. detail is
	// cut to MaxErrorDetail and redacted.
	Verdict(item RetestItem, v Verdict, detail string)
}

RetestContext is the Context of a retest task.

type RetestItem

type RetestItem struct {
	// Ref is the platform's opaque id of the item, echoed in its verdict.
	Ref string `json:"ref"`
	// Target is the ref of the task target the item is on.
	Target string `json:"target"`
	// Kind is RetestFinding or RetestAsset.
	Kind string `json:"kind"`
	// RuleID is the check that reported the finding (a template id, a rule
	// id).
	RuleID string `json:"rule_id,omitempty"`
	// Fingerprint is the finding's fingerprint as the platform keeps it.
	Fingerprint string `json:"fingerprint,omitempty"`
	// Attrs are typed hints from the platform.
	Attrs map[string]string `json:"attrs,omitempty"`
}

RetestItem is one thing the platform asks the tool to check again.

type RetestVerdict

type RetestVerdict struct {
	Ref     string  `json:"ref"`
	Verdict Verdict `json:"verdict"`
	Detail  string  `json:"detail,omitempty"`
}

RetestVerdict is an item's final verdict, as the runtime reports it.

type Retester

type Retester interface {
	Retest(ctx RetestContext, task Task) error
}

Retester is implemented by a tool that can retest (Manifest.Retest). The runtime calls Retest instead of Run for a retest task.

type RunSpec

type RunSpec struct {
	// Profile is ProfileAdapter (default) or ProfileExec.
	Profile string `json:"profile,omitempty"`
	// Argv is the program and its arguments. Never a shell. Placeholders
	// are a closed set: {{config.<key>}} (a scalar key of the config
	// schema), {{target.value}}, {{target.host}}, {{target.port}},
	// {{target.url}}, {{task.targets_file}}, {{task.targets_json}},
	// {{task.config_file}}, {{task.output}}, {{task.workdir}}.
	Argv []string `json:"argv"`
	// Output is where an exec-profile tool writes its results.
	Output *OutputSpec `json:"output,omitempty"`
	// ExitCodes maps an exec-profile tool's exit codes to outcomes
	// ("ok", "partial") or error classes; default: 0 ok, else tool_error.
	ExitCodes map[string]string `json:"exit_codes,omitempty"`
}

RunSpec says how the runtime starts a tool that is its own program.

type Scope

type Scope struct {
	Includes []string `json:"includes,omitempty"`
	Excludes []string `json:"excludes,omitempty"`
}

Scope is a job's scope.

type Secret

type Secret struct {
	// contains filtered or unexported fields
}

Secret is a credential. It prints, formats, logs and marshals as [REDACTED]; Reveal returns the value, and the SDK never logs it.

func NewSecret

func NewSecret(v string) Secret

NewSecret wraps a value (runtimes and tests).

func (Secret) Format

func (s Secret) Format(f fmt.State, _ rune)

Format prints [REDACTED] for every verb.

func (Secret) GoString

func (s Secret) GoString() string

func (Secret) IsZero

func (s Secret) IsZero() bool

IsZero reports whether the secret is empty.

func (Secret) LogValue

func (s Secret) LogValue() slog.Value

LogValue logs [REDACTED].

func (Secret) MarshalJSON

func (s Secret) MarshalJSON() ([]byte, error)

MarshalJSON writes "[REDACTED]".

func (Secret) MarshalText

func (s Secret) MarshalText() ([]byte, error)

MarshalText writes [REDACTED].

func (Secret) Reveal

func (s Secret) Reveal() string

Reveal returns the value. Call it where the value is used, never to log.

func (Secret) String

func (s Secret) String() string

type Status

type Status string

Status is how a task ended.

const (
	// StatusOK: every target done.
	StatusOK Status = "ok"
	// StatusPartial: some targets failed, or output was capped or
	// quarantined.
	StatusPartial Status = "partial"
	// StatusFailed: the task failed.
	StatusFailed Status = "failed"
	// StatusCanceled: the task was canceled.
	StatusCanceled Status = "canceled"
)

Task outcomes.

type Target

type Target struct {
	// Ref is an opaque reference that links records to the platform's
	// asset.
	Ref string `json:"ref"`
	// Type is a CTIS asset type ("http_service", "domain", "repository").
	Type string `json:"type,omitempty"`
	// Value is the target itself ("https://a.example:8443", "10.0.4.7",
	// "/src").
	Value string `json:"value"`
	// Attrs are typed hints from the platform (port, scheme, tech, ...).
	Attrs map[string]string `json:"attrs,omitempty"`
}

Target is one thing to work on.

func (Target) Host

func (t Target) Host() string

Host returns the target's host name or IP address ("" for a path).

func (Target) Port

func (t Target) Port() int

Port returns the target's port: explicit, from Attrs["port"], or the scheme's default (0 when unknown).

func (Target) URL

func (t Target) URL(path string) string

URL returns the target as a URL with path appended ("" when the target is not a URL or a host).

type TargetState

type TargetState string

TargetState is the outcome of one target.

const (
	StateDone    TargetState = "done"
	StateFailed  TargetState = "failed"
	StateSkipped TargetState = "skipped"
)

Target outcomes.

type Task

type Task struct {
	ID       string    `json:"id"`
	Attempt  int       `json:"attempt,omitempty"`
	Deadline time.Time `json:"deadline,omitzero"`
	Targets  []Target  `json:"targets,omitempty"`
	// Config is the effective configuration (defaults applied).
	Config json.RawMessage `json:"config,omitempty"`
	// Inputs are files placed in the task's directory (parsers).
	Inputs []Input `json:"inputs,omitempty"`
	// Repo is the repository of a runner-mode (CI) task.
	Repo *Repo `json:"repo,omitempty"`
	// Scope is the job's scope, read-only, for tools that expand targets.
	// Expanded targets are emitted as assets, never scanned in the same
	// task.
	Scope Scope `json:"scope,omitzero"`
	// Local is configuration the sensor itself supplies for a tool compiled
	// into it (operator settings, managed content paths): never from the
	// platform, never for an adapter shipped on its own.
	Local json.RawMessage `json:"local,omitempty"`
	// Retest, when not empty, makes the task a retest: the items to check
	// again, each on one of Targets (see Retester).
	Retest []RetestItem `json:"retest,omitempty"`
}

Task is one unit of work. Its targets were admitted by the runtime and its configuration validated against the manifest's schema before the tool sees it.

func (Task) IsRetest

func (t Task) IsRetest() bool

IsRetest reports whether the task is a retest.

type Tier

type Tier string

Tier is a tool's intrusiveness.

const (
	// T0 is passive: the tool reads, it does not probe.
	T0 Tier = "T0"
	// T1 is active and non-intrusive.
	T1 Tier = "T1"
	// T2 is intrusive (allowed for target-scan tools only).
	T2 Tier = "T2"
)

Tiers.

type Tool

type Tool interface {
	// Manifest describes the tool. It is static: the runtime reads it
	// before any tool code runs.
	Manifest() Manifest
	// Run does the work of one task.
	Run(ctx Context, task Task) error
}

Tool is the one contract for every workload a sensor runs.

func New

func New[C any](m Manifest, run func(ctx Context, task Task, cfg C) error) Tool

New builds a Tool from a manifest and a typed run function. C is the tool's configuration: the runtime validates the task's configuration against the manifest's schema, then the tool decodes it into C (defaults filled, unknown keys refused).

When m.Config is nil and C has fields, the schema is derived from C's struct tags: json (the key), default, min, max, enum (comma-separated), pattern, maxLength, maxItems, title, description, scope ("scan" lets a scan set the key), required ("true"). A field tagged secret:"true" is refused: secrets are credentials, never configuration.

New panics when the manifest is invalid: a tool compiled into a sensor with a broken manifest must stop the sensor at start, not run.

func WithRetest

func WithRetest(t Tool, fn func(ctx RetestContext, task Task) error) Tool

WithRetest returns t with a retest handler: the manifest declares Retest, Run is t's, Retest is fn. It panics without a tool or a handler, like New on an invalid manifest.

type Validator

type Validator interface {
	Validate(ctx context.Context, task Task) error
}

Validator is optional: semantic checks beyond the manifest's schemas, run before any target is touched.

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value is a parameter's value. The runtime validated it against the parameter's declaration, so the typed accessors only fall back when the parameter is unset (no default) or of another type.

func (Value) Bool

func (v Value) Bool() bool

Bool returns the value as a bool (false when unset).

func (Value) BoolOr

func (v Value) BoolOr(d bool) bool

BoolOr returns the value, or d when it is unset or not a bool.

func (Value) Float

func (v Value) Float() float64

Float returns the value as a float64 (0 when unset).

func (Value) FloatOr

func (v Value) FloatOr(d float64) float64

FloatOr returns the value, or d when it is unset or not a number.

func (Value) Int

func (v Value) Int() int

Int returns the value as an int (0 when unset).

func (Value) IntOr

func (v Value) IntOr(d int) int

IntOr returns the value, or d when it is unset or not a number.

func (Value) IsSet

func (v Value) IsSet() bool

IsSet reports whether the parameter has a value (given or default).

func (Value) String

func (v Value) String() string

String returns the value as a string ("" when unset).

func (Value) StringOr

func (v Value) StringOr(d string) string

StringOr returns the value, or d when it is unset or not a string.

func (Value) Strings

func (v Value) Strings() []string

Strings returns a list value (nil when unset).

type Verdict

type Verdict string

Verdict is what a retest concluded about one item.

const (
	// StillPresent: the issue (or asset) was observed again.
	StillPresent Verdict = "still_present"
	// Fixed: the check ran against the reached target and the issue is
	// gone (an asset: it no longer exists).
	Fixed Verdict = "fixed"
	// Unverifiable: the tool could not tell (unreachable, check not
	// available, error).
	Unverifiable Verdict = "unverifiable"
)

Verdicts.

func (Verdict) Valid

func (v Verdict) Valid() bool

Valid reports whether v is a known verdict.

Directories

Path Synopsis
Package adapter runs a tool (pkg/tool) as an adapter process: the tool's side of adapter protocol v1, newline-delimited JSON on stdin and stdout, one task per process (docs/rfcs/sensor-sdk-v2.md, D.4.2).
Package adapter runs a tool (pkg/tool) as an adapter process: the tool's side of adapter protocol v1, newline-delimited JSON on stdin and stdout, one task per process (docs/rfcs/sensor-sdk-v2.md, D.4.2).
Package toolcompat runs code written against the older interfaces (core.Scanner, core.Collector) as tools of the tool contract (pkg/tool), so it gets the contract's runtime without a rewrite: out-of-process execution in the task sandbox, admission against the policy, declared credentials only, checked and stamped output.
Package toolcompat runs code written against the older interfaces (core.Scanner, core.Collector) as tools of the tool contract (pkg/tool), so it gets the contract's runtime without a rewrite: out-of-process execution in the task sandbox, admission against the policy, declared credentials only, checked and stamped output.

Jump to

Keyboard shortcuts

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