conformance

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: 30 Imported by: 0

Documentation

Overview

Package conformance is the sensor protocol conformance suite (RFC-026 WP-S3, RFC-023 D23), starting with results ingest.

It has two halves:

  • FakePlatform, a control plane that implements the v2 results contract (RFC-026 §3) strictly and the v2 control plane (RFC-029), records every request and can be told to fail in each §3.8 class. The SDK's own tests run against it: they check the SDK sends the right headers and digest, retries only what it should, splits on 413, never resends a partially accepted report and reuses report_id on retry.
  • The live suite (live_test.go), which runs the same contract against a real API: OPENCTEM_CONFORMANCE_URL and OPENCTEM_CONFORMANCE_KEY (a sensor key) select it; see the package README in the test file.

Stability: Stable (docs/STABILITY.md).

Index

Constants

View Source
const FutureMember = "x_openctem_future"

FutureMember is the member FutureFields adds to every JSON object answer.

View Source
const UnknownCommandType = "x_conformance_unknown"

UnknownCommandType is the command type the suite dispatches to check that a sensor fails a command it does not know instead of hanging or crashing.

Variables

This section is empty.

Functions

func RunSensorSuite added in v0.16.0

func RunSensorSuite(t *testing.T, start StartSensor, opts SuiteOptions)

RunSensorSuite checks a sensor against the platform contract every sensor must keep, whatever tools it runs, so a third-party sensor can prove it in its own tests:

func TestConformance(t *testing.T) {
	httpsec.AllowLoopback = true // the fake listens on 127.0.0.1
	conformance.RunSensorSuite(t, func(t *testing.T, p conformance.SensorEndpoint) func() {
		kit, err := sensorkit.New(sensorkit.Options{Name: "my-sensor", Version: "1.0.0",
			APIURL: p.URL, APIKey: p.APIKey, StateDir: t.TempDir(),
			Outbox: sensorkit.OutboxSettings{Dir: t.TempDir()}})
		if err != nil {
			t.Fatal(err)
		}
		kit.AddScanner(myScanner)
		ctx, cancel := context.WithCancel(context.Background())
		done := make(chan error, 1)
		go func() { done <- kit.Run(ctx) }()
		return func() { cancel(); <-done }
	}, conformance.SuiteOptions{ScanPayload: json.RawMessage(`{"scanner":"my-tool","target":"..."}`)})
}

Each check runs against its own FakePlatform (protocol v2, control plane and manifest on):

  • Heartbeat: the sensor heartbeats and names the SDK it is built on.
  • Manifest: a sensor that announces a manifest digest registers the manifest when the platform asks for it.
  • UnknownCommand: a command of a type the sensor does not handle is left pending for a sensor that does (or failed), never claimed and abandoned, and the sensor keeps polling.
  • ForwardCompatible: the same with every answer carrying a JSON member no SDK knows (FutureMember), as a newer platform's would: the sensor ignores it and keeps working (and runs its own command, with ScanPayload).
  • Command (with ScanPayload): the command completes, and every report sent for it is committed before the command is completed.

func RunToolSuite added in v0.18.0

func RunToolSuite(t T, manifestPath string, opts ToolSuiteOptions)

RunToolSuite checks a tool that runs as its own program (a tool.yaml with a run section, any language) against adapter protocol v1, the way the sensor's runtime will run it:

  • Manifest: the tool.yaml loads strictly and validates, and has a run section.
  • Handshake: the program answers the runtime's hello with protocol 1, ignores a message type it does not know, and answers describe with exactly its tool.yaml (the run section aside).
  • Validate: a configuration with an unknown key is refused.
  • StdoutIsProtocol: everything it writes to standard output is a protocol message (diagnostics belong on standard error).
  • ExitsOnEOF: it exits, with status 0, when its input ends.
  • Cancel: after run and cancel it sends its result, or exits, within the 10-second grace.
  • Fixtures: each self-test fixture (manifest selftest: a task file and the CTIS it must produce) runs through the runtime's own host, with every runtime check, and produces exactly the expected normalized report, with no invalid message.

Use it from go test (conformance.RunToolSuite(t, "tool.yaml", conformance.ToolSuiteOptions{})) or with the command cmd/openctem-conformance.

Types

type FakePlatform

type FakePlatform struct {
	Server *httptest.Server
	// APIKey is the sensor key it accepts.
	APIKey string
	// V2 serves the v2 routes and advertises them; false behaves like a
	// platform from before RFC-026 (404 on /api/v2).
	V2 bool
	// Limits are published on hello.
	Limits protov2.Limits
	// Tools are the sensor's declared tools (empty: any).
	Tools []string
	// Control serves the api RFC-029 control plane on v2 (heartbeat,
	// commands, suppressions, fingerprints, keys) and lists it on hello.
	// False (with V2) is a platform from api v0.8: v2 results only.
	Control bool
	// Paused answers heartbeats as for a disabled sensor (v2: 200 + pause).
	Paused bool
	// Manifest serves PUT /manifest (api RFC-033) with Control and lists it
	// on hello: the fake stores the manifest's digest and asks for it again
	// (send_manifest) when a heartbeat names another one.
	Manifest bool
	// FutureFields adds a member no SDK knows to every JSON object the
	// fake answers with, as a newer platform would: a sensor must ignore
	// it (forward compatibility, docs/STABILITY.md).
	FutureFields bool
	// contains filtered or unexported fields
}

FakePlatform is an in-process control plane. Safe for concurrent use.

func NewFakePlatform

func NewFakePlatform(v2 bool) *FakePlatform

NewFakePlatform starts a fake platform. Close it with Close. v2 false is a platform without sensor protocol v2 (every v2 route answers 404), against which the SDK must fail with ErrV2Unsupported; v2 true serves results and the RFC-029 control plane (SetControl(false) leaves only results).

func (*FakePlatform) AcceptedFindings

func (f *FakePlatform) AcceptedFindings() int

AcceptedFindings counts the findings of committed reports.

func (*FakePlatform) AcceptedReports added in v0.18.0

func (f *FakePlatform) AcceptedReports() []*ctis.Report

AcceptedReports returns the reports the fake accepted: every segment of each committed report.

func (*FakePlatform) Close

func (f *FakePlatform) Close()

Close stops the server.

func (*FakePlatform) CommandLogs added in v0.18.0

func (f *FakePlatform) CommandLogs(id string) []protov2.CommandLogLine

CommandLogs returns the log lines received for a command, in seq order.

func (*FakePlatform) CommandResult added in v0.9.0

func (f *FakePlatform) CommandResult(id string) json.RawMessage

CommandResult returns the result a completed command stored.

func (*FakePlatform) CommandState

func (f *FakePlatform) CommandState(id string) (string, string)

CommandState returns a command's state and its error message.

func (*FakePlatform) ConfigReports added in v0.18.0

func (f *FakePlatform) ConfigReports() []json.RawMessage

ConfigReports returns the config reports received, in order.

func (*FakePlatform) ForgetConfigReport added in v0.18.0

func (f *FakePlatform) ForgetConfigReport()

ForgetConfigReport drops the stored config report digest, as a platform that lost it: the next heartbeat naming one is asked to send it again.

func (*FakePlatform) ForgetManifest added in v0.14.0

func (f *FakePlatform) ForgetManifest()

ForgetManifest drops the stored digest, as a platform that lost it: the next heartbeat naming it is asked for the manifest again.

func (*FakePlatform) HeartbeatBuilds added in v0.10.0

func (f *FakePlatform) HeartbeatBuilds() []HeartbeatBuild

HeartbeatBuilds returns the "sdk" and "sensor" blocks of every heartbeat received, in order.

func (*FakePlatform) HeartbeatOutbox

func (f *FakePlatform) HeartbeatOutbox() []json.RawMessage

HeartbeatOutbox returns the outbox objects heartbeats carried.

func (*FakePlatform) Heartbeats added in v0.10.0

func (f *FakePlatform) Heartbeats() []json.RawMessage

Heartbeats returns the heartbeat bodies received, in order.

func (*FakePlatform) LeaseEpoch added in v0.16.0

func (f *FakePlatform) LeaseEpoch(id string) int

LeaseEpoch returns the command's lease epoch (0 before its first claim).

func (*FakePlatform) Manifests added in v0.14.0

func (f *FakePlatform) Manifests() []json.RawMessage

Manifests returns the manifests registered, in order.

func (*FakePlatform) OpenCommand

func (f *FakePlatform) OpenCommand(id string)

OpenCommand registers a running command assigned to the sensor.

func (*FakePlatform) QueueCommand added in v0.9.0

func (f *FakePlatform) QueueCommand(id string)

QueueCommand adds a pending command a poll offers.

func (*FakePlatform) QueueCommandPayload added in v0.16.0

func (f *FakePlatform) QueueCommandPayload(id, typ string, payload json.RawMessage)

QueueCommandPayload adds a pending command of the given type ("" is "scan") and payload, as the platform dispatches it.

func (*FakePlatform) ReclaimCommand added in v0.16.0

func (f *FakePlatform) ReclaimCommand(id string)

ReclaimCommand simulates a lease loss (api RFC-035 D6): the command was re-queued and claimed again, so its lease epoch moves on while its state stays. A complete or fail that echoes the old epoch is refused with invalid-transition, as the platform answers it.

func (*FakePlatform) Releases added in v0.10.0

func (f *FakePlatform) Releases() []Release

Releases returns the releases received, in order.

func (*FakePlatform) Reports

func (f *FakePlatform) Reports() map[string]*StoredReport

Reports returns the accepted v2 reports by id.

func (*FakePlatform) Requests

func (f *FakePlatform) Requests() []Request

Requests returns the recorded requests.

func (*FakePlatform) RequestsTo

func (f *FakePlatform) RequestsTo(method, prefix string) []Request

RequestsTo returns the recorded requests whose path has prefix.

func (*FakePlatform) SetAPIKey

func (f *FakePlatform) SetAPIKey(key string)

SetAPIKey replaces the accepted sensor key (a key rotation).

func (*FakePlatform) SetConfigReport added in v0.18.0

func (f *FakePlatform) SetConfigReport(on bool)

SetConfigReport lists or stops listing the "config_report" feature on hello and serves PUT /config-report (api RFC-033, config report): the fake stores each report and asks for it again (send_config_report) when a heartbeat names another digest.

func (*FakePlatform) SetControl added in v0.9.0

func (f *FakePlatform) SetControl(on bool)

SetControl switches the RFC-029 control plane on v2 on or off.

func (*FakePlatform) SetFault

func (f *FakePlatform) SetFault(fl Fault)

SetFault installs a fault injector (nil removes it).

func (*FakePlatform) SetFutureFields added in v0.16.0

func (f *FakePlatform) SetFutureFields(on bool)

SetFutureFields switches FutureFields.

func (*FakePlatform) SetLocalPolicy added in v0.18.0

func (f *FakePlatform) SetLocalPolicy(on bool)

SetLocalPolicy lists or stops listing the "local_policy" feature on hello (api RFC-040 §5.7): with it, heartbeats and manifests carry the sensor's local policy report.

func (*FakePlatform) SetLogs added in v0.18.0

func (f *FakePlatform) SetLogs(on bool)

SetLogs makes the fake serve POST /commands/{id}/logs and list "logs" on hello.

func (*FakePlatform) SetManifest added in v0.14.0

func (f *FakePlatform) SetManifest(on bool)

SetManifest serves or stops serving the sensor manifest (api RFC-033).

func (*FakePlatform) SetManifestPolicy added in v0.15.0

func (f *FakePlatform) SetManifestPolicy(allowedTools []string, omitInventory bool)

SetManifestPolicy sets what the manifest answers say from now on (api RFC-033 §6.12): the allowed tools (nil: every tool of the manifest) and whether heartbeats may leave the inventory out.

func (*FakePlatform) SetPaused added in v0.9.0

func (f *FakePlatform) SetPaused(on bool)

SetPaused answers heartbeats as for a disabled sensor.

func (*FakePlatform) SetV2

func (f *FakePlatform) SetV2(on bool)

SetV2 switches the v2 routes on or off.

func (*FakePlatform) URL

func (f *FakePlatform) URL() string

URL is the base URL.

type Fault

type Fault func(r *http.Request, seq int) *FaultAnswer

Fault makes the fake answer a request with an error instead of handling it. Return nil to handle it normally.

type FaultAnswer

type FaultAnswer struct {
	Status     int
	Problem    protov2.ProblemType // "" for a plain (non-problem) answer
	RetryAfter string
	// Drop closes the connection without answering (a network error, the
	// sender cannot tell whether the request was processed). With Process
	// the request is handled first: "the response was lost".
	Drop    bool
	Process bool
}

FaultAnswer is an injected answer.

type HeartbeatBuild added in v0.10.0

type HeartbeatBuild struct {
	SDK *struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	} `json:"sdk"`
	Sensor *struct {
		Name      string `json:"name"`
		Version   string `json:"version"`
		Commit    string `json:"commit"`
		BuildTime string `json:"build_time"`
	} `json:"sensor"`
}

HeartbeatBuild is what a heartbeat says about the SDK and the sensor binary ("sdk" and "sensor"); nil where the heartbeat did not carry it.

type Release added in v0.10.0

type Release struct {
	CommandID, Reason string
}

Release is one release a sensor sent (api RFC-030).

type Request

type Request struct {
	Method, Path string
	Header       http.Header
	Body         []byte // as sent (compressed when Content-Encoding is set)
	Status       int    // what the fake answered
}

Request is one request the fake received.

type SensorEndpoint added in v0.16.0

type SensorEndpoint struct {
	URL    string
	APIKey string
}

SensorEndpoint is the platform RunSensorSuite points the sensor under test at: the fake's base URL and the sensor key it accepts.

type StartSensor added in v0.16.0

type StartSensor func(t *testing.T, p SensorEndpoint) (stop func())

StartSensor starts the sensor under test against p (in process, or as a child process with API_URL / API_KEY set) and returns a function that stops it and waits until it has exited. The fake listens on 127.0.0.1: an SDK-based sensor needs httpsec.AllowLoopback (or OPENCTEM_SDK_HTTPSEC_ALLOW_LOOPBACK=1 for a child process) to reach it.

type StoredReport

type StoredReport struct {
	ReportID  string
	CommandID string
	Segments  map[int]segment
	Committed bool
	Status    protov2.Status
}

StoredReport is a report the fake accepted.

type SuiteOptions added in v0.16.0

type SuiteOptions struct {
	// ScanType and ScanPayload are a command the sensor can run to
	// completion against the fake ("" ScanType is "scan"). With no
	// ScanPayload the command checks are skipped.
	ScanType    string
	ScanPayload json.RawMessage
	// Timeout bounds every wait. Zero is 60s.
	Timeout time.Duration
}

SuiteOptions configures RunSensorSuite.

type T added in v0.18.0

type T interface {
	Helper()
	Logf(format string, args ...any)
	Errorf(format string, args ...any)
	Fatalf(format string, args ...any)
}

T is what the tool suite reports to: *testing.T, or the conformance command's printer.

type ToolSuiteOptions added in v0.18.0

type ToolSuiteOptions struct {
	// Timeout bounds each check (zero: 30s).
	Timeout time.Duration
	// Credentials are what an operator would store for the tool; only
	// those the manifest declares are delivered.
	Credentials map[string]string
	// Update rewrites each fixture's expect file with what the tool
	// produced (also OPENCTEM_UPDATE_GOLDEN=1).
	Update bool
}

ToolSuiteOptions configure RunToolSuite.

Jump to

Keyboard shortcuts

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