privacy

package
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package privacy is the SOURCE-SIDE telemetry redaction classifier (A6, #627 — the A0.6 privacy half of #611). It is pure domain: given a field category and value it decides allow / redact / hash / drop, and Scrub applies a Policy to a telemetry envelope on the agent BEFORE the spool/ship, so unredacted secrets never persist or leave the host. Safe-by-default: no environment collected, bounded argv/path, and known secret patterns (tokens, keys, passwords, connection strings) redacted even inside otherwise-allowed fields. The policy is attributed by a distinct RedactionPolicyDigest recorded with the data — separate from the sampling policy digest.

Index

Constants

View Source
const RedactionPlaceholder = "[redacted]"

RedactionPlaceholder replaces a redacted value or secret span. It is a stable, distinctive ASCII marker so a reader (and a test) can tell redaction happened without guessing.

Variables

This section is empty.

Functions

func IsCredentialFlag

func IsCredentialFlag(arg string) bool

IsCredentialFlag reports whether an argv element is a lone credential flag whose next element is a value to redact.

func RedactionPolicyDigest

func RedactionPolicyDigest(p Policy) string

RedactionPolicyDigest is a deterministic, domain-separated identity for source-redaction behavior, recorded with scrubbed data so a reader knows exactly how it was redacted. It is DISTINCT from the sampling-policy digest. Human policy version labels are aliases around this content identity and are therefore excluded. The HashSalt is folded in as its own digest (not raw) so the identity is stable without echoing the salt into the commitment.

func SameAssignment

func SameAssignment(left, right Assignment) bool

SameAssignment compares the declarative immutable policy-version identity. CreatedAt is server-owned admission metadata: insert-first stores preserve the first value when a client retries the same policy under a later server clock.

Types

type Activation

type Activation struct {
	TenantID      shared.ID `json:"tenant_id"`
	OperationID   shared.ID `json:"operation_id"`
	Revision      uint64    `json:"revision"`
	PolicyDigest  string    `json:"policy_digest"`
	PolicyVersion string    `json:"policy_version"`
	ActivatedBy   string    `json:"activated_by"`
	ActivatedAt   time.Time `json:"activated_at"`
}

Activation is one immutable active-policy pointer transition. Revision is monotonic per tenant, so A -> B -> A remains three independently auditable governance facts while an exact retry can recover the original transition.

func (Activation) Validate

func (a Activation) Validate() error

type Assignment

type Assignment struct {
	TenantID  shared.ID `json:"tenant_id"`
	Policy    Policy    `json:"policy"`
	Digest    string    `json:"digest"`
	CreatedBy string    `json:"created_by"`
	CreatedAt time.Time `json:"created_at"`
}

Assignment is an immutable tenant policy version. Active selection is stored separately so policy history remains append-only.

func NewAssignment

func NewAssignment(
	tenantID shared.ID,
	policy Policy,
	createdBy string,
	now time.Time,
) (Assignment, error)

NewAssignment validates a source-safe policy and binds its immutable digest.

func (Assignment) Validate

func (a Assignment) Validate() error

Validate verifies durable provenance and recomputes policy identity.

type FieldCategory

type FieldCategory string

FieldCategory names a logical telemetry field the policy reasons about, so a policy is field-aware without coupling to concrete struct layouts.

const (
	CategoryProcessArg  FieldCategory = "process.arg"
	CategoryProcessPath FieldCategory = "process.path"
	CategoryProcessComm FieldCategory = "process.comm"
	// CategoryProcessEnv is reserved: the schema collects no environment today, and the default policy
	// drops it, so if an env field is ever added it is redacted-by-omission by default.
	CategoryProcessEnv    FieldCategory = "process.env"
	CategoryFilePath      FieldCategory = "file.path"
	CategoryFileComm      FieldCategory = "file.comm"
	CategoryNetworkComm   FieldCategory = "network.comm"
	CategoryPrivilegeComm FieldCategory = "privilege.comm"
)

func (FieldCategory) Valid

func (c FieldCategory) Valid() bool

type FieldDisposition

type FieldDisposition string

FieldDisposition is what the policy does with a field's value.

const (
	// DispositionAllow keeps the value, but (when Policy.RedactSecrets) still scrubs embedded secret
	// patterns — an allowed field is never a bypass for a token pasted into an argument.
	DispositionAllow FieldDisposition = "allow"
	// DispositionRedact replaces the whole value with the redaction placeholder.
	DispositionRedact FieldDisposition = "redact"
	// DispositionHash replaces the value with a keyed digest — correlation without the cleartext.
	DispositionHash FieldDisposition = "hash"
	// DispositionDrop removes the value entirely.
	DispositionDrop FieldDisposition = "drop"
)

func (FieldDisposition) Valid

func (d FieldDisposition) Valid() bool

Valid reports whether d is a known disposition.

type Policy

type Policy struct {
	// Dispositions maps a category to its disposition; a category absent here uses DispositionAllow.
	Dispositions map[FieldCategory]FieldDisposition
	// RedactSecrets scans allowed/redacted string values for known secret patterns and scrubs the matches.
	RedactSecrets bool
	// MaxArgLen / MaxArgCount bound argv so a pathological command line cannot exfiltrate unbounded data;
	// MaxPathLen bounds a path. <=0 means unbounded for that dimension.
	MaxArgLen   int
	MaxArgCount int
	MaxPathLen  int
	// HashSalt keys the DispositionHash digest. It is NOT a secret store; it only prevents trivial
	// dictionary correlation across policies. Same salt+value → same hash (correlation preserved).
	HashSalt string
	// Version is the immutable policy-version alias and lineage label. It is not part of the redaction-content digest.
	Version string
}

Policy is a tenant-configurable source-side redaction policy. The zero value is NOT safe to use; call DefaultPolicy (or Normalize on a partially-built one) so caps and secret scanning are set.

func DefaultPolicy

func DefaultPolicy() Policy

DefaultPolicy is the safe default: no environment collected, argv/path bounded, comms/paths/args allowed but secret-scanned. It never drops argv wholesale (that would destroy forensic value); instead it redacts only the secret spans within.

func (Policy) Classify

func (p Policy) Classify(cat FieldCategory, value string) (string, FieldDisposition)

Classify applies the category's disposition to a single value and reports the disposition actually applied (an allowed value with a scrubbed secret reports DispositionRedact, so the caller can flag it).

func (Policy) RedactArgv

func (p Policy) RedactArgv(args []string) (out []string, redacted, dropped int)

RedactArgv redacts a whole argv slice as a unit (positions preserved). Each element is classified as a process arg (disposition + per-element secret scan), AND — the reason this is slice-aware — when an element is a lone credential flag its FOLLOWING element (the space-separated value) is redacted wholesale. It also handles command-scoped MySQL/MariaDB -pPASSWORD when argv[0] identifies that client. Source scrubbers additionally pass the authoritative Process.Path/Comm so this does not rely on argv[0].

func (Policy) Validate

func (p Policy) Validate() error

Validate checks the policy is well-formed.

func (Policy) ValidateSourceFloor

func (p Policy) ValidateSourceFloor() error

ValidateSourceFloor enforces the non-relaxable source-privacy floor. Tenant policy may collect less data than the default, but it cannot enable process environments, disable known-secret scrubbing, or loosen argv/path bounds.

type Report

type Report struct {
	PolicyDigest string
	Redacted     int // values redacted, hashed, or secret-scrubbed
	Dropped      int // values dropped entirely
}

Report summarizes what a Scrub did, for honesty signals and tests. PolicyDigest is the RedactionPolicyDigest of the applied policy (also stamped onto the returned envelope).

func Scrub

Scrub applies the policy to a telemetry envelope on the SOURCE side, returning a scrubbed copy (the input is never mutated — it operates on env.Clone()), a Report, and any error. It stamps the envelope's RedactionPolicyDigest and sets QualityRedacted when anything changed, so the redaction travels with the data. Callers apply it BEFORE the spool/ship so unredacted secrets never persist or leave the host.

func ScrubDetection

func ScrubDetection(det detection.Detection, policy Policy) (detection.Detection, Report, error)

ScrubDetection redacts a confirmed detection's EVIDENCE at the source (A6, #627) before it is persisted and shipped. A detection embeds the raw events that triggered it (argv, paths, comms), so without this a rule firing on a credential-bearing command line would ship the exact secret the telemetry path already scrubs — and, once sealed into the permanent evidence chain (A5), unredactably so. It returns a redacted DEEP COPY (the caller's Detection, still held by the engine, is never mutated) and a Report.

It applies the same field classifier and argv/path bounds as telemetry Scrub. Detection evidence has no\n// per-field truncation-honesty flags, but its evidence is already bounded by the rule window.

func (Report) Changed

func (r Report) Changed() bool

Changed reports whether the scrub altered any value.

Jump to

Keyboard shortcuts

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