finding

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2026 License: GPL-3.0 Imports: 30 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBulkBudgetExceeded = errors.New("bulk operation budget exceeded for this hour")

ErrBulkBudgetExceeded is returned when the rolling-hour budget is exhausted for the tenant.

View Source
var ErrBulkNegativeSize = errors.New("bulk request size must be positive")

ErrBulkNegativeSize defends against callers passing a computed size that went negative (overflow bugs, bad filters).

View Source
var ErrBulkTooLarge = errors.New("bulk request exceeds size ceiling; requires operator approval")

ErrBulkTooLarge is returned when a single request exceeds the per-request ceiling without operator approval.

View Source
var ErrPriorityFloodSuppressed = errors.New("tenant priority flood budget exceeded; downstream fan-out suppressed")

ErrPriorityFloodSuppressed is returned by ShouldFanOut when the tenant has already consumed its rolling budget. Classification is NOT reverted; only downstream side effects are skipped.

Functions

This section is empty.

Types

type AddCommentInput

type AddCommentInput struct {
	TenantID  string `validate:"required,uuid"`
	FindingID string `validate:"required,uuid"`
	AuthorID  string `validate:"required,uuid"`
	Content   string `validate:"required,min=1,max=10000"`
}

AddCommentInput represents the input for adding a comment.

type AddStatusChangeCommentInput

type AddStatusChangeCommentInput struct {
	TenantID  string `validate:"required,uuid"`
	FindingID string `validate:"required,uuid"`
	AuthorID  string `validate:"required,uuid"`
	Content   string `validate:"max=10000"`
	OldStatus string `validate:"required,finding_status"`
	NewStatus string `validate:"required,finding_status"`
}

AddStatusChangeCommentInput represents the input for adding a status change comment.

type ApproveStatusInput

type ApproveStatusInput struct {
	TenantID   string `json:"tenant_id" validate:"required,uuid"`
	ApprovalID string `json:"approval_id" validate:"required,uuid"`
	ApprovedBy string `json:"approved_by" validate:"required,uuid"`
}

ApproveStatusInput represents the input for approving a status change.

type AutoAssignToOwnersResult

type AutoAssignToOwnersResult struct {
	Assigned   int            `json:"assigned"`
	ByOwner    map[string]int `json:"by_owner"`
	Unassigned int            `json:"unassigned"`
}

AutoAssignToOwnersResult is the result of auto-assign operation.

type BulkAssignInput

type BulkAssignInput struct {
	FindingIDs []string
	UserID     string
	AssignerID string
}

BulkAssignInput represents input for bulk assignment.

type BulkFixAppliedInput

type BulkFixAppliedInput struct {
	Filter             vulnerability.FindingFilter
	IncludeRelatedCVEs bool
	Note               string // REQUIRED
	Reference          string // optional (commit hash, patch ID)
}

BulkFixAppliedInput is the input for bulk fix-applied operation.

type BulkFixAppliedResult

type BulkFixAppliedResult struct {
	Updated        int            `json:"updated"`
	Skipped        int            `json:"skipped"`
	ByCVE          map[string]int `json:"by_cve,omitempty"`
	AssetsAffected int            `json:"assets_affected"`
}

BulkFixAppliedResult is the result of bulk fix-applied operation.

type BulkGuard

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

BulkGuard is the single service used by all bulk handlers before executing. Usage:

if err := guard.CheckBulk(tenantID, len(findingIDs), operatorApproved); err != nil {
    return apierror.BadRequest(err.Error()).WriteJSON(w)
}

func NewBulkGuard

func NewBulkGuard(cfg BulkGuardConfig) *BulkGuard

NewBulkGuard constructs a guard with defaults applied.

func (*BulkGuard) CheckBulk

func (g *BulkGuard) CheckBulk(ctx context.Context, tenantID shared.ID, size int, operatorApproved bool) error

CheckBulk enforces both guards for a single request. On success, records the usage so subsequent calls see it. On rejection, usage is NOT recorded (the operation didn't happen).

The operator-approved flag bypasses SizeCeiling but NOT the hourly budget — even an approved operator cannot blast the tenant.

func (*BulkGuard) UsageThisHour

func (g *BulkGuard) UsageThisHour(tenantID shared.ID) int

UsageThisHour returns the number of rows consumed in the rolling hour window for `tenantID`. Exposed for the maturity dashboard.

type BulkGuardConfig

type BulkGuardConfig struct {
	// SizeCeiling is the max number of rows a single bulk request
	// can touch without operator approval. Default 500.
	SizeCeiling int
	// HourlyBudget is the max rows per tenant per hour. Default 10_000.
	HourlyBudget int
	// Now is injectable for deterministic tests.
	Now func() time.Time
}

BulkGuardConfig tunes the two thresholds. Zero values take the documented defaults.

type BulkUpdateResult

type BulkUpdateResult struct {
	Updated int
	Failed  int
	Errors  []string
}

BulkUpdateResult represents the result of a bulk operation.

type BulkUpdateStatusInput

type BulkUpdateStatusInput struct {
	FindingIDs []string
	Status     string
	Resolution string
	ActorID    string // User performing the bulk update
}

BulkUpdateStatusInput represents input for bulk status update.

type BurpIssue

type BurpIssue struct {
	XMLName           xml.Name `xml:"issue"`
	SerialNumber      string   `xml:"serialNumber"`
	Type              string   `xml:"type"`
	Name              string   `xml:"name"`
	Host              string   `xml:"host"`
	Path              string   `xml:"path"`
	Location          string   `xml:"location"`
	Severity          string   `xml:"severity"`
	Confidence        string   `xml:"confidence"`
	IssueBackground   string   `xml:"issueBackground"`
	RemediationBG     string   `xml:"remediationBackground"`
	IssueDetail       string   `xml:"issueDetail"`
	RemediationDetail string   `xml:"remediationDetail"`
	RequestResponse   []struct {
		Request  string `xml:"request"`
		Response string `xml:"response"`
	} `xml:"requestresponse"`
}

BurpIssue represents a single issue in Burp Suite XML export.

type BurpIssues

type BurpIssues struct {
	XMLName xml.Name    `xml:"issues"`
	Issues  []BurpIssue `xml:"issue"`
}

BurpIssues is the root XML element.

type CachedCategory

type CachedCategory struct {
	ID           string `json:"id"`
	Code         string `json:"code"`
	Name         string `json:"name"`
	Description  string `json:"description,omitempty"`
	Icon         string `json:"icon,omitempty"`
	DisplayOrder int    `json:"display_order"`
}

CachedCategory represents a category in the cache.

type CachedFindingSource

type CachedFindingSource struct {
	ID           string `json:"id"`
	Code         string `json:"code"`
	Name         string `json:"name"`
	Description  string `json:"description,omitempty"`
	CategoryID   string `json:"category_id,omitempty"`
	CategoryCode string `json:"category_code,omitempty"`
	CategoryName string `json:"category_name,omitempty"`
	Icon         string `json:"icon,omitempty"`
	Color        string `json:"color,omitempty"`
	DisplayOrder int    `json:"display_order"`
	IsSystem     bool   `json:"is_system"`
}

CachedFindingSource represents a finding source in the cache.

type CachedFindingSources

type CachedFindingSources struct {
	Sources    []*CachedFindingSource `json:"sources"`
	Categories []*CachedCategory      `json:"categories"`
	ByCode     map[string]int         `json:"by_code"`     // code -> index in Sources for O(1) lookup
	ByCategory map[string][]int       `json:"by_category"` // category_code -> indices in Sources
	CachedAt   time.Time              `json:"cached_at"`
}

CachedFindingSources represents the cached finding source data.

type CancelApprovalInput

type CancelApprovalInput struct {
	TenantID   string `json:"tenant_id" validate:"required,uuid"`
	ApprovalID string `json:"approval_id" validate:"required,uuid"`
	CanceledBy string `json:"canceled_by" validate:"required,uuid"`
}

CancelApprovalInput represents the input for cancelling a status change request.

type ClassifyFindingInput

type ClassifyFindingInput struct {
	CVEID      string
	CVSSScore  *float64
	CVSSVector string
	CWEIDs     []string
	OWASPIDs   []string
}

ClassifyFindingInput represents input for classification.

type CompensatingControlLookup

type CompensatingControlLookup interface {
	// GetEffectiveForAssets returns max reduction_factor per asset with active+effective controls.
	// Returns map[assetID]reductionFactor (0.0-1.0).
	GetEffectiveForAssets(ctx context.Context, tenantID shared.ID, assetIDs []shared.ID) (map[shared.ID]float64, error)
}

CompensatingControlLookup provides lookups for effective controls on assets/findings.

type CreateFindingInput

type CreateFindingInput struct {
	TenantID        string `validate:"required,uuid"`
	AssetID         string `validate:"required,uuid"`
	BranchID        string `validate:"omitempty,uuid"`
	VulnerabilityID string `validate:"omitempty,uuid"`
	ComponentID     string `validate:"omitempty,uuid"`
	Source          string `validate:"required,finding_source"`
	ToolName        string `validate:"required,max=100"`
	ToolVersion     string `validate:"max=50"`
	RuleID          string `validate:"max=255"`
	FilePath        string `validate:"max=500"`
	StartLine       int    `validate:"min=0"`
	EndLine         int    `validate:"min=0"`
	StartColumn     int    `validate:"min=0"`
	EndColumn       int    `validate:"min=0"`
	Snippet         string `validate:"max=5000"`
	Message         string `validate:"required,max=2000"`
	Severity        string `validate:"required,severity"`
	ScanID          string `validate:"max=100"`
}

CreateFindingInput represents the input for creating a finding.

type CreateVulnerabilityInput

type CreateVulnerabilityInput struct {
	CVEID            string   `validate:"required,cve_id"`
	Title            string   `validate:"required,min=1,max=500"`
	Description      string   `validate:"max=10000"`
	Severity         string   `validate:"required,severity"`
	CVSSScore        *float64 `validate:"omitempty,min=0,max=10"`
	CVSSVector       string   `validate:"max=100"`
	EPSSScore        *float64 `validate:"omitempty,min=0,max=1"`
	EPSSPercentile   *float64 `validate:"omitempty,min=0,max=1"`
	ExploitAvailable bool
	ExploitMaturity  string   `validate:"omitempty,exploit_maturity"`
	FixedVersions    []string `validate:"max=50,dive,max=100"`
	Remediation      string   `validate:"max=5000"`
}

CreateVulnerabilityInput represents the input for creating a vulnerability.

type EPSSData

type EPSSData struct {
	Score      float64
	Percentile float64
}

EPSSData holds EPSS score for a CVE.

type EPSSRepository

type EPSSRepository interface {
	GetByCVEIDs(ctx context.Context, cveIDs []string) (map[string]EPSSData, error)
}

EPSSRepository provides EPSS score lookups.

type FindingActionsService

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

FindingActionsService handles the closed-loop finding lifecycle: in_progress → fix_applied → resolved (verified by scan or security).

func NewFindingActionsService

func NewFindingActionsService(
	findingRepo vulnerability.FindingRepository,
	accessCtrlRepo accesscontrol.Repository,
	groupRepo group.Repository,
	assetRepo asset.Repository,
	activityService *activity.FindingActivityService,
	db *sql.DB,
	logger *logger.Logger,
) *FindingActionsService

NewFindingActionsService creates a new FindingActionsService.

func (*FindingActionsService) AutoAssignToOwners

func (s *FindingActionsService) AutoAssignToOwners(
	ctx context.Context, tenantID string, assignerID string, filter vulnerability.FindingFilter,
) (*AutoAssignToOwnersResult, error)

AutoAssignToOwners assigns findings to their asset owners. Only assigns findings that don't already have an assignee.

func (*FindingActionsService) BulkFixApplied

func (s *FindingActionsService) BulkFixApplied(
	ctx context.Context, tenantID string, userID string, input BulkFixAppliedInput,
) (*BulkFixAppliedResult, error)

BulkFixApplied marks findings as fix_applied. Authorization: user must be assignee, group member, or asset owner for each finding.

func (*FindingActionsService) BulkRejectByFilter

func (s *FindingActionsService) BulkRejectByFilter(
	ctx context.Context, tenantID string, userID string, input RejectByFilterInput,
) (int64, error)

BulkRejectByFilter reopens all fix_applied findings matching a filter.

func (*FindingActionsService) BulkRejectFix

func (s *FindingActionsService) BulkRejectFix(
	ctx context.Context, tenantID string, userID string, findingIDs []string, reason string,
) (*BulkUpdateResult, error)

BulkRejectFix reopens fix_applied findings (fix was incorrect).

func (*FindingActionsService) BulkVerify

func (s *FindingActionsService) BulkVerify(
	ctx context.Context, tenantID string, userID string, findingIDs []string, note string,
) (*BulkUpdateResult, error)

BulkVerify resolves fix_applied findings (manual security review).

func (*FindingActionsService) BulkVerifyByFilter

func (s *FindingActionsService) BulkVerifyByFilter(
	ctx context.Context, tenantID string, userID string, input VerifyByFilterInput,
) (int64, error)

BulkVerifyByFilter resolves all fix_applied findings matching a filter. Used by Pending Review tab to approve entire groups at once.

func (*FindingActionsService) GetRelatedCVEs

func (s *FindingActionsService) GetRelatedCVEs(
	ctx context.Context, tenantID string, cveID string, filter vulnerability.FindingFilter,
) ([]vulnerability.RelatedCVE, error)

GetRelatedCVEs finds CVEs that share the same component as the given CVE.

func (*FindingActionsService) ListFindingGroups

ListFindingGroups returns findings grouped by a dimension.

func (*FindingActionsService) RequestVerificationScan

func (s *FindingActionsService) RequestVerificationScan(
	ctx context.Context, tenantID, userID string, input RequestVerificationScanInput,
) (*RequestVerificationScanResult, error)

RequestVerificationScan triggers a targeted quick scan on the asset associated with a finding. The finding must be in fix_applied status (dev has marked it as fixed; awaiting scan verification). The scan result is expected to either confirm the fix (→ resolved) or reveal the vuln still exists (→ back to in_progress) via the normal ingest pipeline.

func (*FindingActionsService) SetVerificationScanTrigger

func (s *FindingActionsService) SetVerificationScanTrigger(trigger VerificationScanTrigger)

SetVerificationScanTrigger wires the scan trigger (called after both services are initialized).

type FindingCommentService

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

FindingCommentService handles finding comment operations.

func NewFindingCommentService

func NewFindingCommentService(
	commentRepo vulnerability.FindingCommentRepository,
	findingRepo vulnerability.FindingRepository,
	log *logger.Logger,
) *FindingCommentService

NewFindingCommentService creates a new FindingCommentService.

func (*FindingCommentService) AddComment

AddComment adds a new comment to a finding.

func (*FindingCommentService) AddStatusChangeComment

AddStatusChangeComment adds a comment recording a status change.

func (*FindingCommentService) CountFindingComments

func (s *FindingCommentService) CountFindingComments(ctx context.Context, findingID string) (int, error)

CountFindingComments counts comments for a finding.

func (*FindingCommentService) DeleteComment

func (s *FindingCommentService) DeleteComment(ctx context.Context, tenantID, commentID, authorID string) error

DeleteComment deletes a comment. tenantID is required so the storage-layer WHERE clause rejects cross-tenant IDs (IDOR defense).

func (*FindingCommentService) GetComment

func (s *FindingCommentService) GetComment(ctx context.Context, commentID string) (*vulnerability.FindingComment, error)

GetComment retrieves a comment by ID.

func (*FindingCommentService) ListFindingComments

func (s *FindingCommentService) ListFindingComments(ctx context.Context, findingID string) ([]*vulnerability.FindingComment, error)

ListFindingComments retrieves all comments for a finding.

func (*FindingCommentService) UpdateComment

func (s *FindingCommentService) UpdateComment(ctx context.Context, tenantID, commentID, authorID string, input UpdateCommentInput) (*vulnerability.FindingComment, error)

UpdateComment updates an existing comment. tenantID scopes the lookup — user in tenant A cannot resolve a comment id from tenant B even if they guess the UUID.

type FindingImportService

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

FindingImportService handles importing findings from external scanner formats.

func NewFindingImportService

func NewFindingImportService(repo vulnerability.FindingRepository, log *logger.Logger) *FindingImportService

NewFindingImportService creates a new import service.

func (*FindingImportService) ImportBurpXML

func (s *FindingImportService) ImportBurpXML(ctx context.Context, tenantID, campaignID string, reader io.Reader) (*ImportResult, error)

ImportBurpXML parses Burp Suite XML and creates findings.

func (*FindingImportService) ImportCSV

func (s *FindingImportService) ImportCSV(ctx context.Context, tenantID, campaignID string, reader io.Reader) (*ImportResult, error)

ImportCSV parses CSV with headers and creates findings. Expected headers: title, severity, description, affected_assets, steps_to_reproduce, poc_code, business_impact, remediation

type FindingLifecycleScheduler

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

FindingLifecycleScheduler manages background tasks for finding lifecycle. This includes expiring findings on feature branches that have been inactive.

func NewFindingLifecycleScheduler

func NewFindingLifecycleScheduler(
	findingRepo vulnerability.FindingRepository,
	tenantLister TenantLister,
	cfg FindingLifecycleSchedulerConfig,
	log *logger.Logger,
) *FindingLifecycleScheduler

NewFindingLifecycleScheduler creates a new scheduler.

func (*FindingLifecycleScheduler) Start

func (s *FindingLifecycleScheduler) Start()

Start starts the scheduler.

func (*FindingLifecycleScheduler) Stop

func (s *FindingLifecycleScheduler) Stop()

Stop stops the scheduler gracefully. Safe to call even if Start() was never called (e.g. when Enabled=false).

type FindingLifecycleSchedulerConfig

type FindingLifecycleSchedulerConfig struct {
	// CheckInterval is how often to run lifecycle tasks (default: 1 hour)
	CheckInterval time.Duration

	// DefaultExpiryDays is the default number of days after which
	// feature branch findings are expired if not seen (default: 30)
	// Individual branches can override this via retention_days setting.
	DefaultExpiryDays int

	// Enabled controls whether the scheduler runs (default: true)
	Enabled bool
}

FindingLifecycleSchedulerConfig holds configuration for the scheduler.

func DefaultFindingLifecycleSchedulerConfig

func DefaultFindingLifecycleSchedulerConfig() FindingLifecycleSchedulerConfig

DefaultFindingLifecycleSchedulerConfig returns default configuration.

type FindingNotifier

type FindingNotifier interface {
	// NotifyNewFinding sends a notification for a new finding.
	// This should be called asynchronously to not block finding creation.
	NotifyNewFinding(tenantID, title, body, severity, url string)
}

FindingNotifier is an interface for sending finding notifications asynchronously. Deprecated: Use outbox.Service with transactional outbox pattern instead.

type FindingSourceCacheService

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

FindingSourceCacheService provides cached access to finding sources. Finding sources are system-level configuration that rarely changes, making them ideal candidates for aggressive caching.

Cache structure: - Key: "finding_sources:all" → CachedFindingSources (all sources with categories) - Key: "finding_sources:code:{code}" → single source (optional, for high-frequency lookups)

Cache invalidation: - Manual via InvalidateAll() when sources are modified (rare) - TTL-based expiration (24 hours)

This cache is GLOBAL (not per-tenant) because finding sources are system configuration.

func NewFindingSourceCacheService

func NewFindingSourceCacheService(
	redisClient *redis.Client,
	repo findingsource.Repository,
	log *logger.Logger,
) (*FindingSourceCacheService, error)

NewFindingSourceCacheService creates a new finding source cache service.

func (*FindingSourceCacheService) GetAll

GetAll returns all active finding sources with their categories (cached). This is the primary method for UI dropdowns and validation.

func (*FindingSourceCacheService) GetByCategory

func (s *FindingSourceCacheService) GetByCategory(ctx context.Context, categoryCode string) ([]*CachedFindingSource, error)

GetByCategory returns finding sources filtered by category code (from cache).

func (*FindingSourceCacheService) GetByCode

GetByCode returns a finding source by code (from cache). Returns nil if not found.

func (*FindingSourceCacheService) GetCategories

func (s *FindingSourceCacheService) GetCategories(ctx context.Context) ([]*CachedCategory, error)

GetCategories returns all active categories (from cache).

func (*FindingSourceCacheService) InvalidateAll

func (s *FindingSourceCacheService) InvalidateAll(ctx context.Context) error

InvalidateAll removes all cached finding source data. Call this when finding sources are modified (rare operation).

func (*FindingSourceCacheService) IsValidCode

func (s *FindingSourceCacheService) IsValidCode(ctx context.Context, code string) (bool, error)

IsValidCode checks if a code is a valid active finding source (from cache).

func (*FindingSourceCacheService) Refresh

Refresh forces a cache refresh by invalidating and reloading.

func (*FindingSourceCacheService) WarmCache

func (s *FindingSourceCacheService) WarmCache(ctx context.Context) error

WarmCache preloads the cache on startup. Call this during application initialization.

type FindingSourceService

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

FindingSourceService handles finding source-related business operations. Finding sources are read-only system configuration created via DB seed or by system admin.

func NewFindingSourceService

func NewFindingSourceService(repo findingsource.Repository, categoryRepo findingsource.CategoryRepository, log *logger.Logger) *FindingSourceService

NewFindingSourceService creates a new FindingSourceService.

func (*FindingSourceService) GetCategory

func (s *FindingSourceService) GetCategory(ctx context.Context, categoryID string) (*findingsource.Category, error)

GetCategory retrieves a category by ID.

func (*FindingSourceService) GetCategoryByCode

func (s *FindingSourceService) GetCategoryByCode(ctx context.Context, code string) (*findingsource.Category, error)

GetCategoryByCode retrieves a category by code.

func (*FindingSourceService) GetFindingSource

func (s *FindingSourceService) GetFindingSource(ctx context.Context, findingSourceID string) (*findingsource.FindingSource, error)

GetFindingSource retrieves a finding source by ID.

func (*FindingSourceService) GetFindingSourceByCode

func (s *FindingSourceService) GetFindingSourceByCode(ctx context.Context, code string) (*findingsource.FindingSource, error)

GetFindingSourceByCode retrieves a finding source by code.

func (*FindingSourceService) IsValidSourceCode

func (s *FindingSourceService) IsValidSourceCode(ctx context.Context, code string) (bool, error)

IsValidSourceCode checks if the code is a valid active finding source.

func (*FindingSourceService) ListActiveCategories

func (s *FindingSourceService) ListActiveCategories(ctx context.Context) ([]*findingsource.Category, error)

ListActiveCategories lists all active categories.

func (*FindingSourceService) ListActiveFindingSources

func (s *FindingSourceService) ListActiveFindingSources(ctx context.Context) ([]*findingsource.FindingSource, error)

ListActiveFindingSources lists all active finding sources.

func (*FindingSourceService) ListActiveFindingSourcesByCategory

func (s *FindingSourceService) ListActiveFindingSourcesByCategory(ctx context.Context, categoryID string) ([]*findingsource.FindingSource, error)

ListActiveFindingSourcesByCategory lists active finding sources by category.

func (*FindingSourceService) ListActiveFindingSourcesWithCategory

func (s *FindingSourceService) ListActiveFindingSourcesWithCategory(ctx context.Context) ([]*findingsource.FindingSourceWithCategory, error)

ListActiveFindingSourcesWithCategory lists all active finding sources with their categories.

func (*FindingSourceService) ListCategories

ListCategories lists categories with pagination.

func (*FindingSourceService) ListFindingSources

ListFindingSources lists finding sources with filtering and pagination.

func (*FindingSourceService) ListFindingSourcesWithCategory

ListFindingSourcesWithCategory lists finding sources with their categories.

type GetFindingStatsInput

type GetFindingStatsInput struct {
	TenantID     string
	ActingUserID string
	IsAdmin      bool
	// AssetID — when non-empty, restrict the stats to findings on this
	// specific asset. Used by /findings?assetId=… so the severity cards
	// match the filtered table.
	AssetID string
}

GetFindingStatsInput represents input for getting finding stats with data scope.

type ImportResult

type ImportResult struct {
	Total    int      `json:"total"`
	Created  int      `json:"created"`
	Skipped  int      `json:"skipped"`
	Errors   int      `json:"errors"`
	Messages []string `json:"messages,omitempty"`
}

ImportResult contains the result of an import operation.

type KEVData

type KEVData struct {
	DueDate    *time.Time
	Ransomware string
}

KEVData holds KEV catalog info for a CVE.

type KEVRepository

type KEVRepository interface {
	GetByCVEIDs(ctx context.Context, cveIDs []string) (map[string]KEVData, error)
}

KEVRepository provides KEV catalog lookups.

type ListFindingsInput

type ListFindingsInput struct {
	TenantID        string   `validate:"required,uuid"`
	AssetID         string   `validate:"omitempty,uuid"`
	BranchID        string   `validate:"omitempty,uuid"`
	ComponentID     string   `validate:"omitempty,uuid"`
	VulnerabilityID string   `validate:"omitempty,uuid"`
	Severities      []string `validate:"max=5,dive,severity"`
	Statuses        []string `validate:"max=10,dive,finding_status"`
	ExcludeStatuses []string `validate:"max=10,dive,finding_status"`
	Sources         []string `validate:"max=5,dive,finding_source"`
	ToolName        string   `validate:"max=100"`
	RuleID          string   `validate:"max=255"`
	ScanID          string   `validate:"max=100"`
	FilePath        string   `validate:"max=500"`
	Search          string   `validate:"max=255"` // Full-text search across title, description, and file path
	Sort            string   `validate:"max=100"` // Sort field (e.g., "-severity", "created_at")
	Page            int      `validate:"min=0"`
	PerPage         int      `validate:"min=0,max=100"`

	// Layer 2: Data Scope
	ActingUserID string // From JWT context
	IsAdmin      bool   // True for owner/admin (bypasses data scope)
}

ListFindingsInput represents the input for listing findings.

type ListVulnerabilitiesInput

type ListVulnerabilitiesInput struct {
	CVEIDs           []string `validate:"max=50,dive,max=20"`
	Severities       []string `validate:"max=5,dive,severity"`
	MinCVSS          *float64 `validate:"omitempty,min=0,max=10"`
	MaxCVSS          *float64 `validate:"omitempty,min=0,max=10"`
	MinEPSS          *float64 `validate:"omitempty,min=0,max=1"`
	ExploitAvailable *bool
	CISAKEVOnly      *bool
	Statuses         []string `validate:"max=5,dive,vulnerability_status"`
	Search           string   `validate:"max=255"` // Full-text search across CVE ID and description
	Sort             string   `validate:"max=100"` // Sort field (e.g., "-cvss_score", "cve_id")
	Page             int      `validate:"min=0"`
	PerPage          int      `validate:"min=0,max=100"`
}

ListVulnerabilitiesInput represents the input for listing vulnerabilities.

type PriorityAuditEntry

type PriorityAuditEntry struct {
	TenantID      shared.ID
	FindingID     shared.ID
	PreviousClass *vulnerability.PriorityClass
	NewClass      vulnerability.PriorityClass
	Reason        string
	Source        string // "auto", "rule", "manual"
	RuleID        *shared.ID
	ActorID       *shared.ID
}

PriorityAuditEntry represents a priority change log entry.

type PriorityAuditRepository

type PriorityAuditRepository interface {
	LogChange(ctx context.Context, entry PriorityAuditEntry) error
}

PriorityAuditRepository records priority changes.

type PriorityChangeEvent

type PriorityChangeEvent struct {
	TenantID      shared.ID
	FindingID     shared.ID
	PreviousClass *vulnerability.PriorityClass // nil for first classification
	NewClass      vulnerability.PriorityClass
	Reason        string
	Source        string // "auto" | "rule" | "sweep" | "manual"
	RuleID        *shared.ID
	At            time.Time
}

PriorityChangeEvent is emitted whenever a finding's priority class transitions to a new value. Downstream consumers (notification service, assignment-rule service, dashboard live feed) subscribe via the outbox.

(F3, B1, B2): emission is the mechanism that wires the reclassification sweep to the rest of the system. Without this event, a priority change is a silent dashboard update — an operator can miss that a P3 just became P0.

type PriorityChangePublisher

type PriorityChangePublisher interface {
	Publish(ctx context.Context, event PriorityChangeEvent) error
}

PriorityChangePublisher delivers priority-change events to downstream consumers, typically by inserting into the notification outbox. Optional — when nil, classification still runs but no event is fired.

type PriorityClassificationService

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

PriorityClassificationService orchestrates finding priority classification. It enriches findings with EPSS/KEV data, evaluates override rules, and applies the default CTEM classification logic.

func NewPriorityClassificationService

func NewPriorityClassificationService(
	findingRepo vulnerability.FindingRepository,
	assetRepo asset.Repository,
	epssRepo EPSSRepository,
	kevRepo KEVRepository,
	ruleRepo PriorityRuleRepository,
	auditRepo PriorityAuditRepository,
	log *logger.Logger,
) *PriorityClassificationService

NewPriorityClassificationService creates a new service.

func (*PriorityClassificationService) ClassifyFinding

func (s *PriorityClassificationService) ClassifyFinding(
	ctx context.Context,
	tenantID shared.ID,
	finding *vulnerability.Finding,
	assetEntity *asset.Asset,
) error

ClassifyFinding computes priority for a single finding.

(O1 invariant): emits ctem_stage_* metrics so dashboards and alert rules have real numbers for the Prioritization stage. Skipped on manual overrides — those bypass the stage entirely.

func (*PriorityClassificationService) EnrichAndClassifyBatch

func (s *PriorityClassificationService) EnrichAndClassifyBatch(
	ctx context.Context,
	tenantID shared.ID,
	findings []*vulnerability.Finding,
	assets map[shared.ID]*asset.Asset,
) error

EnrichAndClassifyBatch enriches findings with EPSS/KEV and classifies priority. Used after ingest to process a batch of new/updated findings.

func (*PriorityClassificationService) SetChangePublisher

func (s *PriorityClassificationService) SetChangePublisher(p PriorityChangePublisher)

SetChangePublisher wires the priority-change event publisher. Safe to call after construction; nil disables publishing.

func (*PriorityClassificationService) SetControlLookup

func (s *PriorityClassificationService) SetControlLookup(lookup CompensatingControlLookup)

SetControlLookup wires the compensating control lookup for priority calculation.

func (*PriorityClassificationService) SetPriorityFloodGuard

func (s *PriorityClassificationService) SetPriorityFloodGuard(g *PriorityFloodGuard)

SetPriorityFloodGuard wires the anti-flap budget used to suppress downstream fan-out on top-class bursts from noisy scanners. Nil disables the guard. Safe to call after construction.

type PriorityFloodConfig

type PriorityFloodConfig struct {
	// ProtectedClass is the priority class whose downstream fan-out
	// is capped. Defaults to vulnerability.PriorityP0 — the highest
	// class is the one that actually moves SLA clocks and pages
	// humans, so flooding there is the acute failure mode.
	ProtectedClass vulnerability.PriorityClass
	// MaxPerHour is the ceiling of newly-classified findings at the
	// protected class per tenant per rolling hour at which we start
	// suppressing downstream side effects. Default 50 — chosen
	// because a single tenant realistically cannot action more than
	// ~50 of the top class per hour.
	MaxPerHour int
	// Now is injectable for deterministic tests.
	Now func() time.Time
}

PriorityFloodConfig tunes the guard. Zero values take documented defaults.

type PriorityFloodGuard

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

PriorityFloodGuard is per-process in-memory. A future iteration moves it to Redis for cross-replica dedup; as long as there is ONE background worker doing classification (the sweep controller), in-memory is sufficient.

func NewPriorityFloodGuard

func NewPriorityFloodGuard(cfg PriorityFloodConfig) *PriorityFloodGuard

NewPriorityFloodGuard constructs the guard with defaults.

func (*PriorityFloodGuard) CurrentUsage

func (g *PriorityFloodGuard) CurrentUsage(tenantID shared.ID) int

CurrentUsage exposes the rolling-hour count for dashboards.

func (*PriorityFloodGuard) Refund

func (g *PriorityFloodGuard) Refund(tenantID shared.ID)

Refund returns a slot to the tenant's budget. Used when the caller's fan-out fails and will retry — otherwise a delivery failure would permanently burn the slot.

func (*PriorityFloodGuard) ShouldFanOut

func (g *PriorityFloodGuard) ShouldFanOut(
	ctx context.Context,
	tenantID shared.ID,
	class vulnerability.PriorityClass,
) (bool, error)

ShouldFanOut reports whether the downstream side effects (Jira, outbox, fast-lane queue) should run for a newly-classified finding at the guard's protected class.

Returns (true, nil) under budget — record AND fire. Returns (false, ErrPriorityFloodSuppressed) over budget — record only.

A classification that is NOT the protected class short-circuits immediately and the budget is not charged.

type PriorityRuleRepository

type PriorityRuleRepository interface {
	ListActiveByTenant(ctx context.Context, tenantID shared.ID) ([]*vulnerability.PriorityOverrideRule, error)
}

PriorityRuleRepository provides override rule lookups.

type RejectApprovalInput

type RejectApprovalInput struct {
	TenantID   string `json:"tenant_id" validate:"required,uuid"`
	ApprovalID string `json:"approval_id" validate:"required,uuid"`
	RejectedBy string `json:"rejected_by" validate:"required,uuid"`
	Reason     string `json:"reason" validate:"required,min=1,max=2000"`
}

RejectApprovalInput represents the input for rejecting a status change.

type RejectByFilterInput

type RejectByFilterInput struct {
	Filter vulnerability.FindingFilter
	Reason string
}

RejectByFilterInput is the input for bulk reject by filter.

type RequestApprovalInput

type RequestApprovalInput struct {
	TenantID        string  `json:"tenant_id" validate:"required,uuid"`
	FindingID       string  `json:"finding_id" validate:"required,uuid"`
	RequestedStatus string  `json:"requested_status" validate:"required"`
	Justification   string  `json:"justification" validate:"required,min=1,max=2000"`
	RequestedBy     string  `json:"requested_by" validate:"required,uuid"`
	ExpiresAt       *string `json:"expires_at"`
}

RequestApprovalInput represents the input for requesting a status approval.

type RequestVerificationScanInput

type RequestVerificationScanInput struct {
	FindingID   string
	ScannerName string // required if WorkflowID is empty
	WorkflowID  string // required if ScannerName is empty
}

RequestVerificationScanInput is the input for requesting a verification scan.

type RequestVerificationScanResult

type RequestVerificationScanResult struct {
	FindingID     string `json:"finding_id"`
	AssetID       string `json:"asset_id"`
	AssetName     string `json:"asset_name"`
	PipelineRunID string `json:"pipeline_run_id"`
	ScanID        string `json:"scan_id"`
}

RequestVerificationScanResult is the result of requesting a verification scan.

type TenantLister

type TenantLister interface {
	// ListActiveTenantIDs returns all active tenant IDs for batch processing.
	ListActiveTenantIDs(ctx context.Context) ([]shared.ID, error)
}

TenantLister provides a list of active tenant IDs.

type UpdateCommentInput

type UpdateCommentInput struct {
	Content string `validate:"required,min=1,max=10000"`
}

UpdateCommentInput represents the input for updating a comment.

type UpdateFindingStatusInput

type UpdateFindingStatusInput struct {
	Status              string `validate:"required,finding_status"`
	Resolution          string `validate:"max=1000"`
	ActorID             string // Authenticated user ID from middleware (required for audit trail and resolved_by)
	HasVerifyPermission bool   // True if user has findings:verify permission (for direct resolve guard)
}

UpdateFindingStatusInput represents the input for updating a finding's status.

type UpdateVulnerabilityInput

type UpdateVulnerabilityInput struct {
	Title            *string  `validate:"omitempty,min=1,max=500"`
	Description      *string  `validate:"omitempty,max=10000"`
	Severity         *string  `validate:"omitempty,severity"`
	CVSSScore        *float64 `validate:"omitempty,min=0,max=10"`
	CVSSVector       *string  `validate:"omitempty,max=100"`
	EPSSScore        *float64 `validate:"omitempty,min=0,max=1"`
	EPSSPercentile   *float64 `validate:"omitempty,min=0,max=1"`
	ExploitAvailable *bool
	ExploitMaturity  *string  `validate:"omitempty,exploit_maturity"`
	FixedVersions    []string `validate:"omitempty,max=50,dive,max=100"`
	Remediation      *string  `validate:"omitempty,max=5000"`
	Status           *string  `validate:"omitempty,vulnerability_status"`
}

UpdateVulnerabilityInput represents the input for updating a vulnerability.

type VerificationScanTrigger

type VerificationScanTrigger interface {
	// TriggerVerificationScan launches a quick scan on the given targets.
	// targets is a list of asset identifiers (names / hostnames / URLs).
	// Returns the pipeline run ID and scan ID on success.
	TriggerVerificationScan(ctx context.Context, tenantID, createdBy, scannerName, workflowID string, targets []string) (pipelineRunID, scanID string, err error)
}

VerificationScanTrigger is the interface for triggering targeted verification scans. Implemented by the scan.Service; kept as interface to avoid import cycles.

type VerifyByFilterInput

type VerifyByFilterInput struct {
	Filter vulnerability.FindingFilter
	Note   string
}

VerifyByFilterInput is the input for bulk verify by filter.

type VulnerabilityService

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

VulnerabilityService handles vulnerability-related business operations.

func NewVulnerabilityService

func NewVulnerabilityService(
	vulnRepo vulnerability.VulnerabilityRepository,
	findingRepo vulnerability.FindingRepository,
	log *logger.Logger,
) *VulnerabilityService

NewVulnerabilityService creates a new VulnerabilityService.

func (*VulnerabilityService) AddFindingComment

func (s *VulnerabilityService) AddFindingComment(ctx context.Context, tenantID, findingID, authorID, content string) (*vulnerability.FindingComment, error)

AddFindingComment adds a comment to a finding. tenantID is used for IDOR prevention and activity logging.

func (*VulnerabilityService) ApproveStatus

ApproveStatus approves a pending status change request and applies the status change.

func (*VulnerabilityService) AssignFinding

func (s *VulnerabilityService) AssignFinding(ctx context.Context, findingID, tenantID, userID, assignerID string) (*vulnerability.Finding, error)

AssignFinding assigns a finding to a user.

func (*VulnerabilityService) BulkAssignFindings

func (s *VulnerabilityService) BulkAssignFindings(ctx context.Context, tenantID string, input BulkAssignInput) (*BulkUpdateResult, error)

BulkAssignFindings assigns multiple findings to a user.

func (*VulnerabilityService) BulkUpdateFindingStatus

func (s *VulnerabilityService) BulkUpdateFindingStatus(ctx context.Context, tenantID shared.ID, ids []shared.ID, status vulnerability.FindingStatus, resolution string, resolvedBy *shared.ID) error

BulkUpdateFindingStatus updates the status of multiple findings. Pentest findings are excluded — they must be managed via the pentest module.

func (*VulnerabilityService) BulkUpdateFindingsStatus

func (s *VulnerabilityService) BulkUpdateFindingsStatus(ctx context.Context, tenantID string, input BulkUpdateStatusInput) (*BulkUpdateResult, error)

BulkUpdateFindingsStatus updates the status of multiple findings. Optimized: batch-fetches all findings in 1 query, validates transitions in-memory, then batch-updates valid ones in 1 query. Reduces N+1 to 2 queries total.

func (*VulnerabilityService) CancelApproval

CancelApproval cancels a pending approval request. Only the requester can cancel.

func (*VulnerabilityService) ClassifyFinding

func (s *VulnerabilityService) ClassifyFinding(ctx context.Context, findingID, tenantID string, input ClassifyFindingInput) (*vulnerability.Finding, error)

ClassifyFinding sets classification data on a finding.

func (*VulnerabilityService) CountAssetFindings

func (s *VulnerabilityService) CountAssetFindings(
	ctx context.Context,
	tenantID string,
	assetID string,
) (int64, error)

CountAssetFindings counts findings for an asset. tenantID is used for IDOR prevention - ensures the findings belong to the caller's tenant.

func (*VulnerabilityService) CountOpenAssetFindings

func (s *VulnerabilityService) CountOpenAssetFindings(
	ctx context.Context,
	tenantID string,
	assetID string,
) (int64, error)

CountOpenAssetFindings counts open findings for an asset. tenantID is used for IDOR prevention - ensures the findings belong to the caller's tenant.

func (*VulnerabilityService) CreateFinding

CreateFinding creates a new finding.

func (*VulnerabilityService) CreateVulnerability

CreateVulnerability creates a new vulnerability.

func (*VulnerabilityService) DeleteAssetFindings

func (s *VulnerabilityService) DeleteAssetFindings(
	ctx context.Context,
	tenantID string,
	assetID string,
) error

DeleteAssetFindings deletes all findings for an asset. tenantID is used for IDOR prevention - ensures the findings belong to the caller's tenant.

func (*VulnerabilityService) DeleteFinding

func (s *VulnerabilityService) DeleteFinding(ctx context.Context, findingID string, tenantID string) error

DeleteFinding deletes a finding by ID. tenantID is used for IDOR prevention - ensures the finding belongs to the caller's tenant.

func (*VulnerabilityService) DeleteFindingComment

func (s *VulnerabilityService) DeleteFindingComment(ctx context.Context, tenantID, commentID, authorID string) error

DeleteFindingComment deletes a comment. tenantID scopes the lookup and the DELETE WHERE clause — closes the same IDOR vector as UpdateFindingComment.

func (*VulnerabilityService) DeleteVulnerability

func (s *VulnerabilityService) DeleteVulnerability(ctx context.Context, vulnID string) error

DeleteVulnerability deletes a vulnerability by ID.

func (*VulnerabilityService) GetFinding

func (s *VulnerabilityService) GetFinding(ctx context.Context, tenantID, findingID string) (*vulnerability.Finding, error)

GetFinding retrieves a finding by ID. tenantID is used for IDOR prevention - ensures the finding belongs to the caller's tenant.

func (*VulnerabilityService) GetFindingCountsByScanID

func (s *VulnerabilityService) GetFindingCountsByScanID(ctx context.Context, tenantID, scanID string) (vulnerability.SeverityCounts, error)

GetFindingCountsByScanID returns the count of findings by severity for a scan. Used for quality gate evaluation.

func (*VulnerabilityService) GetFindingStats

func (s *VulnerabilityService) GetFindingStats(ctx context.Context, tenantID string) (*vulnerability.FindingStats, error)

GetFindingStats retrieves aggregated statistics for findings of a tenant.

func (*VulnerabilityService) GetFindingStatsWithScope

func (s *VulnerabilityService) GetFindingStatsWithScope(ctx context.Context, input GetFindingStatsInput) (*vulnerability.FindingStats, error)

GetFindingStatsWithScope retrieves stats with optional data scope enforcement.

func (*VulnerabilityService) GetFindingWithScope

func (s *VulnerabilityService) GetFindingWithScope(ctx context.Context, tenantID, findingID, actingUserID string, isAdmin bool) (*vulnerability.Finding, error)

GetFindingWithScope retrieves a finding with optional data scope enforcement. Non-admin users with group assignments can only access findings for assets in their groups. Security: fail-closed — any error during scope check denies access. Returns ErrNotFound (not ErrForbidden) to prevent information disclosure.

func (*VulnerabilityService) GetVulnerability

func (s *VulnerabilityService) GetVulnerability(ctx context.Context, vulnID string) (*vulnerability.Vulnerability, error)

GetVulnerability retrieves a vulnerability by ID.

func (*VulnerabilityService) GetVulnerabilityByCVE

func (s *VulnerabilityService) GetVulnerabilityByCVE(ctx context.Context, cveID string) (*vulnerability.Vulnerability, error)

GetVulnerabilityByCVE retrieves a vulnerability by CVE ID.

func (*VulnerabilityService) ListAssetFindings

func (s *VulnerabilityService) ListAssetFindings(
	ctx context.Context,
	tenantID string,
	assetID string,
	sort string,
	page,
	perPage int,
) (pagination.Result[*vulnerability.Finding], error)

ListAssetFindings retrieves findings for a specific asset. tenantID is used for IDOR prevention - ensures the findings belong to the caller's tenant.

func (*VulnerabilityService) ListFindingApprovals

func (s *VulnerabilityService) ListFindingApprovals(ctx context.Context, tenantID, findingID string) ([]*vulnerability.Approval, error)

ListFindingApprovals lists all approvals for a finding.

func (*VulnerabilityService) ListFindingComments

func (s *VulnerabilityService) ListFindingComments(ctx context.Context, findingID string) ([]*vulnerability.FindingComment, error)

ListFindingComments retrieves all comments for a finding.

func (*VulnerabilityService) ListFindings

ListFindings retrieves findings with filtering and pagination.

func (*VulnerabilityService) ListPendingApprovals

func (s *VulnerabilityService) ListPendingApprovals(ctx context.Context, tenantID string, page, perPage int) (pagination.Result[*vulnerability.Approval], error)

ListPendingApprovals lists all pending approvals for a tenant.

func (*VulnerabilityService) ListVulnerabilities

ListVulnerabilities retrieves vulnerabilities with filtering and pagination.

func (*VulnerabilityService) RejectApproval

RejectApproval rejects a pending status change request.

func (*VulnerabilityService) RequestApproval

RequestApproval creates a new pending approval request for a status change.

func (*VulnerabilityService) SetAITriageService

func (s *VulnerabilityService) SetAITriageService(svc *aitriage.AITriageService)

SetAITriageService sets the AI triage service for auto-triage on finding creation.

func (*VulnerabilityService) SetAccessControlRepository

func (s *VulnerabilityService) SetAccessControlRepository(repo accesscontrol.Repository)

SetAccessControlRepository sets the access control repository for Layer 2 data scope checks.

func (*VulnerabilityService) SetActivityService

func (s *VulnerabilityService) SetActivityService(svc *activity.FindingActivityService)

SetActivityService sets the activity service for audit trail tracking. When set, the service will automatically record activities for finding changes.

func (*VulnerabilityService) SetApprovalRepository

func (s *VulnerabilityService) SetApprovalRepository(repo vulnerability.ApprovalRepository)

SetApprovalRepository sets the approval repository for status approval workflow.

func (*VulnerabilityService) SetAssignmentEngine

func (s *VulnerabilityService) SetAssignmentEngine(engine *assignment.Engine)

SetAssignmentEngine sets the assignment engine for auto-routing findings to groups.

func (*VulnerabilityService) SetCommentRepository

func (s *VulnerabilityService) SetCommentRepository(repo vulnerability.FindingCommentRepository)

SetCommentRepository sets the comment repository for comment operations. This is optional and can be called after service creation.

func (*VulnerabilityService) SetDataFlowRepository

func (s *VulnerabilityService) SetDataFlowRepository(repo vulnerability.DataFlowRepository)

SetDataFlowRepository sets the data flow repository for loading data flow traces.

func (*VulnerabilityService) SetFindingNotifier

func (s *VulnerabilityService) SetFindingNotifier(notifier FindingNotifier)

SetFindingNotifier sets the notifier for new findings. Deprecated: Use SetOutboxService for reliable transactional notifications.

func (*VulnerabilityService) SetFindingTags

func (s *VulnerabilityService) SetFindingTags(ctx context.Context, findingID, tenantID string, tags []string) (*vulnerability.Finding, error)

SetFindingTags sets tags on a finding.

func (*VulnerabilityService) SetOutboxService

func (s *VulnerabilityService) SetOutboxService(db *sql.DB, svc *outbox.Service)

SetOutboxService sets the notification service for transactional outbox pattern. When set, notifications are enqueued in the same transaction as finding creation, ensuring reliable delivery even if the server crashes.

func (*VulnerabilityService) SetUserNotificationService

func (s *VulnerabilityService) SetUserNotificationService(svc *integration.NotificationService)

SetUserNotificationService sets the in-app notification service for user-facing notifications.

func (*VulnerabilityService) SetUserRepository

func (s *VulnerabilityService) SetUserRepository(repo user.Repository)

SetUserRepository sets the user repository for user lookup (e.g., for activity recording).

func (*VulnerabilityService) TriageFinding

func (s *VulnerabilityService) TriageFinding(ctx context.Context, findingID, tenantID, userID, reason string) (*vulnerability.Finding, error)

TriageFinding confirms a finding (triage = confirm the finding is real). This is a convenience method that transitions status from "new" to "confirmed". For other status changes, use UpdateFindingStatus.

func (*VulnerabilityService) UnassignFinding

func (s *VulnerabilityService) UnassignFinding(ctx context.Context, findingID, tenantID, actorID string) (*vulnerability.Finding, error)

UnassignFinding removes the assignment from a finding.

func (*VulnerabilityService) UpdateFindingComment

func (s *VulnerabilityService) UpdateFindingComment(ctx context.Context, tenantID, commentID, authorID, content string) (*vulnerability.FindingComment, error)

UpdateFindingComment updates a comment's content. tenantID scopes the lookup so a user in tenant A cannot resolve a commentID owned by tenant B — closes the IDOR vector where the author-ID short-circuit (authorID=="") would let an admin-path bypass the per-row owner check.

func (*VulnerabilityService) UpdateFindingSeverity

func (s *VulnerabilityService) UpdateFindingSeverity(ctx context.Context, findingID, tenantID, severityStr, actorID string) (*vulnerability.Finding, error)

UpdateFindingSeverity updates a finding's severity.

func (*VulnerabilityService) UpdateFindingStatus

func (s *VulnerabilityService) UpdateFindingStatus(ctx context.Context, findingID string, tenantID string, input UpdateFindingStatusInput) (*vulnerability.Finding, error)

UpdateFindingStatus updates a finding's status. tenantID is used for IDOR prevention - ensures the finding belongs to the caller's tenant.

func (*VulnerabilityService) UpdateVulnerability

UpdateVulnerability updates an existing vulnerability.

func (*VulnerabilityService) VerifyFinding

func (s *VulnerabilityService) VerifyFinding(ctx context.Context, findingID, tenantID, userID string) (*vulnerability.Finding, error)

VerifyFinding marks a resolved finding as verified.

Jump to

Keyboard shortcuts

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