conformance

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: GPL-3.0 Imports: 21 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 v1 routes a sensor uses, 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.

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.

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.

func (*FakePlatform) AcceptedFindings

func (f *FakePlatform) AcceptedFindings() int

AcceptedFindings counts the findings of committed v2 reports plus v1 reports.

func (*FakePlatform) Close

func (f *FakePlatform) Close()

Close stops the server.

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) 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, v1 and v2, 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, v1 and v2, 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) 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) 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.

func (*FakePlatform) V1Reports

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

V1Reports returns the reports received on the v1 ingest route.

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.

Jump to

Keyboard shortcuts

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