risk

package
v0.0.0-...-c2977dc Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2026 License: AGPL-3.0 Imports: 101 Imported by: 0

Documentation

Index

Constants

View Source
const (

	// PolicyBypassTargetKindShadowMCPServer identifies a Shadow MCP server target.
	PolicyBypassTargetKindShadowMCPServer = "shadow_mcp_server"
	// PolicyBypassWholePolicyTargetKey identifies a whole-policy target.
	PolicyBypassWholePolicyTargetKey = "policy"
)
View Source
const (
	ShadowMCPDispositionBlockAll = shadowmcp.DispositionBlockAll
	ShadowMCPDispositionAllowAll = shadowmcp.DispositionAllowAll
)

Default dispositions for shadow MCP blocking policies, aliased from the shadowmcp package (which enforcement code uses directly). The disposition is immutable after create.

Variables

View Source
var (
	ErrInvalidFingerprintPepperJSON    = errors.New("invalid fingerprint pepper keyring json")
	ErrInvalidFingerprintPepperKeyRing = errors.New("invalid fingerprint pepper keyring")
	ErrFingerprintPepperKeyNotFound    = errors.New("fingerprint pepper key not found")
)

Functions

func Attach

func Attach(mux goahttp.Muxer, service *Service)

func CanBypassBatchWithGrants

func CanBypassBatchWithGrants(grants []authz.Grant, inputs []PolicyBypassEvaluation) map[PolicyBypassEvaluation]bool

CanBypassBatchWithGrants evaluates several bypasses against one already-loaded caller grant snapshot, canonicalizing legacy selectors only once.

func CanBypassWithGrants

func CanBypassWithGrants(grants []authz.Grant, input PolicyBypassEvaluation) bool

CanBypassWithGrants evaluates a bypass against an already-loaded caller grant snapshot. Callers that also need policy-audience checks use this to keep both decisions consistent under the same transaction snapshot.

func EncodeFingerprint

func EncodeFingerprint(sum []byte) string

EncodeFingerprint renders a fingerprint sum in the encoding stored on ClickHouse rows (unpadded base64url, matching the ingest writer).

func GeneratePolicyAckToken

func GeneratePolicyAckToken(ctx context.Context, c cache.Cache, input PolicyAckTokenInput, ttl time.Duration) (string, time.Time, error)

GeneratePolicyAckToken stores the challenge state in the cache and returns the rpak1 token (prefix + cache id) plus its expiry.

func GeneratePolicyAckURL

func GeneratePolicyAckURL(ctx context.Context, c cache.Cache, siteURL *url.URL, input PolicyAckTokenInput, ttl time.Duration) (string, time.Time, error)

GeneratePolicyAckURL stores the challenge state and returns the dashboard URL that redeems it (token in the fragment, never the query — same rationale as GeneratePolicyBypassRequestURL) plus the link expiry.

func GeneratePolicyBypassRequestToken

func GeneratePolicyBypassRequestToken(ctx context.Context, c cache.Cache, input PolicyBypassRequestTokenInput, ttl time.Duration) (string, time.Time, error)

GeneratePolicyBypassRequestToken stores the request state in the cache and returns a short rpbr2 token (the cache id) plus its expiry. The token is the only reference to the state — it must be stored under the same cache the redeem handler reads from.

func GeneratePolicyBypassRequestURL

func GeneratePolicyBypassRequestURL(ctx context.Context, c cache.Cache, siteURL *url.URL, input PolicyBypassRequestTokenInput, ttl time.Duration) (string, string, time.Time, error)

func MatchingReconstruction

func MatchingReconstruction(matchLen uint32, candidates [][]byte) (string, bool)

MatchingReconstruction selects the reconstruction to trust: the first candidate whose byte length equals the recorded match_len. The length gate is a plain integrity check, not cryptography — a candidate of the wrong size proves the underlying data no longer lines up with what was scanned (edited content, shifted offsets, changed account email) and using it would misattribute bytes as the finding's match. A zero match_len means the finding never had match content (judge verdicts, dead-lettered scans).

func NewExclusionMutationCore

func NewExclusionMutationCore(logger *slog.Logger, db *pgxpool.Pool, auditLogger *audit.Logger, reconciler RiskExclusionReconciler, redactionKey string) *exclusioncore.Core

NewExclusionMutationCore composes the shared exclusion command for non-Goa adapters with the same audit, reconciliation, and keyed-redaction behavior.

func NewObserver

func NewObserver(
	logger *slog.Logger,
	tracerProvider trace.TracerProvider,
	db *pgxpool.Pool,
	signaler RiskAnalysisSignaler,
	auditLogger *audit.Logger,
	riskRecorder *metering.RiskRecorder,
) chat.MessageObserver

NewObserver creates a lightweight chat.MessageObserver that signals the risk drain workflow when new messages are stored. Use this in contexts (e.g. the worker process) where the full risk Service is not needed.

func NewPolicyMutationCore

func NewPolicyMutationCore(db *pgxpool.Pool, auditLogger *audit.Logger, approvals policycore.ApprovalCoordinator, signaler policycore.PolicySignaler, cacheInvalidator policycore.PolicyCacheInvalidator) *policycore.Core

NewPolicyMutationCore composes the shared risk policy command for non-Goa adapters. It preserves the same audit, approval, URL-grant, signal, and cache dependencies used by the dashboard service.

func RedactMatchAll

func RedactMatchAll(match string, orgID string) string

RedactMatchAll encodes a match value as `<redacted len=N sha=XXXXXXXX>`, redacting every source with no passthrough. An empty match collapses to `<redacted len=0>` without a sha component so the absence of a finding payload is distinguishable from a real hash.

The hash is salted by orgID with a NUL separator so two different orgs holding the same secret produce different fingerprints — defense in depth against any future surface that crosses an org boundary. Within an org the fingerprint stays deterministic so agents can still dedupe.

This is the canonical redaction for the ClickHouse analytics store (match_redacted), where no source may store plaintext. The API-facing redactMatch wraps it for its non-passthrough sources, and the risk_findings backfill (server/cmd/tools/migrations) calls it directly, so the two never drift in salt layout, prefix, or sha truncation.

func ResolveChatID

func ResolveChatID(row *chrepo.RiskFindingUnmaskRow, anchor RevealAnchor) (uuid.UUID, bool)

ResolveChatID resolves the chat a finding's reconstructed content belongs to: the chat id stamped on the ClickHouse row at ingest, falling back to the anchored Postgres row's chat. When neither resolves (attribution never ran and the anchor is gone) it yields the nil UUID, mirroring the Postgres path's NULL chat_id.

The second return is false when the stamped id and the anchor's chat are both present and disagree: the anchored message was re-parented (or the stamp is stale), so the anchor's content is not the stamped chat's to serve and callers must refuse the reveal. The chat id is nil in that case, so a caller that ignores the flag can only widen to the nil-chat gate, never to the wrong chat's.

Types

type EnforcementDispatcher

type EnforcementDispatcher interface {
	Dispatch(context.Context, enforcereply.DispatchRequest) (enforcereply.Outcome, error)
}

EnforcementDispatcher is the request-reply seam used by realtime scanning.

type FindingCHWriter

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

FindingCHWriter consumes Finding messages off the shared Pub/Sub topic and writes them to the ClickHouse risk_findings table. It never stores the raw matched value: only its length, a partial-mask display string (maskdisplay), and one-way fingerprints. The verbatim value stays in Postgres for the audited unmask path.

Delivery contract: at-least-once into ClickHouse. A failed insert or a failed attribution read nacks the whole batch for redelivery; a message the writer can never persist — malformed id or timestamp — nacks only itself so it retries alone without dragging the rest of the batch along. There is no dead-letter queue by design: transient failures self-heal under the subscription's retry backoff, and a poison message (always an internal producer bug — every publisher is ours) redelivers within the subscription's retention window, surfaced by the skipped metric and oldest-unacked-age monitoring so the bug is fixed and the still-retained message then processes. Redelivered duplicates are expected and converge at read time: rows share their deterministic id and the read paths resolve each id to one winning copy.

func NewFindingCHWriter

func NewFindingCHWriter(logger *slog.Logger, db repo.DBTX, meterProvider metric.MeterProvider, inserter RiskFindingInserter, fingerprinter Fingerprinter) *FindingCHWriter

func (*FindingCHWriter) HandleBatchWithResult

func (w *FindingCHWriter) HandleBatchWithResult(ctx context.Context, batch []gcp.BatchMessage[*riskv1.Finding]) error

HandleBatchWithResult adapts ProcessBatch to the streams runner: per-message failures are staged as individual nacks, a batch-level error nacks the whole batch.

func (*FindingCHWriter) ProcessBatch

func (w *FindingCHWriter) ProcessBatch(ctx context.Context, messages []*riskv1.Finding) ([]error, error)

ProcessBatch writes one batch of findings to ClickHouse. The returned slice is parallel to messages: a non-nil entry is a per-message rejection (the message can never be persisted and should redeliver on its own). A non-nil error means the whole batch failed (attribution read or insert) and must be redelivered.

type FindingExclusionResolver

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

FindingExclusionResolver evaluates findings against the currently enabled project and global exclusions for their policy.

func NewFindingExclusionResolver

func NewFindingExclusionResolver(db repo.DBTX) *FindingExclusionResolver

func (*FindingExclusionResolver) ExcludedBy

func (r *FindingExclusionResolver) ExcludedBy(ctx context.Context, message *riskv1.Finding) (uuid.UUID, bool, error)

ExcludedBy returns the matching exclusion id. Lookup and identifier errors are returned so each caller can apply its own delivery policy.

type Fingerprinter

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

func ParsePepperKeyRing

func ParsePepperKeyRing(jsonSecret []byte) (Fingerprinter, error)

ParsePepperKeyRing parses a JSON payload containing the pepper keyring for fingerprinting risk findings. The expected format is:

{
  "current": "v2",
  "keys": {
    "v1": "base64-encoded-key-for-v1",
    "v2": "base64-encoded-key-for-v2"
  }
}

func (Fingerprinter) HS256

func (p Fingerprinter) HS256(message []byte) ([]byte, string, error)

func (Fingerprinter) HS256WithVersion

func (p Fingerprinter) HS256WithVersion(version string, message []byte) ([]byte, error)

func (Fingerprinter) TenantedHS256

func (p Fingerprinter) TenantedHS256(tenantID string, message []byte, opts ...TenantedOption) ([]byte, string, error)

TenantedHS256 fingerprints message under a per-tenant key derived from the current pepper version, so the same secret in two different tenants produces unrelated fingerprints (tenant isolation).

func (Fingerprinter) TenantedHS256WithVersion

func (p Fingerprinter) TenantedHS256WithVersion(version string, tenantID string, message []byte, opts ...TenantedOption) ([]byte, error)

TenantedHS256WithVersion is like HS256WithVersion but keys the HMAC with a per-tenant key instead of the raw pepper. See deriveKey for the derivation.

func (Fingerprinter) Versions

func (p Fingerprinter) Versions() []string

Versions returns every pepper version in the keyring, sorted, for callers that must match fingerprints written under any historical pepper (e.g. the retroactive exclusion reconcile matching rows across rotations). A zero Fingerprinter returns nil, which such callers treat as "fingerprinting unavailable".

type InferenceScanOutcome

type InferenceScanOutcome struct {
	Result   *ScanResult
	Complete bool
}

InferenceScanOutcome separates the legacy enforcement disposition from whether the scan encountered incomplete evaluations. Only a complete, error-free scan with no result establishes a clean verdict under the current configuration.

type PolicyAckTokenInput

type PolicyAckTokenInput struct {
	OrganizationID string
	ProjectID      string
	UserID         string
	RiskPolicyID   string
	PolicyName     string
	ToolName       *string
	// CallFingerprint scopes the acknowledgement to the concrete call that was
	// challenged (SHA-256 of the scanned input). Redeeming writes it onto the
	// challenge row so only an identical retry — not any same-tool call — clears.
	CallFingerprint  string
	ChallengeMessage string
	// RememberFor is how long the acknowledgement, once granted, suppresses
	// re-challenging that same call. Zero uses the ack window default.
	RememberFor time.Duration
}

PolicyAckTokenInput is the state a challenge link points at. Deliberately carries the log-safe identity needed to record the ack, plus ChallengeMessage.

ChallengeMessage is the human-facing warning shown on the approval page (the same text rendered to the operator in the terminal). It MAY contain the matched value: this is an ephemeral, token-gated cache record (~10 min TTL) — the same scoped exposure as the terminal display, NOT durable persistence. It must never be copied into ClickHouse, tool_call_blocks, or audit.

type PolicyBypassEvaluation

type PolicyBypassEvaluation struct {
	OrganizationID string
	UserID         string
	PolicyID       string
	Target         *PolicyBypassTarget
}

type PolicyBypassEvaluator

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

func NewPolicyBypassEvaluator

func NewPolicyBypassEvaluator(logger *slog.Logger, db repo.DBTX) *PolicyBypassEvaluator

func (*PolicyBypassEvaluator) CanBypass

func (*PolicyBypassEvaluator) CanBypassBatch

CanBypassBatch evaluates inputs after loading principals and grants once per organization/user pair. Results are keyed by their complete input so callers do not have to correlate parallel slices. Missing entries deny by default.

type PolicyBypassRequestTokenInput

type PolicyBypassRequestTokenInput struct {
	OrganizationID         string
	ProjectID              string
	RequesterUserID        string
	ObservedName           *string
	ObservedFullURL        *string
	ObservedURLHost        *string
	ObservedServerIdentity *string
	ToolName               *string
	ToolCall               *string
	BlockReason            *string
	RiskPolicyID           string
	RiskResultID           *string
}

type PolicyBypassTarget

type PolicyBypassTarget struct {
	Kind       string
	Label      string
	Key        string
	Dimensions map[string]string
}

PolicyBypassTarget identifies the generic resource a bypass request or runtime bypass check applies to.

func ShadowMCPPolicyBypassTarget

func ShadowMCPPolicyBypassTarget(evidence shadowmcp.AccessEvidence, toolName string) *PolicyBypassTarget

func ShadowMCPServerPolicyBypassTarget

func ShadowMCPServerPolicyBypassTarget(serverURL string, serverIdentity string, label string) PolicyBypassTarget

ShadowMCPServerPolicyBypassTarget applies to a specific Shadow MCP server.

func WholePolicyBypassTarget

func WholePolicyBypassTarget() PolicyBypassTarget

WholePolicyBypassTarget applies to the policy as a whole.

func (PolicyBypassTarget) IsWholePolicy

func (t PolicyBypassTarget) IsWholePolicy() bool

IsWholePolicy reports whether the target represents a bypass for the entire risk policy rather than a narrower target such as a specific Shadow MCP server. Runtime checks use this to build a dimensionless authz check.

type RealtimeScanRequest

type RealtimeScanRequest struct {
	// Provenance carries immutable tenant, request, message, and hook attribution.
	Provenance metering.RiskProvenance
	// Text is the exact content sent to each applicable scanner.
	Text string
	// MessageType identifies the semantic message role used by scopes and judges.
	MessageType message.Type
	// ToolName identifies the invoked tool when MessageType is tool-shaped.
	ToolName string
	// ToolCallID is the harness-assigned id of the tool call when MessageType
	// is a tool request, so the LLM analyzer lane can show the model the id
	// the agent used. Empty when the harness supplied none.
	ToolCallID string
}

RealtimeScanRequest snapshots one hook call and its immutable attribution.

type RevealAnchor

type RevealAnchor struct {
	// ChatID is the anchored Postgres row's chat, exported so the unmask
	// handler can authorize against it and detect divergence from the chat id
	// stamped on the ClickHouse row.
	ChatID uuid.NullUUID
	// contains filtered or unexported fields
}

RevealAnchor is the Postgres source material behind one ClickHouse finding: the anchored chat message's recorded content and tool calls, or a content part's asset location, plus the chat id used for authorization. All loads are best-effort — a deleted anchor leaves the relevant fields zeroed and the reveal refuses later for lack of candidates.

type RevealMatcher

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

RevealMatcher reconstructs a ClickHouse finding's raw match text from the original chat data (Postgres chat rows plus content-part assets), guided by the row's surface metadata. ClickHouse never stores plaintext, so this is the only way to recover a match after the fact. Shared by the audited unmask endpoint and the retroactive exclusion reconcile's regex evaluation — the latter keeps the plaintext strictly in-process, mirroring scan-time matching, so no unmask audit entry is written there.

func NewRevealMatcher

func NewRevealMatcher(logger *slog.Logger, repoQueries *repo.Queries, assetStorage blobio.Reader) *RevealMatcher

NewRevealMatcher assembles a matcher from its dependencies.

func (*RevealMatcher) Candidates

func (m *RevealMatcher) Candidates(ctx context.Context, chatID uuid.UUID, row *chrepo.RiskFindingUnmaskRow, anchor RevealAnchor) [][]byte

Candidates assembles the ordered reconstruction candidates for a finding per its surface metadata. Every candidate still has to pass the match-length gate (MatchingReconstruction) before it is used.

func (*RevealMatcher) HydratePartContent

func (m *RevealMatcher) HydratePartContent(ctx context.Context, anchor *RevealAnchor)

HydratePartContent reads a content-part anchor's asset into the anchor, size-capped like the batch scanner's hydration. Best-effort: a missing or unreadable asset (or a nil asset reader) just leaves the anchor without content and the reveal refuses for lack of candidates.

func (*RevealMatcher) LoadAnchor

func (m *RevealMatcher) LoadAnchor(ctx context.Context, projectID uuid.UUID, row *chrepo.RiskFindingUnmaskRow) RevealAnchor

LoadAnchor resolves the finding's Postgres anchor row (chat message or content part). Content-part asset bytes are NOT read here — that happens after the caller's authorization check, in HydratePartContent.

type RiskAnalysisSignaler

type RiskAnalysisSignaler interface {
	Signal(ctx context.Context, projectID uuid.UUID) error
	analysisstatus.Describer
}

RiskAnalysisSignaler signals the per-project risk analysis coordinator workflow and reports its run state for the Watchdog "last analysed" badge.

type RiskExclusionReconciler

type RiskExclusionReconciler interface {
	Reconcile(ctx context.Context, projectID, exclusionID uuid.UUID) error
}

RiskExclusionReconciler triggers the retroactive reconcile sweep for an exclusion (flag/unflag matching findings in risk_results). Best-effort: a failed trigger is logged, not fatal — the reconcile itself is idempotent.

type RiskFindingInserter

type RiskFindingInserter interface {
	InsertRiskFindings(ctx context.Context, rows []chrepo.RiskFindingRow) error
}

RiskFindingInserter writes a batch of findings to ClickHouse. *chrepo.Queries satisfies it; tests supply a fake.

type RiskPolicyResultsCleaner

type RiskPolicyResultsCleaner interface {
	Clean(ctx context.Context, projectID, policyID uuid.UUID) error
}

RiskPolicyResultsCleaner asynchronously deletes risk_results rows for a soft-deleted policy. Best-effort: a failed trigger is logged, not fatal.

type RiskScanner

type RiskScanner interface {
	// ScanForEnforcement scans text against enabled blocking policies that
	// apply to the given user. Everyone-audience policies always apply;
	// targeted policies require a matching risk_policy:evaluate grant.
	ScanForEnforcement(ctx context.Context, request RealtimeScanRequest) (*ScanResult, error)
	// LookupShadowMCPBlockingPolicy returns the first enabled shadow-MCP
	// policy that applies to the given user. Returns nil when no such policy
	// exists. Used by hooks to gate the realtime deny path.
	LookupShadowMCPBlockingPolicy(ctx context.Context, organizationID string, projectID uuid.UUID, userID string) (*ShadowMCPPolicy, error)
	// HasEnabledShadowMCPPolicy reports whether the project has at least one
	// enabled shadow-MCP policy (any action). Used by the MCP server to
	// decide whether to inject the x-gram-toolset-id constant into tool
	// schemas.
	HasEnabledShadowMCPPolicy(ctx context.Context, projectID uuid.UUID) (bool, error)
	// HasAcknowledgedChallenge reports whether a live acknowledgement exists for
	// a warn (challenge) policy match by this (user, policy, tool, callFingerprint).
	// The hooks layer calls this before denying a warn match: true means the user
	// already acknowledged THIS concrete call and the identical retry should be
	// allowed. Fail-closed.
	HasAcknowledgedChallenge(ctx context.Context, projectID uuid.UUID, userID, policyID, toolName, callFingerprint string) bool
	// RecordPolicyChallenge upserts the challenged-state row for a warn match so
	// the challenge is auditable and linkable. Keyed per concrete call via
	// callFingerprint. Log-safe: never receives the raw matched value. Best-effort.
	RecordPolicyChallenge(ctx context.Context, organizationID string, projectID uuid.UUID, userID, policyID, toolName, policyName, entity, ruleID, callFingerprint string)
}

RiskScanner checks text against blocking risk policies.

type ScanResult

type ScanResult struct {
	Action      string // "flag" | "block" | "warn"
	PolicyID    string
	PolicyName  string
	Source      string
	MessageType message.Type
	RuleID      string
	Description string
	UserMessage *string // optional override for the rendered block/warn message

	// Sensitive - see the type doc. Only for the ephemeral warn render.
	MatchedValue string
	Entity       string

	// CallFingerprint is a SHA-256 (hex) of the exact scanned input (the tool-
	// call arguments / prompt text). It scopes a warn acknowledgement to THIS
	// concrete call: the challenge row and the ack are keyed on it, so
	// acknowledging one command clears only an identical retry, not every call
	// of the same tool under the same policy. Not sensitive (a one-way digest).
	CallFingerprint string

	// DeadLetterReason is non-empty when the "match" is a scanner dead-letter
	// sentinel (the analyzer failed after exhausting retries, e.g. rule
	// pii.dead_letter) rather than an actual finding. A warn policy must not
	// challenge the user over an analyzer outage, so ScanForEnforcement skips
	// the challenge for such results; block policies still deny (fail closed).
	// A sentinel from the LLM analyzer lane (Source llm_analyzer) is returned
	// for every enforcing action, warn included, because that lane fails
	// closed: the hooks layer renders it as a plain deny.
	DeadLetterReason string
}

ScanResult describes a match from an enforcing risk policy (block or warn).

The base fields are safe to log, store, or serialize; block messages render PolicyName + Description, never the matched value.

MatchedValue and Entity are the EXCEPTION: MatchedValue is the raw matched substring (the secret/PII itself) and MUST NOT be logged, persisted to ClickHouse traces, written to tool_call_blocks.reason, or included in audit snapshots. They exist solely so the `warn` (challenge) path can render the ephemeral, user-facing warning ("... %{match} identified as %{entity} ..."). MatchedValue is empty for judge-based matches (prompt-based policies) that have no literal substring.

CAVEAT: via %{match} the value does reach the agent's permission prompt (Claude permissionDecisionReason/SystemMessage; Cursor/Codex UserMessage/ AgentMessage) and therefore the local agent transcript. That is by design - the human needs to see what tripped the challenge - but it means the "never leaves the server" invariant is scoped to Gram's own persistence, not the agent host. Any Gram-side ingestion that captures permission-prompt or transcript content (e.g. a future session-replay path) MUST scrub %{match} before persisting, or the invariant breaks silently.

func (*ScanResult) AnalysisUnavailable

func (r *ScanResult) AnalysisUnavailable() bool

AnalysisUnavailable reports whether the result is the LLM analyzer lane's fail-closed sentinel: the model could not be consulted, so the policy denies with an "analysis unavailable" reason rather than a finding.

func (*ScanResult) IsWarnChallenge

func (r *ScanResult) IsWarnChallenge() bool

IsWarnChallenge reports whether the result should be delivered as a warn challenge (deny plus an acknowledgement link). A fail-closed sentinel on a warn policy is never challengeable: an analyzer outage is not something the user can acknowledge, so it degrades to a plain deny.

type Scanner

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

Scanner implements RiskScanner using gitleaks and optionally Presidio. It pre-creates a gitleaks detector at construction time to avoid the per-scan mutex+init overhead on the hot path.

func NewScanner

func NewScanner(
	logger *slog.Logger,
	tracerProvider trace.TracerProvider,
	meterProvider metric.MeterProvider,
	db *pgxpool.Pool,
	customRuleScanner *customruleanalyzer.Scanner,
	piiScanner ra.PIIScanner,
	piScanner *promptinjection.Scanner,
	promptPolicy *promptpolicy.Scanner,
	flags feature.Provider,
	celEng *celenv.Engine,
	riskRecorder *metering.RiskRecorder,
) (*Scanner, error)

NewScanner creates a RiskScanner. piiScanner may be nil if Presidio is not available in the server process. piScanner must be non-nil; a nil classifier fails open through NoopClassifier. Primes the gitleaks detector to avoid per-scan rule compilation on the real-time hook path; returns an error if the detector cannot be built (init relies on viper global state and should never realistically fail, but propagating the error keeps startup honest).

func NewScannerWithEnforcementDispatcher

func NewScannerWithEnforcementDispatcher(
	logger *slog.Logger,
	tracerProvider trace.TracerProvider,
	meterProvider metric.MeterProvider,
	db *pgxpool.Pool,
	customRuleScanner *customruleanalyzer.Scanner,
	piiScanner ra.PIIScanner,
	piScanner *promptinjection.Scanner,
	promptPolicy *promptpolicy.Scanner,
	flags feature.Provider,
	celEng *celenv.Engine,
	dispatcher EnforcementDispatcher,
	riskRecorder *metering.RiskRecorder,
) (*Scanner, error)

NewScannerWithEnforcementDispatcher creates a scanner with Pub/Sub enforcement enabled when flagged.

func (*Scanner) HasAcknowledgedChallenge

func (s *Scanner) HasAcknowledgedChallenge(ctx context.Context, projectID uuid.UUID, userID, policyID, toolName, callFingerprint string) bool

HasAcknowledgedChallenge reports whether a live acknowledgement exists for this (user, policy, tool). The agent retries a warn-challenged call with NO token, so this DB lookup — not the ack link's cache token — is what lets the retry through. Fail-closed: any error (including an unreachable DB) returns false so a warn never silently allows on infra failure.

func (*Scanner) HasEnabledShadowMCPPolicy

func (s *Scanner) HasEnabledShadowMCPPolicy(ctx context.Context, projectID uuid.UUID) (bool, error)

HasEnabledShadowMCPPolicy reports whether the project has at least one enabled shadow-MCP policy (flag or block). The MCP server uses this to decide whether to inject the x-gram-toolset-id constant into tool schemas.

func (*Scanner) LookupShadowMCPBlockingPolicy

func (s *Scanner) LookupShadowMCPBlockingPolicy(ctx context.Context, organizationID string, projectID uuid.UUID, userID string) (*ShadowMCPPolicy, error)

LookupShadowMCPBlockingPolicy returns the first enabled shadow-MCP policy for the project whose action is "block". Flag-action policies surface as findings via the batch scanner instead of denying at the hook layer.

func (*Scanner) RecordPolicyChallenge

func (s *Scanner) RecordPolicyChallenge(ctx context.Context, organizationID string, projectID uuid.UUID, userID, policyID, toolName, policyName, entity, ruleID, callFingerprint string)

RecordPolicyChallenge upserts the challenged-state row for a warn match. Log-safe only: it never receives or stores the raw matched value. Best-effort — a failure to record must not change the enforcement decision.

func (*Scanner) ScanForEnforcement

func (s *Scanner) ScanForEnforcement(ctx context.Context, request RealtimeScanRequest) (*ScanResult, error)

func (*Scanner) ScanForInferenceEnforcement

func (s *Scanner) ScanForInferenceEnforcement(ctx context.Context, request RealtimeScanRequest) (*InferenceScanOutcome, error)

ScanForInferenceEnforcement preserves ScanForEnforcement's disposition and error behavior while reporting suppressed failures separately. It scans once.

func (*Scanner) Shutdown

func (s *Scanner) Shutdown(ctx context.Context) error

Shutdown stops accepting detached realtime recordings and waits for every recording already admitted to finish. Call it after HTTP handlers quiesce and before stopping the meter publisher.

type Service

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

func NewService

func NewService(
	logger *slog.Logger,
	tracerProvider trace.TracerProvider,
	db *pgxpool.Pool,
	sessions *sessions.Manager,
	authzEngine *authz.Engine,
	signaler RiskAnalysisSignaler,
	reconciler RiskExclusionReconciler,
	resultsCleaner RiskPolicyResultsCleaner,
	completionClient openrouter.CompletionClient,
	shadowMCPClient *shadowmcp.Client,
	auditLogger *audit.Logger,
	cacheImpl cache.Cache,
	jwtSecret string,
	approvalIntake ShadowMCPApprovalIntake,
	piiScanner ra.PIIScanner,
	piScanner *promptinjection.Scanner,
	flags feature.Provider,
	celEng *celenv.Engine,
	builtinPresets *presetlib.Library,
	promptJudge promptpolicy.Evaluator,
	reconcileShadowMCPPolicyURLs ShadowMCPPolicyURLReconciler,
	shadowMCPInventoryURLLookup ShadowMCPInventoryURLLookup,
	findingsCH *chrepo.Queries,
	assetStorage blobio.Reader,
	riskRecorder *metering.RiskRecorder,
) *Service

func (*Service) APIKeyAuth

func (s *Service) APIKeyAuth(ctx context.Context, key string, schema *security.APIKeyScheme) (context.Context, error)

func (*Service) AcknowledgeRiskPolicyChallenge

AcknowledgeRiskPolicyChallenge redeems a warn/challenge ack link. Self-service: the same user who was warned confirms and the challenge is recorded as acknowledged, so the retried action proceeds. No admin approval, no RBAC grant.

func (*Service) ApproveRiskPolicyBypassRequest

func (s *Service) ApproveRiskPolicyBypassRequest(ctx context.Context, payload *gen.ApproveRiskPolicyBypassRequestPayload) (*gen.RiskPolicyBypassRequest, error)

func (*Service) CompileExpr

func (s *Service) CompileExpr(ctx context.Context, payload *gen.CompileExprPayload) (*gen.ExprCompileResult, error)

CompileExpr compiles a single CEL expression without evaluating it, so the editor can validate as the author types. It mirrors the save-time gate (celenv.Compile via the shared engine) so an expression that compiles here also saves. An empty expression is valid.

func (*Service) CreateCustomDetectionRule

func (s *Service) CreateCustomDetectionRule(ctx context.Context, payload *gen.CreateCustomDetectionRulePayload) (*types.RiskCustomDetectionRule, error)

func (*Service) CreateRiskExclusion

func (s *Service) CreateRiskExclusion(ctx context.Context, payload *gen.CreateRiskExclusionPayload) (*types.RiskExclusion, error)

func (*Service) CreateRiskPolicy

func (s *Service) CreateRiskPolicy(ctx context.Context, payload *gen.CreateRiskPolicyPayload) (*types.RiskPolicy, error)

func (*Service) CreateRiskPolicyBypassRequest

func (s *Service) CreateRiskPolicyBypassRequest(ctx context.Context, payload *gen.CreateRiskPolicyBypassRequestPayload) (*gen.PolicyBypassRedemption, error)

func (*Service) DeclineRiskPolicyChallenge

func (s *Service) DeclineRiskPolicyChallenge(ctx context.Context, payload *gen.DeclineRiskPolicyChallengePayload) (*gen.DeclineRiskPolicyChallengeResult, error)

DeclineRiskPolicyChallenge marks a warn/challenge declined and invalidates the link so it can't later be approved. The blocked action stays blocked.

func (*Service) DeleteCustomDetectionRule

func (s *Service) DeleteCustomDetectionRule(ctx context.Context, payload *gen.DeleteCustomDetectionRulePayload) error

func (*Service) DeleteRiskEvalReview

func (s *Service) DeleteRiskEvalReview(ctx context.Context, payload *gen.DeleteRiskEvalReviewPayload) error

DeleteRiskEvalReview clears the current reviewer's verdict.

func (*Service) DeleteRiskExclusion

func (s *Service) DeleteRiskExclusion(ctx context.Context, payload *gen.DeleteRiskExclusionPayload) error

func (*Service) DeleteRiskPolicy

func (s *Service) DeleteRiskPolicy(ctx context.Context, payload *gen.DeleteRiskPolicyPayload) error

func (*Service) DenyRiskPolicyBypassRequest

func (s *Service) DenyRiskPolicyBypassRequest(ctx context.Context, payload *gen.DenyRiskPolicyBypassRequestPayload) (*gen.RiskPolicyBypassRequest, error)

func (*Service) EvaluatePromptGuardrail

func (s *Service) EvaluatePromptGuardrail(ctx context.Context, payload *gen.EvaluatePromptGuardrailPayload) (*gen.PromptGuardrailEvalResult, error)

EvaluatePromptGuardrail replays an inline guardrail without persisting findings.

func (*Service) GetCustomDetectionRule

func (s *Service) GetCustomDetectionRule(ctx context.Context, payload *gen.GetCustomDetectionRulePayload) (*types.RiskCustomDetectionRule, error)

func (*Service) GetRiskAnalysisStatus

GetRiskAnalysisStatus reports the run state of the project's risk analysis coordinator for the Watchdog "last analyzed" badge. It shares the signals endpoint's gates (org:admin plus the Watchdog flag) because it describes the same surface, and it reads Temporal directly rather than Postgres: the coordinator is signal-driven, so the latest workflow run is the only record of when analysis last happened.

func (*Service) GetRiskBlock

func (s *Service) GetRiskBlock(ctx context.Context, payload *gen.GetRiskBlockPayload) (*gen.RiskBlock, error)

GetRiskBlock returns a durable tool call block by its ID. Blocks are recorded at hook-time deny into tool_call_blocks, carrying the exact reason shown to the agent. The block page is opened from the link an agent embeds in its deny message, so access is intentionally NOT gated on org-admin — the person whose agent was blocked is usually a regular member.

Org MEMBERSHIP is the floor (see the query below): a block is loadable by any signed-in member of the owning org, regardless of their active org. authorizeBlockView then tightens that when the block records an owner: only that owner, or a project admin (project:write), may view it. A block with no recorded owner (empty user_id) stays readable by any member.

func (*Service) GetRiskOverview

func (s *Service) GetRiskOverview(ctx context.Context, payload *gen.GetRiskOverviewPayload) (*gen.RiskOverviewResult, error)

func (*Service) GetRiskPolicy

func (s *Service) GetRiskPolicy(ctx context.Context, payload *gen.GetRiskPolicyPayload) (*types.RiskPolicy, error)

func (*Service) GetRiskPolicyChallenge

func (s *Service) GetRiskPolicyChallenge(ctx context.Context, payload *gen.GetRiskPolicyChallengePayload) (*gen.GetRiskPolicyChallengeResult, error)

GetRiskPolicyChallenge returns a warn/challenge's details from its ack link WITHOUT redeeming it, so the approval page can show what was flagged before the operator chooses Approve or Deny. Binds to the session org+user like the redeem path — a leaked link reveals nothing to anyone else.

func (*Service) GetRiskPolicyStatus

func (s *Service) GetRiskPolicyStatus(ctx context.Context, payload *gen.GetRiskPolicyStatusPayload) (*types.RiskPolicyStatus, error)

func (*Service) GetRiskRuleBreakdown

func (s *Service) GetRiskRuleBreakdown(ctx context.Context, payload *gen.GetRiskRuleBreakdownPayload) (*gen.RiskRuleBreakdownResult, error)

func (*Service) GetRiskSignals

func (s *Service) GetRiskSignals(ctx context.Context, payload *gen.GetRiskSignalsPayload) (*gen.RiskSignalsResult, error)

GetRiskSignals serves the Watchdog page: findings clustered by rule into ranked signals, window-level KPI counts with previous-window comparisons, and the exposure-by-category rollup. ClickHouse-only — there is no Postgres fallback, so orgs must be on the ClickHouse findings ingest before the Watchdog UI is enabled for them.

func (*Service) GetRiskUserBreakdown

func (s *Service) GetRiskUserBreakdown(ctx context.Context, payload *gen.GetRiskUserBreakdownPayload) (*gen.RiskUserBreakdownResult, error)

func (*Service) ListBuiltinExclusions

ListBuiltinExclusions returns the built-in exclusion library grouped by category. The catalog is static, embedded reference data (see presetlib), so this is a read gated by org admin with no project data access.

func (*Service) ListCustomDetectionRules

func (s *Service) ListCustomDetectionRules(ctx context.Context, payload *gen.ListCustomDetectionRulesPayload) (*gen.ListCustomDetectionRulesResult, error)

func (*Service) ListDismissedRiskResults

func (s *Service) ListDismissedRiskResults(ctx context.Context, payload *gen.ListDismissedRiskResultsPayload) (*gen.ListRiskResultsResult, error)

ListDismissedRiskResults serves the Dismissed tab from ClickHouse: findings whose latest state is a manual or automated dismissal, newest dismissal first. Rows arrive store-side redacted like the Risk Events listing — the raw match never reaches ClickHouse — so results carry MatchRedacted and a nil Match where the Postgres-backed listing returned the raw value.

func (*Service) ListRiskCategories

func (s *Service) ListRiskCategories(ctx context.Context, payload *gen.ListRiskCategoriesPayload) (*gen.RiskCategoriesResult, error)

func (*Service) ListRiskEvalReviews

func (s *Service) ListRiskEvalReviews(ctx context.Context, payload *gen.ListRiskEvalReviewsPayload) (*gen.ListRiskEvalReviewsResult, error)

ListRiskEvalReviews returns the active regression set for a policy: every reviewer's current verdicts.

func (*Service) ListRiskExclusions

func (s *Service) ListRiskExclusions(ctx context.Context, payload *gen.ListRiskExclusionsPayload) (*gen.ListRiskExclusionsResult, error)

func (*Service) ListRiskPolicies

func (s *Service) ListRiskPolicies(ctx context.Context, payload *gen.ListRiskPoliciesPayload) (*gen.ListRiskPoliciesResult, error)

func (*Service) ListRiskResults

func (s *Service) ListRiskResults(ctx context.Context, payload *gen.ListRiskResultsPayload) (*gen.ListRiskResultsResult, error)

ListRiskResults serves the dashboard's default risk results listing. It always requires org:admin, but only returns raw `match`/`spans` content when the request is scoped to a single chat_id AND the caller separately holds chat:read for that exact chat — the same condition that already lets them load the chat's full transcript (which contains the same secret embedded in message content) via chat.LoadChat. That is a soft check (FindMatched, not Require): missing chat:read doesn't fail the request, it just falls back to a redacted response. Every other call — notably the Risk Events page, which lists across many chats with no single chat_id filter — gets match_redacted instead of the raw secret.

func (*Service) ListRiskResultsByChat

func (s *Service) ListRiskResultsByChat(ctx context.Context, payload *gen.ListRiskResultsByChatPayload) (*gen.ListRiskResultsByChatResult, error)

func (*Service) ListRiskResultsForAgent

func (s *Service) ListRiskResultsForAgent(ctx context.Context, payload *gen.ListRiskResultsForAgentPayload) (*gen.ListRiskResultsForAgentResult, error)

ListRiskResultsForAgent serves the same data as ListRiskResults but strips raw `match` content from non-shadow_mcp findings before returning, so the agent / MCP surface never holds secret values in model context. Shadow-MCP findings pass `match` through verbatim because the value is a server URL or stdio command identifier the dashboard already exposes unmasked.

func (*Service) MarkRiskResultsFalsePositive

func (s *Service) MarkRiskResultsFalsePositive(ctx context.Context, payload *gen.MarkRiskResultsFalsePositivePayload) error

func (*Service) OnMessagesStored

func (s *Service) OnMessagesStored(ctx context.Context, projectID uuid.UUID)

OnMessagesStored implements chat.MessageObserver. The caller (notifyObservers) already dispatches this in a goroutine with a detached context, so this method can safely perform I/O.

func (*Service) ReleaseSessionQuarantine

func (s *Service) ReleaseSessionQuarantine(ctx context.Context, payload *gen.ReleaseSessionQuarantinePayload) (*gen.SessionQuarantine, error)

func (*Service) RevokeRiskPolicyBypassRequest

func (s *Service) RevokeRiskPolicyBypassRequest(ctx context.Context, payload *gen.RevokeRiskPolicyBypassRequestPayload) (*gen.RiskPolicyBypassRequest, error)

func (*Service) SaveRiskEvalReview

func (s *Service) SaveRiskEvalReview(ctx context.Context, payload *gen.SaveRiskEvalReviewPayload) (*types.RiskPolicyEvalReview, error)

SaveRiskEvalReview records one review verdict in the policy regression set.

func (*Service) SubmitRiskBlockFeedback

func (s *Service) SubmitRiskBlockFeedback(ctx context.Context, payload *gen.SubmitRiskBlockFeedbackPayload) (*gen.RiskBlock, error)

SubmitRiskBlockFeedback records 👍/👎 feedback on a tool call block and returns the refreshed block so the page reflects the vote.

func (*Service) SuggestCustomDetectionRule

func (s *Service) SuggestCustomDetectionRule(ctx context.Context, payload *gen.SuggestCustomDetectionRulePayload) (*gen.SuggestCustomDetectionRuleResult, error)

SuggestCustomDetectionRule turns a natural-language description ("what do you want to detect?") into a structured custom-rule suggestion. The response is intentionally minimal — the dashboard prefills its create form with these values and the operator edits before saving.

func (*Service) SuggestExclusion

func (s *Service) SuggestExclusion(ctx context.Context, payload *gen.SuggestExclusionPayload) (*gen.SuggestExclusionResult, error)

SuggestExclusion turns a natural-language description of findings an operator wants to stop flagging into a structured exclusion suggestion (match_type, match_value, filters), validated with the same gate the create/update exclusion handlers use (RE2 compile, 512-char cap). The exclusion form serializes the result into its criteria expression via the existing client-side mapping. Falls back to an editable exact-match prefill when the LLM is unavailable, mirroring SuggestCustomDetectionRule's heuristic fallback.

func (*Service) TestDetectionRule

func (s *Service) TestDetectionRule(ctx context.Context, payload *gen.TestDetectionRulePayload) (*gen.TestDetectionRuleResult, error)

TestDetectionRule runs a single detection rule against pasted sample text and returns its matches. The handler dispatches to the same scanners the worker uses during chat-message analysis (gitleaks for secrets.*, the configured PIIScanner for pii.*, the prompt-injection scanner for prompt_injection.*, and a regex matcher for custom.*) so the playground output mirrors what would be recorded as a risk_result in production.

shadow_mcp.* and destructive_tool.* are inherently tool-call shaped — they have no text-only detector — so the handler returns supported:false for them rather than fabricating a match.

func (*Service) TriggerRiskAnalysis

func (s *Service) TriggerRiskAnalysis(_ context.Context, _ *gen.TriggerRiskAnalysisPayload) error

func (*Service) UnmarkRiskResultsFalsePositive

func (s *Service) UnmarkRiskResultsFalsePositive(ctx context.Context, payload *gen.UnmarkRiskResultsFalsePositivePayload) error

func (*Service) UnmaskRiskResult

func (s *Service) UnmaskRiskResult(ctx context.Context, payload *gen.UnmaskRiskResultPayload) (*gen.RiskUnmaskResultResult, error)

UnmaskRiskResult returns the plaintext match for a single risk result, on demand. Unlike ListRiskResults it is gated solely on chat:read for the result's chat — not org:admin — so a reveal is a discrete, audited access event distinct from browsing the redacted list.

func (*Service) UpdateCustomDetectionRule

func (s *Service) UpdateCustomDetectionRule(ctx context.Context, payload *gen.UpdateCustomDetectionRulePayload) (*types.RiskCustomDetectionRule, error)

func (*Service) UpdateRiskExclusion

func (s *Service) UpdateRiskExclusion(ctx context.Context, payload *gen.UpdateRiskExclusionPayload) (*types.RiskExclusion, error)

func (*Service) UpdateRiskPolicy

func (s *Service) UpdateRiskPolicy(ctx context.Context, payload *gen.UpdateRiskPolicyPayload) (*types.RiskPolicy, error)

type ShadowMCPApprovalIntake

type ShadowMCPApprovalIntake interface {
	// AdmitBlockedServer records the ask and returns the id and current
	// status of the review it landed on — a repeat ask for an
	// already-approved server attaches without reopening it. A forbidden
	// error means the approval workflow is not enabled for the organization
	// and the caller should fall back to the legacy bypass request.
	AdmitBlockedServer(ctx context.Context, organizationID string, projectID uuid.UUID, serverURL, requesterUserID, requesterEmail, note string) (requestID string, status string, err error)

	// ReconcileStandingDecisionsForPolicy replays the project's recorded
	// decisions onto a newly blocking policy, inside the transaction that
	// creates or transitions it. Without it, ordering decides what an
	// approval means: a policy created after decisions were recorded would
	// block servers whose reviews still read approved. A returned shareable
	// error (an inexpressible blast radius) aborts the policy write with its
	// explanation intact.
	ReconcileStandingDecisionsForPolicy(ctx context.Context, tx pgx.Tx, organizationID string, projectID uuid.UUID, policyID uuid.UUID) error

	// ReviewShadowMCPPolicyURLEdit names the standing decisions a URL-list
	// edit on an already-blocking policy would contradict, plus every URL
	// whose grants carry a standing decision (so the reconciler can leave
	// retained ones untouched). A nil URL list means that list is not being
	// edited.
	ReviewShadowMCPPolicyURLEdit(ctx context.Context, tx pgx.Tx, organizationID string, projectID uuid.UUID, policyID uuid.UUID, disposition string, desiredAllowedURLs []string, desiredBlockedURLs []string) (shadowmcp.StandingDecisionReview, error)

	// SupersedeShadowMCPDecisions transitions each conflicted request to
	// superseded — actor-attributed and audit-logged, decision history and
	// rationale intact — in the same transaction as the policy edit that
	// displaces it.
	SupersedeShadowMCPDecisions(ctx context.Context, tx pgx.Tx, organizationID string, projectID uuid.UUID, conflicts []shadowmcp.StandingDecisionConflict, actor urn.Principal, actorDisplayName *string) error
}

ShadowMCPApprovalIntake admits a blocked shadow-MCP server into the MCP approval workflow: the blocked employee's ask attaches as a requester on the server's single review — evidence gathered, deduplicated by canonical URL — instead of minting a per-user bypass request. It is the seam that makes approval the one flow: the block link redeems into the same review an admin decides, and the decision is what changes enforcement.

Implemented by the mcpapproval service and injected at wiring, so this package never imports it. A nil intake, or an intake reporting the approval feature is unavailable, falls back to the legacy bypass request.

type ShadowMCPInventoryURLLookup

type ShadowMCPInventoryURLLookup func(
	ctx context.Context,
	projectID uuid.UUID,
	canonicalURLs []string,
) ([]string, error)

ShadowMCPInventoryURLLookup returns the requested canonical URLs that were observed in the authenticated project inventory.

type ShadowMCPPolicy

type ShadowMCPPolicy struct {
	ID          string
	Name        string
	Version     int64
	UserMessage *string // nil/empty means "render the default message"
	// Disposition is the policy's default posture: block_all (deny unless
	// allowed — the original behavior) or allow_all (permit unless blocked).
	Disposition string
	// BlockedURLs is the canonical blocked-URL set of an allow_all policy.
	// Always empty under block_all.
	BlockedURLs []string
}

ShadowMCPPolicy is the minimal policy view the hooks layer needs to render a deny message that follows the same `matched policy %q (...)` format as gitleaks/presidio enforcement.

func (*ShadowMCPPolicy) IsAllowAll

func (p *ShadowMCPPolicy) IsAllowAll() bool

IsAllowAll reports whether the policy permits servers by default and blocks only the URLs on its blocked list. Bypass grants and fail-closed inventory checks are block_all concepts and do not apply under allow-all.

type ShadowMCPPolicyURLReconciler

type ShadowMCPPolicyURLReconciler func(
	ctx context.Context,
	db repo.DBTX,
	input policybypass.ReconcilePolicyURLsInput,
) error

ShadowMCPPolicyURLReconciler replaces the URL grants owned by one risk policy.

type TenantedOption

type TenantedOption func(*tenantedOptions)

TenantedOption customizes the behaviour of TenantedHS256 and TenantedHS256WithVersion.

func WithKeyCache

func WithKeyCache(cache map[string][]byte) TenantedOption

WithKeyCache supplies a cache for per-tenant derived keys so that repeated fingerprinting under the same (version, tenant) pair does not re-run HKDF. The cache is keyed by version and tenant ID and is read from and written to by the tenanted fingerprinting methods. The caller owns the map and is responsible for its lifetime and any concurrency control; a fresh map scoped to a single batch is the typical usage.

Directories

Path Synopsis
Package analysisstatus describes the run state of a project's risk analysis coordinator workflow.
Package analysisstatus describes the run state of a project's risk analysis coordinator workflow.
Package categories is the single source of truth for the (source, rule_id) → risk category mapping shown across the dashboard.
Package categories is the single source of truth for the (source, rule_id) → risk category mapping shown across the dashboard.
Package celenv defines the single CEL environment for risk rule expressions.
Package celenv defines the single CEL environment for risk rule expressions.
Package enforcereply binds the generic Redis inbox (internal/redisinbox) to risk enforcement replies and return addresses.
Package enforcereply binds the generic Redis inbox (internal/redisinbox) to risk enforcement replies and return addresses.
Package maskdisplay produces the partial-mask display form of a risk finding's matched value — the string stored in the ClickHouse risk_findings.match_redacted column and rendered as the match by default in listings.
Package maskdisplay produces the partial-mask display form of a risk finding's matched value — the string stored in the ClickHouse risk_findings.match_redacted column and rendered as the match by default in listings.
Package policycatalog owns the release-pinned detector values that Platform policy administration may expose.
Package policycatalog owns the release-pinned detector values that Platform policy administration may expose.
Package presetlib classifies risk findings from ANY detection source against a git-versioned catalog of known-benign values (test credit cards, example API keys/tokens, module hashes, placeholder emails).
Package presetlib classifies risk findings from ANY detection source against a git-versioned catalog of known-benign values (test credit cards, example API keys/tokens, module hashes, placeholder emails).
Package presidiofp classifies Presidio PII findings as false positives.
Package presidiofp classifies Presidio PII findings as false positives.
Package recommendedscopes contains the centrally-maintained detection scope registry for built-in risk categories.
Package recommendedscopes contains the centrally-maintained detection scope registry for built-in risk categories.

Jump to

Keyboard shortcuts

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