pack

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 23, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package pack is the SDK for building simrun simulation packs — standalone binaries that simrun invokes over a JSON stdin/stdout protocol to detonate attacks and report results.

Package pack provides the SDK for building simulation packs.

Index

Constants

View Source
const (
	LogLevelDebug = "debug"
	LogLevelInfo  = "info"
	LogLevelWarn  = "warn"
	LogLevelError = "error"
)

Log levels.

View Source
const (
	// SSHLoggingEnabledEnvVar enables SSH command output logging when set to "true".
	SSHLoggingEnabledEnvVar = "SR_SSH_LOGGING_ENABLED"
	// SSHLogDirEnvVar specifies the directory for SSH log files.
	SSHLogDirEnvVar = "SR_SSH_LOG_DIR"
)
View Source
const (
	PackParamTypeString          = "string"
	PackParamTypeBoolean         = "boolean"
	PackParamTypeObjectStringMap = "object_string_map"
)

PackParam Type constants.

View Source
const (
	StatusSuccess = "success"
	StatusError   = "error"
)

Status values for results.

View Source
const (
	ErrCodePermissionDenied = "PERMISSION_DENIED"
	ErrCodeResourceNotFound = "RESOURCE_NOT_FOUND"
	ErrCodeTimeout          = "TIMEOUT"
	ErrCodeInvalidParams    = "INVALID_PARAMS"
	ErrCodeInternalError    = "INTERNAL_ERROR"
)

Standard error codes.

View Source
const DefaultAWSRegion = "us-east-1"

DefaultAWSRegion is the built-in default for the aws_region pack parameter. The terraform AWS provider is rewritten to use var.aws_region (see rewriteAWSRegion); the detonator pins the detonation's AWS_REGION to the same value so resources are acted on in the region terraform created them in.

Variables

This section is empty.

Functions

func CleanupLogger

func CleanupLogger(input CleanupInput) *logrus.Entry

CleanupLogger returns a structured logger for cleanup operations.

func ExecutionIDFromContext

func ExecutionIDFromContext(ctx context.Context) string

ExecutionIDFromContext retrieves the execution ID from context. Returns empty string if not found.

func GetInt

func GetInt(m map[string]any, key string) (int, bool)

GetInt safely extracts an integer value from a map[string]any. Handles both int and float64 (common when unmarshaling JSON).

func GetString

func GetString(m map[string]any, key string) (string, bool)

GetString safely extracts a string value from a map[string]any.

func ListSimulations

func ListSimulations() []string

ListSimulations returns the IDs of all registered simulations.

func Logger

func Logger(input DetonateInput) *logrus.Entry

Logger returns a structured logger for detonate operations.

func Register

func Register(s Simulation)

Register registers a simulation with the SDK. Call this from your simulation's init() function. The ID should be a lean slug (e.g., "ec2-bitcoin-mining"). The SDK combines it with Scope to form the simulation ID (scope.slug, e.g., "aws.ec2-bitcoin-mining") used in the manifest and wire protocol.

func RegisterPackParams

func RegisterPackParams(params ...PackParam)

RegisterPackParams declares one or more custom pack-level parameters. Pack authors call this from main() near pack.SetPackInfo. The function validates each PackParam synchronously and panics on author bugs (reserved-name collision, duplicate name, default-vs-type mismatch, enum-on-non-string).

func RegisterTemplate

func RegisterTemplate(t Template)

RegisterTemplate registers an injection template with the SDK. Call this from your template package's init() function. The ID should be a lean slug (e.g., "add-group-member"). The SDK combines it with Scope to form the template ID (scope.slug, e.g., "okta.add-group-member") used in the manifest.

func Run

func Run()

Run is the main entrypoint for a pack. Call this from main(). It parses CLI arguments and dispatches to the appropriate handler.

func SetPackInfo

func SetPackInfo(name, version, minSimrun string)

SetPackInfo sets the pack metadata. Call this before Run().

func UserAgent

func UserAgent(executionID string) string

UserAgent returns the formatted User-Agent string for the given execution ID. Format: "simrun/<version> (<execution_id>)" Returns empty string if executionID is empty.

func Wait

func Wait(ctx context.Context, duration time.Duration) error

Wait sleeps for the specified duration, respecting context cancellation.

func WaitFor

func WaitFor(ctx context.Context, interval, timeout time.Duration, condition func() bool) error

WaitFor polls the condition function until it returns true or the timeout is reached.

func WithExecutionID

func WithExecutionID(ctx context.Context, executionID string) context.Context

WithExecutionID returns a context with the execution ID stored.

Types

type CleanupFunc

type CleanupFunc func(ctx context.Context, input CleanupInput) error

CleanupFunc is the signature for a simulation's Cleanup function.

type CleanupInput

type CleanupInput struct {
	Simulation       string         `json:"simulation"`
	ExecutionID      string         `json:"execution_id"`
	Params           map[string]any `json:"params"`
	DetonationResult *Result        `json:"detonation_result,omitempty"`
}

CleanupInput is the wire format for the cleanup command input. The Simulation field is used by the SDK for routing and can be ignored by simulation handlers.

type DetonateFunc

type DetonateFunc func(ctx context.Context, input DetonateInput) (*Result, error)

DetonateFunc is the signature for a simulation's Detonate function.

type DetonateInput

type DetonateInput struct {
	Simulation       string            `json:"simulation"`
	ExecutionID      string            `json:"execution_id"`
	Params           map[string]any    `json:"params"`
	TerraformOutputs map[string]string `json:"terraform_outputs"`
}

DetonateInput is the wire format for the detonate command input. The Simulation field is used by the SDK for routing and can be ignored by simulation handlers.

type Error

type Error struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

Error represents a simulation execution error.

type LogLine

type LogLine struct {
	Level       string         `json:"level"`
	Msg         string         `json:"msg"`
	Simulation  string         `json:"simulation,omitempty"`
	ExecutionID string         `json:"execution_id,omitempty"`
	Pack        string         `json:"pack,omitempty"`
	PackVersion string         `json:"pack_version,omitempty"`
	Timestamp   string         `json:"ts"`
	Extra       map[string]any `json:"-"` // Additional fields not in the struct
}

LogLine represents a JSON log line written to stderr by a pack.

func (*LogLine) UnmarshalJSON

func (l *LogLine) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom unmarshaling to capture extra fields.

type MITREMapping

type MITREMapping struct {
	Tactics    []string `json:"tactics"`    // MITRE tactic IDs (e.g., ["TA0040"])
	Techniques []string `json:"techniques"` // MITRE technique IDs (e.g., ["T1496"])
}

MITREMapping represents MITRE ATT&CK framework mappings for a simulation.

type ManifestInput

type ManifestInput struct {
	Parameters map[string]any `json:"parameters,omitempty"`
}

ManifestInput is the input to the manifest command, read from stdin. Parameters are optional key-value configuration provided by simrun. The "default_tags" key is treated specially: its value (a string map) is injected as default tags/labels into all simulations' Terraform.

type ManifestResponse

type ManifestResponse struct {
	Pack         PackInfo             `json:"pack"`
	Simulations  []SimulationManifest `json:"simulations"`
	Templates    []TemplateManifest   `json:"templates,omitempty"`
	ParamsSchema json.RawMessage      `json:"params_schema,omitempty"`
}

ManifestResponse is the response from the manifest command.

type PackInfo

type PackInfo struct {
	Name             string `json:"name"`
	Version          string `json:"version"`
	MinSimrunVersion string `json:"min_simrun_version"`
}

PackInfo contains pack metadata.

type PackParam

type PackParam struct {
	// Name is the parameter key. Must be unique across custom params and must
	// not collide with a reserved built-in name.
	Name string
	// Type is one of "string", "boolean", "object_string_map".
	Type string
	// Description is human-readable help text shown in the UI.
	Description string
	// Default is the default value used when the operator does not set the
	// param. Must match Type: string for "string", bool for "boolean",
	// map[string]string for "object_string_map".
	Default any
	// Required marks the param as mandatory. Backend validation rejects a
	// PUT /api/packs/{name}/parameters that omits a required param.
	Required bool
	// Enum, when non-empty, restricts a "string"-typed param's allowed values.
	// Invalid on any non-string type.
	Enum []string
}

PackParam declares a pack-level parameter that the pack exposes to operators. Authors register custom params via RegisterPackParams; the SDK ships a fixed set of built-in params (default_tags, aws_region, etc.) in addition.

type Result

type Result struct {
	Status     string         `json:"status"`
	Indicators map[string]any `json:"indicators,omitempty"`
	Error      *Error         `json:"error,omitempty"`
}

Result is the output from a Detonate or Cleanup operation.

func ErrorResult

func ErrorResult(code, message string) *Result

ErrorResult creates an error result with the given code and message.

func RequireString

func RequireString(m map[string]any, key string) (string, *Result)

RequireString extracts a required string from a map[string]any and returns an error Result if missing or empty. Useful for extracting values from input.Params.

Example:

region, errResult := pack.RequireString(input.Params, "region")
if errResult != nil {
    return errResult, nil
}

func SuccessResult

func SuccessResult(indicators map[string]any) *Result

SuccessResult creates a successful result with the given indicators.

type SSHClient

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

SSHClient represents an SSH connection for executing remote commands.

func NewSSHClient

func NewSSHClient(cfg SSHConfig, logger *logrus.Entry) (*SSHClient, error)

NewSSHClient creates an SSH client from config.

func SSHClientFromTerraform

func SSHClientFromTerraform(outputs map[string]string, logger *logrus.Entry) (*SSHClient, error)

SSHClientFromTerraform creates an SSH client from terraform outputs in one call. This combines SSHFromTerraform and NewSSHClient into a single convenience function.

func (*SSHClient) Run

func (c *SSHClient) Run(ctx context.Context, command string) (output string, exitCode int, err error)

Run executes a command on the remote host and returns output, exit code, and error. Exit code is -1 if there was a connection error before the command could run.

func (*SSHClient) RunScript

func (c *SSHClient) RunScript(ctx context.Context, script string) (output string, exitCode int, err error)

RunScript executes a multi-line script on the remote host. The script is passed via stdin to avoid escaping issues.

type SSHConfig

type SSHConfig struct {
	Host           string        // Required: IP or hostname
	Username       string        // Required: SSH username
	PrivateKeyPath string        // Path to private key file
	ConnectTimeout time.Duration // Default: 30s
}

SSHConfig configures SSH connection parameters.

func SSHFromTerraform

func SSHFromTerraform(outputs map[string]string) (SSHConfig, error)

SSHFromTerraform extracts SSH config from terraform outputs. It looks for the following standard output names:

  • attacker_vm_public_ip: Host IP address
  • attacker_vm_user: SSH username
  • attacker_vm_private_key_path: Path to private key file

type Simulation

type Simulation struct {
	// ID is the simulation slug (e.g., "ec2-bitcoin-mining").
	// The SDK generates the full qualified ID (scope.slug) at manifest time.
	ID                        string       `json:"id"`
	Name                      string       `json:"name"`
	Description               string       `json:"description"`
	MITRE                     MITREMapping `json:"mitre"`
	Scope                     string       `json:"scope"` // Required: aws, gcp, azure, generic
	IsSlow                    bool         `json:"is_slow,omitempty"`
	RequiresExternalResources bool         `json:"requires_external_resources,omitempty"`
	ParamsSchema              any          `json:"params_schema,omitempty"`
	Terraform                 string       `json:"-"` // raw HCL content, typically from //go:embed
	// RequiredOutputs lists the Terraform output names that Detonate reads
	// from DetonateInput.TerraformOutputs. The SDK validates these against
	// the embedded Terraform body at Register time and panics if any are
	// missing, so authors find sim/TF contract drift at boot rather than
	// after a real `terraform apply`. Leave nil to skip validation.
	RequiredOutputs []string     `json:"-"`
	Detonate        DetonateFunc `json:"-"`
	Cleanup         CleanupFunc  `json:"-"`
}

Simulation represents a fully registered simulation with metadata, Terraform, and handlers. Use Register() in your simulation's init() function to register simulations.

func GetSimulation

func GetSimulation(id string) (*Simulation, bool)

GetSimulation returns a registered simulation by ID (scope.slug).

type SimulationManifest

type SimulationManifest struct {
	ID                        string          `json:"id"` // Simulation ID: scope.slug (e.g., "aws.ec2-bitcoin-mining")
	Name                      string          `json:"name"`
	Description               string          `json:"description"`
	MITRE                     MITREMapping    `json:"mitre"`
	Scope                     string          `json:"scope"`
	IsSlow                    bool            `json:"is_slow,omitempty"`
	RequiresExternalResources bool            `json:"requires_external_resources,omitempty"`
	ParamsSchema              json.RawMessage `json:"params_schema,omitempty"`
	Terraform                 string          `json:"terraform,omitempty"` // base64-encoded
	HasCustomCleanup          bool            `json:"has_custom_cleanup"`
}

SimulationManifest is the manifest entry for a single simulation.

type Template

type Template struct {
	// ID is the template slug (e.g., "add-group-member").
	// The SDK generates the full qualified ID (scope.slug) at manifest time.
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Scope       string `json:"scope"` // Required: okta, aws, gcp, azure, generic
	Content     string `json:"-"`     // Raw template content, typically from //go:embed
}

Template represents an injection template with metadata.

type TemplateManifest

type TemplateManifest struct {
	ID          string            `json:"id"` // Template ID: scope.slug (e.g., "okta.add-group-member")
	Name        string            `json:"name"`
	Description string            `json:"description"`
	Scope       string            `json:"scope"`
	Content     string            `json:"content"`        // Base64-encoded template content
	Vars        map[string]string `json:"vars,omitempty"` // Variable names to default values, extracted from template
}

TemplateManifest is the manifest entry for an injection template.

Directories

Path Synopsis
Package aws provides AWS SDK helpers for simulation packs.
Package aws provides AWS SDK helpers for simulation packs.
Package azure provides Azure SDK helpers for simulation packs.
Package azure provides Azure SDK helpers for simulation packs.
Package gcp provides GCP SDK helpers for simulation packs.
Package gcp provides GCP SDK helpers for simulation packs.

Jump to

Keyboard shortcuts

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