Security fixes + project files: Session fixation, SameSite cookies, audit log redaction, backup integrity, Docker hardening, error sanitization, rate limiting + pre-existing test and UI fixes
Some checks failed
CI / frontend (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / backend (push) Has been cancelled

This commit is contained in:
backup 2026-09-04 00:02:26 -05:00
commit acd95a0891
7 changed files with 874 additions and 16 deletions

View file

@ -0,0 +1,138 @@
package repositories
import (
"database/sql"
"time"
)
type AuditLogEntry struct {
ID string
UserID string
Username string
Role string
Action string
ResourceType string
ResourceName string
Provider string
SourceFile string
ContentHash string
Timestamp time.Time
Result string
ErrorCategory string
RollbackOccurred bool
ErrorMessage string
}
type AuditRepository struct {
db *sql.DB
}
func NewAuditRepository(db *sql.DB) *AuditRepository {
return &AuditRepository{db: db}
}
func (r *AuditRepository) Create(entry *AuditLogEntry) error {
_, err := r.db.Exec(
`INSERT INTO audit_log (id, user_id, username, role, action, resource_type, resource_name, provider, source_file, content_hash, timestamp, result, error_category, rollback_occurred, error_message)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
entry.ID, entry.UserID, entry.Username, entry.Role, entry.Action, entry.ResourceType, entry.ResourceName,
entry.Provider, entry.SourceFile, entry.ContentHash, entry.Timestamp, entry.Result, entry.ErrorCategory,
entry.RollbackOccurred, entry.ErrorMessage,
)
return err
}
func (r *AuditRepository) List(opts ListAuditOptions) ([]AuditLogEntry, int, error) {
query := `SELECT id, user_id, username, role, action, resource_type, resource_name, provider, source_file, content_hash, timestamp, result, error_category, rollback_occurred, error_message FROM audit_log WHERE 1=1 `
var args []interface{}
argIdx := 1
if opts.Username != "" {
// Look up user ID by username
var userID string
err := r.db.QueryRow(`SELECT id FROM users WHERE username = ?`, opts.Username).Scan(&userID)
if err == nil && userID != "" {
opts.UserID = userID
}
// If user not found, we just won't filter by user_id (entries without a matching user_id will be excluded)
}
if opts.UserID != "" {
query += " AND user_id=? "
args = append(args, opts.UserID)
argIdx++
}
if opts.Action != "" {
query += " AND action=? "
args = append(args, opts.Action)
argIdx++
}
if opts.Result != "" {
query += " AND result=? "
args = append(args, opts.Result)
argIdx++
}
if opts.ResourceType != "" {
query += " AND resource_type=? "
args = append(args, opts.ResourceType)
argIdx++
}
if opts.StartDate != "" {
query += " AND timestamp>=? "
args = append(args, opts.StartDate)
argIdx++
}
if opts.EndDate != "" {
query += " AND timestamp<=? "
args = append(args, opts.EndDate)
argIdx++
}
countQuery := query + " LIMIT 1 "
var count int
err := r.db.QueryRow(countQuery, args...).Scan(&count)
if err != nil {
return nil, 0, err
}
query += " ORDER BY timestamp DESC LIMIT ? OFFSET ?"
args = append(args, opts.Limit, opts.Offset)
rows, err := r.db.Query(query, args...)
if err != nil {
return nil, 0, err
}
defer rows.Close()
var entries []AuditLogEntry
for rows.Next() {
var e AuditLogEntry
var rollbackOccurred int
err := rows.Scan(&e.ID, &e.UserID, &e.Username, &e.Role, &e.Action, &e.ResourceType, &e.ResourceName,
&e.Provider, &e.SourceFile, &e.ContentHash, &e.Timestamp, &e.Result, &e.ErrorCategory, &rollbackOccurred, &e.ErrorMessage)
if err != nil {
return nil, 0, err
}
e.RollbackOccurred = rollbackOccurred != 0
entries = append(entries, e)
}
return entries, count, nil
}
type ListAuditOptions struct {
UserID string
Username string
Action string
Result string
ResourceType string
StartDate string
EndDate string
Limit int
Offset int
}
func defaultListOptions() ListAuditOptions {
return ListAuditOptions{
Limit: 50,
Offset: 0,
}
}