First release of open core

This commit is contained in:
t
2026-04-02 10:57:36 -04:00
parent 1c94f12d1c
commit 084c1321fc
101 changed files with 8812 additions and 17 deletions

127
pkg/sla/sla.go Normal file
View File

@@ -0,0 +1,127 @@
package sla
import (
"context"
"log"
"time"
"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
)
// DefaultSLACalculator implements the SLACalculator interface
type DefaultSLACalculator struct {
Timezone string
BusinessStart int
BusinessEnd int
Holidays map[string]bool
}
// NewSLACalculator returns the interface
func NewSLACalculator() domain.SLACalculator {
return &DefaultSLACalculator{
Timezone: "UTC",
BusinessStart: 9,
BusinessEnd: 17,
Holidays: make(map[string]bool),
}
}
// CalculateDueDate for the finding based on SLA
func (c *DefaultSLACalculator) CalculateDueDate(severity string) *time.Time {
var days int
switch severity {
case "Critical":
days = 3
case "High":
days = 14
case "Medium":
days = 30
case "Low":
days = 90
default:
days = 30
}
loc, err := time.LoadLocation(c.Timezone)
if err != nil {
log.Printf("Warning: Invalid timezone '%s', falling back to UTC", c.Timezone)
loc = time.UTC
}
nowLocal := time.Now().In(loc)
dueDate := c.AddBusinessDays(nowLocal, days)
return &dueDate
}
// AddBusinessDays for working days not weekends and some holidays
func (c *DefaultSLACalculator) AddBusinessDays(start time.Time, businessDays int) time.Time {
current := start
added := 0
for added < businessDays {
current = current.AddDate(0, 0, 1)
weekday := current.Weekday()
dateStr := current.Format("2006-01-02")
if weekday != time.Saturday && weekday != time.Sunday && !c.Holidays[dateStr] {
added++
}
}
return current
}
// CalculateTrueSLAHours based on the time of action for ticket
func (c *DefaultSLACalculator) CalculateTrueSLAHours(ctx context.Context, ticketID int, store domain.Store) (float64, error) {
appConfig, err := store.GetAppConfig(ctx)
if err != nil {
return 0, err
}
ticket, err := store.GetTicketByID(ctx, ticketID)
if err != nil {
return 0, err
}
end := time.Now()
if ticket.PatchedAt != nil {
end = *ticket.PatchedAt
}
totalActiveBusinessHours := c.calculateBusinessHoursBetween(ticket.CreatedAt, end, appConfig)
return totalActiveBusinessHours, nil
}
// calculateBusinessHoursBetween calculates strict working hours between two timestamps
func (c *DefaultSLACalculator) calculateBusinessHoursBetween(start, end time.Time, config domain.AppConfig) float64 {
loc, _ := time.LoadLocation(config.Timezone)
start = start.In(loc)
end = end.In(loc)
if start.After(end) {
return 0
}
var activeHours float64
current := start
for current.Before(end) {
nextHour := current.Add(time.Hour)
if nextHour.After(end) {
nextHour = end
}
weekday := current.Weekday()
dateStr := current.Format("2006-01-02")
hour := current.Hour()
isWeekend := weekday == time.Saturday || weekday == time.Sunday
isHoliday := c.Holidays[dateStr]
isBusinessHour := hour >= config.BusinessStart && hour < config.BusinessEnd
if !isWeekend && !isHoliday && isBusinessHour {
activeHours += nextHour.Sub(current).Hours()
}
current = nextHour
}
return activeHours
}

116
pkg/sla/sla_test.go Normal file
View File

@@ -0,0 +1,116 @@
package sla_test
import (
"database/sql"
"testing"
_ "github.com/mattn/go-sqlite3"
)
// GetSLAPolicy simulates the core engine function that fetches SLA rules
func GetSLAPolicy(db *sql.DB, domain string, severity string) (daysToRemediate int, maxExtensions int, err error) {
query := `SELECT days_to_remediate, max_extensions FROM sla_policies WHERE domain = ? AND severity = ?`
err = db.QueryRow(query, domain, severity).Scan(&daysToRemediate, &maxExtensions)
return daysToRemediate, maxExtensions, err
}
// setupTestDB spins up an isolated, in-memory database for testing
func setupTestDB(t *testing.T) *sql.DB {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("Failed to open test database: %v", err)
}
schema := `
CREATE TABLE domains (name TEXT PRIMARY KEY);
CREATE TABLE sla_policies (
domain TEXT NOT NULL,
severity TEXT NOT NULL,
days_to_remediate INTEGER NOT NULL,
max_extensions INTEGER NOT NULL DEFAULT 3,
PRIMARY KEY (domain, severity)
);
INSERT INTO domains (name) VALUES ('Vulnerability'), ('Privacy'), ('Incident');
INSERT INTO sla_policies (domain, severity, days_to_remediate, max_extensions) VALUES
('Vulnerability', 'Critical', 14, 1),
('Vulnerability', 'High', 30, 2),
('Privacy', 'Critical', 3, 0),
('Incident', 'Critical', 1, 0);
`
if _, err := db.Exec(schema); err != nil {
t.Fatalf("Failed to execute test schema: %v", err)
}
return db
}
func TestSLAEngine(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
tests := []struct {
name string
domain string
severity string
expectDays int
expectExtensions int
expectError bool
}{
{
name: "VM Critical (Standard)",
domain: "Vulnerability",
severity: "Critical",
expectDays: 14,
expectExtensions: 1,
expectError: false,
},
{
name: "Privacy Critical (Strict 72-hour, No Extensions)",
domain: "Privacy",
severity: "Critical",
expectDays: 3,
expectExtensions: 0,
expectError: false,
},
{
name: "Incident Critical (24-hour, No Extensions)",
domain: "Incident",
severity: "Critical",
expectDays: 1,
expectExtensions: 0,
expectError: false,
},
{
name: "Unknown Domain (Should Fail)",
domain: "PhysicalSecurity",
severity: "Critical",
expectError: true,
},
{
name: "Unknown Severity (Should Fail)",
domain: "Vulnerability",
severity: "SuperCritical",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
days, extensions, err := GetSLAPolicy(db, tt.domain, tt.severity)
if (err != nil) != tt.expectError {
t.Fatalf("expected error: %v, got: %v", tt.expectError, err)
}
if tt.expectError {
return
}
if days != tt.expectDays {
t.Errorf("expected %d days, got %d", tt.expectDays, days)
}
if extensions != tt.expectExtensions {
t.Errorf("expected %d max extensions, got %d", tt.expectExtensions, extensions)
}
})
}
}