mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-20 01:47:35 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,209 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuditLog platform operation audit record.
|
||||||
|
type AuditLog struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Level string `json:"level"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Result string `json:"result"`
|
||||||
|
Actor string `json:"actor"`
|
||||||
|
SessionHint string `json:"sessionHint,omitempty"`
|
||||||
|
ClientIP string `json:"clientIp,omitempty"`
|
||||||
|
UserAgent string `json:"userAgent,omitempty"`
|
||||||
|
ResourceType string `json:"resourceType,omitempty"`
|
||||||
|
ResourceID string `json:"resourceId,omitempty"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Detail map[string]interface{} `json:"detail,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAuditLogsFilter query parameters.
|
||||||
|
type ListAuditLogsFilter struct {
|
||||||
|
Level string
|
||||||
|
Category string
|
||||||
|
Action string
|
||||||
|
Result string
|
||||||
|
Query string
|
||||||
|
ResourceType string
|
||||||
|
ResourceID string
|
||||||
|
Since *time.Time
|
||||||
|
Until *time.Time
|
||||||
|
Limit int
|
||||||
|
Offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAuditLogsWhere(filter ListAuditLogsFilter) (string, []interface{}) {
|
||||||
|
conditions := []string{"1=1"}
|
||||||
|
args := []interface{}{}
|
||||||
|
if filter.Level != "" {
|
||||||
|
conditions = append(conditions, "level = ?")
|
||||||
|
args = append(args, filter.Level)
|
||||||
|
}
|
||||||
|
if filter.Category != "" {
|
||||||
|
conditions = append(conditions, "category = ?")
|
||||||
|
args = append(args, filter.Category)
|
||||||
|
}
|
||||||
|
if filter.Action != "" {
|
||||||
|
conditions = append(conditions, "action = ?")
|
||||||
|
args = append(args, filter.Action)
|
||||||
|
}
|
||||||
|
if filter.Result != "" {
|
||||||
|
conditions = append(conditions, "result = ?")
|
||||||
|
args = append(args, filter.Result)
|
||||||
|
}
|
||||||
|
if filter.ResourceType != "" {
|
||||||
|
conditions = append(conditions, "resource_type = ?")
|
||||||
|
args = append(args, filter.ResourceType)
|
||||||
|
}
|
||||||
|
if filter.ResourceID != "" {
|
||||||
|
conditions = append(conditions, "resource_id = ?")
|
||||||
|
args = append(args, filter.ResourceID)
|
||||||
|
}
|
||||||
|
if filter.Since != nil {
|
||||||
|
conditions = append(conditions, "created_at >= ?")
|
||||||
|
args = append(args, *filter.Since)
|
||||||
|
}
|
||||||
|
if filter.Until != nil {
|
||||||
|
conditions = append(conditions, "created_at <= ?")
|
||||||
|
args = append(args, *filter.Until)
|
||||||
|
}
|
||||||
|
if q := strings.TrimSpace(filter.Query); q != "" {
|
||||||
|
like := "%" + q + "%"
|
||||||
|
conditions = append(conditions, "(message LIKE ? OR resource_id LIKE ? OR action LIKE ? OR category LIKE ?)")
|
||||||
|
args = append(args, like, like, like, like)
|
||||||
|
}
|
||||||
|
return strings.Join(conditions, " AND "), args
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendAuditLog inserts one audit row.
|
||||||
|
func (db *DB) AppendAuditLog(row *AuditLog) error {
|
||||||
|
if row == nil {
|
||||||
|
return errors.New("audit log is nil")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(row.ID) == "" {
|
||||||
|
return errors.New("audit id is required")
|
||||||
|
}
|
||||||
|
if row.CreatedAt.IsZero() {
|
||||||
|
row.CreatedAt = time.Now()
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(row.Level) == "" {
|
||||||
|
row.Level = "info"
|
||||||
|
}
|
||||||
|
detailJSON := ""
|
||||||
|
if len(row.Detail) > 0 {
|
||||||
|
if b, err := json.Marshal(row.Detail); err == nil {
|
||||||
|
detailJSON = string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query := `
|
||||||
|
INSERT INTO audit_logs (
|
||||||
|
id, created_at, level, category, action, result, actor, session_hint,
|
||||||
|
client_ip, user_agent, resource_type, resource_id, message, detail_json
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
`
|
||||||
|
_, err := db.Exec(query,
|
||||||
|
row.ID, row.CreatedAt, row.Level, row.Category, row.Action, row.Result,
|
||||||
|
row.Actor, row.SessionHint, row.ClientIP, row.UserAgent,
|
||||||
|
row.ResourceType, row.ResourceID, row.Message, detailJSON,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuditLogByID returns one row.
|
||||||
|
func (db *DB) GetAuditLogByID(id string) (*AuditLog, error) {
|
||||||
|
id = strings.TrimSpace(id)
|
||||||
|
if id == "" {
|
||||||
|
return nil, errors.New("id is required")
|
||||||
|
}
|
||||||
|
query := `
|
||||||
|
SELECT id, created_at, level, category, action, result, actor,
|
||||||
|
COALESCE(session_hint, ''), COALESCE(client_ip, ''), COALESCE(user_agent, ''),
|
||||||
|
COALESCE(resource_type, ''), COALESCE(resource_id, ''), message, COALESCE(detail_json, '')
|
||||||
|
FROM audit_logs WHERE id = ?
|
||||||
|
`
|
||||||
|
var row AuditLog
|
||||||
|
var detailJSON string
|
||||||
|
err := db.QueryRow(query, id).Scan(
|
||||||
|
&row.ID, &row.CreatedAt, &row.Level, &row.Category, &row.Action, &row.Result, &row.Actor,
|
||||||
|
&row.SessionHint, &row.ClientIP, &row.UserAgent,
|
||||||
|
&row.ResourceType, &row.ResourceID, &row.Message, &detailJSON,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if detailJSON != "" {
|
||||||
|
_ = json.Unmarshal([]byte(detailJSON), &row.Detail)
|
||||||
|
}
|
||||||
|
return &row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountAuditLogs counts rows matching filter.
|
||||||
|
func (db *DB) CountAuditLogs(filter ListAuditLogsFilter) (int64, error) {
|
||||||
|
where, args := buildAuditLogsWhere(filter)
|
||||||
|
query := `SELECT COUNT(*) FROM audit_logs WHERE ` + where
|
||||||
|
var n int64
|
||||||
|
err := db.QueryRow(query, args...).Scan(&n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAuditLogs lists audit rows newest first.
|
||||||
|
func (db *DB) ListAuditLogs(filter ListAuditLogsFilter) ([]*AuditLog, error) {
|
||||||
|
where, args := buildAuditLogsWhere(filter)
|
||||||
|
limit := filter.Limit
|
||||||
|
if limit <= 0 || limit > 500 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
offset := filter.Offset
|
||||||
|
if offset < 0 {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
query := `
|
||||||
|
SELECT id, created_at, level, category, action, result, actor,
|
||||||
|
COALESCE(session_hint, ''), COALESCE(client_ip, ''), COALESCE(user_agent, ''),
|
||||||
|
COALESCE(resource_type, ''), COALESCE(resource_id, ''), message, COALESCE(detail_json, '')
|
||||||
|
FROM audit_logs
|
||||||
|
WHERE ` + where + `
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
`
|
||||||
|
args = append(args, limit, offset)
|
||||||
|
rows, err := db.Query(query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var list []*AuditLog
|
||||||
|
for rows.Next() {
|
||||||
|
var row AuditLog
|
||||||
|
var detailJSON string
|
||||||
|
if err := rows.Scan(
|
||||||
|
&row.ID, &row.CreatedAt, &row.Level, &row.Category, &row.Action, &row.Result, &row.Actor,
|
||||||
|
&row.SessionHint, &row.ClientIP, &row.UserAgent,
|
||||||
|
&row.ResourceType, &row.ResourceID, &row.Message, &detailJSON,
|
||||||
|
); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if detailJSON != "" {
|
||||||
|
_ = json.Unmarshal([]byte(detailJSON), &row.Detail)
|
||||||
|
}
|
||||||
|
list = append(list, &row)
|
||||||
|
}
|
||||||
|
return list, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAuditLogsBefore removes rows older than cutoff.
|
||||||
|
func (db *DB) DeleteAuditLogsBefore(cutoff time.Time) (int64, error) {
|
||||||
|
res, err := db.Exec(`DELETE FROM audit_logs WHERE created_at < ?`, cutoff)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return res.RowsAffected()
|
||||||
|
}
|
||||||
@@ -387,6 +387,24 @@ func (db *DB) initTables() error {
|
|||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
);`
|
);`
|
||||||
|
|
||||||
|
createAuditLogsTable := `
|
||||||
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
level TEXT NOT NULL DEFAULT 'info',
|
||||||
|
category TEXT NOT NULL,
|
||||||
|
action TEXT NOT NULL,
|
||||||
|
result TEXT NOT NULL,
|
||||||
|
actor TEXT NOT NULL DEFAULT 'admin',
|
||||||
|
session_hint TEXT,
|
||||||
|
client_ip TEXT,
|
||||||
|
user_agent TEXT,
|
||||||
|
resource_type TEXT,
|
||||||
|
resource_id TEXT,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
detail_json TEXT
|
||||||
|
);`
|
||||||
|
|
||||||
createC2ProfilesTable := `
|
createC2ProfilesTable := `
|
||||||
CREATE TABLE IF NOT EXISTS c2_profiles (
|
CREATE TABLE IF NOT EXISTS c2_profiles (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
@@ -445,6 +463,10 @@ func (db *DB) initTables() error {
|
|||||||
CREATE INDEX IF NOT EXISTS idx_c2_events_created_at ON c2_events(created_at);
|
CREATE INDEX IF NOT EXISTS idx_c2_events_created_at ON c2_events(created_at);
|
||||||
CREATE INDEX IF NOT EXISTS idx_c2_events_category ON c2_events(category);
|
CREATE INDEX IF NOT EXISTS idx_c2_events_category ON c2_events(category);
|
||||||
CREATE INDEX IF NOT EXISTS idx_c2_events_session ON c2_events(session_id);
|
CREATE INDEX IF NOT EXISTS idx_c2_events_session ON c2_events(session_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_category ON audit_logs(category);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_result ON audit_logs(result);
|
||||||
`
|
`
|
||||||
|
|
||||||
if _, err := db.Exec(createConversationsTable); err != nil {
|
if _, err := db.Exec(createConversationsTable); err != nil {
|
||||||
@@ -514,6 +536,10 @@ func (db *DB) initTables() error {
|
|||||||
return fmt.Errorf("创建webshell_connection_states表失败: %w", err)
|
return fmt.Errorf("创建webshell_connection_states表失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := db.Exec(createAuditLogsTable); err != nil {
|
||||||
|
return fmt.Errorf("创建audit_logs表失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
for tableName, ddl := range map[string]string{
|
for tableName, ddl := range map[string]string{
|
||||||
"c2_listeners": createC2ListenersTable,
|
"c2_listeners": createC2ListenersTable,
|
||||||
"c2_sessions": createC2SessionsTable,
|
"c2_sessions": createC2SessionsTable,
|
||||||
|
|||||||
Reference in New Issue
Block a user