mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(audit): extensible event type system with shared constants
This commit is contained in:
+11
-26
@@ -8,36 +8,14 @@ export function sanitizeAuditInput(raw: string): string {
|
|||||||
return raw.replace(/[<>&"']/g, "").slice(0, MAX_AUDIT_INPUT_LENGTH) || "(empty)";
|
return raw.replace(/[<>&"']/g, "").slice(0, MAX_AUDIT_INPUT_LENGTH) || "(empty)";
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuditEvent =
|
|
||||||
| "LOGIN_SUCCESS"
|
|
||||||
| "LOGIN_FAILED"
|
|
||||||
| "LOGOUT"
|
|
||||||
| "PASSWORD_CHANGED"
|
|
||||||
| "PASSWORD_RESET"
|
|
||||||
| "USER_CREATED"
|
|
||||||
| "USER_DELETED"
|
|
||||||
| "USER_UPDATED"
|
|
||||||
| "FILE_UPLOADED"
|
|
||||||
| "FILE_DELETED"
|
|
||||||
| "API_KEY_CREATED"
|
|
||||||
| "API_KEY_DELETED"
|
|
||||||
| "ROLE_CREATED"
|
|
||||||
| "ROLE_UPDATED"
|
|
||||||
| "ROLE_DELETED"
|
|
||||||
| "SETTINGS_UPDATED"
|
|
||||||
| "OIDC_LOGIN_SUCCESS"
|
|
||||||
| "OIDC_USER_CREATED"
|
|
||||||
| "OIDC_USER_LINKED"
|
|
||||||
| "OIDC_LOGIN_FAILED";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit a structured audit log entry for security-relevant events.
|
* Emit a structured audit log entry for security-relevant events.
|
||||||
*
|
*
|
||||||
* Dual-writes: structured stdout log (for aggregators) + SQLite row.
|
* Dual-writes: structured stdout log (for aggregators) + DB row.
|
||||||
*/
|
*/
|
||||||
export async function auditLog(
|
export async function auditLog(
|
||||||
logger: FastifyBaseLogger,
|
logger: FastifyBaseLogger,
|
||||||
event: AuditEvent,
|
event: string,
|
||||||
details: Record<string, unknown> = {},
|
details: Record<string, unknown> = {},
|
||||||
ip: string | null = null,
|
ip: string | null = null,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -64,18 +42,25 @@ export async function auditLog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deriveTargetType(event: AuditEvent): string | null {
|
function deriveTargetType(event: string): string | null {
|
||||||
if (
|
if (
|
||||||
event.startsWith("USER_") ||
|
event.startsWith("USER_") ||
|
||||||
event.startsWith("LOGIN") ||
|
event.startsWith("LOGIN") ||
|
||||||
event.startsWith("PASSWORD") ||
|
event.startsWith("PASSWORD") ||
|
||||||
event.startsWith("OIDC_") ||
|
event.startsWith("OIDC_") ||
|
||||||
|
event.startsWith("SAML_") ||
|
||||||
|
event.startsWith("SCIM_") ||
|
||||||
|
event.startsWith("MFA_") ||
|
||||||
event === "LOGOUT"
|
event === "LOGOUT"
|
||||||
)
|
)
|
||||||
return "user";
|
return "user";
|
||||||
if (event.startsWith("API_KEY")) return "api_key";
|
if (event.startsWith("API_KEY")) return "api_key";
|
||||||
if (event.startsWith("FILE")) return "file";
|
if (event.startsWith("FILE")) return "file";
|
||||||
if (event.startsWith("ROLE")) return "role";
|
if (event.startsWith("ROLE")) return "role";
|
||||||
if (event === "SETTINGS_UPDATED") return "setting";
|
if (event === "SETTINGS_UPDATED" || event === "IP_ALLOWLIST_UPDATED") return "setting";
|
||||||
|
if (event.startsWith("TOOL_") || event.startsWith("BATCH_") || event.startsWith("PIPELINE_"))
|
||||||
|
return "tool";
|
||||||
|
if (event.startsWith("LEGAL_HOLD")) return "compliance";
|
||||||
|
if (event.startsWith("SIEM_") || event.startsWith("WEBHOOK_")) return "integration";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
export const CORE_AUDIT_EVENTS = [
|
||||||
|
"LOGIN_SUCCESS",
|
||||||
|
"LOGIN_FAILED",
|
||||||
|
"LOGOUT",
|
||||||
|
"PASSWORD_CHANGED",
|
||||||
|
"PASSWORD_RESET",
|
||||||
|
"USER_CREATED",
|
||||||
|
"USER_DELETED",
|
||||||
|
"USER_UPDATED",
|
||||||
|
"FILE_UPLOADED",
|
||||||
|
"FILE_DELETED",
|
||||||
|
"API_KEY_CREATED",
|
||||||
|
"API_KEY_DELETED",
|
||||||
|
"ROLE_CREATED",
|
||||||
|
"ROLE_UPDATED",
|
||||||
|
"ROLE_DELETED",
|
||||||
|
"SETTINGS_UPDATED",
|
||||||
|
"OIDC_LOGIN_SUCCESS",
|
||||||
|
"OIDC_USER_CREATED",
|
||||||
|
"OIDC_USER_LINKED",
|
||||||
|
"OIDC_LOGIN_FAILED",
|
||||||
|
"TOOL_EXECUTED",
|
||||||
|
"BATCH_EXECUTED",
|
||||||
|
"PIPELINE_EXECUTED",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type CoreAuditEvent = (typeof CORE_AUDIT_EVENTS)[number];
|
||||||
|
|
||||||
|
export const ALL_AUDIT_EVENTS = [...CORE_AUDIT_EVENTS] as string[];
|
||||||
|
|
||||||
|
export function registerAuditEvents(events: string[]) {
|
||||||
|
for (const e of events) {
|
||||||
|
if (!ALL_AUDIT_EVENTS.includes(e)) {
|
||||||
|
ALL_AUDIT_EVENTS.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from "./analytics/consent.js";
|
export * from "./analytics/consent.js";
|
||||||
export * from "./analytics/events.js";
|
export * from "./analytics/events.js";
|
||||||
export * from "./analytics/types.js";
|
export * from "./analytics/types.js";
|
||||||
|
export * from "./audit-events.js";
|
||||||
export * from "./constants.js";
|
export * from "./constants.js";
|
||||||
export * from "./features.js";
|
export * from "./features.js";
|
||||||
export * from "./i18n/index.js";
|
export * from "./i18n/index.js";
|
||||||
|
|||||||
Reference in New Issue
Block a user