@useatlas/create 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -0
- package/index.ts +829 -0
- package/package.json +38 -0
- package/templates/docker/.env.example +67 -0
- package/templates/docker/Dockerfile +52 -0
- package/templates/docker/bin/__tests__/benchmark.test.ts +598 -0
- package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +171 -0
- package/templates/docker/bin/__tests__/eval.test.ts +434 -0
- package/templates/docker/bin/__tests__/matview-partition.test.ts +615 -0
- package/templates/docker/bin/__tests__/multi-source.test.ts +113 -0
- package/templates/docker/bin/__tests__/plugin-cli.test.ts +322 -0
- package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +608 -0
- package/templates/docker/bin/__tests__/query.test.ts +240 -0
- package/templates/docker/bin/__tests__/schema-drift.test.ts +542 -0
- package/templates/docker/bin/__tests__/view-yaml-generation.test.ts +146 -0
- package/templates/docker/bin/atlas.ts +5044 -0
- package/templates/docker/bin/benchmark.ts +695 -0
- package/templates/docker/bin/enrich.ts +559 -0
- package/templates/docker/bin/eval.ts +770 -0
- package/templates/docker/bin/smoke.ts +438 -0
- package/templates/docker/data/.gitkeep +0 -0
- package/templates/docker/data/cybersec.sql +1961 -0
- package/templates/docker/data/demo-semantic/catalog.yml +40 -0
- package/templates/docker/data/demo-semantic/entities/accounts.yml +170 -0
- package/templates/docker/data/demo-semantic/entities/companies.yml +207 -0
- package/templates/docker/data/demo-semantic/entities/people.yml +145 -0
- package/templates/docker/data/demo-semantic/glossary.yml +22 -0
- package/templates/docker/data/demo-semantic/metrics/accounts.yml +38 -0
- package/templates/docker/data/demo-semantic/metrics/companies.yml +89 -0
- package/templates/docker/data/demo.sql +373 -0
- package/templates/docker/data/ecommerce.sql +1690 -0
- package/templates/docker/data/init-demo-db.sql +8 -0
- package/templates/docker/docker-compose.yml +34 -0
- package/templates/docker/docs/deploy.md +390 -0
- package/templates/docker/eslint.config.mjs +18 -0
- package/templates/docker/gitignore +5 -0
- package/templates/docker/next.config.ts +9 -0
- package/templates/docker/package.json +59 -0
- package/templates/docker/postcss.config.mjs +8 -0
- package/templates/docker/public/.gitkeep +0 -0
- package/templates/docker/public/favicon.svg +4 -0
- package/templates/docker/railway.json +13 -0
- package/templates/docker/render.yaml +34 -0
- package/templates/docker/semantic/catalog.yml +5 -0
- package/templates/docker/semantic/entities/.gitkeep +0 -0
- package/templates/docker/semantic/glossary.yml +6 -0
- package/templates/docker/semantic/metrics/.gitkeep +0 -0
- package/templates/docker/sidecar/Dockerfile +28 -0
- package/templates/docker/sidecar/railway.json +14 -0
- package/templates/docker/sidecar/server.ts +188 -0
- package/templates/docker/src/api/__tests__/actions.test.ts +683 -0
- package/templates/docker/src/api/__tests__/admin.test.ts +820 -0
- package/templates/docker/src/api/__tests__/auth.test.ts +165 -0
- package/templates/docker/src/api/__tests__/chat.test.ts +376 -0
- package/templates/docker/src/api/__tests__/conversations.test.ts +555 -0
- package/templates/docker/src/api/__tests__/cors.test.ts +135 -0
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +169 -0
- package/templates/docker/src/api/__tests__/health.test.ts +261 -0
- package/templates/docker/src/api/__tests__/query.test.ts +891 -0
- package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +601 -0
- package/templates/docker/src/api/__tests__/slack.test.ts +847 -0
- package/templates/docker/src/api/index.ts +117 -0
- package/templates/docker/src/api/routes/actions.ts +274 -0
- package/templates/docker/src/api/routes/admin.ts +757 -0
- package/templates/docker/src/api/routes/auth.ts +48 -0
- package/templates/docker/src/api/routes/chat.ts +465 -0
- package/templates/docker/src/api/routes/conversations.ts +266 -0
- package/templates/docker/src/api/routes/health.ts +287 -0
- package/templates/docker/src/api/routes/openapi.ts +390 -0
- package/templates/docker/src/api/routes/query.ts +318 -0
- package/templates/docker/src/api/routes/scheduled-tasks.ts +467 -0
- package/templates/docker/src/api/routes/slack.ts +611 -0
- package/templates/docker/src/api/server.ts +226 -0
- package/templates/docker/src/app/api/[...route]/route.ts +33 -0
- package/templates/docker/src/app/error.tsx +24 -0
- package/templates/docker/src/app/globals.css +126 -0
- package/templates/docker/src/app/layout.tsx +19 -0
- package/templates/docker/src/app/page.tsx +14 -0
- package/templates/docker/src/global.d.ts +1 -0
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +437 -0
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +114 -0
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +514 -0
- package/templates/docker/src/lib/__tests__/config-actions.test.ts +166 -0
- package/templates/docker/src/lib/__tests__/config.test.ts +1063 -0
- package/templates/docker/src/lib/__tests__/conversations.test.ts +589 -0
- package/templates/docker/src/lib/__tests__/errors.test.ts +256 -0
- package/templates/docker/src/lib/__tests__/logger.test.ts +200 -0
- package/templates/docker/src/lib/__tests__/providers.test.ts +99 -0
- package/templates/docker/src/lib/__tests__/rls.test.ts +435 -0
- package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
- package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
- package/templates/docker/src/lib/__tests__/semantic-index.test.ts +547 -0
- package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +544 -0
- package/templates/docker/src/lib/__tests__/semantic.test.ts +363 -0
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +452 -0
- package/templates/docker/src/lib/__tests__/startup.test.ts +465 -0
- package/templates/docker/src/lib/__tests__/tracing.test.ts +28 -0
- package/templates/docker/src/lib/action-types.ts +95 -0
- package/templates/docker/src/lib/agent-query.ts +178 -0
- package/templates/docker/src/lib/agent.ts +505 -0
- package/templates/docker/src/lib/api-url.ts +2 -0
- package/templates/docker/src/lib/auth/__tests__/audit.test.ts +418 -0
- package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
- package/templates/docker/src/lib/auth/__tests__/byot.test.ts +366 -0
- package/templates/docker/src/lib/auth/__tests__/detect.test.ts +190 -0
- package/templates/docker/src/lib/auth/__tests__/managed.test.ts +173 -0
- package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +456 -0
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +201 -0
- package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +225 -0
- package/templates/docker/src/lib/auth/__tests__/server.test.ts +34 -0
- package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +176 -0
- package/templates/docker/src/lib/auth/__tests__/types.test.ts +44 -0
- package/templates/docker/src/lib/auth/audit.ts +89 -0
- package/templates/docker/src/lib/auth/byot.ts +158 -0
- package/templates/docker/src/lib/auth/client.ts +35 -0
- package/templates/docker/src/lib/auth/detect.ts +83 -0
- package/templates/docker/src/lib/auth/managed.ts +73 -0
- package/templates/docker/src/lib/auth/middleware.ts +208 -0
- package/templates/docker/src/lib/auth/migrate.ts +111 -0
- package/templates/docker/src/lib/auth/permissions.ts +156 -0
- package/templates/docker/src/lib/auth/server.ts +142 -0
- package/templates/docker/src/lib/auth/simple-key.ts +92 -0
- package/templates/docker/src/lib/auth/types.ts +49 -0
- package/templates/docker/src/lib/config.ts +704 -0
- package/templates/docker/src/lib/conversation-types.ts +29 -0
- package/templates/docker/src/lib/conversations.ts +270 -0
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +69 -0
- package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +141 -0
- package/templates/docker/src/lib/db/__tests__/internal.test.ts +387 -0
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +207 -0
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +595 -0
- package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +339 -0
- package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +217 -0
- package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
- package/templates/docker/src/lib/db/connection.ts +753 -0
- package/templates/docker/src/lib/db/duckdb.ts +122 -0
- package/templates/docker/src/lib/db/internal.ts +273 -0
- package/templates/docker/src/lib/db/salesforce.ts +342 -0
- package/templates/docker/src/lib/db/source-rate-limit.ts +191 -0
- package/templates/docker/src/lib/errors.ts +154 -0
- package/templates/docker/src/lib/logger.ts +98 -0
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +529 -0
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +521 -0
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +346 -0
- package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +49 -0
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +585 -0
- package/templates/docker/src/lib/plugins/hooks.ts +162 -0
- package/templates/docker/src/lib/plugins/index.ts +9 -0
- package/templates/docker/src/lib/plugins/migrate.ts +309 -0
- package/templates/docker/src/lib/plugins/registry.ts +231 -0
- package/templates/docker/src/lib/plugins/tools.ts +39 -0
- package/templates/docker/src/lib/plugins/wiring.ts +291 -0
- package/templates/docker/src/lib/providers.ts +102 -0
- package/templates/docker/src/lib/rls.ts +321 -0
- package/templates/docker/src/lib/scheduled-task-types.ts +132 -0
- package/templates/docker/src/lib/scheduled-tasks.ts +475 -0
- package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
- package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +248 -0
- package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
- package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
- package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
- package/templates/docker/src/lib/scheduler/delivery.ts +248 -0
- package/templates/docker/src/lib/scheduler/engine.ts +317 -0
- package/templates/docker/src/lib/scheduler/executor.ts +73 -0
- package/templates/docker/src/lib/scheduler/format-email.ts +109 -0
- package/templates/docker/src/lib/scheduler/format-slack.ts +35 -0
- package/templates/docker/src/lib/scheduler/format-webhook.ts +37 -0
- package/templates/docker/src/lib/scheduler/index.ts +7 -0
- package/templates/docker/src/lib/security.ts +11 -0
- package/templates/docker/src/lib/semantic-index.ts +503 -0
- package/templates/docker/src/lib/semantic.ts +387 -0
- package/templates/docker/src/lib/sidecar-types.ts +16 -0
- package/templates/docker/src/lib/slack/__tests__/api.test.ts +160 -0
- package/templates/docker/src/lib/slack/__tests__/format.test.ts +237 -0
- package/templates/docker/src/lib/slack/__tests__/store.test.ts +188 -0
- package/templates/docker/src/lib/slack/__tests__/threads.test.ts +112 -0
- package/templates/docker/src/lib/slack/__tests__/verify.test.ts +111 -0
- package/templates/docker/src/lib/slack/api.ts +102 -0
- package/templates/docker/src/lib/slack/format.ts +209 -0
- package/templates/docker/src/lib/slack/store.ts +107 -0
- package/templates/docker/src/lib/slack/threads.ts +64 -0
- package/templates/docker/src/lib/slack/verify.ts +71 -0
- package/templates/docker/src/lib/startup.ts +730 -0
- package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
- package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
- package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +235 -0
- package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
- package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
- package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +1012 -0
- package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
- package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +378 -0
- package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
- package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
- package/templates/docker/src/lib/tools/actions/audit.ts +47 -0
- package/templates/docker/src/lib/tools/actions/email.ts +191 -0
- package/templates/docker/src/lib/tools/actions/handler.ts +591 -0
- package/templates/docker/src/lib/tools/actions/index.ts +23 -0
- package/templates/docker/src/lib/tools/actions/jira.ts +220 -0
- package/templates/docker/src/lib/tools/explore-nsjail.ts +343 -0
- package/templates/docker/src/lib/tools/explore-sandbox.ts +264 -0
- package/templates/docker/src/lib/tools/explore-sidecar.ts +163 -0
- package/templates/docker/src/lib/tools/explore.ts +379 -0
- package/templates/docker/src/lib/tools/registry.ts +221 -0
- package/templates/docker/src/lib/tools/salesforce.ts +138 -0
- package/templates/docker/src/lib/tools/soql-validation.ts +172 -0
- package/templates/docker/src/lib/tools/sql.ts +680 -0
- package/templates/docker/src/lib/tracing.ts +40 -0
- package/templates/docker/src/lib/utils.ts +6 -0
- package/templates/docker/src/test-setup.ts +38 -0
- package/templates/docker/src/types/vercel-sandbox.d.ts +54 -0
- package/templates/docker/src/ui/components/actions/action-approval-card.tsx +295 -0
- package/templates/docker/src/ui/components/actions/action-status-badge.tsx +50 -0
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +26 -0
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +96 -0
- package/templates/docker/src/ui/components/admin/empty-state.tsx +24 -0
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +233 -0
- package/templates/docker/src/ui/components/admin/entity-list.tsx +96 -0
- package/templates/docker/src/ui/components/admin/error-banner.tsx +22 -0
- package/templates/docker/src/ui/components/admin/feature-disabled.tsx +44 -0
- package/templates/docker/src/ui/components/admin/health-badge.tsx +30 -0
- package/templates/docker/src/ui/components/admin/loading-state.tsx +14 -0
- package/templates/docker/src/ui/components/admin/stat-card.tsx +32 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +370 -0
- package/templates/docker/src/ui/components/chart/chart-detection.ts +261 -0
- package/templates/docker/src/ui/components/chart/result-chart.tsx +375 -0
- package/templates/docker/src/ui/components/chat/api-key-bar.tsx +66 -0
- package/templates/docker/src/ui/components/chat/copy-button.tsx +25 -0
- package/templates/docker/src/ui/components/chat/data-table.tsx +102 -0
- package/templates/docker/src/ui/components/chat/error-banner.tsx +32 -0
- package/templates/docker/src/ui/components/chat/explore-card.tsx +41 -0
- package/templates/docker/src/ui/components/chat/loading-card.tsx +10 -0
- package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +116 -0
- package/templates/docker/src/ui/components/chat/markdown.tsx +72 -0
- package/templates/docker/src/ui/components/chat/sql-block.tsx +30 -0
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +144 -0
- package/templates/docker/src/ui/components/chat/starter-prompts.ts +6 -0
- package/templates/docker/src/ui/components/chat/tool-part.tsx +40 -0
- package/templates/docker/src/ui/components/chat/typing-indicator.tsx +19 -0
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +120 -0
- package/templates/docker/src/ui/components/conversations/conversation-list.tsx +66 -0
- package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
- package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +27 -0
- package/templates/docker/src/ui/context.tsx +78 -0
- package/templates/docker/src/ui/hooks/use-admin-fetch.ts +104 -0
- package/templates/docker/src/ui/hooks/use-conversations.ts +184 -0
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -0
- package/templates/docker/src/ui/lib/action-types.ts +63 -0
- package/templates/docker/src/ui/lib/helpers.ts +104 -0
- package/templates/docker/src/ui/lib/types.ts +145 -0
- package/templates/docker/tsconfig.json +41 -0
- package/templates/docker/vercel.json +3 -0
- package/templates/nextjs-standalone/.env.example +68 -0
- package/templates/nextjs-standalone/bin/__tests__/benchmark.test.ts +598 -0
- package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +171 -0
- package/templates/nextjs-standalone/bin/__tests__/eval.test.ts +434 -0
- package/templates/nextjs-standalone/bin/__tests__/matview-partition.test.ts +615 -0
- package/templates/nextjs-standalone/bin/__tests__/multi-source.test.ts +113 -0
- package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +322 -0
- package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +608 -0
- package/templates/nextjs-standalone/bin/__tests__/query.test.ts +240 -0
- package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +542 -0
- package/templates/nextjs-standalone/bin/__tests__/view-yaml-generation.test.ts +146 -0
- package/templates/nextjs-standalone/bin/atlas.ts +5044 -0
- package/templates/nextjs-standalone/bin/benchmark.ts +695 -0
- package/templates/nextjs-standalone/bin/enrich.ts +559 -0
- package/templates/nextjs-standalone/bin/eval.ts +770 -0
- package/templates/nextjs-standalone/bin/smoke.ts +438 -0
- package/templates/nextjs-standalone/data/.gitkeep +0 -0
- package/templates/nextjs-standalone/data/cybersec.sql +1961 -0
- package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +40 -0
- package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +170 -0
- package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +207 -0
- package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +145 -0
- package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +22 -0
- package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +38 -0
- package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +89 -0
- package/templates/nextjs-standalone/data/demo.sql +373 -0
- package/templates/nextjs-standalone/data/ecommerce.sql +1690 -0
- package/templates/nextjs-standalone/data/init-demo-db.sql +8 -0
- package/templates/nextjs-standalone/docs/deploy.md +390 -0
- package/templates/nextjs-standalone/eslint.config.mjs +18 -0
- package/templates/nextjs-standalone/gitignore +5 -0
- package/templates/nextjs-standalone/next.config.ts +10 -0
- package/templates/nextjs-standalone/package.json +63 -0
- package/templates/nextjs-standalone/postcss.config.mjs +8 -0
- package/templates/nextjs-standalone/semantic/catalog.yml +5 -0
- package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
- package/templates/nextjs-standalone/semantic/glossary.yml +6 -0
- package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
- package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +683 -0
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +820 -0
- package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +165 -0
- package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +376 -0
- package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +555 -0
- package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +135 -0
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +169 -0
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +261 -0
- package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +891 -0
- package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +601 -0
- package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +847 -0
- package/templates/nextjs-standalone/src/api/index.ts +117 -0
- package/templates/nextjs-standalone/src/api/routes/actions.ts +274 -0
- package/templates/nextjs-standalone/src/api/routes/admin.ts +757 -0
- package/templates/nextjs-standalone/src/api/routes/auth.ts +48 -0
- package/templates/nextjs-standalone/src/api/routes/chat.ts +465 -0
- package/templates/nextjs-standalone/src/api/routes/conversations.ts +266 -0
- package/templates/nextjs-standalone/src/api/routes/health.ts +287 -0
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +390 -0
- package/templates/nextjs-standalone/src/api/routes/query.ts +318 -0
- package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +467 -0
- package/templates/nextjs-standalone/src/api/routes/slack.ts +611 -0
- package/templates/nextjs-standalone/src/api/server.ts +226 -0
- package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +33 -0
- package/templates/nextjs-standalone/src/app/error.tsx +24 -0
- package/templates/nextjs-standalone/src/app/global-error.tsx +68 -0
- package/templates/nextjs-standalone/src/app/globals.css +126 -0
- package/templates/nextjs-standalone/src/app/layout.tsx +19 -0
- package/templates/nextjs-standalone/src/app/page.tsx +14 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +437 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +114 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +514 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +166 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +1063 -0
- package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +589 -0
- package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +256 -0
- package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +200 -0
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +99 -0
- package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +435 -0
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +547 -0
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +544 -0
- package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +363 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +452 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +465 -0
- package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +28 -0
- package/templates/nextjs-standalone/src/lib/action-types.ts +95 -0
- package/templates/nextjs-standalone/src/lib/agent-query.ts +178 -0
- package/templates/nextjs-standalone/src/lib/agent.ts +505 -0
- package/templates/nextjs-standalone/src/lib/api-url.ts +3 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +418 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +366 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +190 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +173 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +456 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +201 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +225 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +34 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +176 -0
- package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +44 -0
- package/templates/nextjs-standalone/src/lib/auth/audit.ts +89 -0
- package/templates/nextjs-standalone/src/lib/auth/byot.ts +158 -0
- package/templates/nextjs-standalone/src/lib/auth/client.ts +23 -0
- package/templates/nextjs-standalone/src/lib/auth/detect.ts +83 -0
- package/templates/nextjs-standalone/src/lib/auth/managed.ts +73 -0
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +208 -0
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +111 -0
- package/templates/nextjs-standalone/src/lib/auth/permissions.ts +156 -0
- package/templates/nextjs-standalone/src/lib/auth/server.ts +142 -0
- package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +92 -0
- package/templates/nextjs-standalone/src/lib/auth/types.ts +49 -0
- package/templates/nextjs-standalone/src/lib/config.ts +704 -0
- package/templates/nextjs-standalone/src/lib/conversation-types.ts +29 -0
- package/templates/nextjs-standalone/src/lib/conversations.ts +270 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +69 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +141 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +387 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +207 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +595 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +339 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +217 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
- package/templates/nextjs-standalone/src/lib/db/connection.ts +753 -0
- package/templates/nextjs-standalone/src/lib/db/duckdb.ts +122 -0
- package/templates/nextjs-standalone/src/lib/db/internal.ts +273 -0
- package/templates/nextjs-standalone/src/lib/db/salesforce.ts +342 -0
- package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +191 -0
- package/templates/nextjs-standalone/src/lib/errors.ts +154 -0
- package/templates/nextjs-standalone/src/lib/logger.ts +98 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +529 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +521 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +346 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +49 -0
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +585 -0
- package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +162 -0
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +9 -0
- package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +309 -0
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +231 -0
- package/templates/nextjs-standalone/src/lib/plugins/tools.ts +39 -0
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +291 -0
- package/templates/nextjs-standalone/src/lib/providers.ts +102 -0
- package/templates/nextjs-standalone/src/lib/rls.ts +321 -0
- package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +132 -0
- package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +475 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +248 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
- package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -0
- package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +317 -0
- package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +73 -0
- package/templates/nextjs-standalone/src/lib/scheduler/format-email.ts +109 -0
- package/templates/nextjs-standalone/src/lib/scheduler/format-slack.ts +35 -0
- package/templates/nextjs-standalone/src/lib/scheduler/format-webhook.ts +37 -0
- package/templates/nextjs-standalone/src/lib/scheduler/index.ts +7 -0
- package/templates/nextjs-standalone/src/lib/security.ts +11 -0
- package/templates/nextjs-standalone/src/lib/semantic-index.ts +503 -0
- package/templates/nextjs-standalone/src/lib/semantic.ts +387 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +16 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +160 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +237 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +188 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +112 -0
- package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +111 -0
- package/templates/nextjs-standalone/src/lib/slack/api.ts +102 -0
- package/templates/nextjs-standalone/src/lib/slack/format.ts +209 -0
- package/templates/nextjs-standalone/src/lib/slack/store.ts +107 -0
- package/templates/nextjs-standalone/src/lib/slack/threads.ts +64 -0
- package/templates/nextjs-standalone/src/lib/slack/verify.ts +71 -0
- package/templates/nextjs-standalone/src/lib/startup.ts +730 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +235 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +1012 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +378 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +47 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +191 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +591 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/index.ts +23 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +220 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +343 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +264 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +163 -0
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +379 -0
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +221 -0
- package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +138 -0
- package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +172 -0
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +680 -0
- package/templates/nextjs-standalone/src/lib/tracing.ts +40 -0
- package/templates/nextjs-standalone/src/lib/utils.ts +6 -0
- package/templates/nextjs-standalone/src/test-setup.ts +38 -0
- package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +295 -0
- package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +50 -0
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +26 -0
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +96 -0
- package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +24 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +233 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-list.tsx +96 -0
- package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +22 -0
- package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +44 -0
- package/templates/nextjs-standalone/src/ui/components/admin/health-badge.tsx +30 -0
- package/templates/nextjs-standalone/src/ui/components/admin/loading-state.tsx +14 -0
- package/templates/nextjs-standalone/src/ui/components/admin/stat-card.tsx +32 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +370 -0
- package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +261 -0
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +375 -0
- package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +66 -0
- package/templates/nextjs-standalone/src/ui/components/chat/copy-button.tsx +25 -0
- package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +102 -0
- package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +32 -0
- package/templates/nextjs-standalone/src/ui/components/chat/explore-card.tsx +41 -0
- package/templates/nextjs-standalone/src/ui/components/chat/loading-card.tsx +10 -0
- package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +116 -0
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +72 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +30 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +144 -0
- package/templates/nextjs-standalone/src/ui/components/chat/starter-prompts.ts +6 -0
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +40 -0
- package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +19 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +120 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +66 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
- package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +27 -0
- package/templates/nextjs-standalone/src/ui/context.tsx +78 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +104 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +184 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -0
- package/templates/nextjs-standalone/src/ui/lib/action-types.ts +63 -0
- package/templates/nextjs-standalone/src/ui/lib/helpers.ts +104 -0
- package/templates/nextjs-standalone/src/ui/lib/types.ts +145 -0
- package/templates/nextjs-standalone/tsconfig.json +32 -0
- package/templates/nextjs-standalone/vercel.json +4 -0
|
@@ -0,0 +1,1961 @@
|
|
|
1
|
+
-- ==========================================================================
|
|
2
|
+
-- Sentinel Security — Cybersecurity SaaS Demo Database (PostgreSQL)
|
|
3
|
+
-- ==========================================================================
|
|
4
|
+
-- ~62 tables, ~500K rows. Realistic tech debt patterns:
|
|
5
|
+
-- 1. Abandoned/legacy tables (6 tables nobody reads)
|
|
6
|
+
-- 2. Schema evolution artifacts (columns that changed meaning)
|
|
7
|
+
-- 3. Missing/wrong constraints (logical FKs without DB constraints)
|
|
8
|
+
-- 4. Denormalization & duplication (reporting tables, copied columns)
|
|
9
|
+
--
|
|
10
|
+
-- Usage: psql $ATLAS_DATASOURCE_URL -f data/cybersec.sql
|
|
11
|
+
-- Reset: bun run db:reset (nukes volume, re-seeds)
|
|
12
|
+
-- ==========================================================================
|
|
13
|
+
|
|
14
|
+
BEGIN;
|
|
15
|
+
SELECT setseed(0.42); -- reproducible random data
|
|
16
|
+
|
|
17
|
+
-- ==========================================================================
|
|
18
|
+
-- DROP (safe re-run)
|
|
19
|
+
-- ==========================================================================
|
|
20
|
+
DROP TABLE IF EXISTS legacy_risk_scores CASCADE;
|
|
21
|
+
DROP TABLE IF EXISTS user_sessions_archive CASCADE;
|
|
22
|
+
DROP TABLE IF EXISTS notifications_backup CASCADE;
|
|
23
|
+
DROP TABLE IF EXISTS feature_flags_legacy CASCADE;
|
|
24
|
+
DROP TABLE IF EXISTS temp_asset_import_2024 CASCADE;
|
|
25
|
+
DROP TABLE IF EXISTS old_scan_results_v2 CASCADE;
|
|
26
|
+
DROP TABLE IF EXISTS audit_log CASCADE;
|
|
27
|
+
DROP TABLE IF EXISTS integration_events CASCADE;
|
|
28
|
+
DROP TABLE IF EXISTS integrations CASCADE;
|
|
29
|
+
DROP TABLE IF EXISTS dashboard_widgets CASCADE;
|
|
30
|
+
DROP TABLE IF EXISTS dashboards CASCADE;
|
|
31
|
+
DROP TABLE IF EXISTS report_schedules CASCADE;
|
|
32
|
+
DROP TABLE IF EXISTS reports CASCADE;
|
|
33
|
+
DROP TABLE IF EXISTS executive_dashboard_cache CASCADE;
|
|
34
|
+
DROP TABLE IF EXISTS scan_results_denormalized CASCADE;
|
|
35
|
+
DROP TABLE IF EXISTS organization_health_scores CASCADE;
|
|
36
|
+
DROP TABLE IF EXISTS monthly_vulnerability_summary CASCADE;
|
|
37
|
+
DROP TABLE IF EXISTS daily_scan_stats CASCADE;
|
|
38
|
+
DROP TABLE IF EXISTS notifications CASCADE;
|
|
39
|
+
DROP TABLE IF EXISTS login_events CASCADE;
|
|
40
|
+
DROP TABLE IF EXISTS feature_usage CASCADE;
|
|
41
|
+
DROP TABLE IF EXISTS api_requests CASCADE;
|
|
42
|
+
DROP TABLE IF EXISTS api_keys CASCADE;
|
|
43
|
+
DROP TABLE IF EXISTS compliance_findings CASCADE;
|
|
44
|
+
DROP TABLE IF EXISTS compliance_assessments CASCADE;
|
|
45
|
+
DROP TABLE IF EXISTS compliance_controls CASCADE;
|
|
46
|
+
DROP TABLE IF EXISTS compliance_frameworks CASCADE;
|
|
47
|
+
DROP TABLE IF EXISTS threat_actors CASCADE;
|
|
48
|
+
DROP TABLE IF EXISTS indicators_of_compromise CASCADE;
|
|
49
|
+
DROP TABLE IF EXISTS threat_feeds CASCADE;
|
|
50
|
+
DROP TABLE IF EXISTS alert_acknowledgments CASCADE;
|
|
51
|
+
DROP TABLE IF EXISTS alert_rules CASCADE;
|
|
52
|
+
DROP TABLE IF EXISTS alerts CASCADE;
|
|
53
|
+
DROP TABLE IF EXISTS incident_comments CASCADE;
|
|
54
|
+
DROP TABLE IF EXISTS incident_events CASCADE;
|
|
55
|
+
DROP TABLE IF EXISTS incidents CASCADE;
|
|
56
|
+
DROP TABLE IF EXISTS vulnerability_exceptions CASCADE;
|
|
57
|
+
DROP TABLE IF EXISTS remediation_actions CASCADE;
|
|
58
|
+
DROP TABLE IF EXISTS vulnerability_instances CASCADE;
|
|
59
|
+
DROP TABLE IF EXISTS scan_results CASCADE;
|
|
60
|
+
DROP TABLE IF EXISTS scan_configurations CASCADE;
|
|
61
|
+
DROP TABLE IF EXISTS scans CASCADE;
|
|
62
|
+
DROP TABLE IF EXISTS vulnerabilities CASCADE;
|
|
63
|
+
DROP TABLE IF EXISTS agent_heartbeats CASCADE;
|
|
64
|
+
DROP TABLE IF EXISTS agents CASCADE;
|
|
65
|
+
DROP TABLE IF EXISTS asset_tags CASCADE;
|
|
66
|
+
DROP TABLE IF EXISTS asset_group_memberships CASCADE;
|
|
67
|
+
DROP TABLE IF EXISTS asset_groups CASCADE;
|
|
68
|
+
DROP TABLE IF EXISTS assets CASCADE;
|
|
69
|
+
DROP TABLE IF EXISTS payment_methods CASCADE;
|
|
70
|
+
DROP TABLE IF EXISTS invoice_line_items CASCADE;
|
|
71
|
+
DROP TABLE IF EXISTS invoices CASCADE;
|
|
72
|
+
DROP TABLE IF EXISTS subscription_events CASCADE;
|
|
73
|
+
DROP TABLE IF EXISTS subscriptions CASCADE;
|
|
74
|
+
DROP TABLE IF EXISTS plans CASCADE;
|
|
75
|
+
DROP TABLE IF EXISTS invitations CASCADE;
|
|
76
|
+
DROP TABLE IF EXISTS team_memberships CASCADE;
|
|
77
|
+
DROP TABLE IF EXISTS teams CASCADE;
|
|
78
|
+
DROP TABLE IF EXISTS users CASCADE;
|
|
79
|
+
DROP TABLE IF EXISTS organization_settings CASCADE;
|
|
80
|
+
DROP TABLE IF EXISTS organizations CASCADE;
|
|
81
|
+
DROP TABLE IF EXISTS roles CASCADE;
|
|
82
|
+
|
|
83
|
+
-- ==========================================================================
|
|
84
|
+
-- 1. SCHEMA
|
|
85
|
+
-- ==========================================================================
|
|
86
|
+
|
|
87
|
+
-- ---------- 1.1 Core Business ----------
|
|
88
|
+
|
|
89
|
+
CREATE TABLE roles (
|
|
90
|
+
id SERIAL PRIMARY KEY,
|
|
91
|
+
name TEXT NOT NULL UNIQUE,
|
|
92
|
+
description TEXT
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
CREATE TABLE organizations (
|
|
96
|
+
id SERIAL PRIMARY KEY,
|
|
97
|
+
name TEXT NOT NULL,
|
|
98
|
+
industry TEXT, -- TECH DEBT: no enum constraint, inconsistent values
|
|
99
|
+
size_tier TEXT,
|
|
100
|
+
domain TEXT,
|
|
101
|
+
country TEXT,
|
|
102
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
103
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
104
|
+
is_active BOOLEAN NOT NULL DEFAULT true
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
CREATE TABLE organization_settings (
|
|
108
|
+
id SERIAL PRIMARY KEY,
|
|
109
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
110
|
+
notification_email TEXT,
|
|
111
|
+
scan_frequency TEXT DEFAULT 'weekly',
|
|
112
|
+
retention_days INTEGER DEFAULT 90,
|
|
113
|
+
sso_enabled BOOLEAN DEFAULT false,
|
|
114
|
+
mfa_required BOOLEAN DEFAULT false,
|
|
115
|
+
settings_json TEXT, -- catch-all config as text (realistic)
|
|
116
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
117
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
CREATE TABLE users (
|
|
121
|
+
id SERIAL PRIMARY KEY,
|
|
122
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
123
|
+
organization_name TEXT, -- TECH DEBT: denormalized, sometimes stale
|
|
124
|
+
email TEXT NOT NULL, -- TECH DEBT: should be UNIQUE but 3 dupes from org merge
|
|
125
|
+
full_name TEXT NOT NULL,
|
|
126
|
+
role TEXT, -- TECH DEBT: legacy column, still populated, app ignores it
|
|
127
|
+
last_login TIMESTAMPTZ, -- TECH DEBT: mostly NULL, never read
|
|
128
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
129
|
+
is_active BOOLEAN NOT NULL DEFAULT true
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
CREATE TABLE teams (
|
|
133
|
+
id SERIAL PRIMARY KEY,
|
|
134
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
135
|
+
name TEXT NOT NULL,
|
|
136
|
+
description TEXT,
|
|
137
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
CREATE TABLE team_memberships (
|
|
141
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
142
|
+
team_id INTEGER NOT NULL REFERENCES teams(id),
|
|
143
|
+
role_id INTEGER NOT NULL REFERENCES roles(id),
|
|
144
|
+
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
145
|
+
PRIMARY KEY (user_id, team_id)
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
CREATE TABLE invitations (
|
|
149
|
+
id SERIAL PRIMARY KEY,
|
|
150
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
151
|
+
email TEXT NOT NULL,
|
|
152
|
+
role_name TEXT NOT NULL,
|
|
153
|
+
invited_by INTEGER REFERENCES users(id),
|
|
154
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
155
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
156
|
+
expires_at TIMESTAMPTZ NOT NULL
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
-- ---------- 1.2 Billing & Subscriptions ----------
|
|
160
|
+
|
|
161
|
+
CREATE TABLE plans (
|
|
162
|
+
id SERIAL PRIMARY KEY,
|
|
163
|
+
name TEXT NOT NULL UNIQUE,
|
|
164
|
+
display_name TEXT NOT NULL,
|
|
165
|
+
price_cents INTEGER NOT NULL,
|
|
166
|
+
max_assets INTEGER,
|
|
167
|
+
max_users INTEGER,
|
|
168
|
+
features TEXT, -- JSON-as-text (realistic)
|
|
169
|
+
is_active BOOLEAN NOT NULL DEFAULT true
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
CREATE TABLE subscriptions (
|
|
173
|
+
id SERIAL PRIMARY KEY,
|
|
174
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
175
|
+
plan_id INTEGER NOT NULL REFERENCES plans(id),
|
|
176
|
+
plan_name TEXT, -- TECH DEBT: denormalized from plans.name, ~15% out of sync
|
|
177
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
178
|
+
mrr_cents INTEGER NOT NULL,
|
|
179
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
180
|
+
current_period_end TIMESTAMPTZ,
|
|
181
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
182
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
CREATE TABLE subscription_events (
|
|
186
|
+
id SERIAL PRIMARY KEY,
|
|
187
|
+
subscription_id INTEGER NOT NULL REFERENCES subscriptions(id),
|
|
188
|
+
event_type TEXT NOT NULL,
|
|
189
|
+
old_plan_id INTEGER REFERENCES plans(id),
|
|
190
|
+
new_plan_id INTEGER REFERENCES plans(id),
|
|
191
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
CREATE TABLE invoices (
|
|
195
|
+
id SERIAL PRIMARY KEY,
|
|
196
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
197
|
+
subscription_id INTEGER NOT NULL REFERENCES subscriptions(id),
|
|
198
|
+
amount_cents INTEGER NOT NULL,
|
|
199
|
+
status TEXT NOT NULL DEFAULT 'paid',
|
|
200
|
+
period_start DATE NOT NULL,
|
|
201
|
+
period_end DATE NOT NULL,
|
|
202
|
+
due_date DATE NOT NULL,
|
|
203
|
+
paid_at TIMESTAMPTZ,
|
|
204
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
CREATE TABLE invoice_line_items (
|
|
208
|
+
id SERIAL PRIMARY KEY,
|
|
209
|
+
invoice_id INTEGER NOT NULL REFERENCES invoices(id),
|
|
210
|
+
subscription_id INTEGER, -- TECH DEBT: NO FK to subscriptions (logical only)
|
|
211
|
+
description TEXT NOT NULL,
|
|
212
|
+
amount_cents INTEGER NOT NULL,
|
|
213
|
+
quantity INTEGER NOT NULL DEFAULT 1
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
CREATE TABLE payment_methods (
|
|
217
|
+
id SERIAL PRIMARY KEY,
|
|
218
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
219
|
+
type TEXT NOT NULL,
|
|
220
|
+
last4 TEXT,
|
|
221
|
+
expiry_month INTEGER,
|
|
222
|
+
expiry_year INTEGER,
|
|
223
|
+
is_default BOOLEAN DEFAULT false,
|
|
224
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
-- ---------- 1.3 Asset Management ----------
|
|
228
|
+
|
|
229
|
+
CREATE TABLE assets (
|
|
230
|
+
id SERIAL PRIMARY KEY,
|
|
231
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
232
|
+
hostname TEXT NOT NULL,
|
|
233
|
+
display_name TEXT, -- TECH DEBT: added 2023, NULL for ~40% of assets
|
|
234
|
+
asset_type TEXT NOT NULL, -- TECH DEBT: expanded enum, old data not migrated
|
|
235
|
+
ip_address TEXT,
|
|
236
|
+
os TEXT,
|
|
237
|
+
os_version TEXT,
|
|
238
|
+
environment TEXT,
|
|
239
|
+
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
240
|
+
first_seen TIMESTAMPTZ NOT NULL,
|
|
241
|
+
last_seen TIMESTAMPTZ,
|
|
242
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
CREATE TABLE asset_groups (
|
|
246
|
+
id SERIAL PRIMARY KEY,
|
|
247
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
248
|
+
name TEXT NOT NULL,
|
|
249
|
+
description TEXT,
|
|
250
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
CREATE TABLE asset_group_memberships (
|
|
254
|
+
asset_id INTEGER NOT NULL REFERENCES assets(id),
|
|
255
|
+
asset_group_id INTEGER NOT NULL REFERENCES asset_groups(id),
|
|
256
|
+
added_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
257
|
+
PRIMARY KEY (asset_id, asset_group_id)
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
CREATE TABLE asset_tags (
|
|
261
|
+
id SERIAL PRIMARY KEY,
|
|
262
|
+
asset_id INTEGER NOT NULL REFERENCES assets(id),
|
|
263
|
+
key TEXT NOT NULL,
|
|
264
|
+
value TEXT NOT NULL,
|
|
265
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
CREATE TABLE agents (
|
|
269
|
+
id SERIAL PRIMARY KEY,
|
|
270
|
+
asset_id INTEGER, -- TECH DEBT: no FK, ~200 orphaned (reference deleted assets)
|
|
271
|
+
agent_version TEXT NOT NULL,
|
|
272
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
273
|
+
last_heartbeat TIMESTAMPTZ,
|
|
274
|
+
installed_at TIMESTAMPTZ NOT NULL,
|
|
275
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
CREATE TABLE agent_heartbeats (
|
|
279
|
+
id SERIAL PRIMARY KEY,
|
|
280
|
+
agent_id INTEGER, -- TECH DEBT: NO FK to agents
|
|
281
|
+
cpu_percent REAL,
|
|
282
|
+
memory_percent REAL,
|
|
283
|
+
disk_percent REAL,
|
|
284
|
+
reported_at TIMESTAMPTZ NOT NULL
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
-- ---------- 1.4 Vulnerability Management ----------
|
|
288
|
+
|
|
289
|
+
CREATE TABLE vulnerabilities (
|
|
290
|
+
id SERIAL PRIMARY KEY,
|
|
291
|
+
cve_id TEXT UNIQUE,
|
|
292
|
+
title TEXT NOT NULL,
|
|
293
|
+
description TEXT,
|
|
294
|
+
severity TEXT NOT NULL, -- TECH DEBT: text enum ('low','medium','high','critical')
|
|
295
|
+
cvss_score REAL, -- TECH DEBT: added 2022, NULL for ~30% of older vulns
|
|
296
|
+
category TEXT,
|
|
297
|
+
published_at TIMESTAMPTZ,
|
|
298
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
CREATE TABLE scan_configurations (
|
|
302
|
+
id SERIAL PRIMARY KEY,
|
|
303
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
304
|
+
name TEXT NOT NULL,
|
|
305
|
+
scan_type TEXT NOT NULL,
|
|
306
|
+
target_spec TEXT,
|
|
307
|
+
schedule TEXT,
|
|
308
|
+
is_active BOOLEAN DEFAULT true,
|
|
309
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
CREATE TABLE scans (
|
|
313
|
+
id SERIAL PRIMARY KEY,
|
|
314
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
315
|
+
scan_configuration_id INTEGER REFERENCES scan_configurations(id),
|
|
316
|
+
scan_type TEXT NOT NULL,
|
|
317
|
+
status TEXT NOT NULL DEFAULT 'completed',
|
|
318
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
319
|
+
completed_at TIMESTAMPTZ,
|
|
320
|
+
assets_scanned INTEGER,
|
|
321
|
+
findings_count INTEGER,
|
|
322
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
CREATE TABLE scan_results (
|
|
326
|
+
id SERIAL PRIMARY KEY,
|
|
327
|
+
scan_id INTEGER, -- TECH DEBT: NO FK to scans
|
|
328
|
+
asset_id INTEGER, -- TECH DEBT: NO FK to assets, ~500 orphan rows
|
|
329
|
+
vulnerability_id INTEGER, -- TECH DEBT: NO FK to vulnerabilities
|
|
330
|
+
risk_level INTEGER, -- TECH DEBT: was 1-5, changed to 1-10 mid-2024
|
|
331
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
332
|
+
details TEXT,
|
|
333
|
+
found_at TIMESTAMPTZ NOT NULL,
|
|
334
|
+
resolved_at TIMESTAMPTZ
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
CREATE TABLE vulnerability_instances (
|
|
338
|
+
id SERIAL PRIMARY KEY,
|
|
339
|
+
vulnerability_id INTEGER NOT NULL REFERENCES vulnerabilities(id),
|
|
340
|
+
asset_id INTEGER NOT NULL REFERENCES assets(id),
|
|
341
|
+
scan_result_id INTEGER, -- TECH DEBT: NO FK to scan_results
|
|
342
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
343
|
+
first_seen TIMESTAMPTZ NOT NULL,
|
|
344
|
+
last_seen TIMESTAMPTZ,
|
|
345
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
CREATE TABLE remediation_actions (
|
|
349
|
+
id SERIAL PRIMARY KEY,
|
|
350
|
+
vulnerability_instance_id INTEGER NOT NULL REFERENCES vulnerability_instances(id),
|
|
351
|
+
assigned_to INTEGER REFERENCES users(id),
|
|
352
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
353
|
+
priority TEXT,
|
|
354
|
+
due_date DATE,
|
|
355
|
+
completed_at TIMESTAMPTZ,
|
|
356
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
357
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
CREATE TABLE vulnerability_exceptions (
|
|
361
|
+
id SERIAL PRIMARY KEY,
|
|
362
|
+
vulnerability_id INTEGER NOT NULL REFERENCES vulnerabilities(id),
|
|
363
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
364
|
+
requested_by INTEGER REFERENCES users(id),
|
|
365
|
+
reason TEXT NOT NULL,
|
|
366
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
367
|
+
expires_at TIMESTAMPTZ,
|
|
368
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
-- ---------- 1.5 Threat & Incident Management ----------
|
|
372
|
+
|
|
373
|
+
CREATE TABLE incidents (
|
|
374
|
+
id SERIAL PRIMARY KEY,
|
|
375
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
376
|
+
organization_name TEXT, -- TECH DEBT: denormalized
|
|
377
|
+
title TEXT NOT NULL,
|
|
378
|
+
description TEXT,
|
|
379
|
+
severity TEXT NOT NULL,
|
|
380
|
+
status TEXT NOT NULL, -- TECH DEBT: 'identified'/'monitoring' overlap, 'closed' deprecated
|
|
381
|
+
assigned_to INTEGER REFERENCES users(id),
|
|
382
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
383
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
384
|
+
resolved_at TIMESTAMPTZ
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
CREATE TABLE incident_events (
|
|
388
|
+
id SERIAL PRIMARY KEY,
|
|
389
|
+
incident_id INTEGER NOT NULL REFERENCES incidents(id),
|
|
390
|
+
event_type TEXT NOT NULL,
|
|
391
|
+
description TEXT,
|
|
392
|
+
created_by INTEGER REFERENCES users(id),
|
|
393
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
CREATE TABLE incident_comments (
|
|
397
|
+
id SERIAL PRIMARY KEY,
|
|
398
|
+
incident_id INTEGER NOT NULL REFERENCES incidents(id),
|
|
399
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
400
|
+
content TEXT NOT NULL,
|
|
401
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
CREATE TABLE alert_rules (
|
|
405
|
+
id SERIAL PRIMARY KEY,
|
|
406
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
407
|
+
name TEXT NOT NULL,
|
|
408
|
+
condition_type TEXT NOT NULL,
|
|
409
|
+
threshold REAL,
|
|
410
|
+
severity TEXT NOT NULL,
|
|
411
|
+
is_active BOOLEAN DEFAULT true,
|
|
412
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
CREATE TABLE alerts (
|
|
416
|
+
id SERIAL PRIMARY KEY,
|
|
417
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
418
|
+
alert_rule_id INTEGER REFERENCES alert_rules(id),
|
|
419
|
+
severity TEXT, -- TECH DEBT: should be NOT NULL, ~200 old alerts have NULL
|
|
420
|
+
title TEXT NOT NULL,
|
|
421
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
422
|
+
incident_id INTEGER, -- TECH DEBT: NO FK to incidents (nullable, set on escalation)
|
|
423
|
+
source TEXT,
|
|
424
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
425
|
+
acknowledged_at TIMESTAMPTZ
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
CREATE TABLE alert_acknowledgments (
|
|
429
|
+
id SERIAL PRIMARY KEY,
|
|
430
|
+
alert_id INTEGER NOT NULL REFERENCES alerts(id),
|
|
431
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
432
|
+
acknowledged_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
433
|
+
note TEXT
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
-- ---------- 1.6 Threat Intelligence ----------
|
|
437
|
+
|
|
438
|
+
CREATE TABLE threat_feeds (
|
|
439
|
+
id SERIAL PRIMARY KEY,
|
|
440
|
+
name TEXT NOT NULL,
|
|
441
|
+
provider TEXT NOT NULL,
|
|
442
|
+
url TEXT,
|
|
443
|
+
feed_type TEXT NOT NULL,
|
|
444
|
+
is_active BOOLEAN DEFAULT true,
|
|
445
|
+
last_synced TIMESTAMPTZ,
|
|
446
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
CREATE TABLE threat_actors (
|
|
450
|
+
id SERIAL PRIMARY KEY,
|
|
451
|
+
name TEXT NOT NULL,
|
|
452
|
+
aliases TEXT,
|
|
453
|
+
origin TEXT,
|
|
454
|
+
description TEXT,
|
|
455
|
+
active_since DATE,
|
|
456
|
+
last_activity DATE,
|
|
457
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
CREATE TABLE indicators_of_compromise (
|
|
461
|
+
id SERIAL PRIMARY KEY,
|
|
462
|
+
threat_feed_id INTEGER NOT NULL REFERENCES threat_feeds(id),
|
|
463
|
+
ioc_type TEXT NOT NULL,
|
|
464
|
+
value TEXT NOT NULL,
|
|
465
|
+
confidence INTEGER,
|
|
466
|
+
threat_actor_id INTEGER REFERENCES threat_actors(id),
|
|
467
|
+
first_seen TIMESTAMPTZ,
|
|
468
|
+
last_seen TIMESTAMPTZ,
|
|
469
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
-- ---------- 1.7 Compliance ----------
|
|
473
|
+
|
|
474
|
+
CREATE TABLE compliance_frameworks (
|
|
475
|
+
id SERIAL PRIMARY KEY,
|
|
476
|
+
name TEXT NOT NULL UNIQUE,
|
|
477
|
+
version TEXT,
|
|
478
|
+
description TEXT,
|
|
479
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
CREATE TABLE compliance_controls (
|
|
483
|
+
id SERIAL PRIMARY KEY,
|
|
484
|
+
framework_id INTEGER NOT NULL REFERENCES compliance_frameworks(id),
|
|
485
|
+
control_id_code TEXT NOT NULL,
|
|
486
|
+
title TEXT NOT NULL,
|
|
487
|
+
description TEXT,
|
|
488
|
+
category TEXT
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
CREATE TABLE compliance_assessments (
|
|
492
|
+
id SERIAL PRIMARY KEY,
|
|
493
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
494
|
+
framework_id INTEGER NOT NULL REFERENCES compliance_frameworks(id),
|
|
495
|
+
status TEXT NOT NULL DEFAULT 'in_progress',
|
|
496
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
497
|
+
completed_at TIMESTAMPTZ,
|
|
498
|
+
score REAL,
|
|
499
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
CREATE TABLE compliance_findings (
|
|
503
|
+
id SERIAL PRIMARY KEY,
|
|
504
|
+
assessment_id INTEGER NOT NULL REFERENCES compliance_assessments(id),
|
|
505
|
+
control_id INTEGER NOT NULL REFERENCES compliance_controls(id),
|
|
506
|
+
status TEXT NOT NULL, -- TECH DEBT: case-inconsistent ('pass','Pass','PASS')
|
|
507
|
+
evidence TEXT,
|
|
508
|
+
notes TEXT,
|
|
509
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
-- ---------- 1.8 Product Usage & Audit ----------
|
|
513
|
+
|
|
514
|
+
CREATE TABLE api_keys (
|
|
515
|
+
id SERIAL PRIMARY KEY,
|
|
516
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
517
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
518
|
+
name TEXT NOT NULL,
|
|
519
|
+
key_prefix TEXT NOT NULL,
|
|
520
|
+
key_hash TEXT NOT NULL,
|
|
521
|
+
permissions TEXT,
|
|
522
|
+
is_active BOOLEAN DEFAULT true,
|
|
523
|
+
last_used TIMESTAMPTZ,
|
|
524
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
525
|
+
expires_at TIMESTAMPTZ
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
CREATE TABLE api_requests (
|
|
529
|
+
id SERIAL PRIMARY KEY,
|
|
530
|
+
api_key_id INTEGER NOT NULL REFERENCES api_keys(id),
|
|
531
|
+
user_id INTEGER, -- TECH DEBT: NO FK to users
|
|
532
|
+
method TEXT NOT NULL,
|
|
533
|
+
path TEXT NOT NULL,
|
|
534
|
+
status_code INTEGER NOT NULL,
|
|
535
|
+
response_time_ms INTEGER,
|
|
536
|
+
request_body TEXT, -- TECH DEBT: dead column, always NULL (privacy fix)
|
|
537
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
CREATE TABLE feature_usage (
|
|
541
|
+
id SERIAL PRIMARY KEY,
|
|
542
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
543
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
544
|
+
feature_name TEXT NOT NULL,
|
|
545
|
+
action TEXT NOT NULL,
|
|
546
|
+
metadata TEXT,
|
|
547
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
CREATE TABLE login_events (
|
|
551
|
+
id SERIAL PRIMARY KEY,
|
|
552
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
553
|
+
ip_address TEXT,
|
|
554
|
+
user_agent TEXT,
|
|
555
|
+
success BOOLEAN NOT NULL,
|
|
556
|
+
failure_reason TEXT,
|
|
557
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
CREATE TABLE notifications (
|
|
561
|
+
id SERIAL PRIMARY KEY,
|
|
562
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
563
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
564
|
+
type TEXT NOT NULL,
|
|
565
|
+
title TEXT NOT NULL,
|
|
566
|
+
message TEXT,
|
|
567
|
+
is_read BOOLEAN DEFAULT false,
|
|
568
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
-- ---------- 1.9 Reporting & Denormalized ----------
|
|
572
|
+
|
|
573
|
+
CREATE TABLE daily_scan_stats (
|
|
574
|
+
id SERIAL PRIMARY KEY,
|
|
575
|
+
organization_id INTEGER, -- no FK (denormalized)
|
|
576
|
+
scan_date DATE NOT NULL,
|
|
577
|
+
total_scans INTEGER DEFAULT 0,
|
|
578
|
+
total_findings INTEGER DEFAULT 0,
|
|
579
|
+
critical_count INTEGER DEFAULT 0,
|
|
580
|
+
high_count INTEGER DEFAULT 0,
|
|
581
|
+
medium_count INTEGER DEFAULT 0,
|
|
582
|
+
low_count INTEGER DEFAULT 0
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
CREATE TABLE monthly_vulnerability_summary (
|
|
586
|
+
id SERIAL PRIMARY KEY,
|
|
587
|
+
organization_id INTEGER, -- no FK
|
|
588
|
+
month DATE NOT NULL,
|
|
589
|
+
total_vulns INTEGER DEFAULT 0,
|
|
590
|
+
critical_vulns INTEGER DEFAULT 0,
|
|
591
|
+
high_vulns INTEGER DEFAULT 0,
|
|
592
|
+
medium_vulns INTEGER DEFAULT 0,
|
|
593
|
+
low_vulns INTEGER DEFAULT 0,
|
|
594
|
+
avg_time_to_fix_days REAL
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
CREATE TABLE organization_health_scores (
|
|
598
|
+
id SERIAL PRIMARY KEY,
|
|
599
|
+
organization_id INTEGER, -- no FK
|
|
600
|
+
score REAL,
|
|
601
|
+
vulnerability_score REAL,
|
|
602
|
+
compliance_score REAL,
|
|
603
|
+
asset_coverage_score REAL,
|
|
604
|
+
calculated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
CREATE TABLE scan_results_denormalized (
|
|
608
|
+
id SERIAL PRIMARY KEY,
|
|
609
|
+
scan_id INTEGER,
|
|
610
|
+
scan_type TEXT,
|
|
611
|
+
scan_started_at TIMESTAMPTZ,
|
|
612
|
+
asset_id INTEGER,
|
|
613
|
+
hostname TEXT,
|
|
614
|
+
asset_type TEXT,
|
|
615
|
+
organization_id INTEGER,
|
|
616
|
+
organization_name TEXT,
|
|
617
|
+
vulnerability_id INTEGER,
|
|
618
|
+
cve_id TEXT,
|
|
619
|
+
vulnerability_title TEXT,
|
|
620
|
+
severity TEXT,
|
|
621
|
+
cvss_score REAL,
|
|
622
|
+
risk_level INTEGER,
|
|
623
|
+
status TEXT,
|
|
624
|
+
found_at TIMESTAMPTZ
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
CREATE TABLE executive_dashboard_cache (
|
|
628
|
+
id SERIAL PRIMARY KEY,
|
|
629
|
+
organization_id INTEGER, -- no FK
|
|
630
|
+
total_assets INTEGER,
|
|
631
|
+
active_agents INTEGER,
|
|
632
|
+
open_vulnerabilities INTEGER,
|
|
633
|
+
critical_incidents INTEGER,
|
|
634
|
+
compliance_score REAL,
|
|
635
|
+
risk_score REAL,
|
|
636
|
+
generated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
-- ---------- 1.10 Saved Reports & Dashboards ----------
|
|
640
|
+
|
|
641
|
+
CREATE TABLE reports (
|
|
642
|
+
id SERIAL PRIMARY KEY,
|
|
643
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
644
|
+
created_by INTEGER REFERENCES users(id),
|
|
645
|
+
title TEXT NOT NULL,
|
|
646
|
+
report_type TEXT NOT NULL,
|
|
647
|
+
format TEXT DEFAULT 'pdf',
|
|
648
|
+
parameters TEXT,
|
|
649
|
+
status TEXT DEFAULT 'completed',
|
|
650
|
+
file_path TEXT,
|
|
651
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
CREATE TABLE report_schedules (
|
|
655
|
+
id SERIAL PRIMARY KEY,
|
|
656
|
+
report_id INTEGER NOT NULL REFERENCES reports(id),
|
|
657
|
+
cron_expression TEXT NOT NULL,
|
|
658
|
+
recipients TEXT,
|
|
659
|
+
is_active BOOLEAN DEFAULT true,
|
|
660
|
+
last_run TIMESTAMPTZ,
|
|
661
|
+
next_run TIMESTAMPTZ,
|
|
662
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
CREATE TABLE dashboards (
|
|
666
|
+
id SERIAL PRIMARY KEY,
|
|
667
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
668
|
+
created_by INTEGER REFERENCES users(id),
|
|
669
|
+
name TEXT NOT NULL,
|
|
670
|
+
description TEXT,
|
|
671
|
+
is_default BOOLEAN DEFAULT false,
|
|
672
|
+
layout TEXT,
|
|
673
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
674
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
CREATE TABLE dashboard_widgets (
|
|
678
|
+
id SERIAL PRIMARY KEY,
|
|
679
|
+
dashboard_id INTEGER NOT NULL REFERENCES dashboards(id),
|
|
680
|
+
widget_type TEXT NOT NULL,
|
|
681
|
+
title TEXT,
|
|
682
|
+
config TEXT,
|
|
683
|
+
position_x INTEGER DEFAULT 0,
|
|
684
|
+
position_y INTEGER DEFAULT 0,
|
|
685
|
+
width INTEGER DEFAULT 4,
|
|
686
|
+
height INTEGER DEFAULT 3
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
-- ---------- 1.11 Integration & Audit ----------
|
|
690
|
+
|
|
691
|
+
CREATE TABLE integrations (
|
|
692
|
+
id SERIAL PRIMARY KEY,
|
|
693
|
+
organization_id INTEGER NOT NULL REFERENCES organizations(id),
|
|
694
|
+
type TEXT NOT NULL,
|
|
695
|
+
name TEXT NOT NULL,
|
|
696
|
+
config TEXT,
|
|
697
|
+
status TEXT DEFAULT 'active',
|
|
698
|
+
last_sync TIMESTAMPTZ,
|
|
699
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
CREATE TABLE integration_events (
|
|
703
|
+
id SERIAL PRIMARY KEY,
|
|
704
|
+
integration_id INTEGER NOT NULL REFERENCES integrations(id),
|
|
705
|
+
event_type TEXT NOT NULL,
|
|
706
|
+
direction TEXT NOT NULL,
|
|
707
|
+
payload TEXT,
|
|
708
|
+
status TEXT DEFAULT 'success',
|
|
709
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
CREATE TABLE audit_log (
|
|
713
|
+
id SERIAL PRIMARY KEY,
|
|
714
|
+
organization_id INTEGER, -- no FK (standalone audit)
|
|
715
|
+
user_id INTEGER, -- no FK
|
|
716
|
+
action TEXT NOT NULL,
|
|
717
|
+
resource_type TEXT,
|
|
718
|
+
resource_id TEXT,
|
|
719
|
+
details TEXT,
|
|
720
|
+
ip_address TEXT,
|
|
721
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
-- ---------- 1.12 Legacy & Abandoned ----------
|
|
725
|
+
|
|
726
|
+
CREATE TABLE old_scan_results_v2 (
|
|
727
|
+
id SERIAL PRIMARY KEY,
|
|
728
|
+
scan_run_id INTEGER, -- different name than scan_results.scan_id
|
|
729
|
+
target_host TEXT, -- different name than asset_id
|
|
730
|
+
vuln_code TEXT, -- different name than vulnerability_id
|
|
731
|
+
severity_score REAL, -- different name and type than risk_level
|
|
732
|
+
detection_date TIMESTAMPTZ, -- different name than found_at
|
|
733
|
+
remediated BOOLEAN DEFAULT false
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
CREATE TABLE temp_asset_import_2024 (
|
|
737
|
+
id SERIAL PRIMARY KEY,
|
|
738
|
+
import_hostname TEXT,
|
|
739
|
+
import_ip TEXT,
|
|
740
|
+
import_os TEXT,
|
|
741
|
+
import_env TEXT,
|
|
742
|
+
raw_csv_line TEXT,
|
|
743
|
+
imported_at TIMESTAMPTZ DEFAULT '2024-03-15'::timestamptz
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
CREATE TABLE feature_flags_legacy (
|
|
747
|
+
id SERIAL PRIMARY KEY,
|
|
748
|
+
flag_name TEXT NOT NULL,
|
|
749
|
+
is_enabled BOOLEAN DEFAULT false,
|
|
750
|
+
rollout_percentage INTEGER DEFAULT 0,
|
|
751
|
+
description TEXT,
|
|
752
|
+
updated_at TIMESTAMPTZ DEFAULT '2024-01-01'::timestamptz
|
|
753
|
+
);
|
|
754
|
+
|
|
755
|
+
CREATE TABLE notifications_backup (
|
|
756
|
+
id SERIAL PRIMARY KEY,
|
|
757
|
+
recipient_id INTEGER, -- old user ID column name
|
|
758
|
+
notification_type TEXT, -- different from notifications.type
|
|
759
|
+
subject TEXT, -- different from notifications.title
|
|
760
|
+
body TEXT, -- different from notifications.message
|
|
761
|
+
sent_at TIMESTAMPTZ,
|
|
762
|
+
read_at TIMESTAMPTZ
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
CREATE TABLE user_sessions_archive (
|
|
766
|
+
id SERIAL PRIMARY KEY,
|
|
767
|
+
session_token TEXT,
|
|
768
|
+
user_ref_id INTEGER, -- references OLD user IDs (not current users.id)
|
|
769
|
+
login_time TIMESTAMPTZ,
|
|
770
|
+
logout_time TIMESTAMPTZ,
|
|
771
|
+
ip_addr TEXT,
|
|
772
|
+
browser TEXT
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
CREATE TABLE legacy_risk_scores (
|
|
776
|
+
id SERIAL PRIMARY KEY,
|
|
777
|
+
org_id INTEGER, -- different column name than organization_id
|
|
778
|
+
risk_category TEXT,
|
|
779
|
+
score_value REAL, -- different scale than current health scores
|
|
780
|
+
weight REAL,
|
|
781
|
+
computed_date DATE
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
-- ==========================================================================
|
|
786
|
+
-- 2. INDEXES
|
|
787
|
+
-- ==========================================================================
|
|
788
|
+
|
|
789
|
+
CREATE INDEX idx_users_org ON users(organization_id);
|
|
790
|
+
CREATE INDEX idx_users_email ON users(email);
|
|
791
|
+
CREATE INDEX idx_teams_org ON teams(organization_id);
|
|
792
|
+
CREATE INDEX idx_assets_org ON assets(organization_id);
|
|
793
|
+
CREATE INDEX idx_assets_type ON assets(asset_type);
|
|
794
|
+
CREATE INDEX idx_assets_hostname ON assets(hostname);
|
|
795
|
+
CREATE INDEX idx_agents_asset ON agents(asset_id);
|
|
796
|
+
CREATE INDEX idx_agent_heartbeats_agent ON agent_heartbeats(agent_id);
|
|
797
|
+
CREATE INDEX idx_agent_heartbeats_reported ON agent_heartbeats(reported_at);
|
|
798
|
+
CREATE INDEX idx_scan_results_scan ON scan_results(scan_id);
|
|
799
|
+
CREATE INDEX idx_scan_results_asset ON scan_results(asset_id);
|
|
800
|
+
CREATE INDEX idx_scan_results_vuln ON scan_results(vulnerability_id);
|
|
801
|
+
CREATE INDEX idx_scan_results_found ON scan_results(found_at);
|
|
802
|
+
CREATE INDEX idx_vuln_instances_vuln ON vulnerability_instances(vulnerability_id);
|
|
803
|
+
CREATE INDEX idx_vuln_instances_asset ON vulnerability_instances(asset_id);
|
|
804
|
+
CREATE INDEX idx_alerts_org ON alerts(organization_id);
|
|
805
|
+
CREATE INDEX idx_alerts_created ON alerts(created_at);
|
|
806
|
+
CREATE INDEX idx_incidents_org ON incidents(organization_id);
|
|
807
|
+
CREATE INDEX idx_incidents_status ON incidents(status);
|
|
808
|
+
CREATE INDEX idx_invoices_org ON invoices(organization_id);
|
|
809
|
+
CREATE INDEX idx_subscriptions_org ON subscriptions(organization_id);
|
|
810
|
+
CREATE INDEX idx_compliance_findings_assessment ON compliance_findings(assessment_id);
|
|
811
|
+
CREATE INDEX idx_api_requests_created ON api_requests(created_at);
|
|
812
|
+
CREATE INDEX idx_login_events_user ON login_events(user_id);
|
|
813
|
+
CREATE INDEX idx_audit_log_org ON audit_log(organization_id);
|
|
814
|
+
CREATE INDEX idx_audit_log_created ON audit_log(created_at);
|
|
815
|
+
CREATE INDEX idx_scan_results_denorm_org ON scan_results_denormalized(organization_id);
|
|
816
|
+
CREATE INDEX idx_scan_results_denorm_found ON scan_results_denormalized(found_at);
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
-- ==========================================================================
|
|
820
|
+
-- 3. REFERENCE DATA
|
|
821
|
+
-- ==========================================================================
|
|
822
|
+
|
|
823
|
+
INSERT INTO roles (name, description) VALUES
|
|
824
|
+
('admin', 'Full organization access'),
|
|
825
|
+
('analyst', 'View and query access, can manage scans'),
|
|
826
|
+
('viewer', 'Read-only dashboard access'),
|
|
827
|
+
('responder', 'Incident response and alert management'),
|
|
828
|
+
('auditor', 'Compliance and audit access');
|
|
829
|
+
|
|
830
|
+
INSERT INTO plans (name, display_name, price_cents, max_assets, max_users, features, is_active) VALUES
|
|
831
|
+
('free', 'Free', 0, 25, 3, '{"scans":"weekly","retention":30}', true),
|
|
832
|
+
('starter', 'Starter', 4900, 100, 10, '{"scans":"daily","retention":90}', true),
|
|
833
|
+
('professional', 'Professional', 14900, 500, 50, '{"scans":"continuous","retention":365}', true),
|
|
834
|
+
('enterprise', 'Enterprise', 49900, NULL, NULL,'{"scans":"continuous","retention":730,"sso":true,"api":true}', true),
|
|
835
|
+
('custom', 'Custom', 0, NULL, NULL,'{"custom":true}', true);
|
|
836
|
+
|
|
837
|
+
INSERT INTO compliance_frameworks (name, version, description) VALUES
|
|
838
|
+
('SOC2', 'Type II', 'Service Organization Control 2'),
|
|
839
|
+
('ISO27001', '2022', 'Information Security Management System'),
|
|
840
|
+
('PCI-DSS', 'v4.0', 'Payment Card Industry Data Security Standard'),
|
|
841
|
+
('HIPAA', '2013', 'Health Insurance Portability and Accountability Act'),
|
|
842
|
+
('NIST-CSF', 'v2.0', 'NIST Cybersecurity Framework');
|
|
843
|
+
|
|
844
|
+
INSERT INTO threat_feeds (name, provider, url, feed_type, is_active, last_synced) VALUES
|
|
845
|
+
('AlienVault OTX', 'AlienVault', 'https://otx.alienvault.com', 'ioc', true, now() - interval '2 hours'),
|
|
846
|
+
('Abuse.ch URLhaus', 'abuse.ch', 'https://urlhaus.abuse.ch', 'url', true, now() - interval '4 hours'),
|
|
847
|
+
('MISP Community', 'MISP Project', 'https://misp-project.org', 'ioc', true, now() - interval '1 day'),
|
|
848
|
+
('VirusTotal', 'Google', 'https://virustotal.com', 'hash', true, now() - interval '6 hours'),
|
|
849
|
+
('Shodan', 'Shodan', 'https://shodan.io', 'host', true, now() - interval '12 hours'),
|
|
850
|
+
('CrowdStrike Intel', 'CrowdStrike', NULL, 'threat', true, now() - interval '1 hour'),
|
|
851
|
+
('Recorded Future', 'Recorded Future', NULL, 'threat', true, now() - interval '3 hours'),
|
|
852
|
+
('Mandiant Advantage', 'Mandiant', NULL, 'threat', false, '2024-06-01'::timestamptz),
|
|
853
|
+
('CISA Known Exploited','CISA', 'https://cisa.gov/known-exploited-vulnerabilities', 'vuln', true, now() - interval '8 hours'),
|
|
854
|
+
('PhishTank', 'OpenDNS', 'https://phishtank.org', 'url', true, now() - interval '5 hours');
|
|
855
|
+
|
|
856
|
+
INSERT INTO threat_actors (name, aliases, origin, description, active_since, last_activity) VALUES
|
|
857
|
+
('APT28', 'Fancy Bear, Sofacy, Sednit', 'Russia', 'Russian GRU Unit 26165, military intelligence', '2007-01-01', '2025-06-01'),
|
|
858
|
+
('APT29', 'Cozy Bear, The Dukes, Midnight Blizzard','Russia', 'Russian SVR foreign intelligence service', '2008-01-01', '2025-05-15'),
|
|
859
|
+
('APT41', 'Double Dragon, Barium, Winnti', 'China', 'Chinese state-sponsored, dual espionage and financial crime', '2012-01-01', '2025-04-20'),
|
|
860
|
+
('Lazarus', 'Hidden Cobra, Zinc, Labyrinth Chollima','North Korea', 'North Korean state-sponsored, financial theft and espionage', '2009-01-01', '2025-06-10'),
|
|
861
|
+
('APT1', 'Comment Crew, Shanghai Group', 'China', 'PLA Unit 61398', '2006-01-01', '2023-12-01'),
|
|
862
|
+
('Sandworm', 'Voodoo Bear, Iridium, Seashell Blizzard','Russia', 'Russian GRU Unit 74455, destructive attacks', '2009-01-01', '2025-03-01'),
|
|
863
|
+
('Turla', 'Snake, Venomous Bear, Krypton', 'Russia', 'Russian FSB signals intelligence', '2004-01-01', '2025-01-15'),
|
|
864
|
+
('FIN7', 'Carbanak, Navigator Group', 'Russia', 'Financially motivated, POS malware and ransomware', '2013-01-01', '2025-05-01'),
|
|
865
|
+
('FIN11', 'Clop Gang, TA505', 'Russia', 'Financially motivated, ransomware and extortion', '2016-01-01', '2025-06-01'),
|
|
866
|
+
('Kimsuky', 'Velvet Chollima, Thallium, Black Banshee','North Korea','North Korean intelligence, credential theft and espionage', '2012-01-01', '2025-04-01'),
|
|
867
|
+
('Hafnium', 'Silk Typhoon', 'China', 'Chinese state-sponsored, targeting Exchange servers', '2017-01-01', '2024-12-01'),
|
|
868
|
+
('REvil', 'Sodinokibi', 'Russia', 'Ransomware-as-a-service operation', '2019-04-01', '2024-01-01'),
|
|
869
|
+
('LockBit', 'ABCD Ransomware', 'Russia', 'Ransomware-as-a-service, prolific affiliate program', '2019-09-01', '2025-02-01'),
|
|
870
|
+
('BlackCat', 'ALPHV', 'Russia', 'Rust-based ransomware, triple extortion', '2021-11-01', '2024-12-01'),
|
|
871
|
+
('Scattered Spider','Octo Tempest, UNC3944', 'US/UK', 'Young English-speaking hackers, SIM swapping and social engineering','2022-01-01','2025-05-01'),
|
|
872
|
+
('Volt Typhoon', NULL, 'China', 'Chinese state-sponsored, living-off-the-land, critical infrastructure','2021-01-01','2025-06-01'),
|
|
873
|
+
('MuddyWater','Mercury, Mango Sandstorm', 'Iran', 'Iranian MOIS, espionage targeting Middle East and Central Asia', '2017-01-01', '2025-03-01'),
|
|
874
|
+
('Charming Kitten','APT35, Phosphorus, Mint Sandstorm','Iran', 'Iranian IRGC, spear-phishing and credential harvesting', '2014-01-01', '2025-05-20'),
|
|
875
|
+
('OilRig', 'APT34, Helix Kitten, Hazel Sandstorm', 'Iran', 'Iranian MOIS, targeting energy and government sectors', '2014-01-01', '2025-02-15'),
|
|
876
|
+
('Equation Group', NULL, 'US', 'NSA Tailored Access Operations, sophisticated implants', '2001-01-01', '2023-06-01'),
|
|
877
|
+
('DarkSide', 'BlackMatter', 'Russia', 'Ransomware operation, Colonial Pipeline attack', '2020-08-01', '2022-03-01'),
|
|
878
|
+
('Conti', 'Wizard Spider (partial)', 'Russia', 'Ransomware syndicate, splintered after internal leaks', '2020-05-01', '2023-06-01'),
|
|
879
|
+
('TA577', NULL, 'Unknown', 'Initial access broker, distributes QBot and IcedID', '2020-01-01', '2025-01-01'),
|
|
880
|
+
('Gamaredon', 'Primitive Bear, Armageddon', 'Russia', 'Russian FSB, targeting Ukraine', '2013-01-01', '2025-06-01'),
|
|
881
|
+
('BlueNoroff','APT38, Stardust Chollima', 'North Korea', 'Lazarus subgroup, SWIFT banking theft', '2014-01-01', '2025-04-01'),
|
|
882
|
+
('SilverFox', NULL, 'China', 'Chinese cybercrime group, Winos4.0 framework', '2023-01-01', '2025-03-01'),
|
|
883
|
+
('Akira', NULL, 'Unknown', 'Ransomware operation targeting SMBs and critical infrastructure', '2023-03-01', '2025-06-01'),
|
|
884
|
+
('Play', 'PlayCrypt', 'Unknown', 'Ransomware group, intermittent encryption technique', '2022-06-01', '2025-05-01'),
|
|
885
|
+
('Royal', 'BlackSuit', 'Unknown', 'Ransomware rebranded from Conti splinter', '2022-09-01', '2025-04-01'),
|
|
886
|
+
('Cl0p', NULL, 'Russia', 'Ransomware and extortion, MOVEit and GoAnywhere exploits', '2019-02-01', '2025-06-01'),
|
|
887
|
+
('Rhysida', NULL, 'Unknown', 'Ransomware targeting healthcare and education', '2023-05-01', '2025-05-15'),
|
|
888
|
+
('BianLian', NULL, 'Unknown', 'Ransomware shifted to pure extortion, no encryption', '2022-06-01', '2025-04-01'),
|
|
889
|
+
('NoName057','NoName057(16)', 'Russia', 'Pro-Russian hacktivist, DDoS attacks on NATO allies', '2022-03-01', '2025-06-01'),
|
|
890
|
+
('Anonymous Sudan','Storm-1359', 'Unknown', 'DDoS-as-a-service, politically motivated attacks', '2023-01-01', '2024-10-01'),
|
|
891
|
+
('Killnet', NULL, 'Russia', 'Pro-Russian hacktivist collective, DDoS campaigns', '2022-01-01', '2024-06-01'),
|
|
892
|
+
('Lapsus$', 'DEV-0537', 'UK/Brazil', 'Teenager-led extortion group, high-profile breaches', '2021-12-01', '2023-09-01'),
|
|
893
|
+
('Moses Staff', NULL, 'Iran', 'Iranian hacktivist/APT targeting Israel', '2021-09-01', '2024-08-01'),
|
|
894
|
+
('Witchetty', 'LookingFrog', 'China', 'Chinese APT targeting Middle East and Africa', '2022-02-01', '2024-11-01'),
|
|
895
|
+
('Vice Society', NULL, 'Unknown', 'Ransomware targeting education sector', '2021-06-01', '2024-05-01'),
|
|
896
|
+
('8Base', NULL, 'Unknown', 'Ransomware and extortion, double extortion model', '2022-03-01', '2025-05-01'),
|
|
897
|
+
('Medusa', 'MedusaLocker', 'Unknown', 'Ransomware-as-a-service targeting all sectors', '2022-01-01', '2025-06-01'),
|
|
898
|
+
('INC Ransom', NULL, 'Unknown', 'Ransomware operation with data leak site', '2023-07-01', '2025-04-01'),
|
|
899
|
+
('BlackBasta', NULL, 'Russia', 'Ransomware syndicate, Conti successor', '2022-04-01', '2025-06-01'),
|
|
900
|
+
('Hunters International', NULL, 'Unknown', 'Ransomware using Hive source code after takedown', '2023-10-01', '2025-05-01'),
|
|
901
|
+
('Qilin', 'Agenda', 'Unknown', 'Ransomware-as-a-service, cross-platform variants', '2022-07-01', '2025-06-01'),
|
|
902
|
+
('Storm-0558', NULL, 'China', 'Chinese espionage, forged Azure AD tokens', '2023-05-01', '2024-03-01'),
|
|
903
|
+
('Star Blizzard','Callisto, ColdRiver, SEABORGIUM', 'Russia', 'Russian FSB, credential phishing targeting think tanks', '2019-01-01', '2025-03-01'),
|
|
904
|
+
('Midnight Blizzard', NULL, 'Russia', 'Russian SVR, Microsoft and SolarWinds supply chain compromise', '2008-01-01', '2025-06-01'),
|
|
905
|
+
('Forest Blizzard','Strontium, APT28 rebrand', 'Russia', 'Russian GRU, overlaps with APT28 activity', '2007-01-01', '2025-06-01'),
|
|
906
|
+
('Velvet Ant', NULL, 'China', 'Chinese APT targeting telecom and technology sectors', '2023-06-01', '2025-04-01');
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
-- ==========================================================================
|
|
910
|
+
-- 4. CORE ENTITY DATA
|
|
911
|
+
-- ==========================================================================
|
|
912
|
+
|
|
913
|
+
-- ---------- Organizations (200) ----------
|
|
914
|
+
INSERT INTO organizations (name, industry, size_tier, domain, country, created_at, is_active)
|
|
915
|
+
SELECT
|
|
916
|
+
prefix || ' ' || suffix,
|
|
917
|
+
-- TECH DEBT: Inconsistent industry values
|
|
918
|
+
(ARRAY[
|
|
919
|
+
'Technology','Technology','Technology','Technology','tech','Tech','TECHNOLOGY',
|
|
920
|
+
'Healthcare','Healthcare','Healthcare','healthcare','Health Care',
|
|
921
|
+
'Finance','Finance','Finance',
|
|
922
|
+
'Retail','Retail',
|
|
923
|
+
'Manufacturing','Manufacturing',
|
|
924
|
+
'Energy','Defense','Government'
|
|
925
|
+
])[1 + floor(random() * 22)::int],
|
|
926
|
+
(ARRAY['startup','startup','smb','smb','smb','mid_market','mid_market','mid_market','enterprise','enterprise'])[1 + floor(random() * 10)::int],
|
|
927
|
+
lower(prefix) || lower(suffix) || '.com',
|
|
928
|
+
(ARRAY['US','US','US','US','US','UK','UK','DE','DE','CA','CA','AU','JP','SG','BR','IN','FR','NL','SE','IL'])[1 + floor(random() * 20)::int],
|
|
929
|
+
'2019-01-01'::timestamptz + (random() * interval '1095 days'), -- 2019-2022
|
|
930
|
+
CASE WHEN g <= 170 THEN true ELSE false END -- ~85% active
|
|
931
|
+
FROM (
|
|
932
|
+
SELECT
|
|
933
|
+
g,
|
|
934
|
+
(ARRAY['Apex','Quantum','Vector','Nexus','Cipher','Zenith','Pulse','Stratos','Vortex','Axiom',
|
|
935
|
+
'Helix','Prism','Vertex','Nova','Orbit','Kinetic','Catalyst','Synapse','Cortex','Aegis'])[((g-1) % 20) + 1] AS prefix,
|
|
936
|
+
(ARRAY['Systems','Technologies','Corp','Solutions','Industries','Dynamics','Labs','Networks','Group','Digital'])[((g-1) / 20) + 1] AS suffix
|
|
937
|
+
FROM generate_series(1, 200) AS g
|
|
938
|
+
) AS src;
|
|
939
|
+
|
|
940
|
+
-- Organization settings (1:1)
|
|
941
|
+
INSERT INTO organization_settings (organization_id, notification_email, scan_frequency, retention_days, sso_enabled, mfa_required, settings_json)
|
|
942
|
+
SELECT
|
|
943
|
+
id,
|
|
944
|
+
'security@' || domain,
|
|
945
|
+
(ARRAY['daily','daily','weekly','weekly','weekly','monthly'])[1 + floor(random() * 6)::int],
|
|
946
|
+
(ARRAY[30, 60, 90, 90, 180, 365])[1 + floor(random() * 6)::int],
|
|
947
|
+
size_tier = 'enterprise',
|
|
948
|
+
random() < 0.4,
|
|
949
|
+
'{"timezone":"UTC","language":"en"}'
|
|
950
|
+
FROM organizations;
|
|
951
|
+
|
|
952
|
+
-- ---------- Users (2,000) ----------
|
|
953
|
+
INSERT INTO users (organization_id, organization_name, email, full_name, role, last_login, created_at, is_active)
|
|
954
|
+
SELECT
|
|
955
|
+
org_id,
|
|
956
|
+
(SELECT name FROM organizations WHERE id = org_id),
|
|
957
|
+
lower(first) || '.' || lower(last) || floor(random() * 100)::int || '@' || (SELECT domain FROM organizations WHERE id = org_id),
|
|
958
|
+
first || ' ' || last,
|
|
959
|
+
-- TECH DEBT: legacy role column, still populated, sometimes wrong
|
|
960
|
+
(ARRAY['admin','analyst','analyst','analyst','viewer','viewer','viewer','responder','auditor'])[1 + floor(random() * 9)::int],
|
|
961
|
+
-- TECH DEBT: last_login mostly NULL (~70%)
|
|
962
|
+
CASE WHEN random() < 0.3 THEN now() - (random() * interval '90 days') ELSE NULL END,
|
|
963
|
+
'2019-06-01'::timestamptz + (random() * interval '2000 days'),
|
|
964
|
+
CASE WHEN random() < 0.9 THEN true ELSE false END
|
|
965
|
+
FROM (
|
|
966
|
+
SELECT
|
|
967
|
+
g,
|
|
968
|
+
1 + floor(random() * 200)::int AS org_id,
|
|
969
|
+
(ARRAY['James','Sarah','Michael','Emma','David','Olivia','Robert','Sophia','William','Isabella',
|
|
970
|
+
'Daniel','Mia','Alexander','Charlotte','Ethan','Amelia','Matthew','Harper','Aiden','Evelyn',
|
|
971
|
+
'Lucas','Abigail','Mason','Emily','Logan','Elizabeth','Jackson','Sofia','Liam','Avery'])[1 + floor(random() * 30)::int] AS first,
|
|
972
|
+
(ARRAY['Smith','Johnson','Williams','Brown','Jones','Garcia','Miller','Davis','Rodriguez','Martinez',
|
|
973
|
+
'Hernandez','Lopez','Gonzalez','Wilson','Anderson','Thomas','Taylor','Moore','Jackson','Martin',
|
|
974
|
+
'Lee','Perez','Thompson','White','Harris','Sanchez','Clark','Ramirez','Lewis','Robinson'])[1 + floor(random() * 30)::int] AS last
|
|
975
|
+
FROM generate_series(1, 2000) AS g
|
|
976
|
+
) AS src;
|
|
977
|
+
|
|
978
|
+
-- TECH DEBT: 3 duplicate emails from an org merge
|
|
979
|
+
UPDATE users SET email = 'admin@merged-company.com' WHERE id IN (1, 201);
|
|
980
|
+
UPDATE users SET email = 'ops@merged-company.com' WHERE id IN (50, 250);
|
|
981
|
+
UPDATE users SET email = 'security@merged-company.com' WHERE id IN (100, 300);
|
|
982
|
+
|
|
983
|
+
-- TECH DEBT: ~10% of organization_name values are stale (org renamed but user records not updated)
|
|
984
|
+
UPDATE users SET organization_name = organization_name || ' (old)'
|
|
985
|
+
WHERE id IN (SELECT id FROM users ORDER BY random() LIMIT 200);
|
|
986
|
+
|
|
987
|
+
-- ---------- Teams (400) ----------
|
|
988
|
+
INSERT INTO teams (organization_id, name, description, created_at)
|
|
989
|
+
SELECT
|
|
990
|
+
org_id,
|
|
991
|
+
team_name,
|
|
992
|
+
'Team responsible for ' || lower(team_name),
|
|
993
|
+
'2020-01-01'::timestamptz + (random() * interval '1500 days')
|
|
994
|
+
FROM (
|
|
995
|
+
SELECT
|
|
996
|
+
((g-1) / 2) + 1 AS org_id,
|
|
997
|
+
CASE WHEN g % 2 = 1
|
|
998
|
+
THEN (ARRAY['Security Operations','Threat Hunting','Vulnerability Management','Incident Response','Compliance'])[1 + floor(random() * 5)::int]
|
|
999
|
+
ELSE (ARRAY['Platform Engineering','Security Research','Risk Assessment','Detection Engineering','Red Team'])[1 + floor(random() * 5)::int]
|
|
1000
|
+
END AS team_name
|
|
1001
|
+
FROM generate_series(1, 400) AS g
|
|
1002
|
+
) AS src;
|
|
1003
|
+
|
|
1004
|
+
-- ---------- Team Memberships (3,000) ----------
|
|
1005
|
+
INSERT INTO team_memberships (user_id, team_id, role_id, joined_at)
|
|
1006
|
+
SELECT DISTINCT ON (user_id, team_id)
|
|
1007
|
+
user_id,
|
|
1008
|
+
team_id,
|
|
1009
|
+
1 + floor(random() * 5)::int,
|
|
1010
|
+
'2020-06-01'::timestamptz + (random() * interval '1500 days')
|
|
1011
|
+
FROM (
|
|
1012
|
+
SELECT
|
|
1013
|
+
1 + floor(random() * 2000)::int AS user_id,
|
|
1014
|
+
1 + floor(random() * 400)::int AS team_id
|
|
1015
|
+
FROM generate_series(1, 3500) AS g -- generate extra, DISTINCT removes dupes
|
|
1016
|
+
) AS src;
|
|
1017
|
+
|
|
1018
|
+
-- ---------- Invitations (150) ----------
|
|
1019
|
+
INSERT INTO invitations (organization_id, email, role_name, invited_by, status, created_at, expires_at)
|
|
1020
|
+
SELECT
|
|
1021
|
+
1 + floor(random() * 200)::int,
|
|
1022
|
+
'invite' || g || '@example.com',
|
|
1023
|
+
(ARRAY['analyst','viewer','responder'])[1 + floor(random() * 3)::int],
|
|
1024
|
+
1 + floor(random() * 2000)::int,
|
|
1025
|
+
CASE
|
|
1026
|
+
WHEN g <= 80 THEN 'pending'
|
|
1027
|
+
WHEN g <= 120 THEN 'accepted'
|
|
1028
|
+
ELSE 'expired'
|
|
1029
|
+
END,
|
|
1030
|
+
now() - (random() * interval '90 days'),
|
|
1031
|
+
now() + ((random() - 0.5) * interval '60 days') -- some already expired
|
|
1032
|
+
FROM generate_series(1, 150) AS g;
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
-- ==========================================================================
|
|
1036
|
+
-- 5. BILLING DATA
|
|
1037
|
+
-- ==========================================================================
|
|
1038
|
+
|
|
1039
|
+
-- ---------- Subscriptions (250) ----------
|
|
1040
|
+
-- Some orgs have had multiple subscriptions (plan changes)
|
|
1041
|
+
INSERT INTO subscriptions (organization_id, plan_id, plan_name, status, mrr_cents, started_at, current_period_end, created_at)
|
|
1042
|
+
SELECT
|
|
1043
|
+
org_id,
|
|
1044
|
+
plan_id,
|
|
1045
|
+
-- TECH DEBT: plan_name denormalized, ~15% out of sync with plans.name
|
|
1046
|
+
CASE
|
|
1047
|
+
WHEN random() < 0.15 THEN (ARRAY['starter','Starter Plan','pro','Professional','ent','Enterprise','free_tier'])[1 + floor(random() * 7)::int]
|
|
1048
|
+
ELSE (SELECT name FROM plans WHERE id = plan_id)
|
|
1049
|
+
END,
|
|
1050
|
+
(ARRAY['active','active','active','active','active','active','active','canceled','past_due','trialing'])[1 + floor(random() * 10)::int],
|
|
1051
|
+
CASE plan_id
|
|
1052
|
+
WHEN 1 THEN 0
|
|
1053
|
+
WHEN 2 THEN 4900
|
|
1054
|
+
WHEN 3 THEN 14900
|
|
1055
|
+
WHEN 4 THEN 49900
|
|
1056
|
+
WHEN 5 THEN 25000 + floor(random() * 75000)::int
|
|
1057
|
+
END,
|
|
1058
|
+
'2019-06-01'::timestamptz + (random() * interval '1800 days'),
|
|
1059
|
+
now() + (random() * interval '365 days'),
|
|
1060
|
+
'2019-06-01'::timestamptz + (random() * interval '1800 days')
|
|
1061
|
+
FROM (
|
|
1062
|
+
SELECT
|
|
1063
|
+
g,
|
|
1064
|
+
1 + floor(random() * 200)::int AS org_id,
|
|
1065
|
+
1 + floor(random() * 5)::int AS plan_id
|
|
1066
|
+
FROM generate_series(1, 250) AS g
|
|
1067
|
+
) AS src;
|
|
1068
|
+
|
|
1069
|
+
-- ---------- Subscription Events (1,200) ----------
|
|
1070
|
+
INSERT INTO subscription_events (subscription_id, event_type, old_plan_id, new_plan_id, created_at)
|
|
1071
|
+
SELECT
|
|
1072
|
+
1 + floor(random() * 250)::int,
|
|
1073
|
+
(ARRAY['upgrade','upgrade','upgrade','downgrade','cancel','renew','renew','renew'])[1 + floor(random() * 8)::int],
|
|
1074
|
+
1 + floor(random() * 5)::int,
|
|
1075
|
+
1 + floor(random() * 5)::int,
|
|
1076
|
+
'2020-01-01'::timestamptz + (power(random(), 0.5) * interval '1825 days')
|
|
1077
|
+
FROM generate_series(1, 1200) AS g;
|
|
1078
|
+
|
|
1079
|
+
-- ---------- Invoices (3,000) ----------
|
|
1080
|
+
INSERT INTO invoices (organization_id, subscription_id, amount_cents, status, period_start, period_end, due_date, paid_at, created_at)
|
|
1081
|
+
SELECT
|
|
1082
|
+
1 + floor(random() * 200)::int,
|
|
1083
|
+
1 + floor(random() * 250)::int,
|
|
1084
|
+
(ARRAY[4900, 14900, 49900, 4900, 14900])[1 + floor(random() * 5)::int],
|
|
1085
|
+
(ARRAY['paid','paid','paid','paid','paid','paid','paid','paid','open','void'])[1 + floor(random() * 10)::int],
|
|
1086
|
+
period_start,
|
|
1087
|
+
period_start + interval '1 month',
|
|
1088
|
+
period_start + interval '1 month' + interval '15 days',
|
|
1089
|
+
CASE WHEN random() < 0.85 THEN period_start + (random() * interval '30 days') ELSE NULL END,
|
|
1090
|
+
period_start
|
|
1091
|
+
FROM (
|
|
1092
|
+
SELECT
|
|
1093
|
+
g,
|
|
1094
|
+
('2020-01-01'::date + ((g * 3) % 1800) * interval '1 day')::date AS period_start
|
|
1095
|
+
FROM generate_series(1, 3000) AS g
|
|
1096
|
+
) AS src;
|
|
1097
|
+
|
|
1098
|
+
-- ---------- Invoice Line Items (8,000) ----------
|
|
1099
|
+
INSERT INTO invoice_line_items (invoice_id, subscription_id, description, amount_cents, quantity)
|
|
1100
|
+
SELECT
|
|
1101
|
+
1 + floor(random() * 3000)::int,
|
|
1102
|
+
-- TECH DEBT: no FK to subscriptions
|
|
1103
|
+
CASE WHEN random() < 0.9 THEN 1 + floor(random() * 250)::int ELSE NULL END,
|
|
1104
|
+
(ARRAY['Platform subscription','Asset overage','API call overage','Premium support','Compliance module',
|
|
1105
|
+
'Threat intel add-on','SSO add-on','Extended retention'])[1 + floor(random() * 8)::int],
|
|
1106
|
+
(ARRAY[4900, 14900, 500, 2500, 9900, 4900, 2900, 1900])[1 + floor(random() * 8)::int],
|
|
1107
|
+
1
|
|
1108
|
+
FROM generate_series(1, 8000) AS g;
|
|
1109
|
+
|
|
1110
|
+
-- ---------- Payment Methods (180) ----------
|
|
1111
|
+
INSERT INTO payment_methods (organization_id, type, last4, expiry_month, expiry_year, is_default, created_at)
|
|
1112
|
+
SELECT
|
|
1113
|
+
1 + floor(random() * 200)::int,
|
|
1114
|
+
(ARRAY['credit_card','credit_card','credit_card','credit_card','bank_transfer','wire'])[1 + floor(random() * 6)::int],
|
|
1115
|
+
lpad(floor(random() * 10000)::int::text, 4, '0'),
|
|
1116
|
+
1 + floor(random() * 12)::int,
|
|
1117
|
+
2025 + floor(random() * 4)::int,
|
|
1118
|
+
CASE WHEN g <= 170 THEN true ELSE false END,
|
|
1119
|
+
'2020-01-01'::timestamptz + (random() * interval '1500 days')
|
|
1120
|
+
FROM generate_series(1, 180) AS g;
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
-- ==========================================================================
|
|
1124
|
+
-- 6. PRODUCT DATA — Assets
|
|
1125
|
+
-- ==========================================================================
|
|
1126
|
+
|
|
1127
|
+
-- ---------- Assets (15,000) ----------
|
|
1128
|
+
INSERT INTO assets (organization_id, hostname, display_name, asset_type, ip_address, os, os_version, environment, is_active, first_seen, created_at)
|
|
1129
|
+
SELECT
|
|
1130
|
+
1 + floor(random() * 200)::int,
|
|
1131
|
+
prefix || '-' || lpad(g::text, 5, '0') || '.' || suffix,
|
|
1132
|
+
-- TECH DEBT: display_name added 2023, NULL for ~40%
|
|
1133
|
+
CASE WHEN random() < 0.6 THEN prefix || '-' || lpad(g::text, 5, '0') || ' (' || env || ')' ELSE NULL END,
|
|
1134
|
+
-- TECH DEBT: asset_type expanded but old data not migrated
|
|
1135
|
+
CASE
|
|
1136
|
+
WHEN g <= 5000 THEN (ARRAY['server','endpoint','network'])[1 + floor(random() * 3)::int] -- old types only
|
|
1137
|
+
ELSE (ARRAY['server','endpoint','network','cloud_vm','cloud_vm','container','container','serverless'])[1 + floor(random() * 8)::int]
|
|
1138
|
+
END,
|
|
1139
|
+
(192 + floor(random() * 3)::int)::text || '.' || (168 + floor(random() * 2)::int)::text || '.' ||
|
|
1140
|
+
floor(random() * 256)::int::text || '.' || floor(random() * 256)::int::text,
|
|
1141
|
+
(ARRAY['Ubuntu','CentOS','Windows Server','RHEL','Amazon Linux','Alpine','Debian','macOS'])[1 + floor(random() * 8)::int],
|
|
1142
|
+
(ARRAY['20.04','22.04','7','8','2019','2022','3.18','12','14'])[1 + floor(random() * 9)::int],
|
|
1143
|
+
env,
|
|
1144
|
+
CASE WHEN random() < 0.92 THEN true ELSE false END,
|
|
1145
|
+
'2019-06-01'::timestamptz + (power(random(), 0.6) * interval '2000 days'),
|
|
1146
|
+
'2019-06-01'::timestamptz + (power(random(), 0.6) * interval '2000 days')
|
|
1147
|
+
FROM (
|
|
1148
|
+
SELECT
|
|
1149
|
+
g,
|
|
1150
|
+
(ARRAY['web','app','db','cache','queue','api','worker','proxy','monitor','bastion',
|
|
1151
|
+
'vpn','dns','mail','ldap','jump','ci','log','scan','backup','dev'])[1 + floor(random() * 20)::int] AS prefix,
|
|
1152
|
+
(ARRAY['prod.sentinel.local','staging.sentinel.local','dev.sentinel.local',
|
|
1153
|
+
'us-east.internal','eu-west.internal','ap-south.internal'])[1 + floor(random() * 6)::int] AS suffix,
|
|
1154
|
+
(ARRAY['production','production','production','staging','development','dmz'])[1 + floor(random() * 6)::int] AS env
|
|
1155
|
+
FROM generate_series(1, 15000) AS g
|
|
1156
|
+
) AS src;
|
|
1157
|
+
|
|
1158
|
+
-- Set last_seen for active assets
|
|
1159
|
+
UPDATE assets SET last_seen = now() - (random() * interval '7 days') WHERE is_active = true;
|
|
1160
|
+
UPDATE assets SET last_seen = created_at + (random() * interval '365 days') WHERE is_active = false;
|
|
1161
|
+
|
|
1162
|
+
-- ---------- Asset Groups (600) ----------
|
|
1163
|
+
INSERT INTO asset_groups (organization_id, name, description, created_at)
|
|
1164
|
+
SELECT
|
|
1165
|
+
org_id,
|
|
1166
|
+
group_name || ' - Org ' || org_id,
|
|
1167
|
+
'Asset group for ' || lower(group_name) || ' assets',
|
|
1168
|
+
'2020-01-01'::timestamptz + (random() * interval '1500 days')
|
|
1169
|
+
FROM (
|
|
1170
|
+
SELECT
|
|
1171
|
+
g,
|
|
1172
|
+
((g - 1) / 3) + 1 AS org_id,
|
|
1173
|
+
(ARRAY['Production Servers','Staging Environment','DMZ','Cloud Infrastructure',
|
|
1174
|
+
'Endpoints','Network Devices','Critical Assets','Development',
|
|
1175
|
+
'Database Tier','Web Tier'])[((g - 1) % 3) + 1] AS group_name
|
|
1176
|
+
FROM generate_series(1, 600) AS g
|
|
1177
|
+
) AS src;
|
|
1178
|
+
|
|
1179
|
+
-- ---------- Asset Group Memberships (20,000) ----------
|
|
1180
|
+
INSERT INTO asset_group_memberships (asset_id, asset_group_id, added_at)
|
|
1181
|
+
SELECT DISTINCT ON (asset_id, asset_group_id)
|
|
1182
|
+
asset_id,
|
|
1183
|
+
asset_group_id,
|
|
1184
|
+
'2020-06-01'::timestamptz + (random() * interval '1500 days')
|
|
1185
|
+
FROM (
|
|
1186
|
+
SELECT
|
|
1187
|
+
1 + floor(random() * 15000)::int AS asset_id,
|
|
1188
|
+
1 + floor(random() * 600)::int AS asset_group_id
|
|
1189
|
+
FROM generate_series(1, 25000) AS g
|
|
1190
|
+
) AS src;
|
|
1191
|
+
|
|
1192
|
+
-- ---------- Asset Tags (35,000 — EAV pattern) ----------
|
|
1193
|
+
INSERT INTO asset_tags (asset_id, key, value, created_at)
|
|
1194
|
+
SELECT
|
|
1195
|
+
1 + floor(random() * 15000)::int,
|
|
1196
|
+
tag_key,
|
|
1197
|
+
tag_value,
|
|
1198
|
+
'2020-01-01'::timestamptz + (random() * interval '1800 days')
|
|
1199
|
+
FROM (
|
|
1200
|
+
SELECT
|
|
1201
|
+
g,
|
|
1202
|
+
(ARRAY['env','team','cost_center','compliance','owner','project','region','tier','criticality','backup_policy'])[1 + floor(random() * 10)::int] AS tag_key,
|
|
1203
|
+
(ARRAY['production','staging','dev','soc','infra','platform','us-east-1','eu-west-1','tier-1','tier-2',
|
|
1204
|
+
'daily','weekly','team-alpha','team-bravo','cc-1001','cc-2002','pci','hipaa','soc2','high','medium','low'])[1 + floor(random() * 22)::int] AS tag_value
|
|
1205
|
+
FROM generate_series(1, 35000) AS g
|
|
1206
|
+
) AS src;
|
|
1207
|
+
|
|
1208
|
+
-- ---------- Agents (12,000) ----------
|
|
1209
|
+
-- ~200 agents will reference non-existent assets (orphans)
|
|
1210
|
+
INSERT INTO agents (asset_id, agent_version, status, last_heartbeat, installed_at, created_at)
|
|
1211
|
+
SELECT
|
|
1212
|
+
CASE
|
|
1213
|
+
WHEN g <= 11800 THEN 1 + floor(random() * 15000)::int -- valid asset references
|
|
1214
|
+
ELSE 15001 + (g - 11800) -- TECH DEBT: orphaned references
|
|
1215
|
+
END,
|
|
1216
|
+
'v' || (ARRAY['2.1.0','2.2.0','2.3.1','2.4.0','2.5.0','3.0.0','3.0.1','3.1.0'])[1 + floor(random() * 8)::int],
|
|
1217
|
+
(ARRAY['active','active','active','active','active','active','inactive','disconnected','error'])[1 + floor(random() * 9)::int],
|
|
1218
|
+
CASE WHEN random() < 0.8 THEN now() - (random() * interval '24 hours') ELSE now() - (random() * interval '30 days') END,
|
|
1219
|
+
'2020-01-01'::timestamptz + (power(random(), 0.5) * interval '1800 days'),
|
|
1220
|
+
'2020-01-01'::timestamptz + (power(random(), 0.5) * interval '1800 days')
|
|
1221
|
+
FROM generate_series(1, 12000) AS g;
|
|
1222
|
+
|
|
1223
|
+
-- ---------- Agent Heartbeats (50,000 — last 30 days) ----------
|
|
1224
|
+
INSERT INTO agent_heartbeats (agent_id, cpu_percent, memory_percent, disk_percent, reported_at)
|
|
1225
|
+
SELECT
|
|
1226
|
+
1 + floor(random() * 12000)::int, -- TECH DEBT: no FK
|
|
1227
|
+
round((random() * 100)::numeric, 1),
|
|
1228
|
+
round((30 + random() * 60)::numeric, 1),
|
|
1229
|
+
round((20 + random() * 70)::numeric, 1),
|
|
1230
|
+
now() - (random() * interval '30 days')
|
|
1231
|
+
FROM generate_series(1, 50000) AS g;
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
-- ==========================================================================
|
|
1235
|
+
-- 7. PRODUCT DATA — Vulnerabilities & Scans
|
|
1236
|
+
-- ==========================================================================
|
|
1237
|
+
|
|
1238
|
+
-- ---------- Vulnerabilities (500) ----------
|
|
1239
|
+
INSERT INTO vulnerabilities (cve_id, title, description, severity, cvss_score, category, published_at, created_at)
|
|
1240
|
+
SELECT
|
|
1241
|
+
'CVE-' || (2019 + (g % 7)) || '-' || lpad((g * 97 % 50000 + 10000)::text, 5, '0'),
|
|
1242
|
+
vuln_title,
|
|
1243
|
+
'Detailed description of ' || vuln_title || '. Affects multiple components and requires immediate attention.',
|
|
1244
|
+
sev,
|
|
1245
|
+
-- TECH DEBT: cvss_score NULL for ~30% of older vulns (published before 2022)
|
|
1246
|
+
CASE
|
|
1247
|
+
WHEN pub_year < 2022 AND random() < 0.5 THEN NULL
|
|
1248
|
+
ELSE CASE sev
|
|
1249
|
+
WHEN 'critical' THEN 9.0 + random()
|
|
1250
|
+
WHEN 'high' THEN 7.0 + random() * 2
|
|
1251
|
+
WHEN 'medium' THEN 4.0 + random() * 3
|
|
1252
|
+
WHEN 'low' THEN 0.1 + random() * 3.9
|
|
1253
|
+
END
|
|
1254
|
+
END,
|
|
1255
|
+
cat,
|
|
1256
|
+
(pub_year || '-01-01')::date + floor(random() * 365)::int,
|
|
1257
|
+
(pub_year || '-01-01')::timestamptz + floor(random() * 365)::int * interval '1 day'
|
|
1258
|
+
FROM (
|
|
1259
|
+
SELECT
|
|
1260
|
+
g,
|
|
1261
|
+
(ARRAY['SQL Injection in','Cross-Site Scripting via','Buffer Overflow in','Remote Code Execution in',
|
|
1262
|
+
'Privilege Escalation via','Path Traversal in','Authentication Bypass in','Denial of Service in',
|
|
1263
|
+
'Information Disclosure via','Insecure Deserialization in','Server-Side Request Forgery in',
|
|
1264
|
+
'XML External Entity in','Command Injection in','Broken Access Control in',
|
|
1265
|
+
'Cryptographic Weakness in'])[1 + floor(random() * 15)::int]
|
|
1266
|
+
|| ' ' ||
|
|
1267
|
+
(ARRAY['Apache HTTP Server','OpenSSL','nginx','Log4j','Spring Framework','WordPress','PHP',
|
|
1268
|
+
'Node.js Express','Django','Ruby on Rails','PostgreSQL','MySQL','Docker Engine',
|
|
1269
|
+
'Kubernetes API','Redis','Elasticsearch','Jenkins','GitLab','Grafana','Terraform'])[1 + floor(random() * 20)::int]
|
|
1270
|
+
AS vuln_title,
|
|
1271
|
+
(ARRAY['low','low','medium','medium','medium','high','high','high','critical','critical'])[1 + floor(random() * 10)::int] AS sev,
|
|
1272
|
+
(ARRAY['injection','xss','overflow','rce','privilege_escalation','disclosure','config','crypto','access_control','dos'])[1 + floor(random() * 10)::int] AS cat,
|
|
1273
|
+
2019 + floor(random() * 7)::int AS pub_year
|
|
1274
|
+
FROM generate_series(1, 500) AS g
|
|
1275
|
+
) AS src;
|
|
1276
|
+
|
|
1277
|
+
-- ---------- Scan Configurations (100) ----------
|
|
1278
|
+
INSERT INTO scan_configurations (organization_id, name, scan_type, target_spec, schedule, is_active, created_at)
|
|
1279
|
+
SELECT
|
|
1280
|
+
1 + floor(random() * 200)::int,
|
|
1281
|
+
scan_type_name || ' Scan - Config ' || g,
|
|
1282
|
+
scan_type_val,
|
|
1283
|
+
'192.168.' || floor(random() * 256)::int || '.0/24',
|
|
1284
|
+
(ARRAY['0 2 * * *','0 3 * * 0','0 0 1 * *','0 */6 * * *'])[1 + floor(random() * 4)::int],
|
|
1285
|
+
random() < 0.85,
|
|
1286
|
+
'2020-01-01'::timestamptz + (random() * interval '1500 days')
|
|
1287
|
+
FROM (
|
|
1288
|
+
SELECT
|
|
1289
|
+
g,
|
|
1290
|
+
(ARRAY['Full','Quick','Targeted','Compliance'])[1 + floor(random() * 4)::int] AS scan_type_name,
|
|
1291
|
+
(ARRAY['full','quick','targeted','compliance'])[1 + floor(random() * 4)::int] AS scan_type_val
|
|
1292
|
+
FROM generate_series(1, 100) AS g
|
|
1293
|
+
) AS src;
|
|
1294
|
+
|
|
1295
|
+
-- ---------- Scans (5,000) ----------
|
|
1296
|
+
INSERT INTO scans (organization_id, scan_configuration_id, scan_type, status, started_at, completed_at, assets_scanned, findings_count, created_at)
|
|
1297
|
+
SELECT
|
|
1298
|
+
1 + floor(random() * 200)::int,
|
|
1299
|
+
CASE WHEN random() < 0.7 THEN 1 + floor(random() * 100)::int ELSE NULL END,
|
|
1300
|
+
(ARRAY['full','full','quick','quick','targeted','compliance'])[1 + floor(random() * 6)::int],
|
|
1301
|
+
(ARRAY['completed','completed','completed','completed','completed','completed','completed','running','failed','queued'])[1 + floor(random() * 10)::int],
|
|
1302
|
+
start_ts,
|
|
1303
|
+
CASE WHEN random() < 0.85 THEN start_ts + (random() * interval '4 hours') ELSE NULL END,
|
|
1304
|
+
floor(random() * 500 + 10)::int,
|
|
1305
|
+
floor(random() * 200)::int,
|
|
1306
|
+
start_ts
|
|
1307
|
+
FROM (
|
|
1308
|
+
SELECT
|
|
1309
|
+
g,
|
|
1310
|
+
'2020-01-01'::timestamptz + (power(random(), 0.4) * interval '1825 days') AS start_ts
|
|
1311
|
+
FROM generate_series(1, 5000) AS g
|
|
1312
|
+
) AS src;
|
|
1313
|
+
|
|
1314
|
+
-- ---------- Scan Results (80,000 — LARGEST TABLE) ----------
|
|
1315
|
+
INSERT INTO scan_results (scan_id, asset_id, vulnerability_id, risk_level, status, details, found_at, resolved_at)
|
|
1316
|
+
SELECT
|
|
1317
|
+
1 + floor(random() * 5000)::int, -- TECH DEBT: no FK
|
|
1318
|
+
CASE
|
|
1319
|
+
WHEN g <= 79500 THEN 1 + floor(random() * 15000)::int
|
|
1320
|
+
ELSE 15001 + floor(random() * 500)::int -- TECH DEBT: ~500 orphan asset references
|
|
1321
|
+
END,
|
|
1322
|
+
1 + floor(random() * 500)::int, -- TECH DEBT: no FK
|
|
1323
|
+
-- TECH DEBT: risk_level scale changed mid-2024. First 40K rows = 1-5, last 40K = 1-10
|
|
1324
|
+
CASE
|
|
1325
|
+
WHEN g <= 40000 THEN 1 + floor(random() * 5)::int
|
|
1326
|
+
ELSE 1 + floor(random() * 10)::int
|
|
1327
|
+
END,
|
|
1328
|
+
(ARRAY['open','open','open','fixed','fixed','in_progress','accepted_risk','false_positive'])[1 + floor(random() * 8)::int],
|
|
1329
|
+
CASE WHEN random() < 0.1 THEN 'Auto-detected during scheduled scan' ELSE NULL END,
|
|
1330
|
+
found_ts,
|
|
1331
|
+
CASE WHEN random() < 0.35 THEN found_ts + (random() * interval '90 days') ELSE NULL END
|
|
1332
|
+
FROM (
|
|
1333
|
+
SELECT
|
|
1334
|
+
g,
|
|
1335
|
+
'2020-01-01'::timestamptz + (power(g::float / 80000, 1.0) * interval '1825 days') AS found_ts
|
|
1336
|
+
FROM generate_series(1, 80000) AS g
|
|
1337
|
+
) AS src;
|
|
1338
|
+
|
|
1339
|
+
-- ---------- Vulnerability Instances (40,000) ----------
|
|
1340
|
+
INSERT INTO vulnerability_instances (vulnerability_id, asset_id, scan_result_id, status, first_seen, last_seen, created_at)
|
|
1341
|
+
SELECT
|
|
1342
|
+
1 + floor(random() * 500)::int,
|
|
1343
|
+
1 + floor(random() * 15000)::int,
|
|
1344
|
+
1 + floor(random() * 80000)::int, -- TECH DEBT: no FK
|
|
1345
|
+
(ARRAY['open','open','open','fixed','in_progress','accepted_risk'])[1 + floor(random() * 6)::int],
|
|
1346
|
+
first_ts,
|
|
1347
|
+
first_ts + (random() * interval '180 days'),
|
|
1348
|
+
first_ts
|
|
1349
|
+
FROM (
|
|
1350
|
+
SELECT
|
|
1351
|
+
g,
|
|
1352
|
+
'2020-06-01'::timestamptz + (power(random(), 0.5) * interval '1700 days') AS first_ts
|
|
1353
|
+
FROM generate_series(1, 40000) AS g
|
|
1354
|
+
) AS src;
|
|
1355
|
+
|
|
1356
|
+
-- ---------- Remediation Actions (8,000) ----------
|
|
1357
|
+
INSERT INTO remediation_actions (vulnerability_instance_id, assigned_to, status, priority, due_date, completed_at, created_at)
|
|
1358
|
+
SELECT
|
|
1359
|
+
1 + floor(random() * 40000)::int,
|
|
1360
|
+
1 + floor(random() * 2000)::int,
|
|
1361
|
+
(ARRAY['open','open','in_progress','in_progress','completed','completed','completed','deferred'])[1 + floor(random() * 8)::int],
|
|
1362
|
+
(ARRAY['critical','high','high','medium','medium','medium','low','low'])[1 + floor(random() * 8)::int],
|
|
1363
|
+
(now() + (random() - 0.3) * interval '90 days')::date,
|
|
1364
|
+
CASE WHEN random() < 0.4 THEN now() - (random() * interval '60 days') ELSE NULL END,
|
|
1365
|
+
'2021-01-01'::timestamptz + (power(random(), 0.5) * interval '1500 days')
|
|
1366
|
+
FROM generate_series(1, 8000) AS g;
|
|
1367
|
+
|
|
1368
|
+
-- ---------- Vulnerability Exceptions (500) ----------
|
|
1369
|
+
INSERT INTO vulnerability_exceptions (vulnerability_id, organization_id, requested_by, reason, status, expires_at, created_at)
|
|
1370
|
+
SELECT
|
|
1371
|
+
1 + floor(random() * 500)::int,
|
|
1372
|
+
1 + floor(random() * 200)::int,
|
|
1373
|
+
1 + floor(random() * 2000)::int,
|
|
1374
|
+
(ARRAY['Compensating control in place','False positive — not applicable to our stack',
|
|
1375
|
+
'Vendor patch pending, ETA 30 days','Risk accepted by CISO',
|
|
1376
|
+
'Mitigated by WAF rule','Legacy system, scheduled for decommission',
|
|
1377
|
+
'Low impact in our environment'])[1 + floor(random() * 7)::int],
|
|
1378
|
+
(ARRAY['approved','approved','approved','pending','pending','denied','expired'])[1 + floor(random() * 7)::int],
|
|
1379
|
+
now() + ((random() - 0.3) * interval '365 days'),
|
|
1380
|
+
'2021-01-01'::timestamptz + (random() * interval '1500 days')
|
|
1381
|
+
FROM generate_series(1, 500) AS g;
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
-- ==========================================================================
|
|
1385
|
+
-- 8. THREAT & INCIDENT DATA
|
|
1386
|
+
-- ==========================================================================
|
|
1387
|
+
|
|
1388
|
+
-- ---------- Incidents (1,500) ----------
|
|
1389
|
+
INSERT INTO incidents (organization_id, organization_name, title, description, severity, status, assigned_to, created_at, updated_at, resolved_at)
|
|
1390
|
+
SELECT
|
|
1391
|
+
org_id,
|
|
1392
|
+
(SELECT name FROM organizations WHERE id = org_id),
|
|
1393
|
+
incident_title,
|
|
1394
|
+
'Investigation into ' || lower(incident_title) || '. Multiple indicators observed.',
|
|
1395
|
+
(ARRAY['critical','critical','high','high','high','medium','medium','medium','low','low'])[1 + floor(random() * 10)::int],
|
|
1396
|
+
-- TECH DEBT: 'identified'/'monitoring' overlap, 'closed' deprecated but still present
|
|
1397
|
+
CASE
|
|
1398
|
+
WHEN g <= 50 THEN 'closed' -- old terminal state, deprecated
|
|
1399
|
+
WHEN g <= 300 THEN 'resolved' -- current terminal state
|
|
1400
|
+
WHEN g <= 400 THEN 'monitoring' -- overlaps with 'identified'
|
|
1401
|
+
WHEN g <= 500 THEN 'identified' -- overlaps with 'monitoring'
|
|
1402
|
+
WHEN g <= 900 THEN 'investigating'
|
|
1403
|
+
ELSE 'open'
|
|
1404
|
+
END,
|
|
1405
|
+
CASE WHEN random() < 0.7 THEN 1 + floor(random() * 2000)::int ELSE NULL END,
|
|
1406
|
+
created_ts,
|
|
1407
|
+
created_ts + (random() * interval '30 days'),
|
|
1408
|
+
CASE WHEN g <= 350 THEN created_ts + (random() * interval '14 days') ELSE NULL END
|
|
1409
|
+
FROM (
|
|
1410
|
+
SELECT
|
|
1411
|
+
g,
|
|
1412
|
+
1 + floor(random() * 200)::int AS org_id,
|
|
1413
|
+
(ARRAY['Suspicious login activity from','Malware detected on','Unauthorized access attempt to',
|
|
1414
|
+
'Data exfiltration alert for','Brute force attack against','Phishing campaign targeting',
|
|
1415
|
+
'Ransomware indicator found on','Lateral movement detected in','Privilege escalation on',
|
|
1416
|
+
'DDoS attack targeting','Credential stuffing against','Supply chain compromise in',
|
|
1417
|
+
'Insider threat activity on','Zero-day exploit attempt on','C2 beacon detected from'])[1 + floor(random() * 15)::int]
|
|
1418
|
+
|| ' ' || (ARRAY['production servers','customer portal','internal network','cloud infrastructure',
|
|
1419
|
+
'email gateway','VPN endpoint','database cluster','CI/CD pipeline',
|
|
1420
|
+
'admin console','API gateway'])[1 + floor(random() * 10)::int]
|
|
1421
|
+
AS incident_title,
|
|
1422
|
+
'2020-01-01'::timestamptz + (power(random(), 0.4) * interval '1825 days') AS created_ts
|
|
1423
|
+
FROM generate_series(1, 1500) AS g
|
|
1424
|
+
) AS src;
|
|
1425
|
+
|
|
1426
|
+
-- ---------- Incident Events (6,000) ----------
|
|
1427
|
+
INSERT INTO incident_events (incident_id, event_type, description, created_by, created_at)
|
|
1428
|
+
SELECT
|
|
1429
|
+
1 + floor(random() * 1500)::int,
|
|
1430
|
+
(ARRAY['created','assigned','status_change','comment','evidence_added','escalated',
|
|
1431
|
+
'notification_sent','playbook_executed','containment_action','resolution'])[1 + floor(random() * 10)::int],
|
|
1432
|
+
'Event recorded during incident investigation',
|
|
1433
|
+
1 + floor(random() * 2000)::int,
|
|
1434
|
+
'2020-06-01'::timestamptz + (power(random(), 0.4) * interval '1700 days')
|
|
1435
|
+
FROM generate_series(1, 6000) AS g;
|
|
1436
|
+
|
|
1437
|
+
-- ---------- Incident Comments (3,500) ----------
|
|
1438
|
+
INSERT INTO incident_comments (incident_id, user_id, content, created_at)
|
|
1439
|
+
SELECT
|
|
1440
|
+
1 + floor(random() * 1500)::int,
|
|
1441
|
+
1 + floor(random() * 2000)::int,
|
|
1442
|
+
(ARRAY['Confirmed malicious activity. Escalating to tier 2.',
|
|
1443
|
+
'False positive — benign automation script.',
|
|
1444
|
+
'Containment actions in progress. Affected systems isolated.',
|
|
1445
|
+
'Root cause identified: unpatched vulnerability CVE-2024-XXXX.',
|
|
1446
|
+
'Customer notified per SLA requirements.',
|
|
1447
|
+
'Forensic image captured for evidence preservation.',
|
|
1448
|
+
'IOCs extracted and shared with threat intel team.',
|
|
1449
|
+
'Playbook step 3 complete. Moving to eradication.',
|
|
1450
|
+
'All affected credentials rotated.',
|
|
1451
|
+
'Monitoring for recurrence. Will close after 48h clear window.'])[1 + floor(random() * 10)::int],
|
|
1452
|
+
'2020-06-01'::timestamptz + (power(random(), 0.4) * interval '1700 days')
|
|
1453
|
+
FROM generate_series(1, 3500) AS g;
|
|
1454
|
+
|
|
1455
|
+
-- ---------- Alert Rules (200) ----------
|
|
1456
|
+
INSERT INTO alert_rules (organization_id, name, condition_type, threshold, severity, is_active, created_at)
|
|
1457
|
+
SELECT
|
|
1458
|
+
1 + floor(random() * 200)::int,
|
|
1459
|
+
rule_name || ' - Rule ' || g,
|
|
1460
|
+
(ARRAY['threshold','anomaly','pattern','correlation'])[1 + floor(random() * 4)::int],
|
|
1461
|
+
(ARRAY[1, 5, 10, 50, 100])[1 + floor(random() * 5)::int]::real,
|
|
1462
|
+
(ARRAY['critical','high','medium','low'])[1 + floor(random() * 4)::int],
|
|
1463
|
+
random() < 0.85,
|
|
1464
|
+
'2020-01-01'::timestamptz + (random() * interval '1500 days')
|
|
1465
|
+
FROM (
|
|
1466
|
+
SELECT
|
|
1467
|
+
g,
|
|
1468
|
+
(ARRAY['Failed login threshold','Port scan detection','Malware signature match',
|
|
1469
|
+
'Unusual data transfer','Off-hours access','New admin account created',
|
|
1470
|
+
'Firewall rule violation','DNS tunneling detection','Brute force detection',
|
|
1471
|
+
'Privilege escalation attempt'])[1 + floor(random() * 10)::int] AS rule_name
|
|
1472
|
+
FROM generate_series(1, 200) AS g
|
|
1473
|
+
) AS src;
|
|
1474
|
+
|
|
1475
|
+
-- ---------- Alerts (12,000) ----------
|
|
1476
|
+
INSERT INTO alerts (organization_id, alert_rule_id, severity, title, status, incident_id, source, created_at, acknowledged_at)
|
|
1477
|
+
SELECT
|
|
1478
|
+
1 + floor(random() * 200)::int,
|
|
1479
|
+
CASE WHEN random() < 0.8 THEN 1 + floor(random() * 200)::int ELSE NULL END,
|
|
1480
|
+
-- TECH DEBT: ~200 old alerts have NULL severity
|
|
1481
|
+
CASE
|
|
1482
|
+
WHEN g <= 200 THEN NULL
|
|
1483
|
+
ELSE (ARRAY['critical','high','high','medium','medium','medium','low','low'])[1 + floor(random() * 8)::int]
|
|
1484
|
+
END,
|
|
1485
|
+
(ARRAY['Suspicious login detected','Malware signature match','Port scan activity',
|
|
1486
|
+
'Unusual outbound traffic','Failed login threshold exceeded','New admin account',
|
|
1487
|
+
'Firewall policy violation','Certificate expiration warning','Vulnerability scan finding',
|
|
1488
|
+
'Endpoint agent offline','DNS query anomaly','Unauthorized API access'])[1 + floor(random() * 12)::int],
|
|
1489
|
+
(ARRAY['open','open','acknowledged','acknowledged','resolved','resolved','resolved','dismissed'])[1 + floor(random() * 8)::int],
|
|
1490
|
+
-- TECH DEBT: no FK to incidents. ~10% of alerts escalated
|
|
1491
|
+
CASE WHEN random() < 0.1 THEN 1 + floor(random() * 1500)::int ELSE NULL END,
|
|
1492
|
+
(ARRAY['agent','scanner','siem','ids','firewall','edr','cloud_trail','user_report'])[1 + floor(random() * 8)::int],
|
|
1493
|
+
alert_ts,
|
|
1494
|
+
CASE WHEN random() < 0.6 THEN alert_ts + (random() * interval '4 hours') ELSE NULL END
|
|
1495
|
+
FROM (
|
|
1496
|
+
SELECT
|
|
1497
|
+
g,
|
|
1498
|
+
'2020-01-01'::timestamptz + (power(random(), 0.35) * interval '1825 days') AS alert_ts
|
|
1499
|
+
FROM generate_series(1, 12000) AS g
|
|
1500
|
+
) AS src;
|
|
1501
|
+
|
|
1502
|
+
-- ---------- Alert Acknowledgments (8,000) ----------
|
|
1503
|
+
INSERT INTO alert_acknowledgments (alert_id, user_id, acknowledged_at, note)
|
|
1504
|
+
SELECT
|
|
1505
|
+
1 + floor(random() * 12000)::int,
|
|
1506
|
+
1 + floor(random() * 2000)::int,
|
|
1507
|
+
'2020-06-01'::timestamptz + (power(random(), 0.4) * interval '1700 days'),
|
|
1508
|
+
CASE WHEN random() < 0.3
|
|
1509
|
+
THEN (ARRAY['Investigating','Known issue','Escalated to team lead','False positive','Handled via playbook'])[1 + floor(random() * 5)::int]
|
|
1510
|
+
ELSE NULL
|
|
1511
|
+
END
|
|
1512
|
+
FROM generate_series(1, 8000) AS g;
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
-- ==========================================================================
|
|
1516
|
+
-- 9. THREAT INTELLIGENCE DATA
|
|
1517
|
+
-- ==========================================================================
|
|
1518
|
+
|
|
1519
|
+
-- ---------- Indicators of Compromise (2,000) ----------
|
|
1520
|
+
INSERT INTO indicators_of_compromise (threat_feed_id, ioc_type, value, confidence, threat_actor_id, first_seen, last_seen, created_at)
|
|
1521
|
+
SELECT
|
|
1522
|
+
1 + floor(random() * 10)::int,
|
|
1523
|
+
ioc_type,
|
|
1524
|
+
CASE ioc_type
|
|
1525
|
+
WHEN 'ip' THEN floor(random() * 223 + 1)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int
|
|
1526
|
+
WHEN 'domain' THEN (ARRAY['malware','c2','phish','exfil','drop','loader','beacon','stage'])[1 + floor(random() * 8)::int]
|
|
1527
|
+
|| '-' || lpad(floor(random() * 10000)::int::text, 4, '0') || '.'
|
|
1528
|
+
|| (ARRAY['evil.com','badactor.net','malicious.org','darkweb.xyz','threat.io'])[1 + floor(random() * 5)::int]
|
|
1529
|
+
WHEN 'hash' THEN md5(random()::text)
|
|
1530
|
+
WHEN 'url' THEN 'https://' || (ARRAY['malware','c2','phish'])[1 + floor(random() * 3)::int]
|
|
1531
|
+
|| '.example.com/' || md5(random()::text)
|
|
1532
|
+
WHEN 'email' THEN (ARRAY['attacker','phisher','scammer'])[1 + floor(random() * 3)::int]
|
|
1533
|
+
|| floor(random() * 1000)::int || '@malicious-domain.com'
|
|
1534
|
+
END,
|
|
1535
|
+
floor(random() * 100 + 1)::int,
|
|
1536
|
+
CASE WHEN random() < 0.3 THEN 1 + floor(random() * 50)::int ELSE NULL END,
|
|
1537
|
+
seen_ts,
|
|
1538
|
+
seen_ts + (random() * interval '180 days'),
|
|
1539
|
+
seen_ts
|
|
1540
|
+
FROM (
|
|
1541
|
+
SELECT
|
|
1542
|
+
g,
|
|
1543
|
+
(ARRAY['ip','ip','ip','domain','domain','hash','hash','url','email'])[1 + floor(random() * 9)::int] AS ioc_type,
|
|
1544
|
+
'2020-01-01'::timestamptz + (power(random(), 0.4) * interval '1825 days') AS seen_ts
|
|
1545
|
+
FROM generate_series(1, 2000) AS g
|
|
1546
|
+
) AS src;
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
-- ==========================================================================
|
|
1550
|
+
-- 10. COMPLIANCE DATA
|
|
1551
|
+
-- ==========================================================================
|
|
1552
|
+
|
|
1553
|
+
-- ---------- Compliance Controls (200) ----------
|
|
1554
|
+
INSERT INTO compliance_controls (framework_id, control_id_code, title, description, category)
|
|
1555
|
+
SELECT
|
|
1556
|
+
framework_id,
|
|
1557
|
+
code_prefix || '-' || lpad(g::text, 3, '0'),
|
|
1558
|
+
'Control: ' || control_title,
|
|
1559
|
+
'Detailed requirements for ' || lower(control_title),
|
|
1560
|
+
(ARRAY['Access Control','Audit','Risk Management','Incident Response','Configuration Management',
|
|
1561
|
+
'Data Protection','Network Security','Personnel Security','Physical Security','System Integrity'])[1 + floor(random() * 10)::int]
|
|
1562
|
+
FROM (
|
|
1563
|
+
SELECT
|
|
1564
|
+
g,
|
|
1565
|
+
((g - 1) % 5) + 1 AS framework_id,
|
|
1566
|
+
CASE ((g - 1) % 5) + 1
|
|
1567
|
+
WHEN 1 THEN 'SOC2'
|
|
1568
|
+
WHEN 2 THEN 'ISO'
|
|
1569
|
+
WHEN 3 THEN 'PCI'
|
|
1570
|
+
WHEN 4 THEN 'HIP'
|
|
1571
|
+
WHEN 5 THEN 'NIST'
|
|
1572
|
+
END AS code_prefix,
|
|
1573
|
+
(ARRAY['Access control policy enforcement','Audit log retention','Risk assessment frequency',
|
|
1574
|
+
'Incident response plan testing','Encryption at rest','Network segmentation',
|
|
1575
|
+
'Vulnerability scanning schedule','Change management process','Backup verification',
|
|
1576
|
+
'Security awareness training','MFA enforcement','Third-party risk assessment',
|
|
1577
|
+
'Data classification policy','Endpoint protection deployment','Patch management SLA',
|
|
1578
|
+
'Password complexity requirements','Session timeout configuration','API security controls',
|
|
1579
|
+
'Cloud security posture management','Supply chain security review'])[1 + floor(random() * 20)::int] AS control_title
|
|
1580
|
+
FROM generate_series(1, 200) AS g
|
|
1581
|
+
) AS src;
|
|
1582
|
+
|
|
1583
|
+
-- ---------- Compliance Assessments (600) ----------
|
|
1584
|
+
INSERT INTO compliance_assessments (organization_id, framework_id, status, started_at, completed_at, score, created_at)
|
|
1585
|
+
SELECT
|
|
1586
|
+
1 + floor(random() * 200)::int,
|
|
1587
|
+
1 + floor(random() * 5)::int,
|
|
1588
|
+
(ARRAY['completed','completed','completed','completed','in_progress','scheduled'])[1 + floor(random() * 6)::int],
|
|
1589
|
+
start_ts,
|
|
1590
|
+
CASE WHEN random() < 0.75 THEN start_ts + (random() * interval '14 days') ELSE NULL END,
|
|
1591
|
+
CASE WHEN random() < 0.75 THEN round((50 + random() * 50)::numeric, 1) ELSE NULL END,
|
|
1592
|
+
start_ts
|
|
1593
|
+
FROM (
|
|
1594
|
+
SELECT
|
|
1595
|
+
g,
|
|
1596
|
+
'2020-06-01'::timestamptz + (power(random(), 0.5) * interval '1700 days') AS start_ts
|
|
1597
|
+
FROM generate_series(1, 600) AS g
|
|
1598
|
+
) AS src;
|
|
1599
|
+
|
|
1600
|
+
-- ---------- Compliance Findings (5,000) ----------
|
|
1601
|
+
INSERT INTO compliance_findings (assessment_id, control_id, status, evidence, notes, created_at)
|
|
1602
|
+
SELECT
|
|
1603
|
+
1 + floor(random() * 600)::int,
|
|
1604
|
+
1 + floor(random() * 200)::int,
|
|
1605
|
+
-- TECH DEBT: case-inconsistent status values
|
|
1606
|
+
(ARRAY['pass','pass','pass','Pass','PASS','fail','fail','Fail','partial','partial','not_applicable','N/A'])[1 + floor(random() * 12)::int],
|
|
1607
|
+
CASE WHEN random() < 0.4 THEN 'Evidence collected from audit log and configuration review' ELSE NULL END,
|
|
1608
|
+
CASE WHEN random() < 0.2 THEN 'Remediation required within 30 days' ELSE NULL END,
|
|
1609
|
+
'2021-01-01'::timestamptz + (power(random(), 0.5) * interval '1500 days')
|
|
1610
|
+
FROM generate_series(1, 5000) AS g;
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
-- ==========================================================================
|
|
1614
|
+
-- 11. USAGE & AUDIT DATA
|
|
1615
|
+
-- ==========================================================================
|
|
1616
|
+
|
|
1617
|
+
-- ---------- API Keys (300) ----------
|
|
1618
|
+
INSERT INTO api_keys (organization_id, user_id, name, key_prefix, key_hash, permissions, is_active, last_used, created_at, expires_at)
|
|
1619
|
+
SELECT
|
|
1620
|
+
1 + floor(random() * 200)::int,
|
|
1621
|
+
1 + floor(random() * 2000)::int,
|
|
1622
|
+
(ARRAY['CI/CD Pipeline','Monitoring Integration','Custom Dashboard','Data Export','Slack Bot',
|
|
1623
|
+
'JIRA Integration','Automation Script','Terraform Provider'])[1 + floor(random() * 8)::int] || ' Key',
|
|
1624
|
+
'stl_' || substr(md5(random()::text), 1, 8),
|
|
1625
|
+
md5(random()::text || g::text),
|
|
1626
|
+
(ARRAY['read','read','read:write','admin'])[1 + floor(random() * 4)::int],
|
|
1627
|
+
random() < 0.8,
|
|
1628
|
+
CASE WHEN random() < 0.7 THEN now() - (random() * interval '30 days') ELSE NULL END,
|
|
1629
|
+
'2020-06-01'::timestamptz + (random() * interval '1700 days'),
|
|
1630
|
+
CASE WHEN random() < 0.6 THEN now() + (random() * interval '365 days') ELSE NULL END
|
|
1631
|
+
FROM generate_series(1, 300) AS g;
|
|
1632
|
+
|
|
1633
|
+
-- ---------- API Requests (20,000 — last 90 days) ----------
|
|
1634
|
+
INSERT INTO api_requests (api_key_id, user_id, method, path, status_code, response_time_ms, request_body, created_at)
|
|
1635
|
+
SELECT
|
|
1636
|
+
1 + floor(random() * 300)::int,
|
|
1637
|
+
1 + floor(random() * 2000)::int, -- TECH DEBT: no FK
|
|
1638
|
+
(ARRAY['GET','GET','GET','GET','POST','POST','PUT','DELETE'])[1 + floor(random() * 8)::int],
|
|
1639
|
+
(ARRAY['/api/v1/assets','/api/v1/scans','/api/v1/vulnerabilities','/api/v1/incidents',
|
|
1640
|
+
'/api/v1/reports','/api/v1/alerts','/api/v1/compliance','/api/v1/users'])[1 + floor(random() * 8)::int],
|
|
1641
|
+
(ARRAY[200,200,200,200,200,201,400,401,403,404,500])[1 + floor(random() * 11)::int],
|
|
1642
|
+
floor(random() * 2000 + 10)::int,
|
|
1643
|
+
NULL, -- TECH DEBT: dead column, always NULL (privacy fix applied 2024)
|
|
1644
|
+
now() - (random() * interval '90 days')
|
|
1645
|
+
FROM generate_series(1, 20000) AS g;
|
|
1646
|
+
|
|
1647
|
+
-- ---------- Feature Usage (15,000) ----------
|
|
1648
|
+
INSERT INTO feature_usage (organization_id, user_id, feature_name, action, metadata, created_at)
|
|
1649
|
+
SELECT
|
|
1650
|
+
1 + floor(random() * 200)::int,
|
|
1651
|
+
1 + floor(random() * 2000)::int,
|
|
1652
|
+
(ARRAY['vulnerability_scanner','asset_inventory','incident_manager','compliance_dashboard',
|
|
1653
|
+
'threat_intel','report_builder','api_access','alert_rules','integrations','executive_dashboard'])[1 + floor(random() * 10)::int],
|
|
1654
|
+
(ARRAY['view','view','view','create','update','delete','export','configure'])[1 + floor(random() * 8)::int],
|
|
1655
|
+
NULL,
|
|
1656
|
+
'2021-01-01'::timestamptz + (power(random(), 0.35) * interval '1500 days')
|
|
1657
|
+
FROM generate_series(1, 15000) AS g;
|
|
1658
|
+
|
|
1659
|
+
-- ---------- Login Events (10,000) ----------
|
|
1660
|
+
INSERT INTO login_events (user_id, ip_address, user_agent, success, failure_reason, created_at)
|
|
1661
|
+
SELECT
|
|
1662
|
+
1 + floor(random() * 2000)::int,
|
|
1663
|
+
floor(random() * 223 + 1)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int,
|
|
1664
|
+
(ARRAY['Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120','Mozilla/5.0 (Macintosh; Intel Mac OS X) Safari/17',
|
|
1665
|
+
'Mozilla/5.0 (X11; Linux x86_64) Firefox/121','Mozilla/5.0 (iPhone; CPU iPhone OS 17) Mobile',
|
|
1666
|
+
'python-requests/2.31','curl/8.4.0'])[1 + floor(random() * 6)::int],
|
|
1667
|
+
CASE WHEN random() < 0.92 THEN true ELSE false END,
|
|
1668
|
+
CASE WHEN random() >= 0.92 THEN (ARRAY['invalid_password','account_locked','mfa_failed','expired_session'])[1 + floor(random() * 4)::int] ELSE NULL END,
|
|
1669
|
+
'2021-01-01'::timestamptz + (power(random(), 0.3) * interval '1500 days')
|
|
1670
|
+
FROM generate_series(1, 10000) AS g;
|
|
1671
|
+
|
|
1672
|
+
-- ---------- Notifications (5,000) ----------
|
|
1673
|
+
INSERT INTO notifications (user_id, organization_id, type, title, message, is_read, created_at)
|
|
1674
|
+
SELECT
|
|
1675
|
+
1 + floor(random() * 2000)::int,
|
|
1676
|
+
1 + floor(random() * 200)::int,
|
|
1677
|
+
(ARRAY['alert','incident','scan_complete','compliance','system','billing','report_ready'])[1 + floor(random() * 7)::int],
|
|
1678
|
+
(ARRAY['New critical alert','Incident assigned to you','Scan completed','Compliance assessment due',
|
|
1679
|
+
'System maintenance scheduled','Invoice generated','Weekly report ready',
|
|
1680
|
+
'New vulnerability detected','Agent offline','Password expiring soon'])[1 + floor(random() * 10)::int],
|
|
1681
|
+
'Please review and take appropriate action.',
|
|
1682
|
+
random() < 0.6,
|
|
1683
|
+
'2021-06-01'::timestamptz + (power(random(), 0.3) * interval '1400 days')
|
|
1684
|
+
FROM generate_series(1, 5000) AS g;
|
|
1685
|
+
|
|
1686
|
+
|
|
1687
|
+
-- ==========================================================================
|
|
1688
|
+
-- 12. REPORTING & DENORMALIZED TABLES (populated from existing data)
|
|
1689
|
+
-- ==========================================================================
|
|
1690
|
+
|
|
1691
|
+
-- ---------- Daily Scan Stats (~2,000) ----------
|
|
1692
|
+
INSERT INTO daily_scan_stats (organization_id, scan_date, total_scans, total_findings, critical_count, high_count, medium_count, low_count)
|
|
1693
|
+
SELECT
|
|
1694
|
+
1 + floor(random() * 200)::int,
|
|
1695
|
+
('2023-01-01'::date + g),
|
|
1696
|
+
1 + floor(random() * 10)::int,
|
|
1697
|
+
floor(random() * 100)::int,
|
|
1698
|
+
floor(random() * 5)::int,
|
|
1699
|
+
floor(random() * 20)::int,
|
|
1700
|
+
floor(random() * 50)::int,
|
|
1701
|
+
floor(random() * 30)::int
|
|
1702
|
+
FROM generate_series(0, 1999) AS g;
|
|
1703
|
+
|
|
1704
|
+
-- ---------- Monthly Vulnerability Summary (~500) ----------
|
|
1705
|
+
INSERT INTO monthly_vulnerability_summary (organization_id, month, total_vulns, critical_vulns, high_vulns, medium_vulns, low_vulns, avg_time_to_fix_days)
|
|
1706
|
+
SELECT
|
|
1707
|
+
1 + floor(random() * 200)::int,
|
|
1708
|
+
('2022-01-01'::date + (g * 30 % 1095)),
|
|
1709
|
+
floor(random() * 500 + 50)::int,
|
|
1710
|
+
floor(random() * 20)::int,
|
|
1711
|
+
floor(random() * 80)::int,
|
|
1712
|
+
floor(random() * 200)::int,
|
|
1713
|
+
floor(random() * 100)::int,
|
|
1714
|
+
round((random() * 30 + 5)::numeric, 1)
|
|
1715
|
+
FROM generate_series(1, 500) AS g;
|
|
1716
|
+
|
|
1717
|
+
-- ---------- Organization Health Scores (200) ----------
|
|
1718
|
+
INSERT INTO organization_health_scores (organization_id, score, vulnerability_score, compliance_score, asset_coverage_score, calculated_at)
|
|
1719
|
+
SELECT
|
|
1720
|
+
id,
|
|
1721
|
+
round((50 + random() * 50)::numeric, 1),
|
|
1722
|
+
round((30 + random() * 70)::numeric, 1),
|
|
1723
|
+
round((40 + random() * 60)::numeric, 1),
|
|
1724
|
+
round((60 + random() * 40)::numeric, 1),
|
|
1725
|
+
now() - (random() * interval '7 days')
|
|
1726
|
+
FROM organizations;
|
|
1727
|
+
|
|
1728
|
+
-- ---------- Scan Results Denormalized (from existing data, ~80K) ----------
|
|
1729
|
+
-- This mirrors scan_results with pre-joined columns (no FKs on this table)
|
|
1730
|
+
INSERT INTO scan_results_denormalized
|
|
1731
|
+
(scan_id, scan_type, scan_started_at, asset_id, hostname, asset_type,
|
|
1732
|
+
organization_id, organization_name, vulnerability_id, cve_id, vulnerability_title,
|
|
1733
|
+
severity, cvss_score, risk_level, status, found_at)
|
|
1734
|
+
SELECT
|
|
1735
|
+
sr.scan_id,
|
|
1736
|
+
s.scan_type,
|
|
1737
|
+
s.started_at,
|
|
1738
|
+
sr.asset_id,
|
|
1739
|
+
a.hostname,
|
|
1740
|
+
a.asset_type,
|
|
1741
|
+
a.organization_id,
|
|
1742
|
+
o.name,
|
|
1743
|
+
sr.vulnerability_id,
|
|
1744
|
+
v.cve_id,
|
|
1745
|
+
v.title,
|
|
1746
|
+
v.severity,
|
|
1747
|
+
v.cvss_score,
|
|
1748
|
+
sr.risk_level,
|
|
1749
|
+
sr.status,
|
|
1750
|
+
sr.found_at
|
|
1751
|
+
FROM scan_results sr
|
|
1752
|
+
LEFT JOIN scans s ON s.id = sr.scan_id
|
|
1753
|
+
LEFT JOIN assets a ON a.id = sr.asset_id
|
|
1754
|
+
LEFT JOIN organizations o ON o.id = a.organization_id
|
|
1755
|
+
LEFT JOIN vulnerabilities v ON v.id = sr.vulnerability_id
|
|
1756
|
+
LIMIT 80000;
|
|
1757
|
+
|
|
1758
|
+
-- ---------- Executive Dashboard Cache (200) ----------
|
|
1759
|
+
INSERT INTO executive_dashboard_cache (organization_id, total_assets, active_agents, open_vulnerabilities, critical_incidents, compliance_score, risk_score, generated_at)
|
|
1760
|
+
SELECT
|
|
1761
|
+
o.id,
|
|
1762
|
+
(SELECT count(*) FROM assets WHERE organization_id = o.id),
|
|
1763
|
+
(SELECT count(*) FROM agents ag JOIN assets a ON a.id = ag.asset_id WHERE a.organization_id = o.id AND ag.status = 'active'),
|
|
1764
|
+
floor(random() * 200)::int,
|
|
1765
|
+
floor(random() * 5)::int,
|
|
1766
|
+
round((40 + random() * 60)::numeric, 1),
|
|
1767
|
+
round((random() * 100)::numeric, 1),
|
|
1768
|
+
now() - (random() * interval '24 hours')
|
|
1769
|
+
FROM organizations o;
|
|
1770
|
+
|
|
1771
|
+
|
|
1772
|
+
-- ==========================================================================
|
|
1773
|
+
-- 13. REPORTS, DASHBOARDS, INTEGRATIONS
|
|
1774
|
+
-- ==========================================================================
|
|
1775
|
+
|
|
1776
|
+
-- ---------- Reports (500) ----------
|
|
1777
|
+
INSERT INTO reports (organization_id, created_by, title, report_type, format, parameters, status, file_path, created_at)
|
|
1778
|
+
SELECT
|
|
1779
|
+
1 + floor(random() * 200)::int,
|
|
1780
|
+
1 + floor(random() * 2000)::int,
|
|
1781
|
+
(ARRAY['Monthly Vulnerability Report','Executive Security Summary','Compliance Assessment Report',
|
|
1782
|
+
'Incident Response Summary','Asset Inventory Report','Threat Landscape Analysis',
|
|
1783
|
+
'Risk Scorecard','Scan Coverage Report','Remediation Progress Report','Audit Trail Report'])[1 + floor(random() * 10)::int],
|
|
1784
|
+
(ARRAY['vulnerability','executive','compliance','incident','asset','threat'])[1 + floor(random() * 6)::int],
|
|
1785
|
+
(ARRAY['pdf','pdf','pdf','csv','xlsx'])[1 + floor(random() * 5)::int],
|
|
1786
|
+
'{"period":"monthly","org_filter":null}',
|
|
1787
|
+
(ARRAY['completed','completed','completed','generating','failed'])[1 + floor(random() * 5)::int],
|
|
1788
|
+
'/reports/' || md5(random()::text) || '.pdf',
|
|
1789
|
+
'2021-01-01'::timestamptz + (power(random(), 0.5) * interval '1600 days')
|
|
1790
|
+
FROM generate_series(1, 500) AS g;
|
|
1791
|
+
|
|
1792
|
+
-- ---------- Report Schedules (100) ----------
|
|
1793
|
+
INSERT INTO report_schedules (report_id, cron_expression, recipients, is_active, last_run, next_run, created_at)
|
|
1794
|
+
SELECT
|
|
1795
|
+
1 + floor(random() * 500)::int,
|
|
1796
|
+
(ARRAY['0 8 1 * *','0 9 * * 1','0 6 1 1,4,7,10 *','0 0 1 * *'])[1 + floor(random() * 4)::int],
|
|
1797
|
+
'security-team@example.com,ciso@example.com',
|
|
1798
|
+
random() < 0.8,
|
|
1799
|
+
now() - (random() * interval '30 days'),
|
|
1800
|
+
now() + (random() * interval '30 days'),
|
|
1801
|
+
'2022-01-01'::timestamptz + (random() * interval '1200 days')
|
|
1802
|
+
FROM generate_series(1, 100) AS g;
|
|
1803
|
+
|
|
1804
|
+
-- ---------- Dashboards (300) ----------
|
|
1805
|
+
INSERT INTO dashboards (organization_id, created_by, name, description, is_default, layout, created_at)
|
|
1806
|
+
SELECT
|
|
1807
|
+
1 + floor(random() * 200)::int,
|
|
1808
|
+
1 + floor(random() * 2000)::int,
|
|
1809
|
+
(ARRAY['Security Overview','Vulnerability Dashboard','Incident Tracker','Compliance Status',
|
|
1810
|
+
'Executive Summary','SOC Dashboard','Asset Health','Threat Intel Feed',
|
|
1811
|
+
'Risk Heatmap','Scan Activity'])[1 + floor(random() * 10)::int] || ' - ' || g,
|
|
1812
|
+
'Dashboard for monitoring security posture',
|
|
1813
|
+
g <= 200, -- first 200 are defaults (one per org)
|
|
1814
|
+
'{"columns":12,"rows":"auto"}',
|
|
1815
|
+
'2021-06-01'::timestamptz + (random() * interval '1500 days')
|
|
1816
|
+
FROM generate_series(1, 300) AS g;
|
|
1817
|
+
|
|
1818
|
+
-- ---------- Dashboard Widgets (1,200) ----------
|
|
1819
|
+
INSERT INTO dashboard_widgets (dashboard_id, widget_type, title, config, position_x, position_y, width, height)
|
|
1820
|
+
SELECT
|
|
1821
|
+
1 + floor(random() * 300)::int,
|
|
1822
|
+
(ARRAY['chart','table','metric','map','timeline','heatmap','list','gauge'])[1 + floor(random() * 8)::int],
|
|
1823
|
+
(ARRAY['Critical Vulns','Open Incidents','Scan Coverage','Compliance Score','Alert Volume',
|
|
1824
|
+
'Asset Count','Agent Health','Risk Trend','Top CVEs','Mean Time to Remediate'])[1 + floor(random() * 10)::int],
|
|
1825
|
+
'{"datasource":"auto","refresh":300}',
|
|
1826
|
+
(g % 3) * 4,
|
|
1827
|
+
(g / 3 % 4) * 3,
|
|
1828
|
+
(ARRAY[4, 6, 8, 12])[1 + floor(random() * 4)::int],
|
|
1829
|
+
(ARRAY[2, 3, 4, 6])[1 + floor(random() * 4)::int]
|
|
1830
|
+
FROM generate_series(1, 1200) AS g;
|
|
1831
|
+
|
|
1832
|
+
-- ---------- Integrations (150) ----------
|
|
1833
|
+
INSERT INTO integrations (organization_id, type, name, config, status, last_sync, created_at)
|
|
1834
|
+
SELECT
|
|
1835
|
+
1 + floor(random() * 200)::int,
|
|
1836
|
+
int_type,
|
|
1837
|
+
int_type || ' Integration',
|
|
1838
|
+
'{"webhook_url":"https://hooks.example.com/' || md5(random()::text) || '"}',
|
|
1839
|
+
(ARRAY['active','active','active','active','error','disabled'])[1 + floor(random() * 6)::int],
|
|
1840
|
+
CASE WHEN random() < 0.8 THEN now() - (random() * interval '24 hours') ELSE NULL END,
|
|
1841
|
+
'2021-01-01'::timestamptz + (random() * interval '1600 days')
|
|
1842
|
+
FROM (
|
|
1843
|
+
SELECT
|
|
1844
|
+
g,
|
|
1845
|
+
(ARRAY['slack','slack','jira','jira','pagerduty','pagerduty','teams','email',
|
|
1846
|
+
'webhook','splunk','servicenow','opsgenie'])[1 + floor(random() * 12)::int] AS int_type
|
|
1847
|
+
FROM generate_series(1, 150) AS g
|
|
1848
|
+
) AS src;
|
|
1849
|
+
|
|
1850
|
+
-- ---------- Integration Events (5,000) ----------
|
|
1851
|
+
INSERT INTO integration_events (integration_id, event_type, direction, payload, status, created_at)
|
|
1852
|
+
SELECT
|
|
1853
|
+
1 + floor(random() * 150)::int,
|
|
1854
|
+
(ARRAY['alert_sent','incident_created','ticket_updated','notification_sent','sync_completed','webhook_received'])[1 + floor(random() * 6)::int],
|
|
1855
|
+
(ARRAY['outbound','outbound','outbound','inbound'])[1 + floor(random() * 4)::int],
|
|
1856
|
+
'{"event_id":"' || md5(random()::text) || '"}',
|
|
1857
|
+
(ARRAY['success','success','success','success','failed','retrying'])[1 + floor(random() * 6)::int],
|
|
1858
|
+
'2022-01-01'::timestamptz + (power(random(), 0.4) * interval '1300 days')
|
|
1859
|
+
FROM generate_series(1, 5000) AS g;
|
|
1860
|
+
|
|
1861
|
+
-- ---------- Audit Log (25,000) ----------
|
|
1862
|
+
INSERT INTO audit_log (organization_id, user_id, action, resource_type, resource_id, details, ip_address, created_at)
|
|
1863
|
+
SELECT
|
|
1864
|
+
1 + floor(random() * 200)::int, -- no FK
|
|
1865
|
+
1 + floor(random() * 2000)::int, -- no FK
|
|
1866
|
+
(ARRAY['login','logout','create','update','delete','export','configure','invite','revoke','scan_start',
|
|
1867
|
+
'report_generate','alert_acknowledge','incident_create','role_change','api_key_create'])[1 + floor(random() * 15)::int],
|
|
1868
|
+
(ARRAY['user','asset','scan','incident','alert','report','integration','api_key','team','dashboard'])[1 + floor(random() * 10)::int],
|
|
1869
|
+
floor(random() * 10000)::int::text,
|
|
1870
|
+
CASE WHEN random() < 0.3 THEN '{"ip":"192.168.1.' || floor(random() * 255)::int || '"}' ELSE NULL END,
|
|
1871
|
+
floor(random() * 223 + 1)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int,
|
|
1872
|
+
'2020-01-01'::timestamptz + (power(random(), 0.3) * interval '2000 days')
|
|
1873
|
+
FROM generate_series(1, 25000) AS g;
|
|
1874
|
+
|
|
1875
|
+
|
|
1876
|
+
-- ==========================================================================
|
|
1877
|
+
-- 14. LEGACY & ABANDONED TABLE DATA
|
|
1878
|
+
-- ==========================================================================
|
|
1879
|
+
|
|
1880
|
+
-- ---------- old_scan_results_v2 (5,000) — abandoned 2023 migration ----------
|
|
1881
|
+
INSERT INTO old_scan_results_v2 (scan_run_id, target_host, vuln_code, severity_score, detection_date, remediated)
|
|
1882
|
+
SELECT
|
|
1883
|
+
floor(random() * 1000 + 1)::int,
|
|
1884
|
+
'host-' || lpad(floor(random() * 5000)::int::text, 4, '0') || '.legacy.internal',
|
|
1885
|
+
'VULN-' || lpad(floor(random() * 999)::int::text, 3, '0'),
|
|
1886
|
+
round((random() * 10)::numeric, 1),
|
|
1887
|
+
'2022-01-01'::timestamptz + (random() * interval '365 days'),
|
|
1888
|
+
random() < 0.3
|
|
1889
|
+
FROM generate_series(1, 5000) AS g;
|
|
1890
|
+
|
|
1891
|
+
-- ---------- temp_asset_import_2024 (1,200) — one-time CSV import artifact ----------
|
|
1892
|
+
INSERT INTO temp_asset_import_2024 (import_hostname, import_ip, import_os, import_env, raw_csv_line, imported_at)
|
|
1893
|
+
SELECT
|
|
1894
|
+
'imported-host-' || g,
|
|
1895
|
+
'10.0.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int,
|
|
1896
|
+
(ARRAY['Windows 10','Windows 11','macOS 14','Ubuntu 22.04','RHEL 9'])[1 + floor(random() * 5)::int],
|
|
1897
|
+
(ARRAY['prod','staging','dev','unknown',''])[1 + floor(random() * 5)::int],
|
|
1898
|
+
'imported-host-' || g || ',10.0.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int || ',Windows,prod',
|
|
1899
|
+
'2024-03-15'::timestamptz + (random() * interval '2 hours')
|
|
1900
|
+
FROM generate_series(1, 1200) AS g;
|
|
1901
|
+
|
|
1902
|
+
-- ---------- feature_flags_legacy (50) — replaced by LaunchDarkly ----------
|
|
1903
|
+
INSERT INTO feature_flags_legacy (flag_name, is_enabled, rollout_percentage, description, updated_at)
|
|
1904
|
+
SELECT
|
|
1905
|
+
(ARRAY['new_dashboard','dark_mode','beta_api','advanced_search','ml_detection',
|
|
1906
|
+
'new_onboarding','slack_v2','report_v3','asset_graph','threat_map',
|
|
1907
|
+
'sso_okta','sso_azure','rbac_v2','audit_export','bulk_scan',
|
|
1908
|
+
'auto_remediation','risk_scoring_v2','compliance_auto','api_v2','webhook_v2'])[((g-1) % 20) + 1]
|
|
1909
|
+
|| CASE WHEN g > 20 THEN '_' || (g / 20)::int::text ELSE '' END,
|
|
1910
|
+
random() < 0.3,
|
|
1911
|
+
floor(random() * 100)::int,
|
|
1912
|
+
'Legacy feature flag — migrated to LaunchDarkly',
|
|
1913
|
+
'2023-01-01'::timestamptz + (random() * interval '365 days')
|
|
1914
|
+
FROM generate_series(1, 50) AS g;
|
|
1915
|
+
|
|
1916
|
+
-- ---------- notifications_backup (8,000) — pre-redesign backup ----------
|
|
1917
|
+
INSERT INTO notifications_backup (recipient_id, notification_type, subject, body, sent_at, read_at)
|
|
1918
|
+
SELECT
|
|
1919
|
+
floor(random() * 1500 + 1)::int, -- old user ID scheme
|
|
1920
|
+
(ARRAY['email','in_app','push','sms'])[1 + floor(random() * 4)::int],
|
|
1921
|
+
(ARRAY['Security Alert','Scan Complete','New Incident','Weekly Report','Action Required',
|
|
1922
|
+
'Compliance Reminder','System Update','Account Activity'])[1 + floor(random() * 8)::int],
|
|
1923
|
+
'This is a legacy notification from the old notification system.',
|
|
1924
|
+
'2021-01-01'::timestamptz + (random() * interval '730 days'),
|
|
1925
|
+
CASE WHEN random() < 0.5 THEN '2021-01-01'::timestamptz + (random() * interval '730 days') ELSE NULL END
|
|
1926
|
+
FROM generate_series(1, 8000) AS g;
|
|
1927
|
+
|
|
1928
|
+
-- ---------- user_sessions_archive (15,000) — old session system ----------
|
|
1929
|
+
INSERT INTO user_sessions_archive (session_token, user_ref_id, login_time, logout_time, ip_addr, browser)
|
|
1930
|
+
SELECT
|
|
1931
|
+
md5(random()::text || g::text),
|
|
1932
|
+
floor(random() * 1000 + 5000)::int, -- old user IDs (5000-6000 range, don't match current users)
|
|
1933
|
+
login_ts,
|
|
1934
|
+
CASE WHEN random() < 0.7 THEN login_ts + (random() * interval '8 hours') ELSE NULL END,
|
|
1935
|
+
'10.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int || '.' || floor(random() * 256)::int,
|
|
1936
|
+
(ARRAY['Chrome 95','Firefox 94','Safari 15','Edge 96','Chrome 90','Firefox 88'])[1 + floor(random() * 6)::int]
|
|
1937
|
+
FROM (
|
|
1938
|
+
SELECT g, '2020-01-01'::timestamptz + (random() * interval '1095 days') AS login_ts
|
|
1939
|
+
FROM generate_series(1, 15000) AS g
|
|
1940
|
+
) AS src;
|
|
1941
|
+
|
|
1942
|
+
-- ---------- legacy_risk_scores (1,000) — old scoring algorithm ----------
|
|
1943
|
+
INSERT INTO legacy_risk_scores (org_id, risk_category, score_value, weight, computed_date)
|
|
1944
|
+
SELECT
|
|
1945
|
+
floor(random() * 200 + 1)::int,
|
|
1946
|
+
(ARRAY['vulnerability','compliance','threat','operational','financial'])[1 + floor(random() * 5)::int],
|
|
1947
|
+
round((random() * 1000)::numeric, 2), -- different scale (0-1000) vs current (0-100)
|
|
1948
|
+
round((random())::numeric, 2),
|
|
1949
|
+
('2021-01-01'::date + floor(random() * 730)::int)
|
|
1950
|
+
FROM generate_series(1, 1000) AS g;
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
COMMIT;
|
|
1954
|
+
|
|
1955
|
+
-- ==========================================================================
|
|
1956
|
+
-- Verify row counts
|
|
1957
|
+
-- ==========================================================================
|
|
1958
|
+
SELECT 'Row counts:' AS info;
|
|
1959
|
+
SELECT schemaname, relname AS table_name, n_live_tup AS row_count
|
|
1960
|
+
FROM pg_stat_user_tables
|
|
1961
|
+
ORDER BY n_live_tup DESC;
|