Documentation
¶
Overview ¶
Package report builds the compliance evidence report of `agentguard report`: what AgentGuard decided over a period, how far the audit log that recorded it can be trusted, and how that maps to SOC 2 criteria.
It does no I/O. The caller reads the audit log (files or a server), runs the checks it can, and hands the entries to Aggregate as an iterator; the renderers write the finished Report as Markdown, JSON or HTML. That keeps every number in the report testable from a fixed list of entries.
The JSON form is a stable shape for 1.x: fields are only ever added. FormatVersion changes only if that ever stops being true. See docs/COMPLIANCE.md.
Index ¶
- Constants
- Variables
- func Aggregate(entries iter.Seq[audit.Entry], p Period) (Activity, Approvals)
- func Finish(r *Report)
- func Limits(r *Report) []string
- func Write(w io.Writer, r *Report, format string) error
- func WriteHTML(w io.Writer, r *Report) error
- func WriteJSON(w io.Writer, r *Report) error
- func WriteMarkdown(w io.Writer, r *Report) error
- type Activity
- type Approvals
- type Check
- type Control
- type Count
- type Head
- type Integrity
- type NameList
- type Period
- type PolicyFile
- type Report
- type Source
- type Tally
Constants ¶
const ( // MethodVerify: the files were checked with audit verify. MethodVerify = "verify" // MethodSealHead: the server's GET /v1/audit/head was read. The chain // itself was not checked. MethodSealHead = "seal_head" )
Integrity methods: how the report learned about the audit log's seal.
const ( IntegrityIntact = "intact" // the chain verified IntegrityBroken = "broken" // audit verify found tampering or damage IntegrityNotSealed = "not_sealed" // the log has no seal chain IntegrityNotVerified = "not_verified" // sealed, but the chain was not checked IntegrityUnknown = "unknown" // can't tell whether the log is sealed )
Integrity statuses.
const ( SectionSummary = "Summary" SectionIntegrity = "Audit log integrity" SectionActivity = "Activity" SectionRules = "Top rules" SectionApprovals = "Approvals" SectionSeen = "Agents and sessions" SectionPosture = "Posture checks" SectionControls = "Control mapping" SectionLimits = "Limits" )
Section names, as the renderers title them. Control.Where refers to them.
const ( FormatMarkdown = "md" FormatJSON = "json" FormatHTML = "html" )
Output formats.
const ( SourceFile = "file" // the audit log files were read directly SourceServer = "server" // a running server was asked over GET /v1/audit )
Source modes.
const ( CheckPass = "pass" CheckWarn = "warn" CheckFail = "fail" CheckUnknown = "unknown" )
Check statuses.
const Disclaimer = "" /* 156-byte string literal not displayed */
Disclaimer heads the control mapping in every format.
const FormatVersion = 1
FormatVersion is the version of the report's JSON shape.
Variables ¶
var Formats = []string{FormatMarkdown, FormatJSON, FormatHTML}
Formats lists the output formats Write accepts.
Functions ¶
func Aggregate ¶
Aggregate tallies the entries that fall in p. Entries outside it are skipped, so the caller can pass a whole log.
func Finish ¶
func Finish(r *Report)
Finish fills the parts of r that follow from the rest: the posture checks, the control mapping and the limits. Call it once everything else is set.
func WriteHTML ¶
WriteHTML writes r as one self-contained HTML page: inline CSS, no scripts, nothing loaded from elsewhere, so it can be mailed or archived as is. html/template escapes every value; it leaves control and bidi characters alone, so the page goes through visible as well (the template's own text has none).
Types ¶
type Activity ¶
type Activity struct {
// Entries is the number of audit entries in the period.
Entries int `json:"entries"`
// FirstEntry and LastEntry are the earliest and latest timestamps among
// them; omitted when there are none.
FirstEntry *time.Time `json:"first_entry,omitempty"`
LastEntry *time.Time `json:"last_entry,omitempty"`
// Decisions always lists ALLOW, DENY and REQUIRE_APPROVAL, in that
// order, then any other value found.
Decisions []Count `json:"decisions"`
Scopes Tally `json:"scopes"`
Transports Tally `json:"transports"`
Tenants Tally `json:"tenants"`
Agents Tally `json:"agents"`
// DenyRules and ApprovalRules are the matched rules of DENY and
// REQUIRE_APPROVAL entries. A DENY no rule matched counts as
// "(default deny)".
DenyRules Tally `json:"deny_rules"`
ApprovalRules Tally `json:"approval_rules"`
// AgentsSeen and SessionsSeen list the agent and session ids (entries
// without a session id are not counted as a session).
AgentsSeen NameList `json:"agents_seen"`
SessionsSeen NameList `json:"sessions_seen"`
// RedactedEntries counts entries holding "[REDACTED]", the mask the
// server's --audit-redact writes. UnmaskedSecretEntries counts entries
// whose request or reason still matches one of the built-in secret
// patterns.
RedactedEntries int `json:"redacted_entries"`
UnmaskedSecretEntries int `json:"unmasked_secret_entries"`
// UnsealedEntries counts entries in the period without a seal (no hash
// or no seq). In server mode it is the only seal evidence per entry.
UnsealedEntries int `json:"unsealed_entries"`
}
Activity is what was decided in the period.
type Approvals ¶
type Approvals struct {
// Requested counts approval ids first sent for approval in the period.
Requested int `json:"requested"`
// Approved and Denied count approval ids whose human decision shows in
// the period (allow:approved or deny:approved entries).
Approved int `json:"approved"`
Denied int `json:"denied"`
// NoOutcome counts ids requested in the period with no approved or
// denied entry in it: timed out, resolved but never retried, resolved
// after the period, or still pending.
NoOutcome int `json:"no_outcome"`
// TimedOut is always null: the audit log does not record time-outs.
// They are among NoOutcome.
TimedOut *int `json:"timed_out"`
}
Approvals is the approval lifecycle as the audit log records it. The log has an entry when an action is sent for approval, and another when the agent comes back with the approval id after a human resolved it. It has none for the resolution itself or for a request that timed out.
type Check ¶
type Check struct {
Name string `json:"name"`
// Status is pass, warn, fail or unknown (it can't be told from what the
// report read).
Status string `json:"status"`
Detail string `json:"detail"`
}
Check is one posture check.
type Control ¶
type Control struct {
Criterion string `json:"criterion"`
Title string `json:"title"`
// Shows is what this report holds that bears on the criterion.
Shows string `json:"shows"`
// Where names the report's sections that hold it.
Where []string `json:"where"`
}
Control maps the report's evidence to one SOC 2 criterion. It is supporting evidence for that criterion, not a finding that it is met.
func MapControls ¶
MapControls maps the report's evidence to the SOC 2 criteria it bears on. The wording says what the report shows, never that a criterion is met.
type Head ¶
type Head struct {
Seq uint64 `json:"seq"`
Hash string `json:"hash"`
SegmentID string `json:"segment_id,omitempty"`
At *time.Time `json:"at,omitempty"`
}
Head is a seal anchor: the entry with this seq has this hash. SegmentID and At are set when a server reported it.
type Integrity ¶
type Integrity struct {
Method string `json:"method"`
Status string `json:"status"`
// Summary is one sentence for a person.
Summary string `json:"summary"`
// Seals says how the entries are sealed, e.g. "hmac-sha256 key
// 3f9a…" or "sha256", in the order they appear. Empty when unknown.
Seals []string `json:"seals,omitempty"`
// KeyBased is true for an HMAC key, false for plain SHA-256, nil when
// not sealed or not known. It is read from the log's _meta header,
// which anyone who can write the file can forge: only Status intact
// confirms it.
KeyBased *bool `json:"key_based"`
// Head is the latest seal anchor: the last sealed entry's seq and hash.
Head *Head `json:"head,omitempty"`
// Verify is audit verify's full report (verify method only).
Verify *audit.VerifyReport `json:"verify,omitempty"`
}
Integrity is what the report knows about the audit log's seal chain.
func FromServerHead ¶
FromServerHead describes a server's GET /v1/audit/head answer: a head (sealed), sealingOff (the server says sealing is off), or neither (an older server without the route, or an error, given as why).
func FromVerify ¶
func FromVerify(rep *audit.VerifyReport) Integrity
FromVerify describes the outcome of audit.VerifyChain.
type NameList ¶
type NameList struct {
Count int `json:"count"`
Names []string `json:"names"`
Truncated bool `json:"truncated"`
}
NameList is the set of values a field had: how many, and the first of them in sorted order. Truncated is true when Names does not hold them all.
type Period ¶
Period is the time range the report covers: Since inclusive, Until exclusive, both UTC.
type PolicyFile ¶
type PolicyFile struct {
Path string `json:"path"`
// SHA256 is the hex SHA-256 of the file's bytes when the report was
// made.
SHA256 string `json:"sha256"`
// Found says how the file was chosen: "--policy", "AGENTGUARD_POLICY",
// "the default" or "found automatically".
Found string `json:"found"`
}
PolicyFile identifies a policy file by content.
type Report ¶
type Report struct {
FormatVersion int `json:"format_version"`
// GeneratedAt is when the report was made (UTC).
GeneratedAt time.Time `json:"generated_at"`
// AgentGuardVersion is the version of the binary that made the report,
// e.g. "1.4.0 (abc1234)".
AgentGuardVersion string `json:"agentguard_version"`
Period Period `json:"period"`
Source Source `json:"source"`
// Policy is the policy file the report names, or nil when none was
// given or found.
Policy *PolicyFile `json:"policy"`
Integrity Integrity `json:"integrity"`
Activity Activity `json:"activity"`
Approvals Approvals `json:"approvals"`
Posture []Check `json:"posture"`
Controls []Control `json:"controls"`
// Limits says what the report can't show. Read it with the numbers.
Limits []string `json:"limits"`
}
Report is the whole report. Its JSON form is what `--output json` prints.
type Source ¶
type Source struct {
Mode string `json:"mode"`
// AuditLog is the live audit file (file mode).
AuditLog string `json:"audit_log,omitempty"`
// Files are the files read, oldest first, by base name (file mode).
Files []string `json:"files,omitempty"`
// BadLines counts lines in those files that are not audit entries
// (file mode; always 0 in server mode, where the server skips them).
BadLines int `json:"bad_lines"`
// ReadErrors are files that could not be read to the end (file mode).
// Entries before the error are in the report.
ReadErrors []string `json:"read_errors,omitempty"`
// Server is the server's URL and Tenant the tenant asked for (server
// mode; Tenant is "local" for the legacy /v1/audit route).
Server string `json:"server,omitempty"`
Tenant string `json:"tenant,omitempty"`
// Fetched counts the entries the server returned, and OutsidePeriod
// those of them the report dropped because their timestamp is outside
// the period: a server older than 1.4 does not filter by time.
Fetched int `json:"fetched,omitempty"`
OutsidePeriod int `json:"outside_period,omitempty"`
// AuthRequired is whether the server refused an audit query that
// carried no API key (server mode). nil when it was not checked.
AuthRequired *bool `json:"auth_required,omitempty"`
// Warnings say why the entries may be incomplete, e.g. the server's
// live file rotated while the report read it.
Warnings []string `json:"warnings,omitempty"`
}
Source says where the entries came from.