rulesync

package
v0.4.3 Latest Latest
Warning

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

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

Documentation

Overview

Package rulesync fetches, verifies, and caches a signed rule-pack bundle from the airomhq/airom-rules release channel, so rules can move faster than the airom binary without the user installing a second tool (Model B).

Trust model. A bundle is a gzipped tar of the YAML packs plus a manifest.json (version, tarball SHA-256, counts) and a detached ed25519 signature over the manifest bytes. airom verifies the signature against a public key embedded in the binary, then checks the tarball's SHA-256 against the manifest, then extracts. Any failure is fatal — a bundle is never partially trusted. The only escape hatch is an explicit InsecureSkipSignature.

Determinism and offline. Fetching happens on an explicit `airom rules update`, and — when the caller enables it — through AutoUpdate before a scan resolves its rules. AutoUpdate is throttled to at most one check a day, is skipped entirely under Offline or in a CI environment, and treats every network failure as "keep what we have", so a scan can never fail because a rules server was unreachable. Update refuses before dialing when Offline is set, mirroring the git source. The embedded packs remain the offline floor; a cached bundle is an override, and `--rules` overlays still layer on top of whichever wins.

Auto-update trades reproducibility for freshness, which is why it stands down in CI: two scans of one commit must agree, `airom diff` refuses across a ruleset change by design, and a --fail-on gate that flips with no commit behind it is a broken build rather than a finding. When it does install something, the scan says so in the document, not only on stderr.

This package holds airom's only outbound fetch besides the OSV overlay and its first crypto verification; it lives in internal/ so pkg/airom stays stdlib-only.

Index

Constants

View Source
const DefaultAutoInterval = 24 * time.Hour

DefaultAutoInterval is the minimum time between automatic checks.

Rule releases land on a human's schedule, so checking more often than daily buys nothing and costs a network round-trip on every scan — plus, from a CI fleet behind one NAT, GitHub's unauthenticated rate limit.

Variables

View Source
var (
	// ErrOffline is returned before any network call when Offline is set.
	ErrOffline = errors.New("rulesync: offline — refusing to fetch a rule bundle over the network")
	// ErrNoSigningKey means this airom build embeds no rules-signing public key
	// (verification impossible). Bypass with InsecureSkipSignature at your risk.
	ErrNoSigningKey = errors.New("rulesync: this airom build has no rules-signing public key embedded")
	// ErrSignature means the manifest's ed25519 signature did not verify.
	ErrSignature = errors.New("rulesync: bundle signature verification failed")
	// ErrIntegrity means the downloaded tarball's SHA-256 did not match the
	// (signed) manifest — corruption or tampering after signing.
	ErrIntegrity = errors.New("rulesync: bundle checksum mismatch")
	// ErrVersionMismatch means a pinned version was requested but the fetched
	// manifest declared a different one.
	ErrVersionMismatch = errors.New("rulesync: fetched bundle version does not match the requested version")
)

Sentinel errors — callers match with errors.Is to map to exit codes and messages. Every one is fail-closed: on any of them, the cache is left untouched and the previously active bundle (or the embedded packs) stands.

Functions

func Active

func Active(cacheDir string) (bundle fs.FS, version string, ok bool)

Active returns the currently installed rule bundle as a filesystem plus its version, or ok=false when no valid bundle is cached (so the caller falls back to the embedded packs). It never touches the network and never errors: a missing, unreadable, or dangling pointer is simply "no bundle", because a scan must degrade to the embedded floor, never fail on a bad cache.

Types

type AutoOptions added in v0.3.2

type AutoOptions struct {
	Options
	Interval time.Duration
}

AutoOptions configures one throttled check. Interval ≤ 0 uses DefaultAutoInterval; the embedded Options are passed through to Update.

type AutoResult added in v0.3.2

type AutoResult struct {
	Checked bool   // a version check actually reached the network
	From    string // the version that was active before ("" = none, embedded packs)
	To      string // the version installed by this call ("" = nothing installed)
	Updated bool   // a newer bundle was fetched and made active
}

AutoResult reports what a check did. All three states are distinguishable so a caller can say something truthful without guessing: Checked=false means the network was never touched.

func AutoUpdate added in v0.3.2

func AutoUpdate(ctx context.Context, o AutoOptions) (*AutoResult, error)

AutoUpdate checks for a newer rule bundle and installs it, at most once per Interval. It is deliberately forgiving: every failure short of a corrupt install returns a nil error with Updated=false, because a scan must not fail because a rules server was unreachable. The signed-bundle guarantees are NOT relaxed — installation still runs through Update, so signature and checksum verification are identical to an explicit `airom rules update`.

Only a strictly NEWER version is installed. An older or equal remote version is left alone, so a rollback on the server never silently downgrades a machine that already fetched the newer one.

type Doer

type Doer interface {
	Do(*http.Request) (*http.Response, error)
}

Doer is the minimal HTTP surface Update needs, so tests can inject a server-free transport (mirrors internal/osv).

type Manifest

type Manifest struct {
	Version   string `json:"version"`   // e.g. "v1.2.0"
	Tarball   string `json:"tarball"`   // asset filename of the gzipped tar
	SHA256    string `json:"sha256"`    // lowercase hex of the tarball
	RuleCount int    `json:"ruleCount"` // informational
	PackCount int    `json:"packCount"` // informational
}

Manifest is the signed description of a bundle. The ed25519 signature covers the exact bytes of the manifest.json that carries these fields.

type Options

type Options struct {
	CacheDir string // where bundles live; the caller resolves the default
	Version  string // "" or "latest" → the newest release; otherwise pinned (e.g. "v1.2.0")
	Source   string // base URL override (mirror/testing); "" → the GitHub release channel

	Offline               bool // refuse to touch the network (fail before dialing)
	InsecureSkipSignature bool // skip ed25519 verification — integrity check still runs

	HTTP      Doer              // nil → a default 30s http.Client
	PublicKey ed25519.PublicKey // nil → the key embedded in this build (tests inject their own)
	Now       func() time.Time  // nil → time.Now; injectable for deterministic tests
}

Options configures a single Update. Zero values pick safe defaults.

type Result

type Result struct {
	Version   string // the manifest version now active
	SHA256    string // the tarball hash
	Path      string // the extracted bundle directory
	RuleCount int
	PackCount int
}

Result reports what Update installed.

func Update

func Update(ctx context.Context, o Options) (*Result, error)

Update fetches, verifies, and installs a rule bundle into the cache, then repoints current.json at it. On any error the cache is left as it was.

This and latestVersion (via AutoUpdate) are the only functions here that touch the network; Active never does.

Jump to

Keyboard shortcuts

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