store

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	NoteTypeStatement  = "statement"
	NoteTypeHypothesis = "hypothesis"
	NoteTypeNextAction = "next_action"
	NoteTypeSummary    = "summary"
)

Briefing note types. These occupy Note.LinkedType, alongside the existing "summary" and the "event"/"ioc" links a note can carry.

View Source
const (
	ConfidenceConfirmed = "confirmed"
	ConfidenceLikely    = "likely"
	ConfidenceOpen      = "open"
)

Confidence levels a hypothesis can carry.

View Source
const (
	MemberTypeFinding = "finding"
	MemberTypeEvent   = "event"

	// RoleMember is what the case is about — normally its findings.
	RoleMember = "member"
	// RoleEvidence is what supports it — normally raw events pulled in during
	// investigation.
	RoleEvidence = "evidence"
)

Case member kinds and roles.

The role distinction is the point of this table. A case is *about* its findings (members) and *supported by* its events (evidence). Collapsing both into one undifferentiated pile makes a case with 4 detections and 300 corroborating log lines look like 304 equally important things.

View Source
const (
	CaseStatusOpen          = "open"
	CaseStatusInvestigating = "investigating"
	CaseStatusContained     = "contained"
	CaseStatusResolved      = "resolved"
	CaseStatusClosed        = "closed"
)

Case status labels. These are what the UI shows and what the status column stores; status_id is the OCSF Incident Finding status the case projects onto.

"contained" is not an OCSF status — it maps onto On Hold, which is the closest OCSF equivalent for "action taken, not yet finished". "resolved" is new: OCSF distinguishes Resolved (dealt with) from Closed (filed away), and the app previously had no way to say the former.

View Source
const (
	ObservableSourceAsserted = "asserted"
	ObservableSourceDerived  = "derived"
)

Observable provenance. OCSF observables are assertions made by the producer; anything Console-IR works out for itself is recorded separately so an analyst can tell a vouched-for indicator from an inferred one.

Variables

This section is empty.

Functions

func CaseStatusIDFor added in v0.2.0

func CaseStatusIDFor(label string) int

CaseStatusIDFor resolves a status label to its OCSF status_id, defaulting to New for anything unrecognised.

func CaseStatusLabelFor added in v0.2.0

func CaseStatusLabelFor(statusID int) string

CaseStatusLabelFor resolves an OCSF status_id to the app's label.

func CaseStatuses added in v0.2.0

func CaseStatuses() []string

CaseStatuses lists the selectable case statuses in lifecycle order.

func DefaultRoleFor added in v0.2.0

func DefaultRoleFor(memberType string) string

DefaultRoleFor returns the role a member type takes unless overridden.

func IsBriefingNote added in v0.2.0

func IsBriefingNote(n Note) bool

IsBriefingNote reports whether a note holds briefing content rather than analyst prose. The notes tab must exclude these, or the decision log fills with fragments of the briefing.

Types

type ActiveCases added in v0.2.0

type ActiveCases struct {
	Total int
	// Investigating counts cases in progress, which is the number the card
	// shows: "3 active, 1 investigating" tells an analyst where the work is.
	Investigating int
	// OldestOpened is when the longest-running active case was created. Zero
	// when there are no active cases.
	OldestOpened time.Time
}

ActiveCases is the active-cases metric card.

type AuditEntry

type AuditEntry struct {
	ID        string                 `json:"id"`
	CaseID    string                 `json:"case_id"`
	EventID   string                 `json:"event_id,omitempty"`
	Action    string                 `json:"action"`   // "create_case", "assign_event", "copilot_query", "note_added", etc.
	Actor     string                 `json:"actor"`    // user or system identifier
	Details   map[string]interface{} `json:"details"`  // action-specific data
	Metadata  map[string]string      `json:"metadata"` // tokens, cost, etc.
	Timestamp time.Time              `json:"timestamp"`
	CreatedAt time.Time              `json:"created_at"`
}

AuditEntry represents an audit log entry

type Briefing added in v0.2.0

type Briefing struct {
	Statement   string
	Hypotheses  []Hypothesis
	NextActions []NextAction
	// Summary is generated text. It is kept apart from the rest so nothing can
	// mistake it for something an analyst wrote.
	Summary    string
	SummaryAt  time.Time
	HasSummary bool
}

Briefing is a case's narrative content.

type Case

type Case struct {
	ID          string `json:"id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Severity    string `json:"severity"`
	Status      string `json:"status"`
	AssignedTo  string `json:"assigned_to,omitempty"`

	// OCSF incident-profile fields. StatusID is the authoritative lifecycle
	// state; Status is its display label.
	StatusID          int    `json:"status_id,omitempty"`
	VerdictID         int    `json:"verdict_id,omitempty"`
	PriorityID        int    `json:"priority_id,omitempty"`
	ImpactID          int    `json:"impact_id,omitempty"`
	IsSuspectedBreach bool   `json:"is_suspected_breach,omitempty"`
	AssigneeGroup     string `json:"assignee_group,omitempty"`

	// EventCount is supporting evidence; FindingCount is what the case is about.
	EventCount   int `json:"event_count"`
	FindingCount int `json:"finding_count"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Case represents an incident case.

A Case is an application concept — OCSF has no Case object. Its projection into the schema is an Incident Finding (class_uid 2005), which is why it carries the incident-profile fields below.

func (Case) VerdictName added in v0.2.0

func (c Case) VerdictName() string

VerdictName resolves the case verdict caption, empty when none is set.

type CaseCounts added in v0.2.0

type CaseCounts struct {
	Findings int `json:"findings"`
	Events   int `json:"events"`
}

CaseCounts summarizes what a case holds.

type CaseIndicator added in v0.2.0

type CaseIndicator struct {
	TypeID int
	Type   string
	Value  string
	// Source is "asserted" when any sighting came from the producer, and
	// "derived" only when every sighting was inferred here. An analyst defends
	// those two differently, so the distinction survives aggregation.
	Source    string
	Sightings int
	FirstSeen time.Time
	LastSeen  time.Time
}

CaseIndicator is one observable aggregated across everything a case holds.

type CaseMember added in v0.2.0

type CaseMember struct {
	CaseID     string    `json:"case_id"`
	MemberType string    `json:"member_type"`
	MemberID   string    `json:"member_id"`
	Role       string    `json:"role"`
	AddedBy    string    `json:"added_by,omitempty"`
	AddedAt    time.Time `json:"added_at"`
}

CaseMember links a case to a finding or an event.

type CaseTicket added in v0.2.0

type CaseTicket struct {
	ID     string `json:"id"`
	CaseID string `json:"case_id"`
	UID    string `json:"uid"`
	Title  string `json:"title,omitempty"`
	Type   string `json:"type,omitempty"`
	SrcURL string `json:"src_url,omitempty"`
	Status string `json:"status,omitempty"`
}

CaseTicket links a case to an external tracking system, mirroring the OCSF `tickets` attribute on the incident profile.

type Enrichment

type Enrichment struct {
	ID        string            `json:"id"`
	EventID   string            `json:"event_id"`
	Source    string            `json:"source"`
	Type      string            `json:"type"`
	Data      map[string]string `json:"data"`
	CreatedAt time.Time         `json:"created_at"`
}

Enrichment represents event enrichment data

type Event

type Event struct {
	ID     string `json:"id"`
	CaseID string `json:"case_id,omitempty"`

	// OCSF identity. class_uid is authoritative; EventType is the coarse
	// category grouping derived from it for display and filtering.
	ClassUID    int    `json:"class_uid,omitempty"`
	CategoryUID int    `json:"category_uid,omitempty"`
	ActivityID  int    `json:"activity_id,omitempty"`
	TypeUID     int    `json:"type_uid,omitempty"`
	SeverityID  int    `json:"severity_id,omitempty"`
	MetadataUID string `json:"metadata_uid,omitempty"`

	Timestamp   time.Time `json:"timestamp"`
	EventType   string    `json:"event_type"`
	Severity    string    `json:"severity"`
	Message     string    `json:"message"`
	Host        string    `json:"host,omitempty"`
	SrcIP       string    `json:"src_ip,omitempty"`
	DstIP       string    `json:"dst_ip,omitempty"`
	SrcPort     int       `json:"src_port,omitempty"`
	DstPort     int       `json:"dst_port,omitempty"`
	ProcessName string    `json:"process_name,omitempty"`
	FileName    string    `json:"file_name,omitempty"`
	FileHash    string    `json:"file_hash,omitempty"`
	UserName    string    `json:"user_name,omitempty"`
	RawJSON     string    `json:"raw_json"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

Event represents a stored event

func (Event) CategoryName added in v0.2.0

func (e Event) CategoryName() string

CategoryName returns the OCSF caption for the event's category.

func (Event) ClassName added in v0.2.0

func (e Event) ClassName() string

ClassName returns the OCSF caption for the event's class, e.g. "Detection Finding".

func (Event) IsFinding added in v0.2.0

func (e Event) IsFinding() bool

IsFinding reports whether the event belongs to the OCSF Findings category.

type EventFilter added in v0.2.0

type EventFilter struct {
	CaseID string
	Start  time.Time
	End    time.Time
	// Severities matches events.severity (case-insensitive).
	Severities []string
	// Categories matches events.event_type, the coarse OCSF category slug.
	Categories []string
	// Classes matches events.class_uid exactly, e.g. 2004 for Detection Findings.
	Classes []int

	Limit  int
	Offset int
}

EventFilter describes the optional constraints applied to an events query. The zero value matches everything.

type Finding added in v0.2.0

type Finding struct {
	ID         string `json:"id"`
	FindingUID string `json:"finding_uid"`
	CaseID     string `json:"case_id,omitempty"`

	ClassUID    int `json:"class_uid"`
	CategoryUID int `json:"category_uid"`
	ActivityID  int `json:"activity_id,omitempty"`
	TypeUID     int `json:"type_uid,omitempty"`

	Title        string `json:"title"`
	Message      string `json:"message,omitempty"`
	AnalyticName string `json:"analytic_name,omitempty"`
	AnalyticUID  string `json:"analytic_uid,omitempty"`

	Status    string `json:"status,omitempty"`
	StatusID  int    `json:"status_id"`
	Verdict   string `json:"verdict,omitempty"`
	VerdictID int    `json:"verdict_id,omitempty"`

	Severity     string `json:"severity,omitempty"`
	SeverityID   int    `json:"severity_id,omitempty"`
	ConfidenceID int    `json:"confidence_id,omitempty"`
	RiskLevelID  int    `json:"risk_level_id,omitempty"`
	RiskScore    int    `json:"risk_score,omitempty"`
	ImpactID     int    `json:"impact_id,omitempty"`
	PriorityID   int    `json:"priority_id,omitempty"`

	IsAlert           bool   `json:"is_alert,omitempty"`
	IsSuspectedBreach bool   `json:"is_suspected_breach,omitempty"`
	Assignee          string `json:"assignee,omitempty"`
	MetadataUID       string `json:"metadata_uid,omitempty"`

	FirstSeen   time.Time `json:"first_seen"`
	LastSeen    time.Time `json:"last_seen"`
	CreatedTime time.Time `json:"created_time,omitempty"`

	AttacksJSON         string `json:"attacks_json,omitempty"`
	EvidencesJSON       string `json:"evidences_json,omitempty"`
	RelatedEventsJSON   string `json:"related_events_json,omitempty"`
	FindingInfoListJSON string `json:"finding_info_list_json,omitempty"`
	RawJSON             string `json:"raw_json"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Finding is a stored OCSF Findings-category record — an analytic's conclusion, with a lifecycle. Unlike an event, a finding is mutable: it is keyed on FindingUID and updated in place as the producer revises it.

func (Finding) AttackTechniques added in v0.2.0

func (f Finding) AttackTechniques() []string

AttackTechniques returns the ATT&CK technique identifiers on the finding, for compact display (e.g. "T1059.001"). Sub-techniques win over their parent.

func (Finding) Attacks added in v0.2.0

func (f Finding) Attacks() []ocsf.Attack

Attacks decodes the stored ATT&CK associations.

func (Finding) ClassName added in v0.2.0

func (f Finding) ClassName() string

ClassName returns the OCSF caption for the finding's class.

func (Finding) Evidences added in v0.2.0

func (f Finding) Evidences() []ocsf.Evidence

Evidences decodes the stored evidence artifacts.

func (Finding) IsOpen added in v0.2.0

func (f Finding) IsOpen() bool

IsOpen reports whether the finding still needs analyst attention.

func (Finding) RelatedEvents added in v0.2.0

func (f Finding) RelatedEvents() []ocsf.RelatedEvent

RelatedEvents decodes the events the analytic examined — the documented route from a finding back to the telemetry that produced it.

func (Finding) StatusName added in v0.2.0

func (f Finding) StatusName() string

StatusName resolves the lifecycle status caption for the finding's class.

func (Finding) VerdictName added in v0.2.0

func (f Finding) VerdictName() string

VerdictName resolves the analyst verdict caption, empty when none is set.

type FindingFilter added in v0.2.0

type FindingFilter struct {
	CaseID string
	// Statuses matches status_id.
	Statuses []int
	// Severities matches the severity label (case-insensitive).
	Severities []string
	// Classes matches class_uid, e.g. 2004 for Detection Findings.
	Classes []int
	// OpenOnly restricts to findings still needing attention.
	OpenOnly bool
	Search   string

	// MinSeverityID keeps findings at or above a severity. Fatal counts as
	// critical; Other (99) is a sentinel and never satisfies a minimum.
	MinSeverityID int

	// SeenAfter and SeenBefore bound last_seen. SeenAfter backs "last 24h";
	// SeenBefore backs "stale", which is the same field read the other way.
	SeenAfter  time.Time
	SeenBefore time.Time

	// Assignee matches the owner exactly. Empty matches every owner.
	Assignee string

	// HasObservables keeps only findings carrying at least one indicator.
	HasObservables bool

	// Sort selects the ordering. The zero value keeps the historical
	// most-recently-seen-first, so existing callers are unaffected.
	Sort FindingSort

	Limit  int
	Offset int
}

FindingFilter describes optional constraints on a findings query. The zero value matches everything.

type FindingSort added in v0.2.0

type FindingSort int

FindingSort names an ordering for a findings query.

const (
	// SortRecent is most recently seen first.
	SortRecent FindingSort = iota
	// SortPriority is the triage ordering: the same sequence the Analyst Home
	// priority queue applies, so the queue and its full version agree about
	// which finding matters most.
	SortPriority
)

type Hypothesis added in v0.2.0

type Hypothesis struct {
	Text       string `json:"text"`
	Confidence string `json:"confidence"`
}

Hypothesis is a belief about the incident and how sure the analyst is.

type NextAction added in v0.2.0

type NextAction struct {
	Text string `json:"text"`
	Done bool   `json:"done"`
}

NextAction is one item on the case's checklist.

type Note

type Note struct {
	ID         string    `json:"id"`
	CaseID     string    `json:"case_id"`
	Content    string    `json:"content"`
	Author     string    `json:"author"`
	Color      string    `json:"color,omitempty"`       // hex color e.g. "#f1c40f"
	LinkedType string    `json:"linked_type,omitempty"` // "event", "ioc", or ""
	LinkedID   string    `json:"linked_id,omitempty"`   // event_id or ioc unique value
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
}

Note represents a case note (enhanced for sticky notes and linking)

type Observable added in v0.2.0

type Observable struct {
	ID         string           `json:"id"`
	EventID    string           `json:"event_id,omitempty"`
	FindingID  string           `json:"finding_id,omitempty"`
	TypeID     int              `json:"type_id"`
	Type       string           `json:"type,omitempty"`
	Name       string           `json:"name,omitempty"`
	Value      string           `json:"value"`
	Source     string           `json:"source"`
	Reputation *ocsf.Reputation `json:"reputation,omitempty"`
	CreatedAt  time.Time        `json:"created_at"`
}

Observable is a stored OCSF observable — the pivot element that makes "have I seen this indicator before?" an indexed lookup rather than a scan.

func (Observable) IsAsserted added in v0.2.0

func (o Observable) IsAsserted() bool

IsAsserted reports whether the producer supplied this observable, as opposed to Console-IR deriving it from the event's fields.

type OpenFindings added in v0.2.0

type OpenFindings struct {
	Total    int
	Critical int
	High     int
	Medium   int
	Low      int
	Info     int
}

OpenFindings is the open-findings metric card.

type SavedRecord added in v0.2.0

type SavedRecord struct {
	EventID   string
	FindingID string
}

SavedRecord reports what SaveRecord persisted.

type Store

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

Store represents the SQLite storage implementation

func NewStore

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

func (*Store) AcceptSummary added in v0.2.0

func (s *Store) AcceptSummary(ctx context.Context, caseID, author, summary string) error

AcceptSummary promotes a generated summary into an ordinary authored note.

This is the only route from generated text into the case record, and it takes a deliberate action. The summary itself stays where it was: accepting it is the analyst saying "this is now my words", not a move.

func (*Store) AddAuditEntry

func (s *Store) AddAuditEntry(ctx context.Context, entry AuditEntry) error

AddAuditEntry adds an audit entry to the database

func (*Store) AddCaseMember added in v0.2.0

func (s *Store) AddCaseMember(ctx context.Context, m CaseMember) error

AddCaseMember links one finding or event to a case.

func (*Store) AddCaseMembers added in v0.2.0

func (s *Store) AddCaseMembers(ctx context.Context, members []CaseMember) error

AddCaseMembers links several items to a case in one transaction.

The legacy single-case column is written alongside for compatibility with readers that have not moved over yet. It can only hold one case, so for an item that belongs to several it records the first — the membership table is the source of truth.

func (*Store) AddCaseTicket added in v0.2.0

func (s *Store) AddCaseTicket(ctx context.Context, t CaseTicket) error

AddCaseTicket links a case to an external tracker.

func (*Store) AddHypothesis added in v0.2.0

func (s *Store) AddHypothesis(ctx context.Context, caseID, author string, h Hypothesis) error

AddHypothesis records a belief and its confidence.

func (*Store) AddNextAction added in v0.2.0

func (s *Store) AddNextAction(ctx context.Context, caseID, author string, a NextAction) error

AddNextAction records a checklist item.

func (*Store) AddNote

func (s *Store) AddNote(ctx context.Context, note Note) (string, error)

AddNote adds or updates a note (supports color and linking)

func (*Store) ApplyEnrichment

func (s *Store) ApplyEnrichment(ctx context.Context, eventID string, enrichment Enrichment) error

ApplyEnrichment applies enrichment data to an event

func (*Store) AssignEventToCase

func (s *Store) AssignEventToCase(ctx context.Context, eventID, caseID string) error

AssignEventToCase attaches an event to a case as supporting evidence.

Membership lives in case_members, so an event can belong to more than one case; the events.case_id column is kept in step for readers that have not migrated yet.

func (*Store) AssignFindingToCase added in v0.2.0

func (s *Store) AssignFindingToCase(ctx context.Context, findingID, caseID string) error

AssignFindingToCase makes a finding one of the things a case is about.

Findings join as members rather than evidence: a case is *about* its detections and merely *supported by* the raw events pulled in around them.

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection

func (*Store) CountActiveCases added in v0.2.0

func (s *Store) CountActiveCases(ctx context.Context) (ActiveCases, error)

CountActiveCases returns the active case count, how many are being investigated, and when the oldest was opened.

func (*Store) CountCaseMembers added in v0.2.0

func (s *Store) CountCaseMembers(ctx context.Context, caseID string) (CaseCounts, error)

CountCaseMembers reports how many findings and events a case holds.

func (*Store) CountEvents added in v0.2.0

func (s *Store) CountEvents(ctx context.Context, f EventFilter) (int, error)

CountEvents returns the number of events matching the filter, ignoring Limit/Offset.

func (*Store) CountEventsByObservable added in v0.2.0

func (s *Store) CountEventsByObservable(ctx context.Context, typeID int, value string) (int, error)

CountEventsByObservable reports how many events carry an indicator, for the "have I seen this before?" answer without loading the events.

func (*Store) CountEventsFiltered deprecated

func (s *Store) CountEventsFiltered(
	ctx context.Context,
	caseID string,
	start, end time.Time,
	severities []string,
	types []string,
) (int, error)

CountEventsFiltered returns the total count of events matching the same filters as GetEventsFiltered.

Deprecated: use CountEvents with an EventFilter, which also supports class_uid.

func (*Store) CountEventsToday added in v0.2.0

func (s *Store) CountEventsToday(ctx context.Context, now time.Time) (int, error)

CountEventsToday returns the number of events since local midnight.

Local midnight, not UTC: "today" on a dashboard means the analyst's today.

func (*Store) CountFindings added in v0.2.0

func (s *Store) CountFindings(ctx context.Context, f FindingFilter) (int, error)

CountFindings reports how many findings match the filter.

func (*Store) CountFindingsByObservable added in v0.2.0

func (s *Store) CountFindingsByObservable(ctx context.Context, typeID int, value string) (int, error)

CountFindingsByObservable reports how many findings carry an indicator.

func (*Store) CountObservables added in v0.2.0

func (s *Store) CountObservables(ctx context.Context) (int, error)

CountObservables returns the number of distinct indicators, counted by identity — `(type_id, value)` — not by sighting. The same IP seen in four hundred events is one indicator.

func (*Store) CountOpenFindings added in v0.2.0

func (s *Store) CountOpenFindings(ctx context.Context) (OpenFindings, error)

CountOpenFindings returns the number of findings still needing attention, broken down by severity.

One query rather than six: a card that shows a total and a breakdown taken at different instants can show a breakdown that does not sum to its own total.

func (*Store) CreateOrUpdateCase

func (s *Store) CreateOrUpdateCase(ctx context.Context, case_ Case) (string, error)

CreateOrUpdateCase creates a new case or updates an existing one

func (*Store) DeleteCaseAndUnassign

func (s *Store) DeleteCaseAndUnassign(ctx context.Context, caseID string) error

DeleteCaseAndUnassign deletes a case and unassigns all its events (sets events.case_id=NULL). This keeps events accessible under ALL EVENTS after the case is removed.

func (*Store) DeleteEvents

func (s *Store) DeleteEvents(ctx context.Context, ids []string) error

DeleteEvents deletes events by IDs along with their enrichments, then updates event_count for any affected cases. Deletion is executed in a single transaction. Note: enrichments table has a FK to events without ON DELETE CASCADE, so we must delete enrichments explicitly before deleting events.

func (*Store) DeleteFindings added in v0.2.0

func (s *Store) DeleteFindings(ctx context.Context, ids []string) error

DeleteFindings removes findings by ID.

func (*Store) DeleteNote

func (s *Store) DeleteNote(ctx context.Context, noteID string) error

DeleteNote deletes a note

func (*Store) FindEventsByObservable added in v0.2.0

func (s *Store) FindEventsByObservable(ctx context.Context, typeID int, value string, limit int) ([]Event, error)

FindEventsByObservable returns every event carrying the given indicator. This is the indicator-pivot primitive: with the (type_id, value) index it is a single indexed lookup rather than a scan over every event's text.

A typeID of ocsf.ObservableTypeUnknown matches on value alone.

func (*Store) FindFindingsByObservable added in v0.2.0

func (s *Store) FindFindingsByObservable(ctx context.Context, typeID int, value string, limit int) ([]Finding, error)

FindFindingsByObservable returns every finding carrying the given indicator.

func (*Store) GetAllEvents

func (s *Store) GetAllEvents(ctx context.Context, limit int) ([]Event, error)

GetAllEvents returns all events ordered by timestamp

func (*Store) GetAuditEntries

func (s *Store) GetAuditEntries(ctx context.Context, caseID string, limit int) ([]AuditEntry, error)

GetAuditEntries retrieves audit entries for a case

func (*Store) GetBriefing added in v0.2.0

func (s *Store) GetBriefing(ctx context.Context, caseID string) (Briefing, error)

GetBriefing assembles a case's briefing from its notes.

Malformed entries are skipped rather than failing the whole briefing: one bad row written by an older build must not blank the screen.

func (*Store) GetCase added in v0.2.0

func (s *Store) GetCase(ctx context.Context, caseID string) (*Case, error)

GetCase returns a single case by ID, or nil when it does not exist.

func (*Store) GetCaseEventMembers added in v0.2.0

func (s *Store) GetCaseEventMembers(ctx context.Context, caseID string) ([]Event, error)

GetCaseEventMembers returns the events attached to a case as evidence.

func (*Store) GetCaseFindings added in v0.2.0

func (s *Store) GetCaseFindings(ctx context.Context, caseID string) ([]Finding, error)

GetCaseFindings returns the findings a case is about.

func (*Store) GetCaseIndicators added in v0.2.0

func (s *Store) GetCaseIndicators(ctx context.Context, caseID string) ([]CaseIndicator, error)

GetCaseIndicators aggregates a case's observables by identity.

One query over the (type_id, value) index rather than a text scan of the records: the same address seen forty times is one indicator with forty sightings, and finding that out should not mean reading forty rows.

MIN(source) picks "asserted" over "derived" alphabetically, which is the answer we want: if any sighting came from the producer, the indicator is asserted.

func (*Store) GetCaseMembers added in v0.2.0

func (s *Store) GetCaseMembers(ctx context.Context, caseID string) ([]CaseMember, error)

GetCaseMembers returns every membership row for a case.

func (*Store) GetCaseTickets added in v0.2.0

func (s *Store) GetCaseTickets(ctx context.Context, caseID string) ([]CaseTicket, error)

GetCaseTickets returns the external trackers linked to a case.

func (*Store) GetCasesForMember added in v0.2.0

func (s *Store) GetCasesForMember(ctx context.Context, memberType, memberID string) ([]Case, error)

GetCasesForMember returns every case containing the given item.

This is what one-to-one membership could not express: an alert routinely belongs to both the incident it triggered and a longer-running campaign case.

func (*Store) GetEnrichmentsByEvent

func (s *Store) GetEnrichmentsByEvent(ctx context.Context, eventID string) ([]Enrichment, error)

GetEnrichmentsByEvent returns all enrichments associated with an event (newest first)

func (*Store) GetEvents added in v0.2.0

func (s *Store) GetEvents(ctx context.Context, f EventFilter) ([]Event, error)

GetEvents returns events matching the filter, ordered by timestamp DESC. When Limit is 0, all matching rows are returned.

func (*Store) GetEventsByCase

func (s *Store) GetEventsByCase(ctx context.Context, caseID string) ([]Event, error)

GetEventsByCase returns the events attached to a case as evidence.

func (*Store) GetEventsByTimeRange

func (s *Store) GetEventsByTimeRange(ctx context.Context, caseID string, start, end time.Time, limit int) ([]Event, error)

GetEventsByTimeRange returns events filtered by optional case and time range

func (*Store) GetEventsFiltered deprecated

func (s *Store) GetEventsFiltered(
	ctx context.Context,
	caseID string,
	start, end time.Time,
	severities []string,
	types []string,
	limit, offset int,
) ([]Event, error)

GetEventsFiltered returns events filtered by optional case, time range, severity list, type list, with pagination via limit/offset. Results are ordered by timestamp DESC. When limit is 0, all matching rows are returned (no LIMIT/OFFSET).

Deprecated: use GetEvents with an EventFilter, which also supports class_uid.

func (*Store) GetFindingByUID added in v0.2.0

func (s *Store) GetFindingByUID(ctx context.Context, findingUID string) (*Finding, error)

GetFindingByUID looks a finding up by its OCSF finding_info.uid.

func (*Store) GetFindings added in v0.2.0

func (s *Store) GetFindings(ctx context.Context, f FindingFilter) ([]Finding, error)

GetFindings returns findings matching the filter, most recently seen first.

func (*Store) GetLastEvent added in v0.2.0

func (s *Store) GetLastEvent(ctx context.Context) (time.Time, bool, error)

GetLastEvent returns the timestamp of the most recent event, and false when the database holds none.

This is the freshness signal in the header. "No events yet" and "last event 4 hours ago" are different problems, and both are different from a stalled watcher, so the caller needs to tell them apart.

func (*Store) GetNotes

func (s *Store) GetNotes(ctx context.Context, caseID string) ([]Note, error)

GetNotes retrieves notes for a case (returns color and linking metadata)

func (*Store) GetObservablesByEvent added in v0.2.0

func (s *Store) GetObservablesByEvent(ctx context.Context, eventID string) ([]Observable, error)

GetObservablesByEvent returns every observable recorded for an event.

func (*Store) GetObservablesByFinding added in v0.2.0

func (s *Store) GetObservablesByFinding(ctx context.Context, findingID string) ([]Observable, error)

GetObservablesByFinding returns every observable recorded for a finding.

func (*Store) GetObservablesForEvents added in v0.2.0

func (s *Store) GetObservablesForEvents(ctx context.Context, eventIDs []string) (map[string][]Observable, error)

GetObservablesForEvents returns observables for many events in one query, keyed by event ID. The IOC view needs the whole case at once; issuing a query per event would make it O(n) round trips.

func (*Store) GetPinnedMemberIDs added in v0.2.0

func (s *Store) GetPinnedMemberIDs(ctx context.Context, caseID, memberType string) (map[string]bool, error)

GetPinnedMemberIDs returns the pinned member ids of one type for a case.

Ids rather than rows: the caller already holds the members, and the briefing needs only to know which of them carry a star.

func (*Store) GetPriorityQueue added in v0.2.0

func (s *Store) GetPriorityQueue(ctx context.Context, limit int) ([]Finding, error)

GetPriorityQueue returns the highest-priority open findings, most urgent first.

func (*Store) GetRecentCases added in v0.2.0

func (s *Store) GetRecentCases(ctx context.Context, limit int) ([]Case, error)

GetRecentCases returns the most recently updated cases.

By update rather than by creation, which is what ListCases orders on: the case an analyst touched ten minutes ago is the one they are resuming, not the one they opened first.

func (*Store) ListCases

func (s *Store) ListCases(ctx context.Context) ([]Case, error)

ListCases returns all cases

func (*Store) LogCaseAction

func (s *Store) LogCaseAction(ctx context.Context, caseID, action, actor string, details map[string]interface{}) error

LogCaseAction logs a case-related action

func (*Store) LogCopilotQuery

func (s *Store) LogCopilotQuery(ctx context.Context, caseID, actor, query, response string, tokens int, cost float64) error

LogCopilotQuery logs a Copilot query with token/cost information

func (*Store) LogEventAction

func (s *Store) LogEventAction(ctx context.Context, caseID, eventID, action, actor string, details map[string]interface{}) error

LogEventAction logs an event-related action

func (*Store) OnEnrichment added in v0.2.0

func (s *Store) OnEnrichment(fn func(eventID string))

OnEnrichment registers fn to run after an enrichment is applied to an event.

Enrichment is asynchronous and, in standalone mode, the bus is a no-op — so there is nothing for a view to subscribe to. This is the in-process alternative. Callbacks run on the goroutine that applied the enrichment, usually an enrichment worker, so fn must not block: hand off and return.

func (*Store) RefreshCaseCounts added in v0.2.0

func (s *Store) RefreshCaseCounts(ctx context.Context, caseID string) error

RefreshCaseCounts syncs the denormalized counts on the case row.

func (*Store) RemoveCaseMember added in v0.2.0

func (s *Store) RemoveCaseMember(ctx context.Context, caseID, memberType, memberID string) error

RemoveCaseMember unlinks an item from a case.

func (*Store) SaveEvent

func (s *Store) SaveEvent(ctx context.Context, ocsfEvent *ocsf.Event) (string, error)

SaveEvent saves an OCSF event to the database

func (*Store) SaveFinding added in v0.2.0

func (s *Store) SaveFinding(ctx context.Context, f *ocsf.Finding) (string, error)

SaveFinding writes a finding, updating in place when one with the same finding_info.uid already exists.

This is what makes activity_id meaningful: a finding arrives repeatedly as Create, then Update, then Close. Appending each arrival would turn a single alert into a queue full of near-duplicates.

func (*Store) SaveRecord added in v0.2.0

func (s *Store) SaveRecord(ctx context.Context, rec ingestRecord) (SavedRecord, error)

SaveRecord persists a parsed OCSF record, routing it to the events table, the findings table, or both. Ingest paths call this rather than SaveEvent so the routing decision lives in one place.

func (*Store) SearchEvents

func (s *Store) SearchEvents(ctx context.Context, query string, limit int) ([]Event, error)

SearchEvents performs full-text search on events (falls back to LIKE if FTS unavailable)

func (*Store) SetHypotheses added in v0.2.0

func (s *Store) SetHypotheses(ctx context.Context, caseID, author string, hs []Hypothesis) error

SetHypotheses replaces the whole set, for the same reason as SetNextActions.

func (*Store) SetMemberPinned added in v0.2.0

func (s *Store) SetMemberPinned(ctx context.Context, caseID, memberType, memberID string, pinned bool) error

SetMemberPinned marks or unmarks a case member as pinned.

func (*Store) SetNextActions added in v0.2.0

func (s *Store) SetNextActions(ctx context.Context, caseID, author string, actions []NextAction) error

SetNextActions replaces the whole checklist, which is what ticking an item amounts to when the items have no ids of their own.

func (*Store) SetStatement added in v0.2.0

func (s *Store) SetStatement(ctx context.Context, caseID, author, text string) error

SetStatement records the incident statement, replacing any previous one.

func (*Store) SetupAuditTables

func (s *Store) SetupAuditTables() error

SetupAuditTables creates the audit and notes tables if they don't exist

func (*Store) UpdateCaseEventCount

func (s *Store) UpdateCaseEventCount(ctx context.Context, caseID string) error

UpdateCaseEventCount recalculates and persists the event_count for the given case. UpdateCaseEventCount syncs a case's denormalized counts from its membership.

func (*Store) UpdateCaseStatus added in v0.2.0

func (s *Store) UpdateCaseStatus(ctx context.Context, caseID string, statusID int) error

UpdateCaseStatus records a lifecycle transition, keeping the label and the OCSF status_id in step.

func (*Store) UpdateCaseTriage added in v0.2.0

func (s *Store) UpdateCaseTriage(ctx context.Context, caseID string, priorityID, impactID int, suspectedBreach bool) error

UpdateCaseTriage sets the incident-profile triage fields together.

func (*Store) UpdateCaseVerdict added in v0.2.0

func (s *Store) UpdateCaseVerdict(ctx context.Context, caseID string, verdictID int) error

UpdateCaseVerdict records the analyst's conclusion about a case.

func (*Store) UpdateFindingStatus added in v0.2.0

func (s *Store) UpdateFindingStatus(ctx context.Context, findingID string, statusID int) error

UpdateFindingStatus records an analyst's triage decision.

func (*Store) UpdateFindingVerdict added in v0.2.0

func (s *Store) UpdateFindingVerdict(ctx context.Context, findingID string, verdictID int) error

UpdateFindingVerdict records an analyst's true/false-positive judgement.

Jump to

Keyboard shortcuts

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