sync

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Feb 24, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package sync provides component synchronization and locking operations.

This package coordinates the download, verification, and installation of source-based components (collectors and tools) from registries.

Key operations:

  • Sync: download and verify components from lockfile
  • Lock: resolve versions and create/update lockfile entries

Security features:

  • Sigstore signature verification
  • Digest verification against lockfile
  • Config/lockfile alignment validation (anti-retargeting)
  • Insecure install markers for audit trail

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildGitHubRefTag

func BuildGitHubRefTag(version string) string

BuildGitHubRefTag constructs "refs/tags/version" from a version string.

func BuildGitHubRepoURL

func BuildGitHubRepoURL(owner, repo string) string

BuildGitHubRepoURL constructs "https://github.com/owner/repo" from components.

func BuildSourceURI

func BuildSourceURI(owner, repo string) string

BuildSourceURI constructs "github.com/owner/repo" from components.

func CheckInsecureMarkerAllowed

func CheckInsecureMarkerAllowed(name string, kind componenttypes.ComponentKind, binaryPath string, frozen, allowInsecure bool) error

CheckInsecureMarkerAllowed checks if executing a component with an insecure marker is allowed. Returns nil if allowed, or an error if the marker exists and execution is disallowed.

Policy:

  • In frozen mode: never allowed (security requirement)
  • In non-frozen mode: requires allowInsecure=true

func ClearInsecureMarker

func ClearInsecureMarker(installDir string)

ClearInsecureMarker removes the insecure install marker from the given directory. This is called after a secure verification to remove stale markers. Errors are ignored since the marker may not exist.

func ComputeDigest

func ComputeDigest(path string) (string, error)

ComputeDigest computes sha256 digest of a file.

func HasInsecureMarker

func HasInsecureMarker(installDir string) bool

HasInsecureMarker checks if the given install directory has the insecure marker.

func InstallPath

func InstallPath(baseDir string, kind componenttypes.ComponentKind, name, version, binaryName string) (string, error)

InstallPath returns deterministic install location for source-based components. All path components are validated to prevent path traversal attacks.

func MatchSigner

func MatchSigner(result *SigstoreResult, expected *componenttypes.LockedSigner) error

MatchSigner checks that verification result matches expected signer.

func ParseSourceURI

func ParseSourceURI(source string) (owner, repo string, err error)

ParseSourceURI parses "github.com/owner/repo" into owner and repo components. This is the lockfile format (no version). SECURITY: Error messages use %q to escape untrusted input, preventing log injection.

func ResolveBinaryPath

func ResolveBinaryPath(baseDir, collectorName string, cfg config.CollectorConfig, lf *lockfile.LockFile) (string, error)

ResolveBinaryPath resolves a collector binary path from config + lockfile. For source-based collectors, it returns deterministic install path. For external collectors, it returns the configured absolute binary path.

func ResolveRemoteBinaryPath

func ResolveRemoteBinaryPath(baseDir, remoteName string, cfg config.RemoteConfig, lf *lockfile.LockFile) (string, error)

ResolveRemoteBinaryPath resolves a remote adapter binary path from config + lockfile. For source-based remotes, it returns deterministic install path. For external remotes, it returns the configured absolute binary path. For adapter-only remotes (no source or binary), it returns empty string (use PATH discovery).

func ResolveToolBinaryPath

func ResolveToolBinaryPath(baseDir, toolName string, cfg config.ToolConfig, lf *lockfile.LockFile) (string, error)

ResolveToolBinaryPath resolves a tool binary path from config + lockfile. For source-based tools, it returns deterministic install path. For external tools, it returns the configured absolute binary path.

func SanitizeAssetName

func SanitizeAssetName(assetName string) (string, error)

SanitizeAssetName validates and extracts just the base filename from an asset name. This prevents path traversal attacks via malicious release asset names. Returns an error if the asset name is empty, contains path separators, or is unsafe.

func VerifyDigest

func VerifyDigest(path, expected string) error

VerifyDigest checks that a file matches the expected digest.

func WriteInsecureMarker

func WriteInsecureMarker(installDir string) error

WriteInsecureMarker creates the insecure install marker in the given directory. Uses safefile.WriteFilePrivate to refuse to follow symlinks (O_NOFOLLOW).

SECURITY: All errors are returned, not just symlink errors. If the marker cannot be written (disk full, permissions, etc.), the caller must not proceed with the insecure installation, otherwise the install will appear legitimate to HasInsecureMarker() checks later.

Types

type AssetInfo

type AssetInfo struct {
	// Name is the asset filename.
	Name string

	// URL is the download URL for this asset.
	URL string

	// Size is the asset size in bytes (0 if unknown).
	Size int64

	// Platform is the target platform (e.g., "linux/amd64").
	// Empty if the asset is not platform-specific.
	Platform string

	// IsSigstoreBundle indicates this is a .sigstore.json bundle.
	IsSigstoreBundle bool
}

AssetInfo contains metadata about a release asset.

type AssetNotFoundError

type AssetNotFoundError struct {
	Component string
	Platform  string
}

AssetNotFoundError indicates a binary asset was not found for a platform.

func (*AssetNotFoundError) Error

func (e *AssetNotFoundError) Error() string

type BundleNotFoundError

type BundleNotFoundError struct {
	BinaryAsset string
}

BundleNotFoundError indicates a Sigstore bundle was not found.

func (*BundleNotFoundError) Error

func (e *BundleNotFoundError) Error() string

type DefaultDigestVerifier

type DefaultDigestVerifier struct{}

DefaultDigestVerifier is the production DigestVerifier.

func (DefaultDigestVerifier) Verify

func (DefaultDigestVerifier) Verify(path, expectedDigest string) error

Verify implements DigestVerifier using VerifyDigest.

type DefaultInstaller

type DefaultInstaller struct{}

DefaultInstaller is the production Installer using filesystem operations.

func (DefaultInstaller) Exists

func (DefaultInstaller) Exists(installPath string) bool

Exists implements Installer using os.Stat.

func (DefaultInstaller) Install

func (DefaultInstaller) Install(tmpPath, installPath string) error

Install implements Installer by making the binary executable and renaming atomically.

func (DefaultInstaller) Verify

func (DefaultInstaller) Verify(installPath, expectedDigest string) error

Verify implements Installer using VerifyDigest.

type DefaultSigstoreVerifier

type DefaultSigstoreVerifier struct{}

DefaultSigstoreVerifier is the production SigstoreVerifier.

func (DefaultSigstoreVerifier) Verify

func (DefaultSigstoreVerifier) Verify(bundlePath, binaryPath string, expectedIdentity *ExpectedIdentity) (*SigstoreResult, error)

Verify implements SigstoreVerifier using VerifySigstoreBundle.

type DigestVerifier

type DigestVerifier interface {
	// Verify computes the SHA-256 digest of a file and compares it to expected.
	// Returns nil if match, error if mismatch or read failure.
	Verify(path, expectedDigest string) error
}

DigestVerifier abstracts digest verification for testability.

type ExpectedIdentity

type ExpectedIdentity = sigstore.ExpectedIdentity

ExpectedIdentity specifies the expected source identity for signature verification.

type GitHubRegistry

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

GitHubRegistry adapts github.Client to the RegistryClient interface.

func NewGitHubRegistry

func NewGitHubRegistry() *GitHubRegistry

NewGitHubRegistry creates a RegistryClient backed by GitHub.

func NewGitHubRegistryWithClient

func NewGitHubRegistryWithClient(client *github.Client) *GitHubRegistry

NewGitHubRegistryWithClient injects a github.Client for tests and alternate transports.

func (*GitHubRegistry) DownloadAsset

func (r *GitHubRegistry) DownloadAsset(ctx context.Context, url, destPath string) error

DownloadAsset downloads a release asset to the specified path.

func (*GitHubRegistry) FetchRelease

func (r *GitHubRegistry) FetchRelease(ctx context.Context, source, version string) (*ReleaseInfo, error)

FetchRelease fetches release metadata for a specific version.

func (*GitHubRegistry) FindBinaryAsset

func (r *GitHubRegistry) FindBinaryAsset(release *ReleaseInfo, componentName, goos, goarch string) (*AssetInfo, error)

FindBinaryAsset finds the binary asset for a specific platform. This is a convenience method that wraps the underlying client. Returns a copy of the asset to avoid pointer aliasing issues.

func (*GitHubRegistry) FindSigstoreBundle

func (r *GitHubRegistry) FindSigstoreBundle(release *ReleaseInfo, binaryAssetName string) (*AssetInfo, error)

FindSigstoreBundle finds the Sigstore bundle for a binary asset. Returns a copy of the asset to avoid pointer aliasing issues.

func (*GitHubRegistry) Name

func (r *GitHubRegistry) Name() string

Name returns the stable registry identifier used in resolution and lockfile metadata.

func (*GitHubRegistry) ResolveVersion

func (r *GitHubRegistry) ResolveVersion(ctx context.Context, source, constraint string) (string, error)

ResolveVersion resolves a version constraint to a concrete version. For GitHub, this uses semver constraint matching against available releases.

type Installer

type Installer interface {
	// Install writes a binary to the install path with proper permissions.
	// Returns error if installation fails.
	Install(tmpPath, installPath string) error

	// Verify checks if an installed binary matches the expected digest.
	// Returns nil if digest matches, error otherwise.
	Verify(installPath, expectedDigest string) error

	// Exists checks if a binary is already installed at the given path.
	Exists(installPath string) bool
}

Installer abstracts local binary installation for testability. The default implementation writes to the filesystem.

type LockOpts

type LockOpts struct {
	Platforms    []string // Platforms to lock (e.g., "linux/amd64"). Empty = current platform only.
	AllPlatforms bool     // Lock all available platforms from release.
}

LockOpts controls locking behavior.

type LockResult

type LockResult struct {
	Name      string // Component name (collector, tool, or remote)
	Kind      string // "collector", "tool", or "remote"
	Version   string
	Platforms []string
	IsNew     bool
	Updated   bool
}

LockResult contains the result of a lock operation.

type Locker

type Locker struct {
	Registry     RegistryClient
	LockfilePath string
	BaseDir      string // .epack directory
}

Locker resolves component sources and writes lockfile entries.

func NewLocker

func NewLocker(workDir string) *Locker

NewLocker creates a locker with default paths using the default GitHub registry.

func NewLockerWithRegistry

func NewLockerWithRegistry(registry RegistryClient, workDir string) *Locker

NewLockerWithRegistry creates a locker with a custom registry client. Useful for testing or multi-registry support.

func (*Locker) DetectAvailablePlatforms

func (l *Locker) DetectAvailablePlatforms(release *ReleaseInfo, componentName string) []string

DetectAvailablePlatforms finds all platforms available in a release.

func (*Locker) LoadOrCreateLockfile

func (l *Locker) LoadOrCreateLockfile() (*lockfile.LockFile, error)

LoadOrCreateLockfile loads the existing lockfile or creates a new one.

func (*Locker) Lock

func (l *Locker) Lock(ctx context.Context, cfg *config.JobConfig, opts LockOpts) ([]LockResult, error)

Lock resolves all source-based collectors and tools in config and updates lockfile.

type RegistryClient

type RegistryClient interface {
	// Name returns the registry identifier (e.g., "github", "locktivity").
	Name() string

	// ResolveVersion resolves a version constraint to a concrete version.
	// For example, "^1.0.0" might resolve to "v1.2.3".
	// Returns the resolved version tag and any error.
	ResolveVersion(ctx context.Context, source, constraint string) (string, error)

	// FetchRelease fetches release metadata for a specific version.
	// The source is registry-specific (e.g., "owner/repo" for GitHub).
	FetchRelease(ctx context.Context, source, version string) (*ReleaseInfo, error)

	// DownloadAsset downloads a release asset to the specified path.
	DownloadAsset(ctx context.Context, url, destPath string) error

	// FindBinaryAsset finds the binary asset for a specific platform.
	// Returns the asset info or an error if not found.
	FindBinaryAsset(release *ReleaseInfo, componentName, goos, goarch string) (*AssetInfo, error)

	// FindSigstoreBundle finds the Sigstore bundle for a binary asset.
	// Returns the bundle asset or an error if not found.
	FindSigstoreBundle(release *ReleaseInfo, binaryAssetName string) (*AssetInfo, error)
}

RegistryClient is the interface for resolving and fetching components. Implementations include GitHub (default) and future registries like Locktivity.

type ReleaseInfo

type ReleaseInfo struct {
	// Version is the resolved version tag (e.g., "v1.2.3").
	Version string

	// Assets contains platform-specific binaries and their metadata.
	Assets []AssetInfo

	// SigningInfo contains Sigstore signing metadata if available.
	SigningInfo *SigningInfo
}

ReleaseInfo contains metadata about a component release. This is a registry-agnostic representation of release data.

type SigningInfo

type SigningInfo struct {
	// Issuer is the OIDC issuer that authenticated the signer.
	Issuer string

	// Subject is the certificate subject (e.g., workflow path).
	Subject string

	// SourceRepositoryURI is the source repository URI from the certificate.
	SourceRepositoryURI string

	// SourceRepositoryRef is the source repository ref from the certificate.
	SourceRepositoryRef string
}

SigningInfo contains Sigstore signing metadata.

type SigstoreResult

type SigstoreResult = sigstore.Result

SigstoreResult contains verified signer identity from Sigstore bundle.

func VerifySigstoreBundle

func VerifySigstoreBundle(bundlePath, artifactPath string, expected *ExpectedIdentity) (*SigstoreResult, error)

VerifySigstoreBundle verifies a Sigstore bundle against an artifact. Returns the verified signer identity claims.

type SigstoreVerifier

type SigstoreVerifier interface {
	// Verify verifies a Sigstore bundle against a binary and expected identity.
	// Returns signing result on success, error on verification failure.
	Verify(bundlePath, binaryPath string, expectedIdentity *ExpectedIdentity) (*SigstoreResult, error)
}

SigstoreVerifier abstracts Sigstore bundle verification for testability.

type SyncOpts

type SyncOpts struct {
	Frozen               bool // Verify only, don't download.
	InsecureSkipVerify   bool // Skip Sigstore verification (NOT RECOMMENDED).
	InsecureTrustOnFirst bool // Trust digest from lockfile without Sigstore (NOT RECOMMENDED).
}

SyncOpts controls sync behavior.

type SyncResult

type SyncResult struct {
	Name      string // Component name
	Kind      string // "collector" or "tool"
	Version   string
	Platform  string
	Installed bool
	Verified  bool
	Skipped   bool // External binary, skipped
}

SyncResult contains the result of syncing a component.

type Syncer

type Syncer struct {
	Registry     RegistryClient
	LockfilePath string
	BaseDir      string // .epack directory
}

Syncer downloads components from lockfile.

func NewSyncer

func NewSyncer(workDir string) *Syncer

NewSyncer creates a syncer with default paths using the default GitHub registry.

func NewSyncerWithRegistry

func NewSyncerWithRegistry(registry RegistryClient, workDir string) *Syncer

NewSyncerWithRegistry creates a syncer with a custom registry client. Useful for testing or multi-registry support.

func (*Syncer) Sync

func (s *Syncer) Sync(ctx context.Context, cfg *config.JobConfig, opts SyncOpts) ([]SyncResult, error)

Sync downloads and verifies all components for the current platform.

func (*Syncer) SyncRemote

func (s *Syncer) SyncRemote(ctx context.Context, name string, cfg config.RemoteConfig, lf *lockfile.LockFile, platform string, opts SyncOpts) (*SyncResult, error)

SyncRemote syncs a single remote adapter. This is the exported version for use by other packages that need to install a specific remote adapter on-demand.

func (*Syncer) ValidateAlignment

func (s *Syncer) ValidateAlignment(cfg *config.JobConfig, lf *lockfile.LockFile) error

ValidateAlignment checks that config and lockfile collectors match. SECURITY: This validates that the config source matches the lockfile source, preventing "lockfile retargeting" attacks where an attacker modifies the config to point to a different repository while reusing a valid lockfile entry.

func (*Syncer) VerifyExternalCollector

func (s *Syncer) VerifyExternalCollector(name string, cfg config.CollectorConfig, lf *lockfile.LockFile, platform string, opts SyncOpts) (*SyncResult, error)

VerifyExternalCollector verifies an external binary against lockfile.

Jump to

Keyboard shortcuts

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