diff --git a/.changeset/prune-dead-audit-config-cluster.md b/.changeset/prune-dead-audit-config-cluster.md new file mode 100644 index 0000000000..3f8f790f2c --- /dev/null +++ b/.changeset/prune-dead-audit-config-cluster.md @@ -0,0 +1,38 @@ +--- +"@objectstack/spec": minor +--- + +feat(spec)!: remove the dead `AuditConfig` cluster from `@objectstack/spec/system` (#1878 recheck loose-end) + +The entire `system/audit.zod.ts` module — `AuditConfigSchema`, +`AuditStorageConfigSchema`, `AuditRetentionPolicySchema`, +`AuditEventFilterSchema`, `SuspiciousActivityRuleSchema`, +`DEFAULT_SUSPICIOUS_ACTIVITY_RULES`, and the `AuditEvent` / +`AuditEventActor` / `AuditEventTarget` / `AuditEventChange` / +`AuditEventType` / `AuditEventSeverity` shape schemas (plus all their +type exports) — is removed. Verified zero consumers repo-wide: the live +audit path (`plugin-audit`) imports none of it, defines its own +`sys_audit_log` row shape, and captures **unconditionally** via engine +hooks, so `AuditConfigSchema.enabled: false` advertised a semantic +(turning the compliance ledger off) the platform deliberately rejects. +Same ADR-0056 D8 family as the earlier `compliance.zod` / `masking.zod` / +`RLSAuditConfig` / `PolicySchema` removals: security/compliance-shaped +config must never merely look live. + +**Migration — every dead knob maps to a live surface (or is deliberately +not configurable):** + +| Removed (never enforced) | Live replacement | +| --- | --- | +| `AuditConfigSchema.enabled` | none — audit capture is **always on** (compliance ledger; `object.zod` `trackHistory` contract) | +| `eventTypes` / `excludeEventTypes` / `minimumSeverity` / `AuditEventFilterSchema` | none today — if event filtering ships it lands as an `audit` **settings** namespace (ADR-0069 pattern), not app metadata | +| which fields/objects are summarized + History tab UI | object-level + field-level **`trackHistory`** (live, enforced by plugin-audit) | +| `AuditRetentionPolicySchema` / `storage` | object **`lifecycle`** `audit` category (retain → archive → delete) + per-org settings overrides (ADR-0057) | +| `SuspiciousActivityRuleSchema` / `DEFAULT_SUSPICIOUS_ACTIVITY_RULES` | none — no detection engine exists; security monitoring is org-operations tooling, not app-package metadata | +| `AuditEvent*` shape schemas | the `sys_audit_log` object definition in `plugin-audit` is the row-shape source of truth | + +No first-party, example, or downstream-contract code imported any of +these symbols; `defineStack` never accepted an `audit` key, so no stack +config changes. Docs page `references/system/audit.mdx` is removed by +regeneration; the security-context module doc now marks audit alongside +the previously removed compliance/masking subsystems. diff --git a/content/docs/references/system/audit.mdx b/content/docs/references/system/audit.mdx deleted file mode 100644 index 05ff1003d1..0000000000 --- a/content/docs/references/system/audit.mdx +++ /dev/null @@ -1,269 +0,0 @@ ---- -title: Audit -description: Audit protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -Audit Log Architecture - -Comprehensive audit logging system for compliance and security. - -Supports SOX, HIPAA, GDPR, and other regulatory requirements. - -Features: - -- Records all CRUD operations on data - -- Tracks authentication events (login, logout, password reset) - -- Monitors authorization changes (permissions, roles) - -- Configurable retention policies (180-day GDPR requirement) - -- Suspicious activity detection and alerting - - -**Source:** `packages/spec/src/system/audit.zod.ts` - - -## TypeScript Usage - -```typescript -import { AuditConfig, AuditEvent, AuditEventActor, AuditEventChange, AuditEventFilter, AuditEventSeverity, AuditEventTarget, AuditEventType, AuditRetentionPolicy, AuditStorageConfig, SuspiciousActivityRule } from '@objectstack/spec/system'; -import type { AuditConfig, AuditEvent, AuditEventActor, AuditEventChange, AuditEventFilter, AuditEventSeverity, AuditEventTarget, AuditEventType, AuditRetentionPolicy, AuditStorageConfig, SuspiciousActivityRule } from '@objectstack/spec/system'; - -// Validate data -const result = AuditConfig.parse(data); -``` - ---- - -## AuditConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Configuration name (snake_case, max 64 chars) | -| **label** | `string` | ✅ | Display label | -| **enabled** | `boolean` | optional | Enable audit logging | -| **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to audit | -| **excludeEventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to exclude | -| **minimumSeverity** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>` | optional | Minimum severity level | -| **storage** | `{ type: Enum<'database' \| 'elasticsearch' \| 'mongodb' \| 'clickhouse' \| 's3' \| 'gcs' \| 'azure_blob' \| 'custom'>; connectionString?: string; config?: Record; bufferEnabled?: boolean; … }` | ✅ | Storage configuration | -| **retentionPolicy** | `{ retentionDays?: integer; archiveAfterRetention?: boolean; archiveStorage?: object; customRetention?: Record; … }` | optional | Retention policy | -| **suspiciousActivityRules** | `{ id: string; name: string; description?: string; enabled?: boolean; … }[]` | optional | Suspicious activity rules | -| **includeSensitiveData** | `boolean` | optional | Include sensitive data | -| **redactFields** | `string[]` | optional | Fields to redact | -| **logReads** | `boolean` | optional | Log read operations | -| **readSamplingRate** | `number` | optional | Read sampling rate | -| **logSystemEvents** | `boolean` | optional | Log system events | -| **customHandlers** | `{ eventType: Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>; handlerId: string }[]` | optional | Custom event handler references | -| **compliance** | `{ standards?: Enum<'sox' \| 'hipaa' \| 'gdpr' \| 'pci_dss' \| 'iso_27001' \| 'fedramp'>[]; immutableLogs?: boolean; requireSigning?: boolean; signingKey?: string }` | optional | Compliance configuration | - - ---- - -## AuditEvent - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **id** | `string` | ✅ | Audit event ID | -| **eventType** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>` | ✅ | Event type | -| **severity** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>` | ✅ | Event severity | -| **timestamp** | `string` | ✅ | Event timestamp | -| **actor** | `{ type: Enum<'user' \| 'system' \| 'service' \| 'api_client' \| 'integration'>; id: string; name?: string; email?: string; … }` | ✅ | Event actor | -| **target** | `{ type: string; id: string; name?: string; metadata?: Record }` | optional | Event target | -| **description** | `string` | ✅ | Event description | -| **changes** | `{ field: string; oldValue?: any; newValue?: any }[]` | optional | List of changes | -| **result** | `Enum<'success' \| 'failure' \| 'partial'>` | ✅ | Action result | -| **errorMessage** | `string` | optional | Error message | -| **tenantId** | `string` | optional | Tenant identifier | -| **requestId** | `string` | optional | Request ID for tracing | -| **metadata** | `Record` | optional | Additional metadata | -| **location** | `{ country?: string; region?: string; city?: string }` | optional | Geographic location | - - ---- - -## AuditEventActor - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `Enum<'user' \| 'system' \| 'service' \| 'api_client' \| 'integration'>` | ✅ | Actor type | -| **id** | `string` | ✅ | Actor identifier | -| **name** | `string` | optional | Actor display name | -| **email** | `string` | optional | Actor email address | -| **ipAddress** | `string` | optional | Actor IP address | -| **userAgent** | `string` | optional | User agent string | - - ---- - -## AuditEventChange - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **field** | `string` | ✅ | Changed field name | -| **oldValue** | `any` | optional | Previous value | -| **newValue** | `any` | optional | New value | - - ---- - -## AuditEventFilter - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to include | -| **severities** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>[]` | optional | Severity levels to include | -| **actorId** | `string` | optional | Actor identifier | -| **tenantId** | `string` | optional | Tenant identifier | -| **timeRange** | `{ from: string; to: string }` | optional | Time range filter | -| **result** | `Enum<'success' \| 'failure' \| 'partial'>` | optional | Result status | -| **searchQuery** | `string` | optional | Search query | -| **customFilters** | `Record` | optional | Custom filters | - - ---- - -## AuditEventSeverity - -### Allowed Values - -* `debug` -* `info` -* `notice` -* `warning` -* `error` -* `critical` -* `alert` -* `emergency` - - ---- - -## AuditEventTarget - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `string` | ✅ | Target type | -| **id** | `string` | ✅ | Target identifier | -| **name** | `string` | optional | Target display name | -| **metadata** | `Record` | optional | Target metadata | - - ---- - -## AuditEventType - -### Allowed Values - -* `data.create` -* `data.read` -* `data.update` -* `data.delete` -* `data.export` -* `data.import` -* `data.bulk_update` -* `data.bulk_delete` -* `auth.login` -* `auth.login_failed` -* `auth.logout` -* `auth.session_created` -* `auth.session_expired` -* `auth.password_reset` -* `auth.password_changed` -* `auth.email_verified` -* `auth.mfa_enabled` -* `auth.mfa_disabled` -* `auth.account_locked` -* `auth.account_unlocked` -* `authz.permission_granted` -* `authz.permission_revoked` -* `authz.role_assigned` -* `authz.role_removed` -* `authz.role_created` -* `authz.role_updated` -* `authz.role_deleted` -* `authz.policy_created` -* `authz.policy_updated` -* `authz.policy_deleted` -* `system.config_changed` -* `system.plugin_installed` -* `system.plugin_uninstalled` -* `system.backup_created` -* `system.backup_restored` -* `system.integration_added` -* `system.integration_removed` -* `security.access_denied` -* `security.suspicious_activity` -* `security.data_breach` -* `security.api_key_created` -* `security.api_key_revoked` - - ---- - -## AuditRetentionPolicy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **retentionDays** | `integer` | ✅ | Retention period in days | -| **archiveAfterRetention** | `boolean` | ✅ | Archive logs after retention period | -| **archiveStorage** | `{ type: Enum<'s3' \| 'gcs' \| 'azure_blob' \| 'filesystem'>; endpoint?: string; bucket?: string; path?: string; … }` | optional | Archive storage configuration | -| **customRetention** | `Record` | optional | Custom retention by event type | -| **minimumRetentionDays** | `integer` | optional | Minimum retention for compliance | - - ---- - -## AuditStorageConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `Enum<'database' \| 'elasticsearch' \| 'mongodb' \| 'clickhouse' \| 's3' \| 'gcs' \| 'azure_blob' \| 'custom'>` | ✅ | Storage backend type | -| **connectionString** | `string` | optional | Connection string | -| **config** | `Record` | optional | Storage-specific configuration | -| **bufferEnabled** | `boolean` | ✅ | Enable buffering | -| **bufferSize** | `integer` | ✅ | Buffer size | -| **flushIntervalSeconds** | `integer` | ✅ | Flush interval in seconds | -| **compression** | `boolean` | ✅ | Enable compression | - - ---- - -## SuspiciousActivityRule - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **id** | `string` | ✅ | Rule identifier | -| **name** | `string` | ✅ | Rule name | -| **description** | `string` | optional | Rule description | -| **enabled** | `boolean` | optional | Rule enabled status | -| **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | ✅ | Event types to monitor | -| **condition** | `{ threshold: integer; windowSeconds: integer; groupBy?: string[]; filters?: Record } \| string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | ✅ | Detection condition — structured threshold or CEL predicate | -| **actions** | `Enum<'alert' \| 'lock_account' \| 'block_ip' \| 'require_mfa' \| 'log_critical' \| 'webhook'>[]` | ✅ | Actions to take | -| **alertSeverity** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>` | optional | Alert severity | -| **notifications** | `{ email?: string[]; slack?: string; webhook?: string }` | optional | Notification configuration | - - ---- - diff --git a/content/docs/references/system/index.mdx b/content/docs/references/system/index.mdx index d416d817d8..111ea39759 100644 --- a/content/docs/references/system/index.mdx +++ b/content/docs/references/system/index.mdx @@ -7,7 +7,6 @@ This section contains all protocol schemas for the system layer of ObjectStack. - diff --git a/content/docs/references/system/meta.json b/content/docs/references/system/meta.json index f1484ef6f0..6f4076046b 100644 --- a/content/docs/references/system/meta.json +++ b/content/docs/references/system/meta.json @@ -29,7 +29,6 @@ "translation", "worker", "---Observability---", - "audit", "logging", "metrics", "tracing", diff --git a/content/docs/references/system/security-context.mdx b/content/docs/references/system/security-context.mdx index bbcac1bfd3..a071c6da0c 100644 --- a/content/docs/references/system/security-context.mdx +++ b/content/docs/references/system/security-context.mdx @@ -9,15 +9,21 @@ Unified Security Context Protocol Provides a central governance layer that correlates and unifies -the four independent security subsystems: +the four independent security subsystems it was designed against. Three of -- **Audit** (audit.zod.ts): Event logging and suspicious activity detection +the four have since been REMOVED per ADR-0056 D8 (declared-but-never-enforced; + +see system/index.ts notes) — only encryption survives, marked experimental: + +- **Audit** (audit.zod.ts — REMOVED): the live audit path is plugin-audit's + +always-on capture + object/field `trackHistory` + lifecycle `audit` retention - **Encryption** (encryption.zod.ts): Field-level encryption and key management -- **Compliance** (compliance.zod.ts): Regulatory framework enforcement (GDPR/HIPAA/SOX/PCI-DSS) +- **Compliance** (compliance.zod.ts — REMOVED): GDPR/HIPAA/SOX/PCI-DSS configs -- **Masking** (masking.zod.ts): PII data masking and tokenization +- **Masking** (masking.zod.ts — REMOVED): PII data masking and tokenization This schema enforces cross-cutting security policies, ensuring compliance diff --git a/docs/audits/2026-07-security-props-liveness-recheck.md b/docs/audits/2026-07-security-props-liveness-recheck.md index 255ee01980..f2f4ee2250 100644 --- a/docs/audits/2026-07-security-props-liveness-recheck.md +++ b/docs/audits/2026-07-security-props-liveness-recheck.md @@ -53,12 +53,20 @@ enterprise/cloud-enforced (agent `access`). ## Genuine remaining loose ends (actionable) -1. **Prune `AuditRetentionPolicySchema`** (`packages/spec/src/system/audit.zod.ts`) - — a real parsed-only/dead spec schema with no runtime consumer; retention is - enforced on the `lifecycle` surface. Prune (or wire-rename to `lifecycle`). -2. **SharingRule dead recipient types** — `owner`-type rules and `group`/`guest` - recipients are skipped by the bootstrap while criteria-type rules enforce. - Enforce-or-prune the dead subset so authored rules don't silently no-op. +1. ~~**Prune `AuditRetentionPolicySchema`**~~ — **✅ Done (2026-07-27)**, and the + verified scope was larger: the **entire `audit.zod.ts` module** (AuditConfig / + AuditStorageConfig / AuditRetentionPolicy / AuditEventFilter / + SuspiciousActivityRule + the AuditEvent* shape schemas) had zero consumers — + the live audit path (plugin-audit) captures unconditionally via engine hooks + and defines its own `sys_audit_log` row shape, and `AuditConfigSchema.enabled` + contradicted the always-on compliance-ledger contract. Whole module removed; + the enforced authoring surface is object/field `trackHistory` + the object + `lifecycle` `audit` category, with per-org overrides in settings. +2. ~~**SharingRule dead recipient types**~~ — **✅ Done (#3557)**: recipients + reconciled against the live identity model — `group` wired as `team`, + `business_unit` added, and the unenforceable `guest` recipient + `owner`-type + rules removed from the authoring surface (authored rules no longer silently + no-op). 3. **Per-org / per-user IP allow-list** — global-only today; per-org `sys_organization.allowed_ip_ranges` is unimplemented (tracked **#2571**). 4. **Doc / ledger drift** (non-code-behavior): diff --git a/packages/spec/PROTOCOL_MAP.md b/packages/spec/PROTOCOL_MAP.md index 1d7ed6066b..7021262422 100644 --- a/packages/spec/PROTOCOL_MAP.md +++ b/packages/spec/PROTOCOL_MAP.md @@ -136,7 +136,6 @@ This document serves as the **Grand Map** of the ObjectStack specification. It l | [`auth-config.zod.ts`](src/system/auth-config.zod.ts) | | **Auth Configuration**. SSO, OIDC, SAML settings. | | [`http-server.zod.ts`](src/system/http-server.zod.ts) | | **HTTP Server**. Server port, CORS, and middleware settings. | | [`logging.zod.ts`](src/system/logging.zod.ts) | | **Logging**. Log levels and output formats. | -| [`audit.zod.ts`](src/system/audit.zod.ts) | | **Audit Trail**. Audit logging configuration. | | [`cache.zod.ts`](src/system/cache.zod.ts) | | **Caching**. Redis/Memory cache strategies. | | [`metrics.zod.ts`](src/system/metrics.zod.ts) | | **Observability**. Prometheus/OpenTelemetry metrics. | | [`tracing.zod.ts`](src/system/tracing.zod.ts) | | **Tracing**. Distributed tracing configuration. | diff --git a/packages/spec/api-surface.json b/packages/spec/api-surface.json index 41d5eb04fb..3ae5e36aa5 100644 --- a/packages/spec/api-surface.json +++ b/packages/spec/api-surface.json @@ -605,24 +605,6 @@ "AppTranslationBundleSchema (const)", "AudienceBook (interface)", "AudienceCaller (interface)", - "AuditConfig (type)", - "AuditConfigSchema (const)", - "AuditEvent (type)", - "AuditEventActor (type)", - "AuditEventActorSchema (const)", - "AuditEventChange (type)", - "AuditEventChangeSchema (const)", - "AuditEventFilter (type)", - "AuditEventFilterSchema (const)", - "AuditEventSchema (const)", - "AuditEventSeverity (type)", - "AuditEventTarget (type)", - "AuditEventTargetSchema (const)", - "AuditEventType (type)", - "AuditRetentionPolicy (type)", - "AuditRetentionPolicySchema (const)", - "AuditStorageConfig (type)", - "AuditStorageConfigSchema (const)", "AuthConfig (type)", "AuthConfigSchema (const)", "AuthPluginConfig (type)", @@ -727,7 +709,6 @@ "CursorStyleSchema (const)", "CursorUpdate (type)", "CursorUpdateSchema (const)", - "DEFAULT_SUSPICIOUS_ACTIVITY_RULES (const)", "DashboardLike (interface)", "DataClassification (type)", "DataClassificationPolicy (type)", @@ -1169,8 +1150,6 @@ "SupplierSecurityPolicySchema (const)", "SupplierSecurityRequirement (type)", "SupplierSecurityRequirementSchema (const)", - "SuspiciousActivityRule (type)", - "SuspiciousActivityRuleSchema (const)", "SystemFieldName (type)", "SystemObjectName (type)", "SystemUserId (type)", diff --git a/packages/spec/json-schema.manifest.json b/packages/spec/json-schema.manifest.json index f23530a31b..da865881bb 100644 --- a/packages/spec/json-schema.manifest.json +++ b/packages/spec/json-schema.manifest.json @@ -1262,16 +1262,6 @@ "system/AppInstallResult", "system/AppManifest", "system/AppTranslationBundle", - "system/AuditConfig", - "system/AuditEvent", - "system/AuditEventActor", - "system/AuditEventChange", - "system/AuditEventFilter", - "system/AuditEventSeverity", - "system/AuditEventTarget", - "system/AuditEventType", - "system/AuditRetentionPolicy", - "system/AuditStorageConfig", "system/AuthConfig", "system/AuthPluginConfig", "system/AuthProviderConfig", @@ -1532,7 +1522,6 @@ "system/SupplierSecurityAssessment", "system/SupplierSecurityPolicy", "system/SupplierSecurityRequirement", - "system/SuspiciousActivityRule", "system/Task", "system/TaskExecutionResult", "system/TaskPriority", diff --git a/packages/spec/src/system/audit.test.ts b/packages/spec/src/system/audit.test.ts deleted file mode 100644 index bee7a45faf..0000000000 --- a/packages/spec/src/system/audit.test.ts +++ /dev/null @@ -1,598 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - AuditEventType, - AuditEventSeverity, - AuditEventActorSchema, - AuditEventTargetSchema, - AuditEventChangeSchema, - AuditEventSchema, - AuditRetentionPolicySchema, - SuspiciousActivityRuleSchema, - AuditStorageConfigSchema, - AuditEventFilterSchema, - AuditConfigSchema, - DEFAULT_SUSPICIOUS_ACTIVITY_RULES, - type AuditEvent, - type AuditConfig, - type SuspiciousActivityRule, -} from './audit.zod'; - -describe('AuditEventType', () => { - it('should accept valid event types', () => { - const validTypes = [ - 'data.create', - 'data.read', - 'data.update', - 'data.delete', - 'auth.login', - 'auth.login_failed', - 'auth.logout', - 'authz.permission_granted', - 'authz.role_assigned', - 'security.access_denied', - ]; - - validTypes.forEach((type) => { - expect(() => AuditEventType.parse(type)).not.toThrow(); - }); - }); - - it('should reject invalid event types', () => { - expect(() => AuditEventType.parse('invalid.type')).toThrow(); - expect(() => AuditEventType.parse('dataCreate')).toThrow(); - }); -}); - -describe('AuditEventSeverity', () => { - it('should accept valid severity levels', () => { - const validLevels = ['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency']; - - validLevels.forEach((level) => { - expect(() => AuditEventSeverity.parse(level)).not.toThrow(); - }); - }); - - it('should reject invalid severity levels', () => { - expect(() => AuditEventSeverity.parse('invalid')).toThrow(); - expect(() => AuditEventSeverity.parse('high')).toThrow(); - }); -}); - -describe('AuditEventActorSchema', () => { - it('should accept valid actor configuration', () => { - const validActor = { - type: 'user', - id: 'user_123', - name: 'John Doe', - email: 'john@example.com', - ipAddress: '192.168.1.1', - userAgent: 'Mozilla/5.0', - }; - - expect(() => AuditEventActorSchema.parse(validActor)).not.toThrow(); - }); - - it('should accept minimal actor configuration', () => { - const minimalActor = { - type: 'system', - id: 'system', - }; - - expect(() => AuditEventActorSchema.parse(minimalActor)).not.toThrow(); - }); - - it('should reject invalid email', () => { - const invalidActor = { - type: 'user', - id: 'user_123', - email: 'not-an-email', - }; - - expect(() => AuditEventActorSchema.parse(invalidActor)).toThrow(); - }); -}); - -describe('AuditEventTargetSchema', () => { - it('should accept valid target configuration', () => { - const validTarget = { - type: 'record', - id: 'rec_456', - name: 'Customer Record #456', - metadata: { - objectName: 'customer', - fields: ['name', 'email'], - }, - }; - - expect(() => AuditEventTargetSchema.parse(validTarget)).not.toThrow(); - }); - - it('should accept minimal target configuration', () => { - const minimalTarget = { - type: 'object', - id: 'obj_789', - }; - - expect(() => AuditEventTargetSchema.parse(minimalTarget)).not.toThrow(); - }); -}); - -describe('AuditEventChangeSchema', () => { - it('should accept valid change record', () => { - const validChange = { - field: 'email', - oldValue: 'old@example.com', - newValue: 'new@example.com', - }; - - expect(() => AuditEventChangeSchema.parse(validChange)).not.toThrow(); - }); - - it('should accept change with only new value', () => { - const createChange = { - field: 'name', - newValue: 'John Doe', - }; - - expect(() => AuditEventChangeSchema.parse(createChange)).not.toThrow(); - }); -}); - -describe('AuditEventSchema', () => { - it('should accept complete audit event', () => { - const validEvent: AuditEvent = { - id: 'evt_123', - eventType: 'data.update', - severity: 'info', - timestamp: new Date().toISOString(), - actor: { - type: 'user', - id: 'user_123', - email: 'user@example.com', - }, - target: { - type: 'record', - id: 'rec_456', - }, - description: 'User updated customer record', - changes: [ - { - field: 'email', - oldValue: 'old@example.com', - newValue: 'new@example.com', - }, - ], - result: 'success', - tenantId: 'tenant_789', - requestId: 'req_abc', - }; - - expect(() => AuditEventSchema.parse(validEvent)).not.toThrow(); - }); - - it('should accept minimal audit event', () => { - const minimalEvent = { - id: 'evt_456', - eventType: 'auth.login', - timestamp: new Date().toISOString(), - actor: { - type: 'user', - id: 'user_123', - }, - description: 'User logged in', - }; - - expect(() => AuditEventSchema.parse(minimalEvent)).not.toThrow(); - }); - - it('should default severity to info', () => { - const event = { - id: 'evt_789', - eventType: 'data.read', - timestamp: new Date().toISOString(), - actor: { - type: 'user', - id: 'user_123', - }, - description: 'User viewed record', - }; - - const parsed = AuditEventSchema.parse(event); - expect(parsed.severity).toBe('info'); - }); - - it('should default result to success', () => { - const event = { - id: 'evt_101', - eventType: 'data.create', - timestamp: new Date().toISOString(), - actor: { - type: 'user', - id: 'user_123', - }, - description: 'User created record', - }; - - const parsed = AuditEventSchema.parse(event); - expect(parsed.result).toBe('success'); - }); - - it('should accept failed event with error message', () => { - const failedEvent = { - id: 'evt_202', - eventType: 'auth.login', - severity: 'warning', - timestamp: new Date().toISOString(), - actor: { - type: 'user', - id: 'user_123', - }, - description: 'Login attempt failed', - result: 'failure', - errorMessage: 'Invalid password', - }; - - expect(() => AuditEventSchema.parse(failedEvent)).not.toThrow(); - }); -}); - -describe('AuditRetentionPolicySchema', () => { - it('should accept valid retention policy', () => { - const validPolicy = { - retentionDays: 365, - archiveAfterRetention: true, - archiveStorage: { - type: 's3', - bucket: 'audit-logs-archive', - path: 'logs/', - }, - customRetention: { - 'auth.login_failed': 730, // 2 years - 'security.data_breach': 2555, // 7 years - }, - minimumRetentionDays: 180, - }; - - expect(() => AuditRetentionPolicySchema.parse(validPolicy)).not.toThrow(); - }); - - it('should default to 180 days retention', () => { - const policy = {}; - const parsed = AuditRetentionPolicySchema.parse(policy); - expect(parsed.retentionDays).toBe(180); - }); - - it('should default to archiving enabled', () => { - const policy = {}; - const parsed = AuditRetentionPolicySchema.parse(policy); - expect(parsed.archiveAfterRetention).toBe(true); - }); - - it('should reject negative retention days', () => { - const invalidPolicy = { - retentionDays: -30, - }; - - expect(() => AuditRetentionPolicySchema.parse(invalidPolicy)).toThrow(); - }); -}); - -describe('SuspiciousActivityRuleSchema', () => { - it('should accept valid suspicious activity rule', () => { - const validRule: SuspiciousActivityRule = { - id: 'rule_123', - name: 'Multiple Failed Logins', - description: 'Detects brute force attacks', - enabled: true, - eventTypes: ['auth.login_failed'], - condition: { - threshold: 5, - windowSeconds: 600, - groupBy: ['actor.id'], - }, - actions: ['alert', 'lock_account'], - alertSeverity: 'critical', - notifications: { - email: ['security@example.com'], - slack: 'https://hooks.slack.com/services/xxx', - }, - }; - - expect(() => SuspiciousActivityRuleSchema.parse(validRule)).not.toThrow(); - }); - - it('should default enabled to true', () => { - const rule = { - id: 'rule_456', - name: 'Test Rule', - eventTypes: ['data.export'], - condition: { - threshold: 10, - windowSeconds: 3600, - }, - actions: ['alert'], - }; - - const parsed = SuspiciousActivityRuleSchema.parse(rule); - expect(parsed.enabled).toBe(true); - }); - - it('should default alert severity to warning', () => { - const rule = { - id: 'rule_789', - name: 'Test Rule', - eventTypes: ['data.delete'], - condition: { - threshold: 3, - windowSeconds: 300, - }, - actions: ['alert'], - }; - - const parsed = SuspiciousActivityRuleSchema.parse(rule); - expect(parsed.alertSeverity).toBe('warning'); - }); -}); - -describe('AuditStorageConfigSchema', () => { - it('should accept database storage configuration', () => { - const dbStorage = { - type: 'database', - connectionString: 'postgresql://localhost:5432/audit', - bufferEnabled: true, - bufferSize: 100, - flushIntervalSeconds: 5, - compression: true, - }; - - expect(() => AuditStorageConfigSchema.parse(dbStorage)).not.toThrow(); - }); - - it('should accept elasticsearch storage configuration', () => { - const esStorage = { - type: 'elasticsearch', - connectionString: 'https://elasticsearch:9200', - config: { - index: 'audit-logs', - shards: 5, - }, - }; - - expect(() => AuditStorageConfigSchema.parse(esStorage)).not.toThrow(); - }); - - it('should default buffer settings', () => { - const storage = { - type: 'mongodb', - }; - - const parsed = AuditStorageConfigSchema.parse(storage); - expect(parsed.bufferEnabled).toBe(true); - expect(parsed.bufferSize).toBe(100); - expect(parsed.flushIntervalSeconds).toBe(5); - expect(parsed.compression).toBe(true); - }); -}); - -describe('AuditEventFilterSchema', () => { - it('should accept complete filter configuration', () => { - const validFilter = { - eventTypes: ['data.create', 'data.update'], - severities: ['warning', 'error', 'critical'], - actorId: 'user_123', - tenantId: 'tenant_456', - timeRange: { - from: '2024-01-01T00:00:00Z', - to: '2024-12-31T23:59:59Z', - }, - result: 'failure', - searchQuery: 'password reset', - }; - - expect(() => AuditEventFilterSchema.parse(validFilter)).not.toThrow(); - }); - - it('should accept minimal filter configuration', () => { - const minimalFilter = {}; - expect(() => AuditEventFilterSchema.parse(minimalFilter)).not.toThrow(); - }); - - it('should accept partial filters', () => { - const partialFilter = { - eventTypes: ['auth.login'], - timeRange: { - from: '2024-01-01T00:00:00Z', - to: '2024-01-31T23:59:59Z', - }, - }; - - expect(() => AuditEventFilterSchema.parse(partialFilter)).not.toThrow(); - }); -}); - -describe('AuditConfigSchema', () => { - it('should accept complete audit configuration', () => { - const validConfig: AuditConfig = { - name: 'main_audit', - label: 'Main Audit Configuration', - enabled: true, - eventTypes: ['data.create', 'data.update', 'auth.login'], - minimumSeverity: 'info', - storage: { - type: 'database', - connectionString: 'postgresql://localhost/audit', - }, - retentionPolicy: { - retentionDays: 365, - }, - suspiciousActivityRules: [ - { - id: 'failed_logins', - name: 'Failed Logins', - eventTypes: ['auth.login_failed'], - condition: { - threshold: 5, - windowSeconds: 600, - }, - actions: ['alert'], - }, - ], - includeSensitiveData: false, - logReads: false, - compliance: { - standards: ['gdpr', 'sox'], - immutableLogs: true, - }, - }; - - expect(() => AuditConfigSchema.parse(validConfig)).not.toThrow(); - }); - - it('should accept minimal audit configuration', () => { - const minimalConfig = { - name: 'basic_audit', - label: 'Basic Audit', - storage: { - type: 'database', - }, - }; - - expect(() => AuditConfigSchema.parse(minimalConfig)).not.toThrow(); - }); - - it('should enforce snake_case naming convention', () => { - const invalidConfig = { - name: 'mainAudit', // camelCase, should be snake_case - label: 'Main Audit', - storage: { - type: 'database', - }, - }; - - expect(() => AuditConfigSchema.parse(invalidConfig)).toThrow(); - }); - - it('should accept valid snake_case names', () => { - const validNames = ['audit_config', 'main_audit', 'security_audit_log']; - - validNames.forEach((name) => { - const config = { - name, - label: 'Test Audit', - storage: { type: 'database' }, - }; - expect(() => AuditConfigSchema.parse(config)).not.toThrow(); - }); - }); - - it('should default enabled to true', () => { - const config = { - name: 'test_audit', - label: 'Test Audit', - storage: { type: 'database' }, - }; - - const parsed = AuditConfigSchema.parse(config); - expect(parsed.enabled).toBe(true); - }); - - it('should default minimumSeverity to info', () => { - const config = { - name: 'test_audit', - label: 'Test Audit', - storage: { type: 'database' }, - }; - - const parsed = AuditConfigSchema.parse(config); - expect(parsed.minimumSeverity).toBe('info'); - }); - - it('should default redactFields to common sensitive fields', () => { - const config = { - name: 'test_audit', - label: 'Test Audit', - storage: { type: 'database' }, - }; - - const parsed = AuditConfigSchema.parse(config); - expect(parsed.redactFields).toContain('password'); - expect(parsed.redactFields).toContain('token'); - expect(parsed.redactFields).toContain('apiKey'); - }); - - it('should default logReads to false', () => { - const config = { - name: 'test_audit', - label: 'Test Audit', - storage: { type: 'database' }, - }; - - const parsed = AuditConfigSchema.parse(config); - expect(parsed.logReads).toBe(false); - }); - - it('should default readSamplingRate to 0.1', () => { - const config = { - name: 'test_audit', - label: 'Test Audit', - storage: { type: 'database' }, - }; - - const parsed = AuditConfigSchema.parse(config); - expect(parsed.readSamplingRate).toBe(0.1); - }); - - it('should accept compliance configuration', () => { - const config = { - name: 'compliance_audit', - label: 'Compliance Audit', - storage: { type: 'database' }, - compliance: { - standards: ['sox', 'hipaa', 'gdpr'], - immutableLogs: true, - requireSigning: true, - signingKey: 'secret-key-123', - }, - }; - - expect(() => AuditConfigSchema.parse(config)).not.toThrow(); - }); -}); - -describe('DEFAULT_SUSPICIOUS_ACTIVITY_RULES', () => { - it('should have valid default rules', () => { - expect(DEFAULT_SUSPICIOUS_ACTIVITY_RULES.length).toBeGreaterThan(0); - - DEFAULT_SUSPICIOUS_ACTIVITY_RULES.forEach((rule) => { - expect(() => SuspiciousActivityRuleSchema.parse(rule)).not.toThrow(); - }); - }); - - it('should include multiple failed logins rule', () => { - const rule = DEFAULT_SUSPICIOUS_ACTIVITY_RULES.find( - (r) => r.id === 'multiple_failed_logins' - ); - - expect(rule).toBeDefined(); - expect(rule?.eventTypes).toContain('auth.login_failed'); - expect(rule?.condition.threshold).toBe(5); - }); - - it('should include bulk data export rule', () => { - const rule = DEFAULT_SUSPICIOUS_ACTIVITY_RULES.find( - (r) => r.id === 'bulk_data_export' - ); - - expect(rule).toBeDefined(); - expect(rule?.eventTypes).toContain('data.export'); - }); - - it('should include permission changes rule', () => { - const rule = DEFAULT_SUSPICIOUS_ACTIVITY_RULES.find( - (r) => r.id === 'suspicious_permission_changes' - ); - - expect(rule).toBeDefined(); - expect(rule?.actions).toContain('alert'); - }); -}); diff --git a/packages/spec/src/system/audit.zod.ts b/packages/spec/src/system/audit.zod.ts deleted file mode 100644 index 647a31ff73..0000000000 --- a/packages/spec/src/system/audit.zod.ts +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { ExpressionInputSchema } from '../shared/expression.zod'; - -/** - * Audit Log Architecture - * - * Comprehensive audit logging system for compliance and security. - * Supports SOX, HIPAA, GDPR, and other regulatory requirements. - * - * Features: - * - Records all CRUD operations on data - * - Tracks authentication events (login, logout, password reset) - * - Monitors authorization changes (permissions, roles) - * - Configurable retention policies (180-day GDPR requirement) - * - Suspicious activity detection and alerting - */ - -/** - * Audit Event Type Enum - * Categorizes different types of auditable events in the system - */ -import { lazySchema } from '../shared/lazy-schema'; -export const AuditEventType = z.enum([ - // Data Operations (CRUD) - 'data.create', // Record creation - 'data.read', // Record retrieval/viewing - 'data.update', // Record modification - 'data.delete', // Record deletion - 'data.export', // Data export operations - 'data.import', // Data import operations - 'data.bulk_update', // Bulk update operations - 'data.bulk_delete', // Bulk delete operations - - // Authentication Events - 'auth.login', // Successful login - 'auth.login_failed', // Failed login attempt - 'auth.logout', // User logout - 'auth.session_created', // New session created - 'auth.session_expired', // Session expiration - 'auth.password_reset', // Password reset initiated - 'auth.password_changed', // Password successfully changed - 'auth.email_verified', // Email verification completed - 'auth.mfa_enabled', // Multi-factor auth enabled - 'auth.mfa_disabled', // Multi-factor auth disabled - 'auth.account_locked', // Account locked (too many failures) - 'auth.account_unlocked', // Account unlocked - - // Authorization Events - 'authz.permission_granted', // Permission granted to user - 'authz.permission_revoked', // Permission revoked from user - 'authz.role_assigned', // Role assigned to user - 'authz.role_removed', // Role removed from user - 'authz.role_created', // New role created - 'authz.role_updated', // Role permissions modified - 'authz.role_deleted', // Role deleted - 'authz.policy_created', // Security policy created - 'authz.policy_updated', // Security policy updated - 'authz.policy_deleted', // Security policy deleted - - // System Events - 'system.config_changed', // System configuration modified - 'system.plugin_installed', // Plugin installed - 'system.plugin_uninstalled', // Plugin uninstalled - 'system.backup_created', // Backup created - 'system.backup_restored', // Backup restored - 'system.integration_added', // External integration added - 'system.integration_removed',// External integration removed - - // Security Events - 'security.access_denied', // Access denied (authorization failure) - 'security.suspicious_activity', // Suspicious activity detected - 'security.data_breach', // Potential data breach detected - 'security.api_key_created', // API key created - 'security.api_key_revoked', // API key revoked -]); - -export type AuditEventType = z.infer; - -/** - * Audit Event Severity Level - * Indicates the importance/criticality of an audit event - */ -export const AuditEventSeverity = z.enum([ - 'debug', // Diagnostic information - 'info', // Informational events (normal operations) - 'notice', // Normal but significant events - 'warning', // Warning conditions - 'error', // Error conditions - 'critical', // Critical conditions requiring immediate attention - 'alert', // Action must be taken immediately - 'emergency', // System is unusable -]); - -export type AuditEventSeverity = z.infer; - -/** - * Audit Event Actor Schema - * Identifies who/what performed the action - */ -export const AuditEventActorSchema = lazySchema(() => z.object({ - /** - * Actor type (user, system, service, api_client, etc.) - */ - type: z.enum(['user', 'system', 'service', 'api_client', 'integration']).describe('Actor type'), - - /** - * Unique identifier for the actor - */ - id: z.string().describe('Actor identifier'), - - /** - * Display name of the actor - */ - name: z.string().optional().describe('Actor display name'), - - /** - * Email address (for user actors) - */ - email: z.string().email().optional().describe('Actor email address'), - - /** - * IP address of the actor - */ - ipAddress: z.string().optional().describe('Actor IP address'), - - /** - * User agent string (for web/API requests) - */ - userAgent: z.string().optional().describe('User agent string'), -})); - -export type AuditEventActor = z.infer; - -/** - * Audit Event Target Schema - * Identifies what was acted upon - */ -export const AuditEventTargetSchema = lazySchema(() => z.object({ - /** - * Target type (e.g., 'object', 'record', 'user', 'position', 'config') - */ - type: z.string().describe('Target type'), - - /** - * Unique identifier for the target - */ - id: z.string().describe('Target identifier'), - - /** - * Display name of the target - */ - name: z.string().optional().describe('Target display name'), - - /** - * Additional metadata about the target - */ - metadata: z.record(z.string(), z.unknown()).optional().describe('Target metadata'), -})); - -export type AuditEventTarget = z.infer; - -/** - * Audit Event Change Schema - * Describes what changed (for update operations) - */ -export const AuditEventChangeSchema = lazySchema(() => z.object({ - /** - * Field/property that changed - */ - field: z.string().describe('Changed field name'), - - /** - * Value before the change - */ - oldValue: z.unknown().optional().describe('Previous value'), - - /** - * Value after the change - */ - newValue: z.unknown().optional().describe('New value'), -})); - -export type AuditEventChange = z.infer; - -/** - * Audit Event Schema - * Complete audit event record - */ -export const AuditEventSchema = lazySchema(() => z.object({ - /** - * Unique identifier for this audit event - */ - id: z.string().describe('Audit event ID'), - - /** - * Type of event being audited - */ - eventType: AuditEventType.describe('Event type'), - - /** - * Severity level of the event - */ - severity: AuditEventSeverity.default('info').describe('Event severity'), - - /** - * Timestamp when the event occurred (ISO 8601) - */ - timestamp: z.string().datetime().describe('Event timestamp'), - - /** - * Who/what performed the action - */ - actor: AuditEventActorSchema.describe('Event actor'), - - /** - * What was acted upon - */ - target: AuditEventTargetSchema.optional().describe('Event target'), - - /** - * Human-readable description of the action - */ - description: z.string().describe('Event description'), - - /** - * Detailed changes (for update operations) - */ - changes: z.array(AuditEventChangeSchema).optional().describe('List of changes'), - - /** - * Result of the action (success, failure, partial) - */ - result: z.enum(['success', 'failure', 'partial']).default('success').describe('Action result'), - - /** - * Error message (if result is failure) - */ - errorMessage: z.string().optional().describe('Error message'), - - /** - * Tenant identifier (for multi-tenant systems) - */ - tenantId: z.string().optional().describe('Tenant identifier'), - - /** - * Request/trace ID for correlation - */ - requestId: z.string().optional().describe('Request ID for tracing'), - - /** - * Additional context and metadata - */ - metadata: z.record(z.string(), z.unknown()).optional().describe('Additional metadata'), - - /** - * Geographic location (if available) - */ - location: z.object({ - country: z.string().optional(), - region: z.string().optional(), - city: z.string().optional(), - }).optional().describe('Geographic location'), -})); - -export type AuditEvent = z.infer; - -/** - * Audit Retention Policy Schema - * Defines how long audit logs are retained - */ -export const AuditRetentionPolicySchema = lazySchema(() => z.object({ - /** - * Retention period in days - * Default: 180 days (GDPR 6-month requirement) - */ - retentionDays: z.number().int().min(1).default(180).describe('Retention period in days'), - - /** - * Whether to archive logs after retention period - * If true, logs are moved to cold storage; if false, they are deleted - */ - archiveAfterRetention: z.boolean().default(true).describe('Archive logs after retention period'), - - /** - * Archive storage configuration - */ - archiveStorage: z.object({ - type: z.enum(['s3', 'gcs', 'azure_blob', 'filesystem']).describe('Archive storage type'), - endpoint: z.string().optional().describe('Storage endpoint URL'), - bucket: z.string().optional().describe('Storage bucket/container name'), - path: z.string().optional().describe('Storage path prefix'), - credentials: z.record(z.string(), z.unknown()).optional().describe('Storage credentials'), - }).optional().describe('Archive storage configuration'), - - /** - * Event types that have different retention periods - * Overrides the default retentionDays for specific event types - */ - customRetention: z.record(z.string(), z.number().int().positive()).optional().describe('Custom retention by event type'), - - /** - * Minimum retention period for compliance - * Prevents accidental deletion below compliance requirements - */ - minimumRetentionDays: z.number().int().positive().optional().describe('Minimum retention for compliance'), -})); - -export type AuditRetentionPolicy = z.infer; - -/** - * Suspicious Activity Rule Schema - * Defines rules for detecting suspicious activities - */ -export const SuspiciousActivityRuleSchema = lazySchema(() => z.object({ - /** - * Unique identifier for the rule - */ - id: z.string().describe('Rule identifier'), - - /** - * Rule name - */ - name: z.string().describe('Rule name'), - - /** - * Rule description - */ - description: z.string().optional().describe('Rule description'), - - /** - * Whether the rule is enabled - */ - enabled: z.boolean().default(true).describe('Rule enabled status'), - - /** - * Event types to monitor - */ - eventTypes: z.array(AuditEventType).describe('Event types to monitor'), - - /** - * Detection condition — accepts either a structured threshold/window object - * or a CEL predicate (escape hatch for advanced rules). - */ - condition: z.union([ - z.object({ - threshold: z.number().int().positive().describe('Event threshold'), - windowSeconds: z.number().int().positive().describe('Time window in seconds'), - groupBy: z.array(z.string()).optional().describe('Grouping criteria'), - filters: z.record(z.string(), z.unknown()).optional().describe('Additional filters'), - }), - ExpressionInputSchema, - ]).describe('Detection condition — structured threshold or CEL predicate'), - - /** - * Actions to take when rule is triggered - */ - actions: z.array(z.enum([ - 'alert', // Send alert notification - 'lock_account', // Lock the user account - 'block_ip', // Block the IP address - 'require_mfa', // Require multi-factor authentication - 'log_critical', // Log as critical event - 'webhook', // Call webhook - ])).describe('Actions to take'), - - /** - * Severity level for triggered alerts - */ - alertSeverity: AuditEventSeverity.default('warning').describe('Alert severity'), - - /** - * Notification configuration - */ - notifications: z.object({ - /** - * Email addresses to notify - */ - email: z.array(z.string().email()).optional().describe('Email recipients'), - - /** - * Slack webhook URL - */ - slack: z.string().url().optional().describe('Slack webhook URL'), - - /** - * Custom webhook URL - */ - webhook: z.string().url().optional().describe('Custom webhook URL'), - }).optional().describe('Notification configuration'), -})); - -export type SuspiciousActivityRule = z.infer; - -/** - * Audit Log Storage Configuration - * Defines where and how audit logs are stored - */ -export const AuditStorageConfigSchema = lazySchema(() => z.object({ - /** - * Storage backend type - */ - type: z.enum([ - 'database', // Store in database (PostgreSQL, MySQL, etc.) - 'elasticsearch', // Store in Elasticsearch - 'mongodb', // Store in MongoDB - 'clickhouse', // Store in ClickHouse (for analytics) - 's3', // Store in S3-compatible storage - 'gcs', // Store in Google Cloud Storage - 'azure_blob', // Store in Azure Blob Storage - 'custom', // Custom storage implementation - ]).describe('Storage backend type'), - - /** - * Connection string or configuration - */ - connectionString: z.string().optional().describe('Connection string'), - - /** - * Storage configuration - */ - config: z.record(z.string(), z.unknown()).optional().describe('Storage-specific configuration'), - - /** - * Whether to enable buffering/batching - */ - bufferEnabled: z.boolean().default(true).describe('Enable buffering'), - - /** - * Buffer size (number of events before flush) - */ - bufferSize: z.number().int().positive().default(100).describe('Buffer size'), - - /** - * Buffer flush interval in seconds - */ - flushIntervalSeconds: z.number().int().positive().default(5).describe('Flush interval in seconds'), - - /** - * Whether to compress stored data - */ - compression: z.boolean().default(true).describe('Enable compression'), -})); - -export type AuditStorageConfig = z.infer; - -/** - * Audit Event Filter Schema - * Defines filters for querying audit events - */ -export const AuditEventFilterSchema = lazySchema(() => z.object({ - /** - * Filter by event types - */ - eventTypes: z.array(AuditEventType).optional().describe('Event types to include'), - - /** - * Filter by severity levels - */ - severities: z.array(AuditEventSeverity).optional().describe('Severity levels to include'), - - /** - * Filter by actor ID - */ - actorId: z.string().optional().describe('Actor identifier'), - - /** - * Filter by tenant ID - */ - tenantId: z.string().optional().describe('Tenant identifier'), - - /** - * Filter by time range - */ - timeRange: z.object({ - from: z.string().datetime().describe('Start time'), - to: z.string().datetime().describe('End time'), - }).optional().describe('Time range filter'), - - /** - * Filter by result status - */ - result: z.enum(['success', 'failure', 'partial']).optional().describe('Result status'), - - /** - * Search query (full-text search) - */ - searchQuery: z.string().optional().describe('Search query'), - - /** - * Custom filters - */ - customFilters: z.record(z.string(), z.unknown()).optional().describe('Custom filters'), -})); - -export type AuditEventFilter = z.infer; - -/** - * Complete Audit Configuration Schema - * Main configuration for the audit system - */ -export const AuditConfigSchema = lazySchema(() => z.object({ - /** - * Unique identifier for this audit configuration - * Must be in snake_case following ObjectStack conventions - * Maximum length: 64 characters - */ - name: z.string() - .regex(/^[a-z_][a-z0-9_]*$/) - .max(64) - .describe('Configuration name (snake_case, max 64 chars)'), - - /** - * Human-readable label - */ - label: z.string().describe('Display label'), - - /** - * Whether audit logging is enabled - */ - enabled: z.boolean().default(true).describe('Enable audit logging'), - - /** - * Event types to audit - * If not specified, all event types are audited - */ - eventTypes: z.array(AuditEventType).optional().describe('Event types to audit'), - - /** - * Event types to exclude from auditing - */ - excludeEventTypes: z.array(AuditEventType).optional().describe('Event types to exclude'), - - /** - * Minimum severity level to log - * Events below this level are not logged - */ - minimumSeverity: AuditEventSeverity.default('info').describe('Minimum severity level'), - - /** - * Storage configuration - */ - storage: AuditStorageConfigSchema.describe('Storage configuration'), - - /** - * Retention policy - */ - retentionPolicy: AuditRetentionPolicySchema.optional().describe('Retention policy'), - - /** - * Suspicious activity detection rules - */ - suspiciousActivityRules: z.array(SuspiciousActivityRuleSchema).default([]).describe('Suspicious activity rules'), - - /** - * Whether to include sensitive data in audit logs - * If false, sensitive fields are redacted/masked - */ - includeSensitiveData: z.boolean().default(false).describe('Include sensitive data'), - - /** - * Fields to redact from audit logs - */ - redactFields: z.array(z.string()).default([ - 'password', - 'passwordHash', - 'token', - 'apiKey', - 'secret', - 'creditCard', - 'ssn', - ]).describe('Fields to redact'), - - /** - * Whether to log successful read operations - * Can be disabled to reduce log volume - */ - logReads: z.boolean().default(false).describe('Log read operations'), - - /** - * Sampling rate for read operations (0.0 to 1.0) - * Only applies if logReads is true - */ - readSamplingRate: z.number().min(0).max(1).default(0.1).describe('Read sampling rate'), - - /** - * Whether to log system/internal operations - */ - logSystemEvents: z.boolean().default(true).describe('Log system events'), - - /** - * Custom audit event handlers - * Note: Function handlers are for runtime configuration only and will not be serialized to JSON Schema - */ - customHandlers: z.array(z.object({ - eventType: AuditEventType.describe('Event type to handle'), - handlerId: z.string().describe('Unique identifier for the handler'), - })).optional().describe('Custom event handler references'), - - /** - * Compliance mode configuration - */ - compliance: z.object({ - /** - * Compliance standards to enforce - */ - standards: z.array(z.enum([ - 'sox', // Sarbanes-Oxley Act - 'hipaa', // Health Insurance Portability and Accountability Act - 'gdpr', // General Data Protection Regulation - 'pci_dss', // Payment Card Industry Data Security Standard - 'iso_27001',// ISO/IEC 27001 - 'fedramp', // Federal Risk and Authorization Management Program - ])).optional().describe('Compliance standards'), - - /** - * Whether to enforce immutable audit logs - */ - immutableLogs: z.boolean().default(true).describe('Enforce immutable logs'), - - /** - * Whether to require cryptographic signing - */ - requireSigning: z.boolean().default(false).describe('Require log signing'), - - /** - * Signing key configuration - */ - signingKey: z.string().optional().describe('Signing key'), - }).optional().describe('Compliance configuration'), -})); - -export type AuditConfig = z.infer; - -/** - * Default suspicious activity rules - * Common security patterns to detect - */ -export const DEFAULT_SUSPICIOUS_ACTIVITY_RULES: SuspiciousActivityRule[] = [ - { - id: 'multiple_failed_logins', - name: 'Multiple Failed Login Attempts', - description: 'Detects multiple failed login attempts from the same user or IP', - enabled: true, - eventTypes: ['auth.login_failed'], - condition: { - threshold: 5, - windowSeconds: 600, // 10 minutes - groupBy: ['actor.id', 'actor.ipAddress'], - }, - actions: ['alert', 'lock_account'], - alertSeverity: 'warning', - }, - { - id: 'bulk_data_export', - name: 'Bulk Data Export', - description: 'Detects large data export operations', - enabled: true, - eventTypes: ['data.export'], - condition: { - threshold: 3, - windowSeconds: 3600, // 1 hour - groupBy: ['actor.id'], - }, - actions: ['alert', 'log_critical'], - alertSeverity: 'warning', - }, - { - id: 'suspicious_permission_changes', - name: 'Rapid Permission Changes', - description: 'Detects rapid permission or role changes', - enabled: true, - eventTypes: ['authz.permission_granted', 'authz.role_assigned'], - condition: { - threshold: 10, - windowSeconds: 300, // 5 minutes - groupBy: ['actor.id'], - }, - actions: ['alert', 'log_critical'], - alertSeverity: 'critical', - }, - { - id: 'after_hours_access', - name: 'After Hours Access', - description: 'Detects access during non-business hours', - enabled: false, // Disabled by default, requires time zone configuration - eventTypes: ['auth.login'], - condition: { - threshold: 1, - windowSeconds: 86400, // 24 hours - }, - actions: ['alert'], - alertSeverity: 'notice', - }, -]; diff --git a/packages/spec/src/system/index.ts b/packages/spec/src/system/index.ts index b8fdb07243..41e68f6da2 100644 --- a/packages/spec/src/system/index.ts +++ b/packages/spec/src/system/index.ts @@ -19,7 +19,14 @@ export * from './search-engine.zod'; export * from './http-server.zod'; // Observability & Operations -export * from './audit.zod'; +// audit.zod (AuditConfig/AuditStorageConfig/AuditRetentionPolicy/AuditEventFilter/ +// SuspiciousActivityRule + the AuditEvent* shape schemas) was REMOVED per ADR-0056 +// D8 "design+enforce or remove": the whole module had zero consumers — the LIVE +// audit path (plugin-audit) captures unconditionally via engine hooks and defines +// its own sys_audit_log row shape, and `AuditConfigSchema.enabled` contradicted +// the always-on compliance-ledger contract (object.zod `trackHistory`). The +// enforced authoring surface is object/field `trackHistory` + the object +// `lifecycle` `audit` category (retention), with per-org overrides in settings. export * from './logging.zod'; export * from './metrics.zod'; export * from './tracing.zod'; diff --git a/packages/spec/src/system/security-context.zod.ts b/packages/spec/src/system/security-context.zod.ts index 2b40e6f1e2..0e37d9296e 100644 --- a/packages/spec/src/system/security-context.zod.ts +++ b/packages/spec/src/system/security-context.zod.ts @@ -10,11 +10,14 @@ import { z } from 'zod'; * Unified Security Context Protocol * * Provides a central governance layer that correlates and unifies - * the four independent security subsystems: - * - **Audit** (audit.zod.ts): Event logging and suspicious activity detection + * the four independent security subsystems it was designed against. Three of + * the four have since been REMOVED per ADR-0056 D8 (declared-but-never-enforced; + * see system/index.ts notes) — only encryption survives, marked experimental: + * - **Audit** (audit.zod.ts — REMOVED): the live audit path is plugin-audit's + * always-on capture + object/field `trackHistory` + lifecycle `audit` retention * - **Encryption** (encryption.zod.ts): Field-level encryption and key management - * - **Compliance** (compliance.zod.ts): Regulatory framework enforcement (GDPR/HIPAA/SOX/PCI-DSS) - * - **Masking** (masking.zod.ts): PII data masking and tokenization + * - **Compliance** (compliance.zod.ts — REMOVED): GDPR/HIPAA/SOX/PCI-DSS configs + * - **Masking** (masking.zod.ts — REMOVED): PII data masking and tokenization * * This schema enforces cross-cutting security policies, ensuring compliance * frameworks drive encryption requirements, masking rules respect role-based