First release of open core
This commit is contained in:
16
pkg/domain/adapter.go
Normal file
16
pkg/domain/adapter.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package domain
|
||||
|
||||
// Adapter represents a saved mapping profile for a specific scanner
|
||||
type Adapter struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
SourceName string `json:"source_name"`
|
||||
FindingsPath string `json:"findings_path"`
|
||||
MappingTitle string `json:"mapping_title"`
|
||||
MappingAsset string `json:"mapping_asset"`
|
||||
MappingSeverity string `json:"mapping_severity"`
|
||||
MappingDescription string `json:"mapping_description"`
|
||||
MappingRemediation string `json:"mapping_remediation"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
74
pkg/domain/analytics.go
Normal file
74
pkg/domain/analytics.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package domain
|
||||
|
||||
type ResolutionMetrics struct {
|
||||
Total int
|
||||
Patched int
|
||||
RiskAccepted int
|
||||
FalsePositive int
|
||||
PatchedPct int
|
||||
RiskAccPct int
|
||||
FalsePosPct int
|
||||
}
|
||||
|
||||
type SheriffAnalytics struct {
|
||||
ActiveKEVs int
|
||||
GlobalMTTRDays int
|
||||
OpenCriticals int
|
||||
TotalOverdue int
|
||||
SourceHealth []SourceMetrics
|
||||
Resolution ResolutionMetrics
|
||||
Severity SeverityMetrics
|
||||
TopAssets []AssetMetric
|
||||
}
|
||||
|
||||
type SourceMetrics struct {
|
||||
Source string
|
||||
TotalOpen int
|
||||
Criticals int
|
||||
CisaKEVs int
|
||||
Untriaged int
|
||||
PatchOverdue int
|
||||
PendingRisk int
|
||||
TotalClosed int
|
||||
Patched int
|
||||
RiskAccepted int
|
||||
FalsePositive int
|
||||
TopAssignee string
|
||||
StrategicNote string
|
||||
}
|
||||
|
||||
type FeedItem struct {
|
||||
Actor string
|
||||
ActivityType string
|
||||
NewValue string
|
||||
TimeAgo string
|
||||
}
|
||||
|
||||
type SeverityMetrics struct {
|
||||
Critical int
|
||||
High int
|
||||
Medium int
|
||||
Low int
|
||||
Info int
|
||||
Total int
|
||||
CritPct int
|
||||
HighPct int
|
||||
MedPct int
|
||||
LowPct int
|
||||
InfoPct int
|
||||
}
|
||||
|
||||
type AssetMetric struct {
|
||||
Asset string
|
||||
Count int
|
||||
Percentage int
|
||||
}
|
||||
|
||||
type SyncLog struct {
|
||||
ID int `json:"id"`
|
||||
Source string `json:"source"`
|
||||
Status string `json:"status"`
|
||||
RecordsProcessed int `json:"records_processed"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
18
pkg/domain/auth.go
Normal file
18
pkg/domain/auth.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
Email string `json:"email"`
|
||||
FullName string `json:"full_name"`
|
||||
PasswordHash string `json:"-"`
|
||||
GlobalRole string `json:"global_role"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
type Session struct {
|
||||
Token string `json:"token"`
|
||||
UserID int `json:"user_id"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
15
pkg/domain/config.go
Normal file
15
pkg/domain/config.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package domain
|
||||
|
||||
type AppConfig struct {
|
||||
Timezone string `json:"timezone"`
|
||||
BusinessStart int `json:"business_start"`
|
||||
BusinessEnd int `json:"business_end"`
|
||||
DefaultExtensionDays int `json:"default_extension_days"`
|
||||
Backup BackupPolicy `json:"backup"`
|
||||
}
|
||||
|
||||
type BackupPolicy struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
IntervalHours int `json:"interval_hours"`
|
||||
RetentionDays int `json:"retention_days"`
|
||||
}
|
||||
16
pkg/domain/connector.go
Normal file
16
pkg/domain/connector.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package domain
|
||||
|
||||
// ConnectorTemplate defines how to translate third-party JSON into ticket format
|
||||
type ConnectorTemplate struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
SourceDefault string `json:"source_default"`
|
||||
FindingsArrayPath string `json:"findings_array_path"`
|
||||
FieldMappings struct {
|
||||
Title string `json:"title"`
|
||||
AssetIdentifier string `json:"asset_identifier"`
|
||||
Severity string `json:"severity"`
|
||||
Description string `json:"description"`
|
||||
RecommendedRemediation string `json:"recommended_remediation"`
|
||||
} `json:"field_mappings"`
|
||||
}
|
||||
11
pkg/domain/drafts.go
Normal file
11
pkg/domain/drafts.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package domain
|
||||
|
||||
type DraftTicket struct {
|
||||
ID int `json:"id"`
|
||||
ReportID string `json:"report_id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Severity string `json:"severity"`
|
||||
AssetIdentifier string `json:"asset_identifier"`
|
||||
RecommendedRemediation string `json:"recommended_remediation"`
|
||||
}
|
||||
11
pkg/domain/export.go
Normal file
11
pkg/domain/export.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package domain
|
||||
|
||||
type ExportState struct {
|
||||
AppConfig AppConfig `json:"app_config"`
|
||||
SLAPolicies []SLAPolicy `json:"sla_policies"`
|
||||
Users []User `json:"users"`
|
||||
Adapters []Adapter `json:"adapters"`
|
||||
Tickets []Ticket `json:"tickets"`
|
||||
Version string `json:"export_version"`
|
||||
ExportedAt string `json:"exported_at"`
|
||||
}
|
||||
95
pkg/domain/store.go
Normal file
95
pkg/domain/store.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Store embeds all sub interfaces for Core
|
||||
type Store interface {
|
||||
TicketStore
|
||||
IdentityStore
|
||||
IngestStore
|
||||
ConfigStore
|
||||
AnalyticsStore
|
||||
DraftStore
|
||||
}
|
||||
|
||||
// TicketStore: Core CRUD and Workflow
|
||||
type TicketStore interface {
|
||||
GetTickets(ctx context.Context) ([]Ticket, error)
|
||||
GetDashboardTickets(ctx context.Context, tabStatus, filter, assetFilter, userEmail, userRole string, limit, offset int) ([]Ticket, int, map[string]int, error)
|
||||
CreateTicket(ctx context.Context, t *Ticket) error
|
||||
GetTicketByID(ctx context.Context, id int) (Ticket, error)
|
||||
UpdateTicketInline(ctx context.Context, ticketID int, severity, description, remediation, comment, actor, status, assignee string) error
|
||||
}
|
||||
|
||||
// IdentityStore: Users, Sessions, and Dispatching
|
||||
type IdentityStore interface {
|
||||
CreateUser(ctx context.Context, email, fullName, passwordHash, globalRole string) (*User, error)
|
||||
GetUserByEmail(ctx context.Context, email string) (*User, error)
|
||||
GetUserByID(ctx context.Context, id int) (*User, error)
|
||||
GetAllUsers(ctx context.Context) ([]*User, error)
|
||||
GetUserCount(ctx context.Context) (int, error)
|
||||
UpdateUserPassword(ctx context.Context, id int, newPasswordHash string) error
|
||||
UpdateUserRole(ctx context.Context, id int, newRole string) error
|
||||
DeactivateUserAndReassign(ctx context.Context, userID int) error
|
||||
|
||||
CreateSession(ctx context.Context, token string, userID int, expiresAt time.Time) error
|
||||
GetSession(ctx context.Context, token string) (*Session, error)
|
||||
DeleteSession(ctx context.Context, token string) error
|
||||
|
||||
GetWranglers(ctx context.Context) ([]User, error)
|
||||
}
|
||||
|
||||
// IngestStore: Scanners, Adapters, and Sync History
|
||||
type IngestStore interface {
|
||||
IngestTickets(ctx context.Context, tickets []Ticket) error
|
||||
ProcessIngestionBatch(ctx context.Context, source string, assetIdentifier string, incoming []Ticket) error
|
||||
|
||||
GetAdapters(ctx context.Context) ([]Adapter, error)
|
||||
GetAdapterByID(ctx context.Context, id int) (Adapter, error)
|
||||
GetAdapterByName(ctx context.Context, name string) (Adapter, error)
|
||||
SaveAdapter(ctx context.Context, adapter Adapter) error
|
||||
DeleteAdapter(ctx context.Context, id int) error
|
||||
|
||||
LogSync(ctx context.Context, source, status string, records int, errMsg string) error
|
||||
GetRecentSyncLogs(ctx context.Context, limit int) ([]SyncLog, error)
|
||||
}
|
||||
|
||||
// ConfigStore: Global System Settings
|
||||
type ConfigStore interface {
|
||||
GetAppConfig(ctx context.Context) (AppConfig, error)
|
||||
UpdateAppConfig(ctx context.Context, config AppConfig) error
|
||||
GetSLAPolicies(ctx context.Context) ([]SLAPolicy, error)
|
||||
UpdateSLAPolicies(ctx context.Context, slas []SLAPolicy) error
|
||||
UpdateBackupPolicy(ctx context.Context, policy BackupPolicy) error
|
||||
ExportSystemState(ctx context.Context) (ExportState, error)
|
||||
}
|
||||
|
||||
// AnalyticsStore: Audit Logs and KPI Metrics
|
||||
type AnalyticsStore interface {
|
||||
GetSheriffAnalytics(ctx context.Context) (SheriffAnalytics, error)
|
||||
GetAnalyticsSummary(ctx context.Context) (map[string]int, error)
|
||||
GetGlobalActivityFeed(ctx context.Context, limit int) ([]FeedItem, error)
|
||||
GetPaginatedActivityFeed(ctx context.Context, filter string, limit int, offset int) ([]FeedItem, int, error)
|
||||
}
|
||||
|
||||
// DraftStore: The Pentest Desk OSS, word docx
|
||||
type DraftStore interface {
|
||||
SaveDraft(ctx context.Context, draft DraftTicket) error
|
||||
GetDraftsByReport(ctx context.Context, reportID string) ([]DraftTicket, error)
|
||||
DeleteDraft(ctx context.Context, draftID string) error
|
||||
UpdateDraft(ctx context.Context, draftID int, payload Ticket) error
|
||||
PromotePentestDrafts(ctx context.Context, reportID string, analystEmail string, tickets []Ticket) error
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
Middleware(next http.Handler) http.Handler
|
||||
}
|
||||
|
||||
type SLACalculator interface {
|
||||
CalculateDueDate(severity string) *time.Time
|
||||
CalculateTrueSLAHours(ctx context.Context, ticketID int, store Store) (float64, error)
|
||||
}
|
||||
61
pkg/domain/ticket.go
Normal file
61
pkg/domain/ticket.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SLAPolicy represents the global SLA configuration per severity
|
||||
type SLAPolicy struct {
|
||||
Domain string `json:"domain"`
|
||||
Severity string `json:"severity"`
|
||||
DaysToRemediate int `json:"days_to_remediate"`
|
||||
MaxExtensions int `json:"max_extensions"`
|
||||
DaysToTriage int `json:"days_to_triage"`
|
||||
}
|
||||
|
||||
// AssetRiskSummary holds the rolled-up vulnerability counts for a single asset
|
||||
type AssetRiskSummary struct {
|
||||
AssetIdentifier string
|
||||
TotalActive int
|
||||
Critical int
|
||||
High int
|
||||
Medium int
|
||||
Low int
|
||||
Info int
|
||||
}
|
||||
|
||||
type Ticket struct {
|
||||
ID int `json:"id"`
|
||||
Domain string `json:"domain"`
|
||||
IsOverdue bool `json:"is_overdue"`
|
||||
DaysToResolve *int `json:"days_to_resolve"`
|
||||
Source string `json:"source"`
|
||||
AssetIdentifier string `json:"asset_identifier"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
RecommendedRemediation string `json:"recommended_remediation"`
|
||||
Severity string `json:"severity"`
|
||||
Status string `json:"status"`
|
||||
|
||||
DedupeHash string `json:"dedupe_hash"`
|
||||
|
||||
PatchEvidence *string `json:"patch_evidence"`
|
||||
OwnerViewedAt *time.Time `json:"owner_viewed_at"`
|
||||
|
||||
TriageDueDate time.Time `json:"triage_due_date"`
|
||||
RemediationDueDate time.Time `json:"remediation_due_date"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
PatchedAt *time.Time `json:"patched_at"`
|
||||
|
||||
SLAString string `json:"sla_string"`
|
||||
Assignee string `json:"assignee"`
|
||||
LatestComment string `json:"latest_comment"`
|
||||
}
|
||||
|
||||
// TicketAssignment represents the many-to-many relationship
|
||||
type TicketAssignment struct {
|
||||
TicketID int `json:"ticket_id"`
|
||||
Assignee string `json:"assignee"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
Reference in New Issue
Block a user