client

package
v0.10.0 Latest Latest
Warning

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

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

Documentation

Overview

Package client provides the OpenCTEM API client.

Index

Constants

View Source
const (
	// ProtocolAuto uses v2 when the platform offers it and v1 otherwise
	// (the default).
	ProtocolAuto = "auto"
	// ProtocolV1 always uses the frozen v1 ingest routes.
	ProtocolV1 = "v1"
	// ProtocolV2 always uses v2 and fails against a platform without it.
	ProtocolV2 = "v2"
)

Results protocol selection (Config.Protocol, SENSOR_PROTOCOL).

View Source
const DefaultCommandPollLimit = 10

DefaultCommandPollLimit is how many commands GetCommands asks for.

View Source
const DefaultOutboxSyncWait = 30 * time.Second

DefaultOutboxSyncWait is OutboxConfig.SyncWait's default.

Variables

View Source
var ErrV2NoTool = errors.New("protocol v2 requires report.tool.name")

ErrV2NoTool: v2 requires the report's tool (RFC-026 §3.3 step 8).

View Source
var ErrV2Unsupported = errors.New("the platform does not offer protocol v2 results (set the protocol to auto or v1)")

ErrV2Unsupported: protocol v2 was required but the platform does not offer it.

Functions

func DefaultLegacyRetryQueueDir added in v0.8.0

func DefaultLegacyRetryQueueDir() string

DefaultLegacyRetryQueueDir is where pre-outbox SDKs kept their retry queue.

func IsAuthenticationError

func IsAuthenticationError(err error) bool

IsAuthenticationError checks if the error is a 401 authentication error.

func IsAuthorizationError

func IsAuthorizationError(err error) bool

IsAuthorizationError checks if the error is a 403 authorization error.

func IsClientError

func IsClientError(err error) bool

IsClientError checks if the error is a 4xx client error.

func IsCommandGone added in v0.9.0

func IsCommandGone(err error) bool

IsCommandGone reports whether err says the command can no longer be worked on by this sensor: another sensor claimed it, it was canceled or expired, it already finished, or it is not (or no longer) this sensor's. The poller drops such a command instead of retrying it.

func IsNotFoundError

func IsNotFoundError(err error) bool

IsNotFoundError checks if the error is a 404 not found error.

func IsRateLimitError

func IsRateLimitError(err error) bool

IsRateLimitError checks if the error is a 429 rate limit error.

func IsRetryable

func IsRetryable(err error) bool

IsRetryable checks if the error should be retried.

func IsServerError

func IsServerError(err error) bool

IsServerError checks if the error is a 5xx server error.

func ParseProtocol added in v0.8.0

func ParseProtocol(s string) (string, error)

ParseProtocol validates a protocol setting ("" is auto).

Types

type ChunkUploadResponse

type ChunkUploadResponse struct {
	ChunkID         string `json:"chunk_id"`
	ReportID        string `json:"report_id"`
	ChunkIndex      int    `json:"chunk_index"`
	Status          string `json:"status"`
	AssetsCreated   int    `json:"assets_created"`
	AssetsUpdated   int    `json:"assets_updated"`
	FindingsCreated int    `json:"findings_created"`
	FindingsUpdated int    `json:"findings_updated"`
	FindingsSkipped int    `json:"findings_skipped"`
}

ChunkUploadResponse is the response from chunk upload endpoint.

type Client

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

Client is the OpenCTEM API client. It implements the core.Pusher interface.

func New

func New(cfg *Config) *Client

New creates a new OpenCTEM API client.

func NewWithOptions

func NewWithOptions(opts ...Option) *Client

NewWithOptions creates a new client using functional options. Example:

client := client.NewWithOptions(
    client.WithBaseURL("http://localhost:8080"),
    client.WithAPIKey("xxx"),
    client.WithSensorID("agent-1"),
    client.WithTimeout(30 * time.Second),
)

func (*Client) APIKeyHint added in v0.8.0

func (c *Client) APIKeyHint() string

APIKeyHint names the client's API key in log lines without revealing it (core.APIKeyHint: at most its first 8 characters).

func (*Client) AbandonReport added in v0.8.0

func (c *Client) AbandonReport(ctx context.Context, reportID string) error

AbandonReport deletes an uncommitted segmented report.

func (*Client) AcknowledgeCommand

func (c *Client) AcknowledgeCommand(ctx context.Context, cmdID string) error

AcknowledgeCommand acknowledges receipt of a command.

func (*Client) AsChunkUploader

func (c *Client) AsChunkUploader() chunk.Uploader

AsChunkUploader returns the client as a chunk.Uploader interface. This is useful for passing to chunk.Manager.

func (*Client) BaselineDiff added in v0.4.0

func (c *Client) BaselineDiff(ctx context.Context, repository, baseBranch string, fingerprints []string) ([]string, error)

BaselineDiff returns the subset of fingerprints that are NEW relative to a PR's base/target branch (not already open there). Used to focus a PR gate / inline comments on findings the PR introduces, not pre-existing tech debt. On error or no PR context the caller should treat all findings as new (fail-open for visibility). Returns the new fingerprints.

func (*Client) CheckFingerprints

func (c *Client) CheckFingerprints(ctx context.Context, fingerprints []string) (*retry.FingerprintCheckResult, error)

CheckFingerprints checks which fingerprints already exist on the server. This is used by the retry mechanism to avoid re-uploading data that already exists. It also serves as a connectivity check before processing the retry queue. This method implements retry.FingerprintChecker interface.

func (*Client) Close

func (c *Client) Close() error

Close stops the outbox's delivery and releases its directory. Items not delivered yet stay on disk for the next process.

func (*Client) CompleteCommand

func (c *Client) CompleteCommand(ctx context.Context, cmdID string, result json.RawMessage) error

CompleteCommand marks a command as completed with optional result.

func (*Client) Deliver added in v0.8.0

func (c *Client) Deliver(ctx context.Context, d *outbox.Delivery) (any, error)

Deliver implements outbox.Deliverer: one attempt at one item, classified for the outbox (RFC-026 §3.8 retry table).

func (*Client) DisableRetryQueue deprecated

func (c *Client) DisableRetryQueue(_ context.Context) error

DisableRetryQueue stops and closes the outbox.

Deprecated: use Close.

func (*Client) EnableOutbox added in v0.8.0

func (c *Client) EnableOutbox(cfg OutboxConfig) error

EnableOutbox opens the outbox and starts delivering from it in the background until Close. Reports left in an old retry-queue directory are imported first.

func (*Client) EnableRetryQueue deprecated

func (c *Client) EnableRetryQueue(_ context.Context, cfg *RetryQueueConfig) error

EnableRetryQueue enables the durable outbox in cfg.Dir/outbox (default ~/.openctem/outbox) and imports the reports an older SDK queued in cfg.Dir.

Deprecated: use EnableOutbox.

func (*Client) EnrichFindings

func (c *Client) EnrichFindings(ctx context.Context, findings []ctis.Finding) ([]ctis.Finding, error)

EnrichFindings adds EPSS and KEV data to findings with CVE IDs.

func (*Client) FailCommand

func (c *Client) FailCommand(ctx context.Context, cmdID string, errorMsg string) error

FailCommand marks a command as failed with an error message.

func (*Client) FilterSuppressedFindings

func (c *Client) FilterSuppressedFindings(findings []ctis.Finding, rules []SuppressionRule) []ctis.Finding

FilterSuppressedFindings removes findings that match suppression rules. This is used by the security gate to exclude false positives.

func (*Client) FlushOutbox added in v0.8.0

func (c *Client) FlushOutbox(ctx context.Context) error

FlushOutbox delivers what can be delivered now and returns when nothing is left that could be sent right away (or ctx ends). A one-shot run calls it before exiting.

func (*Client) GetCommands

func (c *Client) GetCommands(ctx context.Context) (*core.GetCommandsResponse, error)

GetCommands retrieves pending commands from the server (implements core.CommandClient).

func (*Client) GetCommandsLimit added in v0.10.0

func (c *Client) GetCommandsLimit(ctx context.Context, limit int) (*core.GetCommandsResponse, error)

GetCommandsLimit retrieves at most limit pending commands (implements core.LimitedCommandClient). The CommandPoller passes its free slots, so the platform keeps the rest for sensors that can run them now. limit < 1 is 1.

func (*Client) GetEPSSScores

func (c *Client) GetEPSSScores(ctx context.Context, cveIDs []string) ([]EPSSScore, error)

GetEPSSScores fetches EPSS scores for the given CVE IDs.

func (*Client) GetKEVEntries

func (c *Client) GetKEVEntries(ctx context.Context, cveIDs []string) ([]KEVEntry, error)

GetKEVEntries fetches CISA KEV entries for the given CVE IDs.

func (*Client) GetReportStatus added in v0.8.0

func (c *Client) GetReportStatus(ctx context.Context, reportID string) (*protov2.Status, error)

GetReportStatus reads a report's status resource.

func (*Client) GetRetryQueueStats deprecated

func (c *Client) GetRetryQueueStats(_ context.Context) (*retry.QueueStats, error)

GetRetryQueueStats returns the outbox's state in the old shape.

Deprecated: use OutboxStats.

func (*Client) GetRetryWorkerStats deprecated

func (c *Client) GetRetryWorkerStats() (*retry.WorkerStats, error)

GetRetryWorkerStats returns the outbox's delivery counters in the old shape.

Deprecated: use OutboxStats.

func (*Client) GetSuppressions

func (c *Client) GetSuppressions(ctx context.Context) ([]SuppressionRule, error)

GetSuppressions fetches the tenant's active suppression rules from the platform, for the security gate to leave out findings the platform has suppressed.

It calls the sensor route (legacyv1.PathSuppressions). An API that predates that route answers 404; the client then tries the user route (legacyv1.PathSuppressionsUser) once, which only accepts a user token.

A failure is returned, never swallowed: the caller decides whether a gate may run without suppressions, and should say so where the operator sees it.

func (*Client) Hello added in v0.8.0

func (c *Client) Hello(ctx context.Context) (*protov2.Hello, error)

Hello fetches GET /api/v2/sensor/hello.

func (*Client) Outbox added in v0.8.0

func (c *Client) Outbox() *outbox.Outbox

Outbox returns the client's outbox, or nil when it is not enabled.

func (*Client) OutboxStats added in v0.8.0

func (c *Client) OutboxStats() (outbox.Stats, bool)

OutboxStats returns the outbox's state; ok is false without an outbox.

func (*Client) PollCommands

func (c *Client) PollCommands(ctx context.Context, limit int) ([]Command, error)

PollCommands retrieves pending commands for this sensor.

func (*Client) ProcessRetryQueueNow deprecated

func (c *Client) ProcessRetryQueueNow(ctx context.Context) error

ProcessRetryQueueNow delivers what the outbox can deliver now.

Deprecated: use FlushOutbox.

func (*Client) ProtocolFeatures added in v0.9.0

func (c *Client) ProtocolFeatures() []string

ProtocolFeatures returns the features this client uses protocol v2 for, as last negotiated (nil: protocol v1 for everything, or not asked yet).

func (*Client) PushAssets

func (c *Client) PushAssets(ctx context.Context, report *ctis.Report) (*core.PushResult, error)

PushAssets sends a report's assets (its findings are dropped). See PushFindings for the outbox behavior.

func (*Client) PushExposures

func (c *Client) PushExposures(ctx context.Context, events []ExposureEvent) (*PushExposuresResult, error)

PushExposures sends exposure events to OpenCTEM.

func (*Client) PushFindings

func (c *Client) PushFindings(ctx context.Context, report *ctis.Report) (*core.PushResult, error)

PushFindings sends a report's findings (and assets) to OpenCTEM, over protocol v2 when the platform offers it (Config.Protocol) and v1 otherwise.

With the outbox enabled (EnableOutbox) the report is first written to disk and this call waits up to OutboxConfig.SyncWait for its delivery: it returns the result when the platform accepted it, a *RefusedError when the platform refused it for good (dead letter), and Queued=true when the platform could not be reached yet; the outbox delivers it later, across restarts. Without the outbox the report is sent once (with the client's own retries) and an error means it was not delivered.

A context from core.WithCommandID binds the results to that command.

func (*Client) PushReport deprecated

func (c *Client) PushReport(ctx context.Context, report *ctis.Report) error

PushReport pushes a report directly (no outbox), findings and assets alike. It implemented the old retry worker's interface.

Deprecated: use PushFindings.

func (*Client) PushResultsV2 added in v0.8.0

func (c *Client) PushResultsV2(ctx context.Context, report *ctis.Report, opts *V2PushOptions) (*protov2.Status, error)

PushResultsV2 sends a report over protocol v2 and returns its status resource. It makes one pass: transient failures (network, 429, 5xx) are returned as errors for the caller to retry with the same report id and progress (the outbox does). It handles the protocol's own recoveries: a 413 re-splits into smaller segments, an expired report or a gone command restarts under a new report id (unbound), and a conflicting replay of a report the server already has is resolved by reading its status.

func (*Client) ReleaseCommand added in v0.10.0

func (c *Client) ReleaseCommand(ctx context.Context, cmdID, reason string) error

ReleaseCommand hands a claimed command back to the platform, which returns it to pending (unpinned) so any sensor can run it at once (implements core.ReleasingCommandClient). On a platform without the release transition (protocol v1, or a v2 platform from before it) the command is failed with "released: <reason>" instead, so it does not wait for a timeout.

func (*Client) ReportCommandProgress

func (c *Client) ReportCommandProgress(ctx context.Context, cmdID string, progress int, message string) error

ReportCommandProgress reports progress of command execution.

func (*Client) ReportCommandResult

func (c *Client) ReportCommandResult(ctx context.Context, cmdID string, result *core.CommandResult) error

ReportCommandResult reports the result of command execution (implements core.CommandClient). With the outbox enabled the result is stored and delivered after every report of the same command was accepted (or refused, which turns a "completed" result into "failed"); it returns once the result was delivered, or after OutboxConfig.SyncWait (or at once while the platform is unreachable) with the result safe on disk.

Waiting for the delivery matters for dispatch (api RFC-030): the platform counts the command as held by this sensor until its complete arrives, so a poller that freed the slot and polled first was refused (no free capacity) or, before the platform knew the sensor's capacity, handed one command too many.

func (*Client) ResultsProtocol added in v0.8.0

func (c *Client) ResultsProtocol(ctx context.Context) (string, error)

ResultsProtocol returns the protocol the client uses for results now ("v1" or "v2"), asking the platform when the decision is stale (auto).

func (*Client) SendHeartbeat

func (c *Client) SendHeartbeat(ctx context.Context, status *core.SensorStatus) error

SendHeartbeat sends a heartbeat to OpenCTEM. It does not announce the heartbeat doorbell and ignores the response body: a caller that acts on the platform's hints uses SendHeartbeatWithHints instead.

func (*Client) SendHeartbeatWithHints added in v0.7.1

func (c *Client) SendHeartbeatWithHints(ctx context.Context, status *core.SensorStatus) (*core.HeartbeatHints, error)

SendHeartbeatWithHints sends a heartbeat that announces the doorbell (X-OpenCTEM-Sensor-Features: doorbell) and returns the hints the platform answered with. Against a server without the doorbell the hints have Present=false. Announcing the feature is a promise to act on it: a disabled sensor is then answered 200 with the pause action instead of 401. On protocol v2 the doorbell is always on.

func (*Client) SetAPIKey added in v0.5.0

func (c *Client) SetAPIKey(key string)

SetAPIKey atomically replaces the API key used by subsequent requests. Safe to call concurrently with in-flight pushes (sensor key auto-renewal).

func (*Client) SetVerbose

func (c *Client) SetVerbose(v bool)

SetVerbose sets verbose mode.

func (*Client) StartCommand

func (c *Client) StartCommand(ctx context.Context, cmdID string) error

StartCommand marks a command as started.

func (*Client) StartRetryWorker deprecated

func (c *Client) StartRetryWorker(_ context.Context) error

StartRetryWorker is a no-op: the outbox delivers in the background from EnableOutbox on. It fails when no outbox is enabled.

Deprecated: use EnableOutbox.

func (*Client) StopRetryWorker deprecated

func (c *Client) StopRetryWorker(_ context.Context) error

StopRetryWorker is a no-op; Close stops the outbox.

Deprecated: use Close.

func (*Client) TestConnection

func (c *Client) TestConnection(ctx context.Context) error

TestConnection tests the API connection.

func (*Client) UploadChunk

func (c *Client) UploadChunk(ctx context.Context, data *chunk.ChunkData) error

UploadChunk uploads a single chunk of a large report. This implements the chunk.Uploader interface.

type Command

type Command struct {
	ID             string          `json:"id"`
	TenantID       string          `json:"tenant_id,omitempty"`
	SourceID       string          `json:"source_id,omitempty"`
	Type           string          `json:"type"`
	Priority       string          `json:"priority"`
	Payload        json.RawMessage `json:"payload,omitempty"`
	Status         string          `json:"status"`
	ErrorMessage   string          `json:"error_message,omitempty"`
	CreatedAt      time.Time       `json:"created_at"`
	ExpiresAt      *time.Time      `json:"expires_at,omitempty"`
	AcknowledgedAt *time.Time      `json:"acknowledged_at,omitempty"`
	StartedAt      *time.Time      `json:"started_at,omitempty"`
	CompletedAt    *time.Time      `json:"completed_at,omitempty"`
	Result         json.RawMessage `json:"result,omitempty"`
}

Command represents a command from the server.

type Config

type Config struct {
	BaseURL    string        `yaml:"base_url" json:"base_url"`
	APIKey     string        `yaml:"api_key" json:"api_key"`
	SensorID   string        `yaml:"sensor_id" json:"sensor_id"` // Registered sensor ID for audit trail; the pre-rename key agent_id is still read (config_compat.go)
	Timeout    time.Duration `yaml:"timeout" json:"timeout"`
	MaxRetries int           `yaml:"max_retries" json:"max_retries"`
	RetryDelay time.Duration `yaml:"retry_delay" json:"retry_delay"`
	Verbose    bool          `yaml:"verbose" json:"verbose"`

	// UserAgent is this client's product token, "name/version" (for example
	// "openctemio-sensor/0.3.1"), sent before the SDK's own token:
	// "openctemio-sensor/0.3.1 openctem-sdk-go/0.7.4". Empty: the process-wide
	// product set with useragent.SetProduct, if any.
	UserAgent string `yaml:"user_agent" json:"user_agent"`

	// Compression configuration
	EnableCompression bool   `yaml:"enable_compression" json:"enable_compression"` // Enable request compression (default: true)
	CompressionAlgo   string `yaml:"compression_algo" json:"compression_algo"`     // "zstd" or "gzip" (default: "zstd")
	CompressionLevel  int    `yaml:"compression_level" json:"compression_level"`   // 1-9 (default: 3)

	// Protocol selects the results protocol: "auto" (default: v2 when the
	// platform offers it, else v1), "v1" or "v2".
	Protocol string `yaml:"protocol" json:"protocol"`

	// OutboxDir enables the durable outbox (see EnableOutbox) in that
	// directory. New logs, and leaves the outbox off, when it cannot be
	// opened; call EnableOutbox yourself to handle the error.
	OutboxDir string `yaml:"outbox_dir" json:"outbox_dir"`
	// OutboxMaxBytes and OutboxMaxAge override the outbox caps.
	OutboxMaxBytes int64         `yaml:"outbox_max_bytes" json:"outbox_max_bytes"`
	OutboxMaxAge   time.Duration `yaml:"outbox_max_age" json:"outbox_max_age"`

	// Deprecated: the retry queue is replaced by the outbox. EnableRetryQueue
	// enables the outbox (in OutboxDir, else RetryQueueDir/outbox, else
	// ~/.openctem/outbox) and imports the reports an older SDK left in
	// RetryQueueDir. RetryInterval and RetryMaxAttempts are ignored: the
	// outbox retries with back-off until RetryTTL (OutboxMaxAge) evicts.
	EnableRetryQueue bool          `yaml:"enable_retry_queue" json:"enable_retry_queue"`
	RetryQueueDir    string        `yaml:"retry_queue_dir" json:"retry_queue_dir"`
	RetryInterval    time.Duration `yaml:"retry_interval" json:"retry_interval"`
	RetryMaxAttempts int           `yaml:"retry_max_attempts" json:"retry_max_attempts"`
	RetryTTL         time.Duration `yaml:"retry_ttl" json:"retry_ttl"`
}

Config holds client configuration.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns default client config.

func (*Config) UnmarshalJSON added in v0.7.0

func (c *Config) UnmarshalJSON(data []byte) error

UnmarshalJSON reads Config, accepting the pre-rename key "agent_id" for SensorID so configuration files written before the agent -> sensor rename keep working (see legacyv1.MergeSensorID).

func (*Config) UnmarshalYAML added in v0.7.0

func (c *Config) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML does the same for YAML. It uses the function-argument form both gopkg.in/yaml.v2 and gopkg.in/yaml.v3 accept, so the SDK does not depend on a YAML library.

type EPSSScore

type EPSSScore struct {
	CVEID      string    `json:"cve_id"`
	Score      float64   `json:"score"`      // 0.0 to 1.0
	Percentile float64   `json:"percentile"` // 0.0 to 100.0
	Date       time.Time `json:"date"`
}

EPSSScore represents an EPSS score for a CVE.

type ExposureEvent

type ExposureEvent struct {
	// Event type: new_asset, asset_removed, exposure_detected, exposure_resolved
	Type string `json:"type"`

	// Asset identifier
	AssetID   string `json:"asset_id,omitempty"`
	AssetType string `json:"asset_type,omitempty"`
	AssetName string `json:"asset_name,omitempty"`

	// Exposure details
	ExposureType string `json:"exposure_type,omitempty"` // port_open, service_exposed, etc.
	Protocol     string `json:"protocol,omitempty"`
	Port         int    `json:"port,omitempty"`
	Service      string `json:"service,omitempty"`

	// Detection info
	DetectedAt  time.Time `json:"detected_at"`
	DetectedBy  string    `json:"detected_by,omitempty"` // scan source
	Severity    string    `json:"severity,omitempty"`
	Description string    `json:"description,omitempty"`

	// Resolution
	ResolvedAt *time.Time `json:"resolved_at,omitempty"`

	// Metadata
	Tags       []string       `json:"tags,omitempty"`
	Properties map[string]any `json:"properties,omitempty"`
}

ExposureEvent represents an attack surface change event.

type HTTPError

type HTTPError struct {
	StatusCode int    `json:"status_code"`
	Body       string `json:"body"`
	RequestID  string `json:"request_id,omitempty"`
	// RetryAfter is the server's Retry-After (429/503), 0 when absent.
	RetryAfter time.Duration `json:"retry_after,omitempty"`
}

HTTPError represents an HTTP error response.

func IsHTTPError

func IsHTTPError(err error) (*HTTPError, bool)

IsHTTPError checks if err is an HTTPError and returns it.

func (*HTTPError) Error

func (e *HTTPError) Error() string

func (*HTTPError) HTTPStatusCode added in v0.8.0

func (e *HTTPError) HTTPStatusCode() int

HTTPStatusCode returns the response status. pkg/core uses it to tell a rejected API key (401/403) from other failures without importing this package.

type HeartbeatOutbox added in v0.8.0

type HeartbeatOutbox struct {
	PendingCount     int   `json:"pending_count"`
	PendingBytes     int64 `json:"pending_bytes"`
	OldestAgeSeconds int64 `json:"oldest_age_seconds"`
	DeadLetterCount  int   `json:"dead_letter_count"`
	EvictedCount     int64 `json:"evicted_count"`
}

HeartbeatOutbox is the outbox state a heartbeat reports (additive to the v1 heartbeat; servers that do not know it ignore it).

type HeartbeatRequest

type HeartbeatRequest struct {
	Name       string           `json:"name,omitempty"`
	Status     core.SensorState `json:"status"`
	Version    string           `json:"version,omitempty"`
	Hostname   string           `json:"hostname,omitempty"`
	Message    string           `json:"message,omitempty"`
	Scanners   []string         `json:"scanners,omitempty"`
	Collectors []string         `json:"collectors,omitempty"`
	Uptime     int64            `json:"uptime_seconds,omitempty"`
	TotalScans int64            `json:"total_scans,omitempty"`
	Errors     int64            `json:"errors,omitempty"`

	// System Metrics
	CPUPercent    float64 `json:"cpu_percent,omitempty"`
	MemoryPercent float64 `json:"memory_percent,omitempty"`
	ActiveJobs    int     `json:"active_jobs,omitempty"`
	Region        string  `json:"region,omitempty"`
	// ActiveJobsReported sends active_jobs even when it is 0 (the sensor
	// measured it). Without it a 0 is left out, as before.
	ActiveJobsReported bool `json:"-"`

	// Outbox is the durable outbox's state (absent without an outbox).
	Outbox *HeartbeatOutbox `json:"outbox,omitempty"`

	// The sensor-reported capabilities (core.CapabilityReport): absent when
	// the sensor reports nothing, and an empty list when it reports none.
	Tools             []core.ToolInfo `json:"tools,omitzero"`
	Capabilities      []string        `json:"capabilities,omitzero"`
	MaxConcurrentJobs int             `json:"max_concurrent_jobs,omitempty"`
	OS                string          `json:"os,omitempty"`
	Arch              string          `json:"arch,omitempty"`

	// The sensor's work now (core.SensorStatus; api RFC-030): absent when
	// not reported.
	Resources       *resource.HostResources `json:"resources,omitempty"`
	Capacity        *resource.Capacity      `json:"capacity,omitempty"`
	Queue           *core.QueueStats        `json:"queue,omitempty"`
	RunningCommands []string                `json:"running,omitzero"`

	// SDK and Sensor name the SDK and the sensor binary. Every heartbeat
	// sends both: what the status carries, else the SDK linked into this
	// binary and a sensor block built from the status's version.
	SDK    *core.SDKInfo     `json:"sdk,omitempty"`
	Sensor *core.SensorBuild `json:"sensor,omitempty"`
}

HeartbeatRequest is the heartbeat payload.

func (HeartbeatRequest) MarshalJSON added in v0.10.0

func (r HeartbeatRequest) MarshalJSON() ([]byte, error)

MarshalJSON encodes the heartbeat. active_jobs is sent even when it is 0 if ActiveJobsReported is set: an idle sensor then reports "0 running", which the platform's dispatch can trust, instead of leaving the field out.

type IngestResponse

type IngestResponse struct {
	ScanID          string   `json:"scan_id"`
	AssetsCreated   int      `json:"assets_created"`
	AssetsUpdated   int      `json:"assets_updated"`
	FindingsCreated int      `json:"findings_created"`
	FindingsUpdated int      `json:"findings_updated"`
	FindingsSkipped int      `json:"findings_skipped"`
	Errors          []string `json:"errors,omitempty"`
}

IngestResponse is the response from ingest endpoints.

type KEVEntry

type KEVEntry struct {
	CVEID                      string    `json:"cve_id"`
	VendorProject              string    `json:"vendor_project"`
	Product                    string    `json:"product"`
	VulnerabilityName          string    `json:"vulnerability_name"`
	DateAdded                  time.Time `json:"date_added"`
	ShortDescription           string    `json:"short_description"`
	RequiredAction             string    `json:"required_action"`
	DueDate                    time.Time `json:"due_date"`
	KnownRansomwareCampaignUse string    `json:"known_ransomware_campaign_use"`
}

KEVEntry represents a CISA Known Exploited Vulnerabilities entry.

type Option

type Option func(*Client)

Option is a function that configures the client.

func WithAPIKey

func WithAPIKey(key string) Option

WithAPIKey sets the API key.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL sets the API base URL.

func WithCompression

func WithCompression(algorithm string, level int) Option

WithCompression enables request compression with the specified algorithm. Supported algorithms: "zstd" (recommended), "gzip"

func WithProtocol added in v0.8.0

func WithProtocol(p string) Option

WithProtocol sets the results protocol: ProtocolAuto (default), ProtocolV1 or ProtocolV2. An unknown value keeps auto.

func WithRetry

func WithRetry(maxRetries int, retryDelay time.Duration) Option

WithRetry sets retry configuration.

func WithSensorID added in v0.7.0

func WithSensorID(id string) Option

WithSensorID sets the sensor ID for tracking which sensor is pushing data.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the HTTP timeout.

func WithUserAgent added in v0.8.0

func WithUserAgent(product string) Option

WithUserAgent sets this client's product token, "name/version", sent before the SDK's own token in the User-Agent (see Config.UserAgent).

func WithVerbose

func WithVerbose(v bool) Option

WithVerbose enables verbose logging.

func WithoutCompression

func WithoutCompression() Option

WithoutCompression disables request compression.

type OutboxConfig added in v0.8.0

type OutboxConfig struct {
	// Dir is the outbox directory (required). The sensor's default is
	// /var/lib/openctem/outbox.
	Dir string
	// KeyFile is the encryption key file (default <Dir>/outbox.key).
	KeyFile string
	// MaxBytes caps the bytes on disk (default 1 GiB, and never more than
	// half of the space the outbox could use).
	MaxBytes int64
	// MaxAge evicts items older than this (default 7 days).
	MaxAge time.Duration
	// Concurrency is 1 (default) or 2.
	Concurrency int
	// SyncWait is how long PushFindings/PushAssets wait for the first
	// delivery before answering "queued" (default 30s; negative: do not
	// wait).
	SyncWait time.Duration
	// LegacyRetryQueueDir is a pre-outbox retry-queue directory whose
	// reports are imported once (default ~/.openctem/retry-queue; "-" skips
	// the import).
	LegacyRetryQueueDir string
	// Logf receives the outbox's log lines (default: stderr with a
	// timestamp).
	Logf func(format string, args ...any)
	// OnEvict is told when the caps dropped results (turn it into an alert).
	OnEvict func(evicted []outbox.Meta, reason string)
}

OutboxConfig enables the durable outbox (pkg/outbox) on a client. With it, every report and command result is written to disk before the first send and deleted only after the platform acknowledged it.

type PushExposuresResult

type PushExposuresResult struct {
	EventsCreated int `json:"events_created"`
	EventsUpdated int `json:"events_updated"`
	EventsSkipped int `json:"events_skipped"`
}

PushExposuresResult is the result of pushing exposure events.

type RefusedError added in v0.8.0

type RefusedError struct {
	DeadLetter outbox.DeadLetter
}

RefusedError says the platform refused a report for good; it is in the outbox's dead-letter folder.

func (*RefusedError) Error added in v0.8.0

func (e *RefusedError) Error() string

type RetryQueueConfig deprecated

type RetryQueueConfig struct {
	Dir         string
	MaxSize     int
	Interval    time.Duration
	BatchSize   int
	MaxAttempts int
	TTL         time.Duration
	Backoff     *retry.BackoffConfig
	AutoStart   bool
}

RetryQueueConfig configures the retry queue.

Deprecated: use EnableOutbox with an OutboxConfig. Dir is where the old queue's files are imported from (the outbox lives in Dir/outbox); TTL becomes the outbox's MaxAge; the other fields are ignored.

func DefaultRetryQueueConfig deprecated

func DefaultRetryQueueConfig() *RetryQueueConfig

DefaultRetryQueueConfig returns a configuration with default values.

Deprecated: use EnableOutbox.

type SuppressionRule

type SuppressionRule struct {
	RuleID      string  `json:"rule_id,omitempty"`
	ToolName    string  `json:"tool_name,omitempty"`
	PathPattern string  `json:"path_pattern,omitempty"`
	AssetID     *string `json:"asset_id,omitempty"`
	ExpiresAt   *string `json:"expires_at,omitempty"`
}

SuppressionRule represents a platform-controlled suppression rule.

type V2Error added in v0.8.0

type V2Error struct {
	Status     int
	Problem    *protov2.Problem
	RetryAfter time.Duration
	// Body is the raw answer when it was not a problem document (truncated).
	Body      string
	RequestID string
}

V2Error is a non-2xx answer of a v2 resource.

func (*V2Error) Error added in v0.8.0

func (e *V2Error) Error() string

func (*V2Error) HTTPStatusCode added in v0.8.0

func (e *V2Error) HTTPStatusCode() int

HTTPStatusCode lets core.AuthFailureStatus classify the error.

func (*V2Error) ProblemName added in v0.8.0

func (e *V2Error) ProblemName() protov2.ProblemType

ProblemName is the problem type's short name, or "".

func (*V2Error) Transient added in v0.8.0

func (e *V2Error) Transient() bool

Transient reports whether retrying later can succeed (RFC-026 §3.8: 429, 500, 502, 503, 504).

type V2Progress added in v0.8.0

type V2Progress struct {
	// ReportID in use (it changes only when the report had to be restarted:
	// expired, or re-split after a 413).
	ReportID string `json:"report_id"`
	// Unbound: the command is gone; the report is sent unsolicited.
	Unbound bool `json:"unbound,omitempty"`
	// SegFindings and SegBytes cap the segments after a 413 (0: the
	// server's limits). Each 413 halves the segment that was refused.
	SegFindings int `json:"seg_findings,omitempty"`
	SegBytes    int `json:"seg_bytes,omitempty"`
	// Split is true once the report was split after a 413: it is then sent
	// as segments plus a commit even when one segment remains.
	Split bool `json:"split,omitempty"`
	// Segments is the planned segment count (0: not planned yet).
	Segments int `json:"segments,omitempty"`
	// Acked maps a segment number to the Content-Digest the server
	// acknowledged for it.
	Acked map[int]string `json:"acked,omitempty"`
	// Restarts counts report-id restarts (bounded).
	Restarts int `json:"restarts,omitempty"`
	// contains filtered or unexported fields
}

V2Progress is the resumable state of one v2 report delivery. The outbox persists it, so a restart neither resends acknowledged segments nor loses the report id.

type V2PushOptions added in v0.8.0

type V2PushOptions struct {
	// ReportID is the stable report id (lower-case UUID). Empty: a new
	// UUIDv7. Retries MUST reuse it.
	ReportID string
	// CommandID binds the report to a command this sensor claimed ("" for
	// unsolicited results).
	CommandID string
	// Progress resumes an earlier attempt (nil: start).
	Progress *V2Progress
	// OnProgress persists progress after every acknowledged segment and
	// every restart. An error stops the push.
	OnProgress func(V2Progress) error
	// Logf receives restarts and fall-backs. Nil: silent.
	Logf func(format string, args ...any)
	// MaxFindingsPerSegment lowers the server's per-segment finding limit
	// (0: the server's). For constrained links and conformance tests.
	MaxFindingsPerSegment int
}

V2PushOptions configures PushResultsV2.

Jump to

Keyboard shortcuts

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