tool

package
v0.33.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: GPL-3.0 Imports: 36 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 (
	ContentDefault = "default"
	ContentCustom  = "custom"
	ContentMerge   = "merge"
)

Content compositions (api RFC-061 §3.3).

View Source
const (
	// BatchOne: one target per task (the default).
	BatchOne = "one"
	// BatchList: a list of targets per task.
	BatchList = "list"
)

Batch shapes.

View Source
const (
	SideEffectIOCInLogs          = "ioc_in_logs"
	SideEffectArtifactsOnTarget  = "artifacts_on_target"
	SideEffectStateChange        = "state_change"
	SideEffectAccountLockout     = "account_lockout"
	SideEffectServiceDisruption  = "service_disruption"
	SideEffectThirdPartyContacts = "third_party_contact"
)

Side effects a tool may declare.

View Source
const (
	// ProxyHonors: the tool sends its traffic through the zone's proxy.
	ProxyHonors = "honors"
	// ProxyIgnores: the tool connects directly; it is ineligible in a zone
	// that requires a proxy.
	ProxyIgnores = "ignores"
)

Proxy behaviors.

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"
	// KindEndpoint is a method and path a web origin serves (CTIS 1.6
	// endpoints[]).
	KindEndpoint = "endpoint"
)

Produce kinds.

View Source
const (
	OutputJSON  = "json"
	OutputJSONL = "jsonl"
)

Output formats of the exec profile that need a mapping file (run.output.mapping): any JSON document or JSON Lines output, turned into CTIS by the declarative mapping language of ctis/importer/mapping.

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 EnvEgressProxy = "OPENCTEM_EGRESS_PROXY"

EnvEgressProxy is set by the sensor's sandbox, for a task whose network it confines, to the task's forwarder (api RFC-060): the task has no other way out. Its value is a proxy URL ("http://127.0.0.1:1080") that takes HTTP CONNECT, absolute-form HTTP and SOCKS5.

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 MaxMappingBytes = 256 << 10

MaxMappingBytes bounds a mapping file.

View Source
const MaxRetestItems = 1000

MaxRetestItems bounds the items of one retest task.

View Source
const MaxVerdictEvidence = 5

MaxVerdictEvidence bounds the evidence items of one verdict.

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 ErrEgressRefused = errors.New("egress refused")

ErrEgressRefused is the error of a connection the task's forwarder refused: the destination is not one the task may reach.

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.

View Source
var KnownContentKinds = []string{"nuclei-templates", "semgrep-rules", "yara-rules", "vuln-db", "wordlist", "signatures"}

KnownContentKinds are the kinds the platform manages packs for; a tool's own kind is namespaced (x-<vendor>/<kind>).

Functions

func AdapterProtocolJSONSchema

func AdapterProtocolJSONSchema() []byte

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

func ArgvPlaceholders added in v0.19.0

func ArgvPlaceholders(element string) []string

ArgvPlaceholders returns the placeholder names in an argv element.

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 CheckTaskContent added in v0.19.0

func CheckTaskContent(m Manifest, task Task, root string) ([]string, error)

CheckTaskContent checks a task's content against the manifest: every slot declared, its mode supported, its selectors understood, every pack a sha256 digest whose path is an absolute path beneath root (the sensor's content cache). It returns the paths to grant the task.

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 Dial added in v0.19.0

func Dial(ctx context.Context, network, address string) (net.Conn, error)

Dial opens a connection for a tool that speaks TCP itself (a port scanner, a TLS checker, a protocol probe). On a sensor that confines the task's network it goes through the task's forwarder with HTTP CONNECT: a direct connection has no route there, and a destination that is not a target fails with ErrEgressRefused. Elsewhere it is a direct connection. network must be "tcp", "tcp4" or "tcp6".

func EgressProxy added in v0.19.0

func EgressProxy() *url.URL

EgressProxy is the task's forwarder, or nil when the task's network is not confined.

func Evidence added in v0.19.0

func Evidence(kind, label string, data map[string]any) (ctis.EvidenceItem, error)

Evidence is an evidence item of a kind this CTIS version does not know, its fields in data (at most ctis.MaxEvidenceDataBytes as JSON). Known kinds have their own fields: build them with HTTPExchange or the ctis builders.

func Failed

func Failed(err error) error

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

func HTTPExchange added in v0.19.0

func HTTPExchange(req *http.Request, reqBody []byte, resp *http.Response, respBody []byte, match ...ctis.EvidenceMatch) (ctis.EvidenceItem, bool)

HTTPExchange is an http_exchange evidence item of one request and the response it got (resp nil: none arrived). reqBody and respBody are the bodies as sent and received (a body already read from resp). match are the spans that matched, with byte offsets into the bodies.

A body over the CTIS cap keeps a window around the first response-body match (the head without one); matches outside the window are dropped. content_sha256 covers the full captured exchange. ok is false without a request or a response.

func ImporterFormats added in v0.19.0

func ImporterFormats() []string

ImporterFormats are the named formats an exec-profile tool may write: the formats of ctis/importer, parsed with no code in the tool.

func Invalid

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

Invalid reports invalid configuration or targets (not retried).

func LoadMappingFile added in v0.19.0

func LoadMappingFile(dir, rel, want string) (*mapping.Mapping, error)

LoadMappingFile reads the mapping file of an exec-profile manifest from dir: JSON or YAML, a regular file inside dir (no symlink), at most MaxMappingBytes. When want is set the mapping's digest must equal it, so a file changed after the manifest was loaded is refused.

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 ArgForm added in v0.19.0

type ArgForm int

ArgForm is how a {{config.<key>}} placeholder of an exec argv expands.

const (
	// ArgScalar, {{config.k}}: the value ("" when the key is not set).
	ArgScalar ArgForm = iota
	// ArgOptional, {{config.k?}}: the value; the whole argv element is
	// left out when the key is not set, empty or false.
	ArgOptional
	// ArgList, {{config.k...}}: an array key. As the whole element, one
	// argument per item; inside a larger element, the items joined with
	// commas. Left out when the list is empty or not set.
	ArgList
	// ArgSwitch, {{config.k?:-flag}}: a boolean key. The whole element
	// becomes the literal flag when the key is true and is left out
	// otherwise.
	ArgSwitch
)

Forms of a config placeholder.

func ConfigArg added in v0.19.0

func ConfigArg(name string) (key string, form ArgForm, flag string, ok bool)

ConfigArg parses a placeholder name of the config family ("config.k", "config.k?", "config.k...", "config.k?:-x"): its key, form and, for a switch, the flag. ok is false for any other name.

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) BatchTargets added in v0.19.0

func (b *Builder) BatchTargets(max int) *Builder

BatchTargets makes the tool take a list of at most max targets per task (0: the runtime's default).

func (*Builder) Build

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

Build checks the declaration and returns the tool.

func (*Builder) Capabilities deprecated

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

Capabilities declares free-form capability words.

Deprecated: use Implements with capability references.

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) Implements added in v0.19.0

func (b *Builder) Implements(refs ...string) *Builder

Implements declares the capabilities the tool implements, by reference with their major ("vuln.templates@1"). A standard param of the capability maps to the tool's parameter of the same name when the tool declares one of a matching type; ImplementsWith maps them explicitly.

func (*Builder) ImplementsWith added in v0.19.0

func (b *Builder) ImplementsWith(im Implementation) *Builder

ImplementsWith declares an implemented capability with an explicit param mapping and output shape.

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 ContentPack added in v0.19.0

type ContentPack struct {
	// Digest is the pack's content address ("sha256:<hex>").
	Digest string `json:"digest"`
	// Source is where the pack came from (platform, tenant).
	Source string `json:"source,omitempty"`
	// Path is where the sensor keeps it, read-only for the task. Set by
	// the sensor's content cache, never taken from a job.
	Path string `json:"path,omitempty"`
}

ContentPack is one immutable pack.

type ContentSlot added in v0.19.0

type ContentSlot struct {
	// Slot names the slot in the tool ("templates").
	Slot string `json:"slot"`
	// Kind is what the content is: a known kind (nuclei-templates,
	// semgrep-rules, yara-rules, vuln-db, wordlist, signatures) or a
	// tool's own, namespaced (x-acme/rules). Packs bind to tools by kind.
	Kind string `json:"kind"`
	// Format is dir (default), file or archive.
	Format string `json:"format,omitempty"`
	// Default is bundled (the platform's managed pack of this kind is
	// used unless a step says otherwise) or none.
	Default string `json:"default,omitempty"`
	// Modes are the compositions the tool supports: default, custom and
	// merge (default: all three).
	Modes []string `json:"modes,omitempty"`
	// Selectors are the filters the tool understands: tags, ids,
	// severities, paths.
	Selectors []string `json:"selectors,omitempty"`
	// MaxBytes bounds a pack of this slot (0: the sensor's limit).
	MaxBytes int64 `json:"max_bytes,omitempty"`
}

ContentSlot is a kind of content a tool reads: templates, rules, signatures, a wordlist, a vulnerability database (tool.yaml content; api RFC-061). A tool declares its slots and never fetches content itself: the sensor resolves each task's packs (by digest, from the platform) and the task gets read-only paths to exactly those.

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. On a sensor that confines the
	// task's network it goes through the task's forwarder (EgressProxy).
	// Requests carry the User-Agent "openctem-<tool>/<version>" unless the
	// tool sets its own. A tool that speaks TCP itself uses Dial.
	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 Deprecation added in v0.19.0

type Deprecation struct {
	Since      string `json:"since"`
	ReplacedBy string `json:"replaced_by,omitempty"`
	Message    string `json:"message,omitempty"`
}

Deprecation marks a tool version that is being replaced.

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
	// Endpoint emits a method and path a web origin serves (CTIS 1.6).
	// A URL in it never carries a query value or credentials.
	Endpoint(e ctis.Endpoint) 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 Engine added in v0.19.0

type Engine struct {
	Name    string `json:"name"`
	License string `json:"license,omitempty"`
	// MinVersion: an older probed engine reports the tool as outdated.
	MinVersion string `json:"min_version,omitempty"`
	// VersionProbe is the argv that prints the engine's version (no
	// shell, no placeholders).
	VersionProbe []string `json:"version_probe,omitempty"`
}

Engine is the program a tool wraps. Its version is probed at run time, never trusted from the descriptor.

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"
	// EgressRefused: the sensor's network confinement refused every
	// connection the tool made toward a target (a target's class only).
	EgressRefused ErrorClass = "egress_refused"
)

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 Features added in v0.19.0

type Features struct {
	// Retest: the tool can check again what it reported (see Retester).
	Retest bool `json:"retest,omitempty"`
	// Cancel: the tool stops promptly on a cancel message.
	Cancel bool `json:"cancel,omitempty"`
	// WebScope: the tool keeps every request inside the job's web scope
	// (Task.WebScope): it maps the scope onto its own flags, or requests
	// only through Context.HTTP, which enforces it. A job with a web scope
	// is refused for a networked tool that does not declare it.
	WebScope bool `json:"web_scope,omitempty"`
	// Streaming is reserved: feeding records to the next stage while the
	// task runs is not part of protocol v1.
	Streaming bool `json:"streaming,omitempty"`
}

Features are optional protocol features a tool supports.

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 HTTPSpec added in v0.19.0

type HTTPSpec struct {
	// UserAgent replaces the default "openctem-<tool>/<version>".
	UserAgent string `json:"user_agent,omitempty"`
	// Headers are added to every request that does not set them.
	Headers map[string]string `json:"headers,omitempty"`
	// Timeout bounds one request (1s to 10m; default 5m).
	Timeout Duration `json:"timeout,omitempty"`
	// TLS adjusts TLS toward the targets.
	TLS *TLSSpec `json:"tls,omitempty"`
}

HTTPSpec is how a tool's requests to its targets look (tool.yaml http; api RFC-060 §4.4): a tool may set these freely. What a request may reach is not among them: the scope, the sandbox and the forwarder decide that.

func (*HTTPSpec) String added in v0.19.0

func (h *HTTPSpec) String() string

String summarizes the spec for a log line (header names only).

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 Implementation added in v0.19.0

type Implementation struct {
	// Capability is the capability reference with its major
	// ("scan.ports@1").
	Capability string `json:"capability"`
	// Params maps the capability's standard params this tool accepts to
	// its own config keys. A standard param not listed is one the tool
	// does not take: a workflow node that sets it cannot run this tool.
	Params map[string]ParamMapping `json:"params,omitempty"`
	// OutputShape names which of the capability's accepted output shapes
	// the tool emits, when the capability has several.
	OutputShape string `json:"output_shape,omitempty"`
}

Implementation is one capability a tool implements.

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 InputSpec added in v0.19.0

type InputSpec struct {
	// Batch is BatchOne (default) or BatchList.
	Batch string `json:"batch,omitempty"`
	// MaxTargets bounds a list task (BatchList only).
	MaxTargets int `json:"max_targets,omitempty"`
}

InputSpec is how a tool takes its targets.

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"`
	// Publisher is who publishes the tool: the signer of a certified
	// tool, or free text (shown as text, never trusted).
	Publisher string `json:"publisher,omitempty"`
	// License is the SPDX license expression of the adapter (the wrapped
	// engine's is engine.license).
	License string `json:"license,omitempty"`
	// Engine is the program the tool wraps; its version is probed.
	Engine *Engine `json:"engine,omitempty"`
	// Presentation is how the platform shows the tool.
	Presentation *Presentation `json:"presentation,omitempty"`
	Class        Class         `json:"class"`
	// Modes the tool runs in; empty means both.
	Modes []Mode `json:"modes,omitempty"`
	// Tier is the tier the tool asks for. It cannot be below the tier
	// floor of a capability it implements; the platform assigns the
	// effective tier and may raise it (MinimumTier).
	Tier Tier `json:"tier"`
	// Implements are the capabilities of the OpenCTEM capability taxonomy
	// the tool implements ("scan.ports@1"), with its param mapping.
	Implements []Implementation `json:"implements,omitempty"`
	// Capabilities is the older free-form capability list. Deprecated:
	// use Implements. With Implements set it may only repeat its ids.
	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"`
	// Input is how the tool takes its targets (default: one per task).
	Input *InputSpec `json:"input,omitempty"`
	// 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"`
	// Safety is what the tool does to its targets beyond reading them.
	Safety *Safety `json:"safety,omitempty"`
	// Features are the optional protocol features the tool supports.
	Features *Features `json:"features,omitempty"`
	// HTTP is how the tool's requests to its targets look (user agent,
	// headers, timeout, TLS): applied by ctx.HTTP(), named in exec argv
	// ({{http.user_agent}}) and readable by an adapter from its manifest.
	HTTP *HTTPSpec `json:"http,omitempty"`

	// Content are the kinds of content the tool reads (templates, rules,
	// wordlists): the task gets the resolved packs (api RFC-061).
	Content []ContentSlot `json:"content,omitempty"`
	// 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. Deprecated spelling of
	// features.retest.
	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"`
	// SDK is the oldest SDK that understands every key this manifest uses.
	SDK *SDKRequirement `json:"sdk,omitempty"`
	// Deprecated marks a tool being replaced.
	Deprecated *Deprecation `json:"deprecated,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) ApplyParams added in v0.19.0

func (m Manifest) ApplyParams(task Task) (Task, error)

ApplyParams maps a task's standard params onto the tool's config keys, through the implements entry of the task's capability, and returns the task with Params cleared and Config set. Every value is checked against the capability's param (type, enum, bounds) and against the tool's narrowing (values, min, max); a param the tool does not map, a value it does not support and a config key the task also sets to another value are invalid_input. Nothing is dropped silently.

A task without a capability must carry no params. A capability the tool does not implement is invalid_input.

func (Manifest) Batches added in v0.19.0

func (m Manifest) Batches() bool

Batches reports whether the tool takes a list of targets per task.

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) Implementation added in v0.19.0

func (m Manifest) Implementation(ref string) (Implementation, bool)

Implementation returns the implements entry of a capability reference ("scan.ports@1").

func (Manifest) ImplementedCapabilities added in v0.19.0

func (m Manifest) ImplementedCapabilities() []capability.Capability

ImplementedCapabilities returns the capabilities the tool implements, in declaration order. A reference that does not resolve is skipped (Validate refuses it).

func (Manifest) MinimumTier added in v0.19.0

func (m Manifest) MinimumTier() Tier

MinimumTier is the lowest tier the tool can be assigned: the highest of its declared tier, the tier floors of the capabilities it implements, and T2 when it declares a side effect. The platform may assign a higher one; never a lower one.

func (Manifest) Normalized

func (m Manifest) Normalized() Manifest

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

func (Manifest) RetestFeature added in v0.19.0

func (m Manifest) RetestFeature() bool

RetestFeature reports whether the tool can retest (features.retest or the older top-level retest key).

func (Manifest) Slot added in v0.19.0

func (m Manifest) Slot(name string) (ContentSlot, bool)

Slot returns a declared content slot.

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"
	// NetResolver: DNS questions about the task's targets only, answered
	// through the sensor's resolvers; no connection to any host, the
	// targets included. A DNS resolution tool that never contacts the
	// target hosts declares it.
	NetResolver Network = "resolver"
	// 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, OutputJSONLCTIS, OutputJSON or
	// OutputJSONL (with Mapping), or a named format of ctis/importer
	// (ImporterFormats: "nuclei", "trivy", "cyclonedx", ...).
	Format string `json:"format"`
	// From is "stdout" or "file" (the {{task.output}} path).
	From string `json:"from"`
	// Mapping is the mapping file (relative to the manifest's directory)
	// that turns json or jsonl output into CTIS: the declarative mapping
	// language of ctis/importer/mapping, in JSON or YAML.
	Mapping string `json:"mapping,omitempty"`
	// MappingDigest is the mapping's digest, set when the manifest file is
	// loaded (LoadManifestFile); the manifest digest covers it, and the
	// runtime refuses a mapping file that no longer matches it.
	MappingDigest string `json:"mapping_digest,omitempty"`
}

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 ParamMapping added in v0.19.0

type ParamMapping struct {
	// Key is the top-level config key; empty means the param's own name.
	Key string `json:"key,omitempty"`
	// Values narrows an enum param (or the items of a list param) to the
	// values the tool supports. A value outside makes the tool ineligible
	// for that node; it is never silently dropped.
	Values []string `json:"values,omitempty"`
	// Min and Max narrow an integer param.
	Min *int `json:"min,omitempty"`
	Max *int `json:"max,omitempty"`
}

ParamMapping maps one standard param to a config key, optionally narrowing the values the tool supports.

func (ParamMapping) ConfigKey added in v0.19.0

func (p ParamMapping) ConfigKey(param string) string

ConfigKey is the config key the param maps to.

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"`
	// Proxy is ProxyHonours or ProxyIgnores; unset is unknown, which the
	// platform treats as ignores where a zone requires a proxy.
	Proxy string `json:"proxy,omitempty"`
}

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

type Presentation added in v0.19.0

type Presentation struct {
	DisplayName string `json:"display_name,omitempty"`
	Category    string `json:"category,omitempty"`
	Icon        string `json:"icon,omitempty"`
	DocsURL     string `json:"docs_url,omitempty"`
}

Presentation is how the platform shows a tool. Every string is rendered as text; the icon is a name from a closed set, never a URL, and the docs URL is a link the platform never fetches.

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)
	// Report is Verdict with the attempt's evidence and the digest of the
	// check that ran. Evidence over MaxVerdictEvidence items or invalid
	// makes the verdict Unverifiable.
	Report(item RetestItem, r VerdictReport)
}

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"`
	// Evidence is what the attempt saw (at most MaxVerdictEvidence items),
	// sensitive values marked (ctis.MarkSensitive), never masked.
	Evidence []ctis.EvidenceItem `json:"evidence,omitempty"`
	// TemplateDigest identifies the exact check that ran (a template's
	// digest), so the platform can tell a fix from a changed check.
	TemplateDigest string `json:"template_digest,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) and its forms {{config.<key>?}} (left out when not set),
	// {{config.<key>...}} (an array key: one argument per item, or
	// comma-joined inside a larger argument) and {{config.<key>?:-flag}}
	// (a boolean key: the flag when true; see ConfigArg), {{target.value}},
	// {{target.host}}, {{target.port}}, {{target.url}},
	// {{task.targets_file}}, {{task.targets_json}}, {{task.config_file}},
	// {{task.web_scope_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 SDKRequirement added in v0.19.0

type SDKRequirement struct {
	Min string `json:"min,omitempty"`
}

SDKRequirement is the oldest SDK that understands every key a descriptor uses.

type Safety added in v0.19.0

type Safety struct {
	// RateParam is the integer config key that bounds requests or packets
	// per second; the runtime caps it by the sensor's local policy.
	RateParam string `json:"rate_param,omitempty"`
	// SideEffects the tool may cause. Any entry makes the tool intrusive
	// (tier T2).
	SideEffects []string `json:"side_effects,omitempty"`
	// ExpandsTargets: the tool discovers new targets. They go to the
	// platform as assets; the tool never scans them in the same task.
	ExpandsTargets bool `json:"expands_targets,omitempty"`
}

Safety says what a tool does to its targets beyond reading them.

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 TLSSpec added in v0.19.0

type TLSSpec struct {
	// InsecureSkipVerify accepts any certificate (scanning a target with a
	// self-signed or expired one). Refused for a vendor-network tool:
	// credentials travel to vendor hosts.
	InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"`
	// MinVersion is "1.0", "1.1", "1.2" or "1.3" (default: Go's).
	MinVersion string `json:"min_version,omitempty"`
}

TLSSpec adjusts TLS toward a tool's targets.

func (*TLSSpec) TLSMinVersion added in v0.19.0

func (t *TLSSpec) TLSMinVersion() uint16

TLSMinVersion maps MinVersion to crypto/tls (0: Go's default).

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"`
	// Capability is the capability the task runs ("scan.ports@1"), one the
	// tool implements. The runtime checks the output against its contract.
	Capability string `json:"capability,omitempty"`
	// Params are the capability's standard params as the workflow set
	// them. The runtime maps them onto the tool's config keys
	// (Manifest.ApplyParams) before the tool starts; the tool reads only
	// Config and never receives Params.
	Params map[string]json.RawMessage `json:"params,omitempty"`
	// MaxTier is the highest tier the job allows (the ceiling of the
	// platform grant). A tool whose MinimumTier is higher is refused.
	MaxTier Tier `json:"max_tier,omitempty"`
	// WebScope, when set, is the hosts, paths and methods a web tool may
	// request (webscope.Scope). Context.HTTP refuses every request outside
	// it; a tool that makes its own requests declares features.web_scope
	// and keeps to it.
	WebScope *webscope.Scope `json:"web_scope,omitempty"`
	// Limits are the port, protocol and path limits of the targets (from
	// the job's verified signed statement; scopelimit). The tool host
	// enforces them outside the tool: the task's egress forwarder refuses
	// a connection to a limited host on another port and an HTTP request
	// outside its path prefixes. A task with limits runs only on a sandbox
	// that confines its network.
	Limits []scopelimit.Limit `json:"limits,omitempty"`
	// HTTP is the effective http settings of the task: the manifest's,
	// with what the sensor's local policy decides (a forced User-Agent).
	// Set by the runtime, never taken from a job; ctx.HTTP() applies it.
	HTTP *HTTPSpec `json:"http,omitempty"`
	// OrgHTTP is the organization's HTTP policy from the job. The tool host
	// merges it into HTTP (after the local policy) and clears it; a tool
	// never acts on it.
	OrgHTTP *core.OrgHTTPPolicy `json:"-"`
	// Content are the task's content packs per slot the manifest
	// declares, resolved by the sensor to read-only paths (ContentPaths).
	Content []TaskContent `json:"content,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) ContentPaths added in v0.19.0

func (t Task) ContentPaths(slot string) []string

ContentPaths are the paths of a slot's packs in the task (none when the task has no content for it).

func (Task) IsRetest

func (t Task) IsRetest() bool

IsRetest reports whether the task is a retest.

type TaskContent added in v0.19.0

type TaskContent struct {
	Slot string `json:"slot"`
	// Mode is default, custom or merge.
	Mode string `json:"mode,omitempty"`
	// Packs are the packs, in order (a merge: custom after default).
	Packs []ContentPack `json:"packs,omitempty"`
	// Selectors filter the content (tags, ids, severities, paths).
	Selectors map[string][]string `json:"selectors,omitempty"`
}

TaskContent is one slot's content for a task, resolved by the sensor.

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.

func (Tier) Exceeds added in v0.19.0

func (t Tier) Exceeds(ceiling Tier) bool

Exceeds reports whether t is more intrusive than ceiling. An unknown tier exceeds every ceiling.

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.

type VerdictReport added in v0.19.0

type VerdictReport struct {
	Verdict        Verdict
	Detail         string
	Evidence       []ctis.EvidenceItem
	TemplateDigest string
}

VerdictReport is a verdict with what supports it (RetestContext.Report).

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