alepha 0.14.1 → 0.14.3
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 +3 -3
- package/dist/api/audits/index.browser.js +5 -5
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +784 -784
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +13 -13
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +5 -5
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +57 -57
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +71 -71
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +5 -5
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +165 -165
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -10
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +10 -10
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +583 -171
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +12 -12
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +163 -10
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +281 -276
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +196 -91
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.browser.js +19 -19
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +778 -764
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +831 -596
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js +6 -6
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +125 -125
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +6 -6
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.d.ts +1 -2
- package/dist/bin/index.js +0 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +249 -218
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +951 -821
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +40 -0
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +97 -17
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +14 -18
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +29 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +21 -24
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +21 -24
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/fake/index.js +195 -168
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +8 -0
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +3 -0
- package/dist/file/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +26 -5
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +146 -121
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +49 -24
- package/dist/orm/index.js.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +29 -29
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +171 -155
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +0 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +2 -0
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/health/index.d.ts +17 -17
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.browser.js +22 -6
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +46 -44
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +24 -41
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/security/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +2 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +8 -3
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +12 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.js.map +1 -1
- package/package.json +7 -7
- package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
- package/src/api/audits/entities/audits.ts +5 -5
- package/src/api/audits/index.browser.ts +1 -1
- package/src/api/audits/index.ts +3 -3
- package/src/api/audits/primitives/$audit.spec.ts +276 -0
- package/src/api/audits/services/AuditService.spec.ts +495 -0
- package/src/api/files/__tests__/$bucket.spec.ts +91 -0
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
- package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
- package/src/api/files/controllers/FileController.spec.ts +558 -0
- package/src/api/files/controllers/FileController.ts +4 -5
- package/src/api/files/entities/files.ts +5 -5
- package/src/api/files/index.browser.ts +1 -1
- package/src/api/files/index.ts +4 -4
- package/src/api/files/jobs/FileJobs.spec.ts +52 -0
- package/src/api/files/services/FileService.spec.ts +109 -0
- package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
- package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
- package/src/api/jobs/entities/jobExecutions.ts +5 -5
- package/src/api/jobs/index.ts +3 -3
- package/src/api/jobs/primitives/$job.spec.ts +476 -0
- package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
- package/src/api/notifications/entities/notifications.ts +5 -5
- package/src/api/notifications/index.browser.ts +1 -1
- package/src/api/notifications/index.ts +4 -4
- package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
- package/src/api/parameters/entities/parameters.ts +7 -17
- package/src/api/parameters/index.ts +3 -3
- package/src/api/parameters/primitives/$config.spec.ts +356 -0
- package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
- package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
- package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
- package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
- package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
- package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
- package/src/api/parameters/schemas/index.ts +15 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
- package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
- package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
- package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
- package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
- package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
- package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
- package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
- package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
- package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
- package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
- package/src/api/users/controllers/AdminUserController.ts +116 -0
- package/src/api/users/controllers/UserController.ts +4 -107
- package/src/api/users/controllers/UserRealmController.ts +3 -0
- package/src/api/users/entities/identities.ts +6 -6
- package/src/api/users/entities/sessions.ts +6 -6
- package/src/api/users/entities/users.ts +9 -9
- package/src/api/users/index.ts +13 -6
- package/src/api/users/primitives/$userRealm.ts +13 -8
- package/src/api/users/services/CredentialService.spec.ts +509 -0
- package/src/api/users/services/CredentialService.ts +46 -0
- package/src/api/users/services/IdentityService.ts +15 -0
- package/src/api/users/services/RegistrationService.spec.ts +630 -0
- package/src/api/users/services/RegistrationService.ts +18 -0
- package/src/api/users/services/SessionService.spec.ts +301 -0
- package/src/api/users/services/SessionService.ts +110 -1
- package/src/api/users/services/UserService.ts +67 -2
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
- package/src/api/verifications/entities/verifications.ts +6 -6
- package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
- package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
- package/src/batch/primitives/$batch.spec.ts +766 -0
- package/src/batch/providers/BatchProvider.spec.ts +786 -0
- package/src/bin/index.ts +0 -1
- package/src/bucket/__tests__/shared.ts +194 -0
- package/src/bucket/primitives/$bucket.spec.ts +104 -0
- package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
- package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
- package/src/cache/core/__tests__/shared.ts +377 -0
- package/src/cache/core/primitives/$cache.spec.ts +111 -0
- package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
- package/src/cli/apps/AlephaCli.ts +54 -16
- package/src/cli/apps/AlephaPackageBuilderCli.ts +2 -1
- package/src/cli/assets/appRouterTs.ts +1 -1
- package/src/cli/commands/{ViteCommands.ts → build.ts} +2 -105
- package/src/cli/commands/clean.ts +14 -0
- package/src/cli/commands/{DrizzleCommands.ts → db.ts} +10 -117
- package/src/cli/commands/{DeployCommands.ts → deploy.ts} +1 -1
- package/src/cli/commands/dev.ts +69 -0
- package/src/cli/commands/format.ts +17 -0
- package/src/cli/commands/gen/changelog.spec.ts +315 -0
- package/src/cli/commands/{ChangelogCommands.ts → gen/changelog.ts} +16 -31
- package/src/cli/commands/gen/openapi.ts +71 -0
- package/src/cli/commands/gen.ts +18 -0
- package/src/cli/commands/{CoreCommands.ts → init.ts} +4 -40
- package/src/cli/commands/lint.ts +17 -0
- package/src/cli/commands/root.ts +41 -0
- package/src/cli/commands/run.ts +24 -0
- package/src/cli/commands/test.ts +42 -0
- package/src/cli/commands/typecheck.ts +24 -0
- package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
- package/src/cli/defineConfig.ts +10 -1
- package/src/cli/index.ts +17 -7
- package/src/cli/services/AlephaCliUtils.ts +71 -32
- package/src/cli/services/GitMessageParser.ts +1 -1
- package/src/command/helpers/Asker.spec.ts +127 -0
- package/src/command/helpers/Runner.spec.ts +126 -0
- package/src/command/primitives/$command.spec.ts +1588 -0
- package/src/command/providers/CliProvider.ts +74 -24
- package/src/core/Alepha.ts +52 -4
- package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
- package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
- package/src/core/__tests__/Alepha-has.spec.ts +41 -0
- package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
- package/src/core/__tests__/Alepha-register.spec.ts +81 -0
- package/src/core/__tests__/Alepha-start.spec.ts +176 -0
- package/src/core/__tests__/Alepha-with.spec.ts +14 -0
- package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
- package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
- package/src/core/__tests__/descriptor.spec.ts +34 -0
- package/src/core/__tests__/fixtures/A.ts +5 -0
- package/src/core/__tests__/pagination.spec.ts +77 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
- package/src/core/primitives/$atom.spec.ts +43 -0
- package/src/core/primitives/$hook.spec.ts +130 -0
- package/src/core/primitives/$inject.spec.ts +175 -0
- package/src/core/primitives/$module.spec.ts +115 -0
- package/src/core/providers/CodecManager.spec.ts +740 -0
- package/src/core/providers/EventManager.spec.ts +762 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/StateManager.spec.ts +365 -0
- package/src/core/providers/TypeProvider.spec.ts +1607 -0
- package/src/core/providers/TypeProvider.ts +20 -26
- package/src/datetime/primitives/$interval.spec.ts +103 -0
- package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
- package/src/email/primitives/$email.spec.ts +175 -0
- package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
- package/src/fake/__tests__/keyName.example.ts +40 -0
- package/src/fake/__tests__/keyName.spec.ts +152 -0
- package/src/fake/__tests__/module.example.ts +32 -0
- package/src/fake/providers/FakeProvider.spec.ts +438 -0
- package/src/file/providers/FileSystemProvider.ts +8 -0
- package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
- package/src/file/providers/NodeFileSystemProvider.ts +5 -0
- package/src/file/services/FileDetector.spec.ts +591 -0
- package/src/lock/core/__tests__/shared.ts +190 -0
- package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
- package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
- package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
- package/src/logger/primitives/$logger.spec.ts +108 -0
- package/src/logger/services/Logger.spec.ts +295 -0
- package/src/mcp/__tests__/errors.spec.ts +175 -0
- package/src/mcp/__tests__/integration.spec.ts +450 -0
- package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
- package/src/mcp/primitives/$prompt.spec.ts +468 -0
- package/src/mcp/primitives/$resource.spec.ts +390 -0
- package/src/mcp/primitives/$tool.spec.ts +406 -0
- package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
- package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
- package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
- package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
- package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
- package/src/orm/__tests__/$repository-save.spec.ts +37 -0
- package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
- package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
- package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
- package/src/orm/__tests__/delete-returning.spec.ts +256 -0
- package/src/orm/__tests__/deletedAt.spec.ts +80 -0
- package/src/orm/__tests__/enums.spec.ts +315 -0
- package/src/orm/__tests__/execute.spec.ts +72 -0
- package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
- package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
- package/src/orm/__tests__/joins.spec.ts +1114 -0
- package/src/orm/__tests__/page.spec.ts +287 -0
- package/src/orm/__tests__/primaryKey.spec.ts +87 -0
- package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
- package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
- package/src/orm/__tests__/references.spec.ts +102 -0
- package/src/orm/__tests__/security.spec.ts +710 -0
- package/src/orm/__tests__/sqlite.spec.ts +111 -0
- package/src/orm/__tests__/string-operators.spec.ts +429 -0
- package/src/orm/__tests__/timestamps.spec.ts +388 -0
- package/src/orm/__tests__/validation.spec.ts +183 -0
- package/src/orm/__tests__/version.spec.ts +64 -0
- package/src/orm/helpers/parseQueryString.spec.ts +196 -0
- package/src/orm/index.browser.ts +1 -1
- package/src/orm/index.ts +10 -6
- package/src/orm/primitives/$repository.spec.ts +137 -0
- package/src/orm/primitives/$sequence.spec.ts +29 -0
- package/src/orm/primitives/$transaction.spec.ts +82 -0
- package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
- package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
- package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
- package/src/orm/services/ModelBuilder.spec.ts +575 -0
- package/src/orm/services/Repository.spec.ts +137 -0
- package/src/queue/core/__tests__/shared.ts +143 -0
- package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
- package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
- package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
- package/src/redis/__tests__/redis.spec.ts +58 -0
- package/src/retry/primitives/$retry.spec.ts +234 -0
- package/src/retry/providers/RetryProvider.spec.ts +438 -0
- package/src/router/__tests__/match.spec.ts +252 -0
- package/src/router/providers/RouterProvider.spec.ts +197 -0
- package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
- package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
- package/src/scheduler/__tests__/shared.ts +77 -0
- package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
- package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
- package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
- package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
- package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
- package/src/security/primitives/$permission.spec.ts +30 -0
- package/src/security/primitives/$permission.ts +2 -2
- package/src/security/primitives/$realm.spec.ts +101 -0
- package/src/security/primitives/$role.spec.ts +52 -0
- package/src/security/primitives/$serviceAccount.spec.ts +61 -0
- package/src/security/providers/SecurityProvider.spec.ts +350 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
- package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
- package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
- package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
- package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
- package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
- package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
- package/src/server/core/primitives/$action.spec.ts +191 -0
- package/src/server/core/primitives/$route.spec.ts +65 -0
- package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
- package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
- package/src/server/core/providers/ServerProvider.ts +3 -1
- package/src/server/core/services/HttpClient.spec.ts +123 -0
- package/src/server/core/services/UserAgentParser.spec.ts +111 -0
- package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
- package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
- package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
- package/src/server/links/__tests__/$action.spec.ts +238 -0
- package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
- package/src/server/links/__tests__/requestId.spec.ts +120 -0
- package/src/server/links/primitives/$remote.spec.ts +228 -0
- package/src/server/links/providers/LinkProvider.spec.ts +54 -0
- package/src/server/links/providers/LinkProvider.ts +49 -3
- package/src/server/links/providers/ServerLinksProvider.ts +1 -53
- package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
- package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
- package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
- package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
- package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
- package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
- package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
- package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
- package/src/server/static/primitives/$serve.spec.ts +193 -0
- package/src/server/swagger/__tests__/ui.spec.ts +52 -0
- package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
- package/src/sms/primitives/$sms.spec.ts +165 -0
- package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
- package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
- package/src/thread/primitives/$thread.spec.ts +186 -0
- package/src/topic/core/__tests__/shared.ts +144 -0
- package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
- package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
- package/src/vite/plugins/viteAlephaDev.ts +16 -4
- package/src/vite/tasks/runAlepha.ts +7 -1
- package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
- package/src/websocket/primitives/$channel.spec.ts +30 -0
- package/src/cli/commands/BiomeCommands.ts +0 -29
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
|
+
import { $entity, $repository, DatabaseProvider, pg, sql } from "../index.ts";
|
|
4
|
+
import {
|
|
5
|
+
NodeSqliteProvider,
|
|
6
|
+
nodeSqliteOptions,
|
|
7
|
+
} from "../providers/drivers/NodeSqliteProvider.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* SQL Injection Security Tests
|
|
11
|
+
*
|
|
12
|
+
* This test suite validates that the ORM properly sanitizes and escapes user input
|
|
13
|
+
* to prevent SQL injection attacks across various scenarios:
|
|
14
|
+
* - Basic filter operators (eq, ne, like, ilike, etc.)
|
|
15
|
+
* - JSONB queries
|
|
16
|
+
* - Array operators
|
|
17
|
+
* - Raw SQL queries
|
|
18
|
+
* - Both PostgreSQL and SQLite
|
|
19
|
+
*/
|
|
20
|
+
describe("SQL Injection Security Tests", () => {
|
|
21
|
+
// Define test entities with various column types
|
|
22
|
+
const users = $entity({
|
|
23
|
+
name: "users",
|
|
24
|
+
schema: t.object({
|
|
25
|
+
id: pg.primaryKey(),
|
|
26
|
+
username: t.text(),
|
|
27
|
+
email: t.text(),
|
|
28
|
+
age: t.integer(),
|
|
29
|
+
profile: t.object({
|
|
30
|
+
bio: t.text(),
|
|
31
|
+
settings: t.object({
|
|
32
|
+
theme: t.text(),
|
|
33
|
+
notifications: t.boolean(),
|
|
34
|
+
}),
|
|
35
|
+
}),
|
|
36
|
+
tags: t.array(t.text()),
|
|
37
|
+
metadata: t.object({
|
|
38
|
+
permissions: t.array(t.text()),
|
|
39
|
+
lastLogin: t.optional(t.text()),
|
|
40
|
+
}),
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
class App {
|
|
45
|
+
users = $repository(users);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const sqlInjectionPayloads = [
|
|
49
|
+
// Classic SQL injection patterns
|
|
50
|
+
"' OR '1'='1",
|
|
51
|
+
"' OR 1=1--",
|
|
52
|
+
"' OR 1=1#",
|
|
53
|
+
"' OR 1=1/*",
|
|
54
|
+
"admin'--",
|
|
55
|
+
"admin' #",
|
|
56
|
+
"admin'/*",
|
|
57
|
+
|
|
58
|
+
// Union-based injection
|
|
59
|
+
"' UNION SELECT NULL--",
|
|
60
|
+
"' UNION SELECT NULL, NULL--",
|
|
61
|
+
"' UNION ALL SELECT NULL--",
|
|
62
|
+
|
|
63
|
+
// Stacked queries
|
|
64
|
+
"'; DROP TABLE users--",
|
|
65
|
+
"'; DELETE FROM users--",
|
|
66
|
+
"'; UPDATE users SET username='hacked'--",
|
|
67
|
+
|
|
68
|
+
// Boolean-based blind injection
|
|
69
|
+
"' AND 1=1--",
|
|
70
|
+
"' AND 1=2--",
|
|
71
|
+
"' AND SUBSTRING(@@version,1,1)='5",
|
|
72
|
+
|
|
73
|
+
// Time-based blind injection
|
|
74
|
+
"'; WAITFOR DELAY '00:00:05'--",
|
|
75
|
+
"'; SELECT SLEEP(5)--",
|
|
76
|
+
"' AND (SELECT * FROM (SELECT(SLEEP(5)))a)--",
|
|
77
|
+
|
|
78
|
+
// Quote escaping attempts
|
|
79
|
+
"''",
|
|
80
|
+
"\\'",
|
|
81
|
+
"\\x27",
|
|
82
|
+
"%27",
|
|
83
|
+
|
|
84
|
+
// Comment variations
|
|
85
|
+
"--",
|
|
86
|
+
"-- -",
|
|
87
|
+
"#",
|
|
88
|
+
"/**/",
|
|
89
|
+
"/* comment */",
|
|
90
|
+
|
|
91
|
+
// Hex encoding
|
|
92
|
+
"0x27",
|
|
93
|
+
"0x4F0x52",
|
|
94
|
+
|
|
95
|
+
// Special characters
|
|
96
|
+
"';!--\"<XSS>=&{()}",
|
|
97
|
+
"@@version",
|
|
98
|
+
"char(39)",
|
|
99
|
+
|
|
100
|
+
// PostgreSQL specific
|
|
101
|
+
"$1",
|
|
102
|
+
"$$",
|
|
103
|
+
"';--",
|
|
104
|
+
"1; SELECT version();",
|
|
105
|
+
|
|
106
|
+
// SQLite specific
|
|
107
|
+
"' || '1",
|
|
108
|
+
"'; ATTACH DATABASE 'file' AS db;--",
|
|
109
|
+
|
|
110
|
+
// JSONB injection attempts (PostgreSQL)
|
|
111
|
+
"' -> 'key",
|
|
112
|
+
"' ->> 'key",
|
|
113
|
+
"' #> '{key}'",
|
|
114
|
+
"' #>> '{key}'",
|
|
115
|
+
'\' @> \'{"key":"value"}\'',
|
|
116
|
+
"' ? 'key",
|
|
117
|
+
"' ?| array['key']",
|
|
118
|
+
"' ?& array['key']",
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
describe("PostgreSQL", () => {
|
|
122
|
+
describe("Basic Filter Operators", () => {
|
|
123
|
+
it("should prevent SQL injection in eq operator", async ({ expect }) => {
|
|
124
|
+
const alepha = Alepha.create();
|
|
125
|
+
const app = alepha.inject(App);
|
|
126
|
+
await alepha.start();
|
|
127
|
+
|
|
128
|
+
// Create a legitimate user
|
|
129
|
+
await app.users.create({
|
|
130
|
+
username: "alice",
|
|
131
|
+
email: "alice@example.com",
|
|
132
|
+
age: 30,
|
|
133
|
+
profile: {
|
|
134
|
+
bio: "Hello world",
|
|
135
|
+
settings: { theme: "dark", notifications: true },
|
|
136
|
+
},
|
|
137
|
+
tags: ["admin"],
|
|
138
|
+
metadata: { permissions: ["read", "write"], lastLogin: "2024-01-01" },
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Try each SQL injection payload
|
|
142
|
+
for (const payload of sqlInjectionPayloads) {
|
|
143
|
+
const result = await app.users.findMany({
|
|
144
|
+
where: { username: { eq: payload } },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Should return empty array since payload doesn't match any real username
|
|
148
|
+
expect(result).toEqual([]);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should prevent SQL injection in ne operator", async ({ expect }) => {
|
|
153
|
+
const alepha = Alepha.create();
|
|
154
|
+
const app = alepha.inject(App);
|
|
155
|
+
await alepha.start();
|
|
156
|
+
|
|
157
|
+
await app.users.create({
|
|
158
|
+
username: "bob",
|
|
159
|
+
email: "bob@example.com",
|
|
160
|
+
age: 25,
|
|
161
|
+
profile: {
|
|
162
|
+
bio: "Developer",
|
|
163
|
+
settings: { theme: "light", notifications: false },
|
|
164
|
+
},
|
|
165
|
+
tags: [],
|
|
166
|
+
metadata: { permissions: ["read"] },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
for (const payload of sqlInjectionPayloads) {
|
|
170
|
+
// Should not cause SQL errors or unexpected behavior
|
|
171
|
+
const result = await app.users.findMany({
|
|
172
|
+
where: { username: { ne: payload } },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Should return the legitimate user
|
|
176
|
+
expect(result.length).toBeGreaterThanOrEqual(0);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should prevent SQL injection in contains operator", async ({
|
|
181
|
+
expect,
|
|
182
|
+
}) => {
|
|
183
|
+
const alepha = Alepha.create();
|
|
184
|
+
const app = alepha.inject(App);
|
|
185
|
+
await alepha.start();
|
|
186
|
+
|
|
187
|
+
await app.users.create({
|
|
188
|
+
username: "charlie",
|
|
189
|
+
email: "charlie@example.com",
|
|
190
|
+
age: 35,
|
|
191
|
+
profile: {
|
|
192
|
+
bio: "Designer",
|
|
193
|
+
settings: { theme: "dark", notifications: true },
|
|
194
|
+
},
|
|
195
|
+
tags: ["designer"],
|
|
196
|
+
metadata: { permissions: ["read", "write"] },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
for (const payload of sqlInjectionPayloads) {
|
|
200
|
+
const result = await app.users.findMany({
|
|
201
|
+
where: { username: { contains: payload } },
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Should safely handle the payload as a literal string
|
|
205
|
+
expect(result).toEqual([]);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should prevent SQL injection in contains operator (email field)", async ({
|
|
210
|
+
expect,
|
|
211
|
+
}) => {
|
|
212
|
+
const alepha = Alepha.create();
|
|
213
|
+
const app = alepha.inject(App);
|
|
214
|
+
await alepha.start();
|
|
215
|
+
|
|
216
|
+
await app.users.create({
|
|
217
|
+
username: "Diana",
|
|
218
|
+
email: "diana@example.com",
|
|
219
|
+
age: 28,
|
|
220
|
+
profile: {
|
|
221
|
+
bio: "Manager",
|
|
222
|
+
settings: { theme: "light", notifications: true },
|
|
223
|
+
},
|
|
224
|
+
tags: ["manager"],
|
|
225
|
+
metadata: { permissions: ["admin"] },
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
for (const payload of sqlInjectionPayloads) {
|
|
229
|
+
const result = await app.users.findMany({
|
|
230
|
+
where: { email: { contains: payload } },
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
expect(result).toEqual([]);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("should prevent SQL injection in gt/gte/lt/lte operators", async ({
|
|
238
|
+
expect,
|
|
239
|
+
}) => {
|
|
240
|
+
const alepha = Alepha.create();
|
|
241
|
+
const app = alepha.inject(App);
|
|
242
|
+
await alepha.start();
|
|
243
|
+
|
|
244
|
+
await app.users.create({
|
|
245
|
+
username: "eve",
|
|
246
|
+
email: "eve@example.com",
|
|
247
|
+
age: 40,
|
|
248
|
+
profile: {
|
|
249
|
+
bio: "Executive",
|
|
250
|
+
settings: { theme: "dark", notifications: false },
|
|
251
|
+
},
|
|
252
|
+
tags: [],
|
|
253
|
+
metadata: { permissions: ["admin", "read", "write"] },
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Numeric operators should either reject non-numeric payloads or treat them safely
|
|
257
|
+
// PostgreSQL will throw a type error for invalid integer syntax
|
|
258
|
+
// This is actually GOOD - the database layer is rejecting malicious input
|
|
259
|
+
for (const payload of sqlInjectionPayloads) {
|
|
260
|
+
try {
|
|
261
|
+
const result = await app.users.findMany({
|
|
262
|
+
where: { age: { gt: payload as any } },
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// If it doesn't throw, it should return safe results
|
|
266
|
+
expect(Array.isArray(result)).toBe(true);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
// PostgreSQL rejects invalid integer syntax - this is expected and safe
|
|
269
|
+
// The SQL is still parameterized, preventing injection
|
|
270
|
+
expect(error).toBeDefined();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should prevent SQL injection in inArray operator", async ({
|
|
276
|
+
expect,
|
|
277
|
+
}) => {
|
|
278
|
+
const alepha = Alepha.create();
|
|
279
|
+
const app = alepha.inject(App);
|
|
280
|
+
await alepha.start();
|
|
281
|
+
|
|
282
|
+
await app.users.create({
|
|
283
|
+
username: "frank",
|
|
284
|
+
email: "frank@example.com",
|
|
285
|
+
age: 33,
|
|
286
|
+
profile: {
|
|
287
|
+
bio: "Developer",
|
|
288
|
+
settings: { theme: "dark", notifications: true },
|
|
289
|
+
},
|
|
290
|
+
tags: ["dev"],
|
|
291
|
+
metadata: { permissions: ["read"] },
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const result = await app.users.findMany({
|
|
295
|
+
where: {
|
|
296
|
+
username: { inArray: sqlInjectionPayloads.slice(0, 10) },
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Should treat each payload as a literal string to compare
|
|
301
|
+
expect(result).toEqual([]);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("Array Operator Injection", () => {
|
|
306
|
+
it("should prevent SQL injection in arrayContains", async ({
|
|
307
|
+
expect,
|
|
308
|
+
}) => {
|
|
309
|
+
const alepha = Alepha.create();
|
|
310
|
+
const app = alepha.inject(App);
|
|
311
|
+
await alepha.start();
|
|
312
|
+
|
|
313
|
+
await app.users.create({
|
|
314
|
+
username: "jack",
|
|
315
|
+
email: "jack@example.com",
|
|
316
|
+
age: 36,
|
|
317
|
+
profile: {
|
|
318
|
+
bio: "Security Expert",
|
|
319
|
+
settings: { theme: "dark", notifications: true },
|
|
320
|
+
},
|
|
321
|
+
tags: ["security", "expert"],
|
|
322
|
+
metadata: { permissions: ["admin", "audit"] },
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
for (const payload of sqlInjectionPayloads) {
|
|
326
|
+
const result = await app.users.findMany({
|
|
327
|
+
where: {
|
|
328
|
+
tags: { arrayContains: [payload] },
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
expect(result).toEqual([]);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should prevent SQL injection in arrayOverlaps", async ({
|
|
337
|
+
expect,
|
|
338
|
+
}) => {
|
|
339
|
+
const alepha = Alepha.create();
|
|
340
|
+
const app = alepha.inject(App);
|
|
341
|
+
await alepha.start();
|
|
342
|
+
|
|
343
|
+
await app.users.create({
|
|
344
|
+
username: "kate",
|
|
345
|
+
email: "kate@example.com",
|
|
346
|
+
age: 32,
|
|
347
|
+
profile: {
|
|
348
|
+
bio: "Product Manager",
|
|
349
|
+
settings: { theme: "light", notifications: false },
|
|
350
|
+
},
|
|
351
|
+
tags: ["product", "manager"],
|
|
352
|
+
metadata: { permissions: ["read", "write"] },
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const result = await app.users.findMany({
|
|
356
|
+
where: {
|
|
357
|
+
tags: { arrayOverlaps: sqlInjectionPayloads.slice(0, 5) },
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
expect(result).toEqual([]);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("should prevent SQL injection in arrayContained", async ({
|
|
365
|
+
expect,
|
|
366
|
+
}) => {
|
|
367
|
+
const alepha = Alepha.create();
|
|
368
|
+
const app = alepha.inject(App);
|
|
369
|
+
await alepha.start();
|
|
370
|
+
|
|
371
|
+
await app.users.create({
|
|
372
|
+
username: "leo",
|
|
373
|
+
email: "leo@example.com",
|
|
374
|
+
age: 38,
|
|
375
|
+
profile: {
|
|
376
|
+
bio: "CTO",
|
|
377
|
+
settings: { theme: "dark", notifications: true },
|
|
378
|
+
},
|
|
379
|
+
tags: ["cto", "leadership"],
|
|
380
|
+
metadata: { permissions: ["admin", "owner"] },
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const result = await app.users.findMany({
|
|
384
|
+
where: {
|
|
385
|
+
tags: { arrayContained: sqlInjectionPayloads.slice(0, 5) },
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
expect(result).toEqual([]);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe("Raw SQL Injection", () => {
|
|
394
|
+
it("should safely handle parameterized queries", async ({ expect }) => {
|
|
395
|
+
const alepha = Alepha.create();
|
|
396
|
+
const app = alepha.inject(App);
|
|
397
|
+
await alepha.start();
|
|
398
|
+
|
|
399
|
+
await app.users.create({
|
|
400
|
+
username: "maria",
|
|
401
|
+
email: "maria@example.com",
|
|
402
|
+
age: 26,
|
|
403
|
+
profile: {
|
|
404
|
+
bio: "Designer",
|
|
405
|
+
settings: { theme: "light", notifications: true },
|
|
406
|
+
},
|
|
407
|
+
tags: ["design"],
|
|
408
|
+
metadata: { permissions: ["read"] },
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
for (const payload of sqlInjectionPayloads) {
|
|
412
|
+
// Using parameterized queries should be safe
|
|
413
|
+
const result = await app.users.query(
|
|
414
|
+
(t) => sql`SELECT * FROM ${t} WHERE ${t.username} = ${payload}`,
|
|
415
|
+
t.pick(users.schema, ["username"]),
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
// Should treat payload as a literal value
|
|
419
|
+
expect(result).toEqual([]);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it("should prevent injection in complex SQL expressions", async ({
|
|
424
|
+
expect,
|
|
425
|
+
}) => {
|
|
426
|
+
const alepha = Alepha.create();
|
|
427
|
+
const app = alepha.inject(App);
|
|
428
|
+
await alepha.start();
|
|
429
|
+
|
|
430
|
+
await app.users.create({
|
|
431
|
+
username: "nathan",
|
|
432
|
+
email: "nathan@example.com",
|
|
433
|
+
age: 41,
|
|
434
|
+
profile: {
|
|
435
|
+
bio: "Consultant",
|
|
436
|
+
settings: { theme: "dark", notifications: false },
|
|
437
|
+
},
|
|
438
|
+
tags: ["consulting"],
|
|
439
|
+
metadata: { permissions: ["read", "write", "admin"] },
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
for (const payload of sqlInjectionPayloads) {
|
|
443
|
+
const result = await app.users.query(
|
|
444
|
+
(t) =>
|
|
445
|
+
sql`SELECT * FROM ${t} WHERE ${t.username} LIKE ${`%${payload}%`}`,
|
|
446
|
+
t.pick(users.schema, ["username"]),
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
expect(result).toEqual([]);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
describe("SQLite", () => {
|
|
456
|
+
describe("Basic Filter Operators", () => {
|
|
457
|
+
it("should prevent SQL injection in eq operator", async ({ expect }) => {
|
|
458
|
+
const alepha = Alepha.create().with({
|
|
459
|
+
provide: DatabaseProvider,
|
|
460
|
+
use: NodeSqliteProvider,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
alepha.store.mut(nodeSqliteOptions, (old) => ({
|
|
464
|
+
...old,
|
|
465
|
+
path: "sqlite://:memory:",
|
|
466
|
+
}));
|
|
467
|
+
|
|
468
|
+
const app = alepha.inject(App);
|
|
469
|
+
await alepha.start();
|
|
470
|
+
|
|
471
|
+
await app.users.create({
|
|
472
|
+
username: "sqlite_alice",
|
|
473
|
+
email: "alice@sqlite.com",
|
|
474
|
+
age: 30,
|
|
475
|
+
profile: {
|
|
476
|
+
bio: "SQLite tester",
|
|
477
|
+
settings: { theme: "dark", notifications: true },
|
|
478
|
+
},
|
|
479
|
+
tags: ["sqlite"],
|
|
480
|
+
metadata: { permissions: ["read"] },
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
for (const payload of sqlInjectionPayloads) {
|
|
484
|
+
const result = await app.users.findMany({
|
|
485
|
+
where: { username: { eq: payload } },
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
expect(result).toEqual([]);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it("should prevent SQL injection in contains operator (SQLite)", async ({
|
|
493
|
+
expect,
|
|
494
|
+
}) => {
|
|
495
|
+
const alepha = Alepha.create().with({
|
|
496
|
+
provide: DatabaseProvider,
|
|
497
|
+
use: NodeSqliteProvider,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
alepha.store.mut(nodeSqliteOptions, (old) => ({
|
|
501
|
+
...old,
|
|
502
|
+
path: "sqlite://:memory:",
|
|
503
|
+
}));
|
|
504
|
+
|
|
505
|
+
const app = alepha.inject(App);
|
|
506
|
+
await alepha.start();
|
|
507
|
+
|
|
508
|
+
await app.users.create({
|
|
509
|
+
username: "sqlite_bob",
|
|
510
|
+
email: "bob@sqlite.com",
|
|
511
|
+
age: 25,
|
|
512
|
+
profile: {
|
|
513
|
+
bio: "SQLite developer",
|
|
514
|
+
settings: { theme: "light", notifications: false },
|
|
515
|
+
},
|
|
516
|
+
tags: ["dev"],
|
|
517
|
+
metadata: { permissions: ["read", "write"] },
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
for (const payload of sqlInjectionPayloads) {
|
|
521
|
+
const result = await app.users.findMany({
|
|
522
|
+
where: { username: { contains: payload } },
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
expect(result).toEqual([]);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("should prevent SQL injection in inArray operator", async ({
|
|
530
|
+
expect,
|
|
531
|
+
}) => {
|
|
532
|
+
const alepha = Alepha.create().with({
|
|
533
|
+
provide: DatabaseProvider,
|
|
534
|
+
use: NodeSqliteProvider,
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
alepha.store.mut(nodeSqliteOptions, (old) => ({
|
|
538
|
+
...old,
|
|
539
|
+
path: "sqlite://:memory:",
|
|
540
|
+
}));
|
|
541
|
+
|
|
542
|
+
const app = alepha.inject(App);
|
|
543
|
+
await alepha.start();
|
|
544
|
+
|
|
545
|
+
await app.users.create({
|
|
546
|
+
username: "sqlite_charlie",
|
|
547
|
+
email: "charlie@sqlite.com",
|
|
548
|
+
age: 35,
|
|
549
|
+
profile: {
|
|
550
|
+
bio: "SQLite admin",
|
|
551
|
+
settings: { theme: "dark", notifications: true },
|
|
552
|
+
},
|
|
553
|
+
tags: ["admin"],
|
|
554
|
+
metadata: { permissions: ["admin"] },
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const result = await app.users.findMany({
|
|
558
|
+
where: {
|
|
559
|
+
username: { inArray: sqlInjectionPayloads.slice(0, 10) },
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
expect(result).toEqual([]);
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
describe("Raw SQL Injection (SQLite)", () => {
|
|
568
|
+
it("should safely handle parameterized queries", async ({ expect }) => {
|
|
569
|
+
const alepha = Alepha.create().with({
|
|
570
|
+
provide: DatabaseProvider,
|
|
571
|
+
use: NodeSqliteProvider,
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
alepha.store.mut(nodeSqliteOptions, (old) => ({
|
|
575
|
+
...old,
|
|
576
|
+
path: "sqlite://:memory:",
|
|
577
|
+
}));
|
|
578
|
+
|
|
579
|
+
const app = alepha.inject(App);
|
|
580
|
+
await alepha.start();
|
|
581
|
+
|
|
582
|
+
await app.users.create({
|
|
583
|
+
username: "sqlite_eve",
|
|
584
|
+
email: "eve@sqlite.com",
|
|
585
|
+
age: 40,
|
|
586
|
+
profile: {
|
|
587
|
+
bio: "SQL expert",
|
|
588
|
+
settings: { theme: "light", notifications: false },
|
|
589
|
+
},
|
|
590
|
+
tags: ["sql"],
|
|
591
|
+
metadata: { permissions: ["admin"] },
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
for (const payload of sqlInjectionPayloads) {
|
|
595
|
+
const result = await app.users.query(
|
|
596
|
+
(t) => sql`SELECT * FROM ${t} WHERE ${t.username} = ${payload}`,
|
|
597
|
+
t.pick(users.schema, ["username"]),
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
expect(result).toEqual([]);
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
describe("Edge Cases and Special Characters", () => {
|
|
607
|
+
it("should handle unicode and special characters safely", async ({
|
|
608
|
+
expect,
|
|
609
|
+
}) => {
|
|
610
|
+
const alepha = Alepha.create();
|
|
611
|
+
const app = alepha.inject(App);
|
|
612
|
+
await alepha.start();
|
|
613
|
+
|
|
614
|
+
const specialChars = [
|
|
615
|
+
"用户名", // Chinese
|
|
616
|
+
"пользователь", // Russian
|
|
617
|
+
"مستخدم", // Arabic
|
|
618
|
+
"🔥💻🚀", // Emojis
|
|
619
|
+
// Note: \u0000 (null byte) is not supported by PostgreSQL (invalid encoding)
|
|
620
|
+
"\n\r\t", // Whitespace
|
|
621
|
+
"\\\\", // Backslashes
|
|
622
|
+
"<<<>>>", // Angle brackets
|
|
623
|
+
];
|
|
624
|
+
|
|
625
|
+
for (const char of specialChars) {
|
|
626
|
+
try {
|
|
627
|
+
const created = await app.users.create({
|
|
628
|
+
username: char,
|
|
629
|
+
email: `${char}@example.com`,
|
|
630
|
+
age: 25,
|
|
631
|
+
profile: {
|
|
632
|
+
bio: char,
|
|
633
|
+
settings: { theme: "dark", notifications: true },
|
|
634
|
+
},
|
|
635
|
+
tags: [char],
|
|
636
|
+
metadata: { permissions: [char] },
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const found = await app.users.findOne({
|
|
640
|
+
where: { username: { eq: char } },
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
expect(found.id).toBe(created.id);
|
|
644
|
+
expect(found.username).toBe(char);
|
|
645
|
+
} catch (error) {
|
|
646
|
+
// Some special characters may have database-specific limitations
|
|
647
|
+
// (e.g., PostgreSQL doesn't support null bytes in text fields)
|
|
648
|
+
// The important thing is that it fails safely without SQL injection
|
|
649
|
+
expect(error).toBeDefined();
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it("should handle long strings with injection attempts safely", async ({
|
|
655
|
+
expect,
|
|
656
|
+
}) => {
|
|
657
|
+
const alepha = Alepha.create();
|
|
658
|
+
const app = alepha.inject(App);
|
|
659
|
+
await alepha.start();
|
|
660
|
+
|
|
661
|
+
// Create a string that's close to max length and contains SQL injection attempt
|
|
662
|
+
const longString = `${"A".repeat(100)}' OR '1'='1${"B".repeat(100)}`;
|
|
663
|
+
|
|
664
|
+
const created = await app.users.create({
|
|
665
|
+
username: longString,
|
|
666
|
+
email: "long@example.com",
|
|
667
|
+
age: 30,
|
|
668
|
+
profile: {
|
|
669
|
+
bio: longString,
|
|
670
|
+
settings: { theme: "dark", notifications: true },
|
|
671
|
+
},
|
|
672
|
+
tags: ["long"],
|
|
673
|
+
metadata: { permissions: ["read"] },
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
const found = await app.users.findOne({
|
|
677
|
+
where: {
|
|
678
|
+
username: { eq: longString },
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
expect(found.id).toBe(created.id);
|
|
683
|
+
expect(found.username).toBe(longString);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it("should handle empty strings and nulls", async ({ expect }) => {
|
|
687
|
+
const alepha = Alepha.create();
|
|
688
|
+
const app = alepha.inject(App);
|
|
689
|
+
await alepha.start();
|
|
690
|
+
|
|
691
|
+
const created = await app.users.create({
|
|
692
|
+
username: "",
|
|
693
|
+
email: "empty@example.com",
|
|
694
|
+
age: 30,
|
|
695
|
+
profile: {
|
|
696
|
+
bio: "",
|
|
697
|
+
settings: { theme: "dark", notifications: true },
|
|
698
|
+
},
|
|
699
|
+
tags: [],
|
|
700
|
+
metadata: { permissions: [] },
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
const found = await app.users.findOne({
|
|
704
|
+
where: { username: { eq: "" } },
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
expect(found.id).toBe(created.id);
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
});
|