spool

package
v0.0.1 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: 10 Imported by: 0

Documentation

Overview

Package spool persists scan results to disk as they are produced.

Results never accumulate in memory: a bodyfile for a million-inode image is well over a hundred megabytes on its own, and it is one of hundreds of outputs. Each output target gets an append-only file with a buffered writer, which is crash-tolerant by construction (a torn final line is the only failure mode), greppable while a long acquisition is still running, and streams back with no seeking.

The line format is UAC's own, not JSON, so the output tree is byte-comparable against the shell implementation and readable by the same downstream tools.

Divergence from UAC

One difference is deliberate: control bytes in a path are escaped before a record is written. UAC passes filenames through find(1) and stat(1) verbatim, so a file whose name contains a newline splits its record in two and lets a suspect fabricate evidence by naming a file. See escapeControl.

The output is also written 0600 under 0700 directories rather than world-readable, because a collection contains whatever the image did -- /etc/shadow, private keys, credential stores.

Index

Constants

View Source
const (
	OutputFilePerm = 0600
	OutputDirPerm  = 0700
)

OutputFilePerm and OutputDirPerm keep a collection readable only by the examiner who made it.

The output is not ordinary program output: a collection contains /etc/shadow, SSH private keys, browser credential stores and anything else the artifacts asked for. Written 0644 under a 0755 directory, as it was, every local user on a shared analysis workstation could read the contents of the image -- material the tool went to some length to read safely in the first place.

Variables

This section is empty.

Functions

func Lines

func Lines(path string) iter.Seq2[string, error]

Lines streams a spool file back without loading it. This is what a leaf collector's ScanResults hands the caller.

func SanitizeName

func SanitizeName(name string) string

SanitizeName mirrors UAC's output filename sanitiser: characters that cannot appear in a filename on every supported platform become underscores.

Types

type Entry

type Entry struct {
	Collector string // rule id that produced it
	Kind      string // bodyfile | hashes | paths | copies | errors
	Path      string // absolute path of the spool file
	Rel       string // path relative to the output root
	Lines     int64
	Bytes     int64
}

Entry describes one spool file in the manifest.

type Store

type Store struct {
	Root string
	// contains filtered or unexported fields
}

Store owns the output tree and hands out writers. Writers are memoised per relative path, so several rules sharing an output_file append to one file exactly as they do under UAC.

func NewStore

func NewStore(root string) (*Store, error)

NewStore prepares an output tree.

The directory must be absent or empty. Appending a second acquisition to an existing one produces line-oriented outputs containing both while copied files are selectively overwritten, and the manifest counts only what this run wrote -- a mixture that looks like a single coherent collection and is not.

func (*Store) Close

func (s *Store) Close() error

Close flushes every writer and removes the ones that stayed empty.

func (*Store) Manifest

func (s *Store) Manifest() []Entry

Manifest lists every spool file written, newest counts included. It is what a composite collector's ScanResults returns: opening hundreds of streams eagerly would defeat the point of spooling in the first place.

func (*Store) Open

func (s *Store) Open(collector, kind, dir, name string) (*Writer, error)

Open returns the writer for an output file, creating it on first use. dir is the artifact's output_directory, relative to the output root.

type Writer

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

Writer is an append-only line sink.

func (*Writer) Close

func (w *Writer) Close() error

func (*Writer) Flush

func (w *Writer) Flush() error

func (*Writer) Lines

func (w *Writer) Lines() int64

func (*Writer) WriteLine

func (w *Writer) WriteLine(s string) error

WriteLine appends one record.

Control bytes are escaped first, so that one call always produces exactly one line. This is a deliberate divergence from UAC: see escapeControl.

func (*Writer) Writef

func (w *Writer) Writef(format string, args ...any) error

Jump to

Keyboard shortcuts

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