Documentation
¶
Overview ¶
Package detect implements passive vulnerability detection over HTTP traffic already captured by the proxy. It reads proxy.CapturedRequest records and reports findings; it never sends requests of its own.
Two kinds of check run against each captured message:
- Regex rules (KindRegex), from rules_builtin.go or created by the operator: a pattern plus gates (content type, status, scheme) and named post-filters that validate the captured group in Go.
- Analyzers (KindAnalyzer), Go functions in analyzers.go, for absence and relational logic a regex cannot express — a missing header, a cookie flag matrix, an Access-Control-Allow-Origin that echoes the request.
Both converge on Engine.newFinding, the only code path that produces a Finding.
Patterns compile with Go's regexp (RE2): no lookahead, no lookbehind, no backreferences. PostFilters is the escape hatch for negative conditions.
Index ¶
- Variables
- func FindingID(ruleID, host, dim string) string
- func PostFilterNames() []string
- func RedactValue(s string) string
- func ShannonEntropy(s string) float64
- func ValidateRule(r *Rule) error
- type Analyzer
- type AnalyzerHit
- type Category
- type Confidence
- type Config
- type Engine
- func (e *Engine) ActiveRuleCount() int
- func (e *Engine) AddRule(r Rule) (Rule, error)
- func (e *Engine) Config() Config
- func (e *Engine) DisabledBuiltins() []string
- func (e *Engine) IsEnabled() bool
- func (e *Engine) RemoveRule(id string) error
- func (e *Engine) ResetRule(id string) error
- func (e *Engine) Rule(id string) (Rule, bool)
- func (e *Engine) RuleEnabledFunc() func(string) bool
- func (e *Engine) Rules() []Rule
- func (e *Engine) Scan(r *proxy.CapturedRequest, inScope proxy.ScopeFunc) []Finding
- func (e *Engine) SetConfig(cfg Config)
- func (e *Engine) SetDisabledBuiltins(ids []string)
- func (e *Engine) SetEnabled(v bool)
- func (e *Engine) SetRuleEnabled(id string, enabled bool) error
- func (e *Engine) SetRuleSeverity(id string, sev Severity) error
- func (e *Engine) SetSeverityOverrides(in map[string]string)
- func (e *Engine) SetUserRules(rules []Rule)
- func (e *Engine) SeverityOverrides() map[string]string
- func (e *Engine) UpdateRule(id string, r Rule) (Rule, error)
- func (e *Engine) UserRules() []Rule
- type Finding
- type FindingFilter
- type FindingSummary
- type GroupBy
- type Message
- type Occurrence
- type RescanRequest
- type Rule
- type RuleKind
- type ScanStatus
- type Scanner
- type Severity
- type Store
- func (s *Store) All() []Finding
- func (s *Store) Clear() int
- func (s *Store) Count() int
- func (s *Store) Delete(id string) bool
- func (s *Store) DeleteFalsePositives() int
- func (s *Store) Generation() uint64
- func (s *Store) Get(id string) (Finding, bool)
- func (s *Store) List(f FindingFilter, ruleEnabled func(string) bool) ([]Finding, int)
- func (s *Store) Load(findings []Finding)
- func (s *Store) NextGeneration() uint64
- func (s *Store) NoteScanned(n int)
- func (s *Store) NoteSkipped(reason string)
- func (s *Store) PurgeBelowGeneration(gen uint64) int
- func (s *Store) Revision() uint64
- func (s *Store) Summary(ruleEnabled func(string) bool) Summary
- func (s *Store) Update(id string, falsePositive *bool, notes *string, severity *Severity) (Finding, bool)
- func (s *Store) Upsert(f Finding) (*Finding, bool)
- type Summary
- type Target
Constants ¶
This section is empty.
Variables ¶
var DefaultOHTTPContentTypes = []string{"message/ohttp-", "application/ohttp-keys"}
DefaultOHTTPContentTypes are the Oblivious HTTP media types never scanned. OHTTP bodies are HPKE-encrypted to a gateway key the proxy never holds, so they are opaque by design rather than a codec gap; skipping them by content type keeps them out of the skippedBinary "unreadable" count. Exported so the project-config backfill adds the same values these defaults ship.
var ErrBuiltinImmutable = errors.New("built-in rules cannot be edited or deleted")
ErrBuiltinImmutable is returned when an edit or delete targets a built-in rule. Built-ins accept only an enabled toggle and a severity override.
var ErrRuleNotFound = errors.New("rule not found")
ErrRuleNotFound is returned when no rule has the given ID.
var ErrScanRunning = errors.New("a scan is already running")
ErrScanRunning is returned when a rescan is already in progress.
Functions ¶
func FindingID ¶
FindingID is the dedupe identity: it contains no request ID, timestamp or seq, so rescanning reproduces byte-identical IDs. Exported so a caller outside the engine can mint a finding in the same identity space.
func PostFilterNames ¶
func PostFilterNames() []string
PostFilterNames returns the registered post-filter names, for API validation of operator-supplied rules.
func RedactValue ¶
RedactValue exposes the masking helper for the rule tester.
func ShannonEntropy ¶
ShannonEntropy returns the Shannon entropy of s in bits per character. Used to validate a value a pattern already matched, never as a detector on its own. For calibration: random base64 scores around 6.0, random hex around 4.0, and English prose 4.0 to 4.5.
func ValidateRule ¶
ValidateRule checks an operator-supplied rule, returning a message suitable for a 400 response. The RE2 compile error is surfaced verbatim.
Types ¶
type Analyzer ¶
type Analyzer func(m *Message, emit func(AnalyzerHit))
Analyzer inspects a whole message and emits zero or more hits, covering the checks a regex cannot express: the absence of a header, a relationship between request and response headers, or a decision requiring a parsed value.
type AnalyzerHit ¶
type AnalyzerHit struct {
Detail string
Evidence string
Severity Severity
Confidence Confidence
// GroupExtra adds a dimension to the dedupe key, e.g. a cookie name for a
// per-cookie rule.
GroupExtra string
// Offset is relative to the buffer named by OffsetIn, not to the raw document;
// newFinding translates it. An unset OffsetIn means no offset.
Offset int
OffsetIn Target
// OffsetLen is the length of the matched region at Offset, which is not
// len(Evidence): an analyzer's Evidence is a synthesized description, not the
// matched bytes. Zero means no usable region, and the finding reports no
// offset.
OffsetLen int
Part string
// contains filtered or unexported fields
}
AnalyzerHit is one result emitted by an analyzer. Empty Severity and Confidence inherit the rule's own values; an analyzer sets them only to escalate or downgrade a specific case.
type Category ¶
type Category string
Category groups rules for filtering and for the Rules UI.
func AllCategories ¶
func AllCategories() []Category
AllCategories lists every category in display order.
type Confidence ¶
type Confidence string
Confidence describes whether a match is what the rule claims it is, independent of Severity.
const ( ConfidenceHigh Confidence = "high" ConfidenceMedium Confidence = "medium" ConfidenceLow Confidence = "low" )
func (Confidence) Valid ¶
func (c Confidence) Valid() bool
Valid reports whether c is a known confidence level.
type Config ¶
type Config struct {
// ScopeOnly limits scanning to in-scope requests. On by default.
ScopeOnly bool `json:"scopeOnly"`
// ScanRequests enables request-side targets (Basic auth, credentials in
// query strings, keys posted by the app).
ScanRequests bool `json:"scanRequests"`
// PersistFindings snapshots findings into the project file, preserving
// false-positive marks and notes. On by default.
PersistFindings bool `json:"persistFindings"`
// ClearFindingsWithHistory clears findings when request history is cleared.
// Off by default.
ClearFindingsWithHistory bool `json:"clearFindingsWithHistory"`
MaxBodyScanBytes int `json:"maxBodyScanBytes"`
MaxRequestBodyScanBytes int `json:"maxRequestBodyScanBytes"`
// SkipContentTypes and SkipExtensions are MIME prefixes and URL suffixes
// never scanned. .js and .css are not in either list.
SkipContentTypes []string `json:"skipContentTypes"`
SkipExtensions []string `json:"skipExtensions"`
// ExcludeHosts suppresses findings whose host contains any of these
// substrings.
ExcludeHosts []string `json:"excludeHosts"`
}
Config holds the tunable engine settings that ride with a project.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the shipped engine configuration.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine holds the rule set and produces findings from captured messages.
Built-in rules are immutable: builtinRules() returns a fresh slice, operator changes are held separately as a disabled-ID set plus a severity-override map, and the API rejects edits and deletes of built-ins.
func NewEngine ¶
func NewEngine() *Engine
NewEngine returns an enabled engine with the built-in library loaded and the default configuration.
func (*Engine) ActiveRuleCount ¶
ActiveRuleCount returns how many rules are currently compiled and live.
func (*Engine) DisabledBuiltins ¶
DisabledBuiltins returns the IDs of built-in rules the operator turned off.
func (*Engine) RemoveRule ¶
RemoveRule deletes an operator rule.
func (*Engine) RuleEnabledFunc ¶
RuleEnabledFunc returns a predicate over rule IDs, snapshotted into a map for callers filtering large finding sets. An unknown ID counts as enabled, so a finding whose custom rule was deleted stays visible.
func (*Engine) Rules ¶
Rules returns every rule, built-in and user, as copies with Enabled and Severity resolved into one flat list.
func (*Engine) Scan ¶
Scan runs every live rule against a captured request and returns the findings. It performs no deduplication; that is Store.Upsert's job.
func (*Engine) SetDisabledBuiltins ¶
SetDisabledBuiltins replaces the disabled-rule set (project load).
func (*Engine) SetEnabled ¶
SetEnabled turns detection on or off.
func (*Engine) SetRuleEnabled ¶
SetRuleEnabled toggles any rule, built-in or user.
func (*Engine) SetRuleSeverity ¶
SetRuleSeverity overrides a rule's severity. Allowed on built-ins.
func (*Engine) SetSeverityOverrides ¶
SetSeverityOverrides replaces the severity override map (project load).
func (*Engine) SetUserRules ¶
SetUserRules replaces all operator rules (project load).
func (*Engine) SeverityOverrides ¶
SeverityOverrides returns the operator's per-rule severity overrides.
func (*Engine) UpdateRule ¶
UpdateRule replaces an operator rule in place. The ID must be preserved: Finding.RuleID references it and Finding.ID derives from it.
type Finding ¶
type Finding struct {
ID string `json:"id"`
RuleID string `json:"ruleId"`
RuleName string `json:"ruleName"`
Category Category `json:"category"`
Severity Severity `json:"severity"`
Confidence Confidence `json:"confidence"`
Target Target `json:"target"`
Host string `json:"host"`
Method string `json:"method"`
URL string `json:"url"`
RequestID string `json:"requestId"`
// Detail is a short human-readable qualifier, e.g. the cookie name for a
// cookie rule or the matched product for a fingerprint rule.
Detail string `json:"detail,omitempty"`
// Evidence is the redacted, truncated, control-character-escaped snippet
// rendered by every list and table.
Evidence string `json:"evidence"`
// RawEvidence is the unmasked matched value, stored verbatim and populated
// only for rules that redact. Redaction is a display control; the operator
// can reveal this per finding.
RawEvidence string `json:"rawEvidence,omitempty"`
EvidenceOffset int `json:"evidenceOffset"`
EvidenceLength int `json:"evidenceLength"`
EvidencePart string `json:"evidencePart,omitempty"`
FirstSeen time.Time `json:"firstSeen"`
LastSeen time.Time `json:"lastSeen"`
Count int `json:"count"`
Occurrences []Occurrence `json:"occurrences,omitempty"`
FalsePositive bool `json:"falsePositive"`
Notes string `json:"notes,omitempty"`
// Truncated marks a finding produced from a body that hit a scan size cap:
// the scan was not exhaustive for that response.
Truncated bool `json:"truncated,omitempty"`
// SeverityOverridden records that an operator changed Severity by hand. A
// rescan leaves it alone.
SeverityOverridden bool `json:"severityOverridden,omitempty"`
// contains filtered or unexported fields
}
Finding is one deduplicated detection result.
ID is a deterministic group hash:
hex(sha256(ruleID \x00 host \x00 groupDim))[:32]
It carries no request ID, timestamp, or sequence number, so rescanning the same traffic reproduces byte-identical IDs. Rescan is therefore idempotent, live merges are a map upsert, and persisted findings reload without ID remapping.
type FindingFilter ¶
type FindingFilter struct {
Severities []string
MinSeverity string
Categories []string
RuleID string
Host string
Search string
Confidence string
// FP selects false-positive handling: "false" (default) hides them, "true"
// shows only them, "all" shows everything.
FP string
// IncludeDisabled includes findings whose rule is currently switched off.
// Disabling a rule retains its findings; this toggle controls visibility.
IncludeDisabled bool
Sort string // "severity" (default) | "lastSeen" | "firstSeen" | "count"
Dir string // "desc" (default) | "asc"
Offset int
Limit int
}
FindingFilter holds the criteria for listing findings.
type FindingSummary ¶
type FindingSummary struct {
ID string `json:"id"`
RuleID string `json:"ruleId"`
RuleName string `json:"ruleName"`
Category Category `json:"category"`
Severity Severity `json:"severity"`
Confidence Confidence `json:"confidence"`
Target Target `json:"target"`
Host string `json:"host"`
Method string `json:"method"`
URL string `json:"url"`
RequestID string `json:"requestId"`
Detail string `json:"detail,omitempty"`
Evidence string `json:"evidence"`
RawEvidence string `json:"rawEvidence,omitempty"`
EvidenceOffset int `json:"evidenceOffset"`
EvidenceLength int `json:"evidenceLength"`
EvidencePart string `json:"evidencePart,omitempty"`
Count int `json:"count"`
FirstSeen time.Time `json:"firstSeen"`
LastSeen time.Time `json:"lastSeen"`
FalsePositive bool `json:"falsePositive"`
HasNotes bool `json:"hasNotes"`
Truncated bool `json:"truncated,omitempty"`
}
FindingSummary is the row shape sent to the UI and returned by list endpoints. It omits occurrences and the internal dedupe bookkeeping.
func Summaries ¶
func Summaries(in []Finding) []FindingSummary
Summaries projects a slice of findings.
type GroupBy ¶
type GroupBy string
GroupBy selects how repeated matches collapse into a single finding.
const ( // GroupByEvidence: one finding per distinct matched value on a host. Used by // secrets and PII rules. GroupByEvidence GroupBy = "evidence" // GroupByURL: one finding per path on a host. Used by panels, directory // listings, and interesting files. GroupByURL GroupBy = "url" // GroupByHost: one finding per host. Used by header, cookie, and // fingerprint rules. GroupByHost GroupBy = "host" )
type Message ¶
type Message struct {
Req *proxy.CapturedRequest
URL *url.URL
Scheme string
Host string
Path string
RespStatus int
RespHeader http.Header
RespRawHdr []byte
RespBody []byte
ReqHeader http.Header
ReqRawHdr []byte
ReqBody []byte
ContentType string
// BodyScannable reports whether RespBody is worth matching against.
BodyScannable bool
// SkipReason explains why not: "binary", "encoding:br", "content-type",
// "extension", "empty", or "no-response".
SkipReason string
// Truncated marks that a body hit a scan size cap, so findings from this
// message cannot claim to be exhaustive.
Truncated bool
// RespBodyStart and ReqBodyStart are where each body begins inside the
// corresponding raw document, used to turn a body-relative match offset into
// an offset into the document the UI renders.
RespBodyStart int
ReqBodyStart int
// RespBodyDecoded records that the body was decompressed for scanning. RespBody
// and RespRaw then share no coordinate system, so no body offset is reported.
RespBodyDecoded bool
// contains filtered or unexported fields
}
Message is a captured request/response pair decomposed into the buffers rules are matched against. Parse handles the raw-byte quirks so rules do not have to:
- RespRaw is Content-Length framed with a dechunked body (every capture helper in internal/proxy clears TransferEncoding), but proxy hook plugins can substitute their own bytes, so an LF-only header terminator is also accepted.
- Bodies may arrive gzip- or deflate-encoded with Content-Encoding intact; Parse decompresses them.
- Brotli and zstd bodies are marked unscannable and counted.
func Parse ¶
func Parse(r *proxy.CapturedRequest, cfg Config) *Message
Parse decomposes a captured request into a Message, applying the false-positive and cost gates from cfg. It never returns nil.
func (*Message) IsHTMLDocument ¶
IsHTMLDocument reports whether the response is an HTML page. Document-level header analyzers (CSP, frame-options) gate on this.
func (*Message) SetCookies ¶
SetCookies returns the raw Set-Cookie header values.
type Occurrence ¶
type Occurrence struct {
RequestID string `json:"requestId"`
Seq int `json:"seq"`
Method string `json:"method"`
URL string `json:"url"`
StatusCode int `json:"statusCode"`
Timestamp time.Time `json:"timestamp"`
// Offset is the byte offset of the match within the scanned part, used to
// highlight the evidence in the response viewer.
Offset int `json:"offset"`
// Part names which buffer Offset indexes into, so the UI knows whether to
// highlight in the request or the response.
Part string `json:"part"`
}
Occurrence records one sighting of a finding.
type RescanRequest ¶
type RescanRequest struct {
// Scope is "all" (every captured request) or "host".
Scope string `json:"scope"`
Host string `json:"host,omitempty"`
// Purge drops findings this pass does not re-confirm, except triaged ones.
Purge bool `json:"purge,omitempty"`
}
RescanRequest describes an on-demand scan.
type Rule ¶
type Rule struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Remediation string `json:"remediation,omitempty"`
Kind RuleKind `json:"kind"`
Category Category `json:"category"`
Severity Severity `json:"severity"`
Confidence Confidence `json:"confidence"`
Target Target `json:"target"`
// Pattern is the RE2 source for KindRegex rules.
Pattern string `json:"pattern,omitempty"`
// Literal is a case-insensitive substring prescreen: when set, the regex only
// runs if the haystack contains it. Must appear in every string the pattern
// can match, or the rule silently never fires.
Literal string `json:"literal,omitempty"`
// Literals is an any-of prescreen for alternation rules: when set, the regex
// only runs if the haystack contains at least one of these case-insensitive
// substrings. Every branch of the pattern must be covered by one of them, or
// matches from an uncovered branch are silently missed. Used by the WAF
// fingerprint rules, whose broad alternations have no single common Literal.
// Literal and Literals are independent; a rule with both must satisfy both.
Literals []string `json:"literals,omitempty"`
// CaptureGroup selects which submatch becomes the evidence (0 = whole match).
CaptureGroup int `json:"captureGroup,omitempty"`
// PostFilters name validators in the postfilters.go registry, run in order
// against the captured group.
PostFilters []string `json:"postFilters,omitempty"`
// Analyzer names a function in the analyzers.go registry (KindAnalyzer only).
Analyzer string `json:"analyzer,omitempty"`
// GroupBy selects the dedupe collapse mode. Empty means GroupByEvidence.
GroupBy GroupBy `json:"groupBy,omitempty"`
// ContentTypes gates body rules to simplified content-type keywords
// ("json", "html", "xml", "csv", "plain", "js", "css"). Empty means any
// scannable body.
ContentTypes []string `json:"contentTypes,omitempty"`
// ExcludeContentTypes is the inverse of ContentTypes and takes precedence
// over it: the rule never runs against these keywords. Rules that look for
// data rather than a format use this instead of ContentTypes.
ExcludeContentTypes []string `json:"excludeContentTypes,omitempty"`
// StatusCodes gates the rule with a proxy status expression, e.g.
// "200,301,302,401,403". Empty means any status.
StatusCodes string `json:"statusCodes,omitempty"`
// Scheme gates the rule to "http" or "https". Empty means either.
Scheme string `json:"scheme,omitempty"`
// MinLength rejects captured groups shorter than this.
MinLength int `json:"minLength,omitempty"`
// MinEntropy rejects captured groups below this Shannon entropy in bits per
// character. 0 disables the check.
MinEntropy float64 `json:"minEntropy,omitempty"`
// MaxPerResponse caps findings per message (0 means defaultMaxPerResponse).
MaxPerResponse int `json:"maxPerResponse,omitempty"`
// RedactEvidence masks the middle of the captured value before storing it.
RedactEvidence bool `json:"redactEvidence,omitempty"`
Builtin bool `json:"builtin"`
Enabled bool `json:"enabled"`
// contains filtered or unexported fields
}
Rule is a single detection check. Built-in rules use stable string IDs, which operator toggles, severity overrides, and Finding.RuleID reference across restarts and project reloads; user rules get a generated ID.
type RuleKind ¶
type RuleKind string
RuleKind distinguishes a declarative pattern from a Go analyzer function. Operators may only create KindRegex rules; KindAnalyzer rules are built in, because their behavior lives in code rather than in the rule record.
type ScanStatus ¶
type ScanStatus struct {
Running bool `json:"running"`
JobID string `json:"jobId,omitempty"`
Kind string `json:"kind,omitempty"`
Scanned int `json:"scanned"`
Total int `json:"total"`
FindingsNew int `json:"findingsNew"`
StartedAt time.Time `json:"startedAt,omitempty"`
FinishedAt time.Time `json:"finishedAt,omitempty"`
Status string `json:"status,omitempty"` // "running" | "complete" | "stopped"
}
ScanStatus reports rescan progress.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner drives detection over the proxy's capture store. It pulls on an interval rather than being called from the proxy's request path, so detection never runs on the goroutine serving the browser, and a rescan is the same code path with a different starting cursor.
func NewScanner ¶
func NewScanner(engine *Engine, findings *Store, store *proxy.Store, scope *proxy.Scope, broadcast chan<- any) *Scanner
NewScanner wires a scanner. scope may be nil (no scope gating available).
func (*Scanner) ResetCursor ¶
ResetCursor sets the live-scan watermark. Must be called wherever the proxy store rewrites its sequence numbering (Store.Clear, Store.LoadItems); a stale high cursor stops the loop from seeing any further request.
func (*Scanner) Run ¶
Run drives the live scan loop until ctx is cancelled. The work is in scanOnce, which is synchronous and has no timing dependency.
func (*Scanner) StartRescan ¶
func (s *Scanner) StartRescan(ctx context.Context, req RescanRequest) (ScanStatus, error)
StartRescan runs the rule set over already-captured traffic in the background. Findings are not cleared first; Store.Upsert merges the results and keeps counts stable.
func (*Scanner) Status ¶
func (s *Scanner) Status() ScanStatus
Status returns a copy of the current scan status.
type Severity ¶
type Severity string
Severity ranks how much a finding matters. The rubric for assigning it to a new rule:
- Info: not directly exploitable, and discloses nothing beyond a surface or an identity — exposed panels, missing headers, software and version fingerprints, analytics keys, identifiers that are not credentials.
- Low: a real if minor weakness, or disclosure of something more than an identity — an exposed configuration file, a verbose error or debug page, a directory listing, a disclosed filesystem path, a CORS or redirect misconfiguration.
- Medium: anything not covered by the other bands.
- High: account credentials, sensitive API keys, database connection strings, low-level PII (phone number, date of birth).
- Critical: high-grade PII (a national identity number or equivalent, or a payment card), or anything that alone leads to severe compromise — remote code execution, authentication bypass, a served database dump.
Every credential is High; none is Critical. A rule that detects a surface (an admin console, an absent header) is Info.
The Info/Low line runs through what the match itself gives away, which matters most for error pages: one that leaks source paths, stack frames, or settings is Low, while one that only names the framework or an exception class is Info.
Severity is orthogonal to Confidence: a low-confidence match on a national ID is still Critical, a certain match on a Server header is still Info.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds deduplicated findings. Every finding, from live scanning or from a rescan, enters through Upsert and is keyed on the engine's deterministic Finding.ID, so rescanning the same traffic merges rather than duplicates.
func (*Store) DeleteFalsePositives ¶
DeleteFalsePositives removes only findings marked as false positives.
func (*Store) Generation ¶
Generation returns the current scan generation.
func (*Store) Load ¶
Load replaces the store contents (project load). Findings keep the IDs they were persisted with; the ID is the dedupe identity.
func (*Store) NextGeneration ¶
NextGeneration advances the scan generation and returns the new value.
func (*Store) NoteScanned ¶
NoteScanned records that a message was scanned.
func (*Store) NoteSkipped ¶
NoteSkipped records a message the scanner could not read.
func (*Store) PurgeBelowGeneration ¶
PurgeBelowGeneration drops findings a scan pass did not re-confirm. Findings marked false-positive or carrying notes are always kept.
func (*Store) Summary ¶
Summary aggregates the store for the header and the summary event. It takes the same rule-enabled predicate as List so the counts match the table.
func (*Store) Update ¶
func (s *Store) Update(id string, falsePositive *bool, notes *string, severity *Severity) (Finding, bool)
Update applies operator edits to a finding. Passing nil for a field leaves it unchanged, so a PUT can carry any subset.
func (*Store) Upsert ¶
Upsert merges a finding into the store, returning the stored finding and whether it was newly created.
On a hit it bumps LastSeen, appends the occurrence, and increments Count only for an occurrence not already in occSeen, which keeps a rescan idempotent. Operator state (false-positive mark, notes, severity override) is preserved.
type Summary ¶
type Summary struct {
Total int `json:"total"`
BySeverity map[string]int `json:"bySeverity"`
ByCategory map[string]int `json:"byCategory"`
FalsePositives int `json:"falsePositives"`
// HiddenByDisabledRule counts findings whose rule is currently switched off.
// The findings table hides them by default.
HiddenByDisabledRule int `json:"hiddenByDisabledRule"`
SkippedEncoded int `json:"skippedEncoded"`
SkippedBinary int `json:"skippedBinary"`
Scanned int `json:"scanned"`
}
Summary aggregates the findings store for the dashboard header and for the detect.summary WebSocket event.
type Target ¶
type Target string
Target names the part of the message a rule is matched against.
const ( TargetResponseBody Target = "response_body" TargetResponseHeader Target = "response_header" TargetRequestBody Target = "request_body" TargetRequestHeader Target = "request_header" TargetURL Target = "url" // TargetMessage is used by analyzers, which receive the whole *Message and // decide for themselves what to inspect. TargetMessage Target = "message" )