@lunora/advisor 1.0.0-alpha.83 → 1.0.0-alpha.85

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.
Files changed (20) hide show
  1. package/dist/index.d.mts +30 -1
  2. package/dist/index.d.ts +30 -1
  3. package/dist/index.mjs +1 -1
  4. package/dist/packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs +1 -0
  5. package/dist/packem_shared/{filterOnPrimaryKey-c37rjqBk.mjs → filterOnPrimaryKey-COQRpmnu.mjs} +1 -1
  6. package/dist/packem_shared/{filterWithoutIndex-DXb__LGI.mjs → filterWithoutIndex-Cf-tXY_A.mjs} +1 -1
  7. package/dist/packem_shared/{fromServerSchema-w16riDj2.mjs → fromServerSchema-D2nTknz0.mjs} +1 -1
  8. package/dist/packem_shared/helpers-CwSEZdku.mjs +1 -0
  9. package/dist/packem_shared/{indexReferencesUnknownField-Dxd5jcVh.mjs → indexReferencesUnknownField-B_c3o9QR.mjs} +1 -1
  10. package/dist/packem_shared/maskWeakHashStrategyOnPii-BC1n0F4-.mjs +1 -0
  11. package/dist/packem_shared/outputProjectionMissingOnPublicRead-BZI8Y7am.mjs +1 -0
  12. package/dist/packem_shared/{publicMutationWithoutRatelimit-DbczxgpL.mjs → publicMutationWithoutRatelimit-Dsd4cSPD.mjs} +1 -1
  13. package/dist/packem_shared/{publicTableRlsOptoutConfusion-C9wCtAfg.mjs → publicTableRlsOptoutConfusion-Dz7lbKKe.mjs} +1 -1
  14. package/dist/packem_shared/{relationReferencesUnknownField-BmWWEFsB.mjs → relationReferencesUnknownField-D8Qyth_P.mjs} +1 -1
  15. package/dist/packem_shared/{signupMutationWithoutDisposableGating-D9MFN8QY.mjs → signupMutationWithoutDisposableGating-BIsSLyhJ.mjs} +1 -1
  16. package/dist/packem_shared/{userCreatingMutationWithoutCaptcha-lGWw1o2a.mjs → userCreatingMutationWithoutCaptcha-B7l-S-rV.mjs} +1 -1
  17. package/package.json +2 -2
  18. package/dist/packem_shared/helpers-DcWLLrH0.mjs +0 -1
  19. package/dist/packem_shared/maskWeakHashStrategyOnPii-d9BGImzK.mjs +0 -1
  20. package/dist/packem_shared/outputProjectionMissingOnPublicRead-BN5-uW0p.mjs +0 -1
package/dist/index.d.mts CHANGED
@@ -1247,6 +1247,15 @@ interface AdvisorTable {
1247
1247
  * kinds omits it, and the type lints then skip the check.
1248
1248
  */
1249
1249
  columnKinds?: Record<string, string>;
1250
+ /**
1251
+ * Set when the table opted into `.commitOrdered()` — every row carries
1252
+ * `_commitSeq`, a per-shard integer allocated once per mutation and strictly
1253
+ * increasing in commit order. Read by `commit_ordered_hard_delete`, which
1254
+ * pairs it against {@link AdvisorTable.softDelete}: without a tombstone, the
1255
+ * feed the sequence exists to serve cannot express a delete. Optional — a
1256
+ * feeder that doesn't track it omits it, and absent must not read as opted-in.
1257
+ */
1258
+ commitOrdered?: boolean;
1250
1259
  /**
1251
1260
  * `true` when the table is written outside Lunora's discoverable insert path
1252
1261
  * — declared via `.externallyManaged()` (e.g. `@lunora/auth`'s better-auth
@@ -2821,6 +2830,26 @@ declare const browserUserUrlWithoutAllowlist: Lint;
2821
2830
  * rotation for a stable cacheKey.
2822
2831
  */
2823
2832
  declare const circularFk: Lint;
2833
+ /**
2834
+ * `.commitOrdered()` gives a table `_commitSeq`, and the point of `_commitSeq` is
2835
+ * that a consumer can page `where _commitSeq > cursor` and be sure it missed
2836
+ * nothing. That guarantee holds for inserts and updates. It does **not** hold for
2837
+ * a hard delete: the sequence lives ON the row, so a physically removed row takes
2838
+ * its sequence with it. The row stops appearing in the feed, but no event ever
2839
+ * says it went away — a consumer holding a materialized copy keeps serving it
2840
+ * forever.
2841
+ *
2842
+ * Pairing the table with `.softDelete()` closes it: the tombstone flip is
2843
+ * mechanically an UPDATE, so it advances `_commitSeq` and pages through like any
2844
+ * other change.
2845
+ *
2846
+ * `WARN`, not `ERROR`, because the combination is legitimate for a genuinely
2847
+ * append-only table — an event log, an audit trail, a ledger — where nothing is
2848
+ * ever deleted and there is no delete to express. The lint exists because the
2849
+ * failure mode is silent and permanent, so it should be a decision rather than
2850
+ * an oversight.
2851
+ */
2852
+ declare const commitOrderedHardDelete: Lint;
2824
2853
  /**
2825
2854
  * Flags a `ctx.containers.<exportName>.get(name, …)` call whose instance key
2826
2855
  * is derived from the handler's `args` with no server-side scoping — a
@@ -4545,4 +4574,4 @@ interface RunAdvisorOptions {
4545
4574
  * `static` lints at build time and defer `runtime` lints to a live shard.
4546
4575
  */
4547
4576
  declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
4548
- export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
4577
+ export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, commitOrderedHardDelete, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
package/dist/index.d.ts CHANGED
@@ -1247,6 +1247,15 @@ interface AdvisorTable {
1247
1247
  * kinds omits it, and the type lints then skip the check.
1248
1248
  */
1249
1249
  columnKinds?: Record<string, string>;
1250
+ /**
1251
+ * Set when the table opted into `.commitOrdered()` — every row carries
1252
+ * `_commitSeq`, a per-shard integer allocated once per mutation and strictly
1253
+ * increasing in commit order. Read by `commit_ordered_hard_delete`, which
1254
+ * pairs it against {@link AdvisorTable.softDelete}: without a tombstone, the
1255
+ * feed the sequence exists to serve cannot express a delete. Optional — a
1256
+ * feeder that doesn't track it omits it, and absent must not read as opted-in.
1257
+ */
1258
+ commitOrdered?: boolean;
1250
1259
  /**
1251
1260
  * `true` when the table is written outside Lunora's discoverable insert path
1252
1261
  * — declared via `.externallyManaged()` (e.g. `@lunora/auth`'s better-auth
@@ -2821,6 +2830,26 @@ declare const browserUserUrlWithoutAllowlist: Lint;
2821
2830
  * rotation for a stable cacheKey.
2822
2831
  */
2823
2832
  declare const circularFk: Lint;
2833
+ /**
2834
+ * `.commitOrdered()` gives a table `_commitSeq`, and the point of `_commitSeq` is
2835
+ * that a consumer can page `where _commitSeq > cursor` and be sure it missed
2836
+ * nothing. That guarantee holds for inserts and updates. It does **not** hold for
2837
+ * a hard delete: the sequence lives ON the row, so a physically removed row takes
2838
+ * its sequence with it. The row stops appearing in the feed, but no event ever
2839
+ * says it went away — a consumer holding a materialized copy keeps serving it
2840
+ * forever.
2841
+ *
2842
+ * Pairing the table with `.softDelete()` closes it: the tombstone flip is
2843
+ * mechanically an UPDATE, so it advances `_commitSeq` and pages through like any
2844
+ * other change.
2845
+ *
2846
+ * `WARN`, not `ERROR`, because the combination is legitimate for a genuinely
2847
+ * append-only table — an event log, an audit trail, a ledger — where nothing is
2848
+ * ever deleted and there is no delete to express. The lint exists because the
2849
+ * failure mode is silent and permanent, so it should be a decision rather than
2850
+ * an oversight.
2851
+ */
2852
+ declare const commitOrderedHardDelete: Lint;
2824
2853
  /**
2825
2854
  * Flags a `ctx.containers.<exportName>.get(name, …)` call whose instance key
2826
2855
  * is derived from the handler's `args` with no server-side scoping — a
@@ -4545,4 +4574,4 @@ interface RunAdvisorOptions {
4545
4574
  * `static` lints at build time and defer `runtime` lints to a live shard.
4546
4575
  */
4547
4576
  declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
4548
- export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
4577
+ export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, commitOrderedHardDelete, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-DtBOHffV.mjs";import d from"./packem_shared/constraintValidator-DoRJD9Is.mjs";import u from"./packem_shared/errorRateOutlier-pr3_ZKbq.mjs";import h from"./packem_shared/fanOutBreadth-CBtmZnoh.mjs";import g from"./packem_shared/hotShard-BwGYZ3Tq.mjs";import b from"./packem_shared/indexUtilization-CkVPZcVe.mjs";import y from"./packem_shared/actionFetchSsrf-Z61P0o8U.mjs";import w from"./packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs";import S from"./packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs";import v from"./packem_shared/aiRawRunEscapeHatch-Dq43DVD9.mjs";import A from"./packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs";import U from"./packem_shared/aiToolSideEffectPromptInjection-K42X3QzJ.mjs";import R from"./packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs";import W from"./packem_shared/allowUnauthenticatedShardAccessEnabled-CjXI-kda.mjs";import T from"./packem_shared/authApiCallWithoutHeaders-C1OOWML5.mjs";import I from"./packem_shared/authCsrfCheckDisabled-Dz27bDzp.mjs";import N from"./packem_shared/authEmailVerificationDisabled-QTDD7TAU.mjs";import k from"./packem_shared/authScimWithoutTransactions-FqdTmUJs.mjs";import x from"./packem_shared/authSecureCookiesDisabled-CrGYulfJ.mjs";import F from"./packem_shared/authSessionFreshageZero-yWE6CGYP.mjs";import C from"./packem_shared/authTrustedOriginsWildcard-xpeRXfGF.mjs";import O from"./packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs";import M from"./packem_shared/browserUserUrlWithoutAllowlist-Wl3xHr7v.mjs";import $ from"./packem_shared/circularFk-DtcWFJxK.mjs";import E from"./packem_shared/containerInstanceKeyFromUserInput-uEQQVsEz.mjs";import P from"./packem_shared/containerOversizedInstance-Bx89uR7E.mjs";import D from"./packem_shared/containerPublicInternet-BFfZf_P4.mjs";import K from"./packem_shared/containerRuntimeEgressRelaxation-pkpyXNou.mjs";import q from"./packem_shared/containerStartEnableInternetOverride-BBY4PMG1.mjs";import L from"./packem_shared/duplicateIndex-Cip6-Rpu.mjs";import G from"./packem_shared/emptyIndex-BnHDcXza.mjs";import _ from"./packem_shared/errorWithoutCatalog-BTfvaXHR.mjs";import z from"./packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs";import B from"./packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs";import H from"./packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs";import V from"./packem_shared/externalSourceUnscoped-BxU2uSXk.mjs";import j from"./packem_shared/filterOnPrimaryKey-c37rjqBk.mjs";import Q from"./packem_shared/filterWithoutIndex-DXb__LGI.mjs";import X from"./packem_shared/flagGatesSecurityWithUnsafeDefault-i-Befg8b.mjs";import Z from"./packem_shared/geoIndexFieldNotGeopoint-D0lOTm-_.mjs";import J from"./packem_shared/geoIndexUnused-D7C9Qr4U.mjs";import Y from"./packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs";import oo from"./packem_shared/hardcodedSecret-Bw_4FYrs.mjs";import eo from"./packem_shared/httpActionMissingAuthGuard-BS3JZgaz.mjs";import ro from"./packem_shared/httpActionResponseHeaderInjection-DHnc8c9f.mjs";import to from"./packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs";import io from"./packem_shared/identityUndeclaredClaimTrusted-BDFqB7Dw.mjs";import no from"./packem_shared/imagesUrlSourceFromUserInput-DizsRh3M.mjs";import ao from"./packem_shared/indexReferencesUnknownField-Dxd5jcVh.mjs";import mo from"./packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs";import so from"./packem_shared/kvUnscopedUserKeyIdor-CIgMPzFj.mjs";import lo from"./packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs";import po from"./packem_shared/mailRecipientFromRequestInput-SDCchLP7.mjs";import fo from"./packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs";import co from"./packem_shared/maskWeakHashStrategyOnPii-d9BGImzK.mjs";import uo from"./packem_shared/maskedRelationLeakViaWith-C8n-Mzb1.mjs";import{e as a}from"./packem_shared/finding-NrKO8idM.mjs";import ho from"./packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs";import go from"./packem_shared/nondeterministicQueryMutation-sxmixc0l.mjs";import bo from"./packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs";import yo from"./packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs";import wo from"./packem_shared/notifySendOutsideAction-CU4T16cR.mjs";import So from"./packem_shared/outputProjectionMissingOnPublicRead-BN5-uW0p.mjs";import vo from"./packem_shared/ownerFieldFromArgsNotAuth-CjOSUDKi.mjs";import Ao from"./packem_shared/paymentCreateWithoutAuthorize-CICGmRFR.mjs";import Uo from"./packem_shared/paymentWebhookWideTolerance-B-F5jOeA.mjs";import Ro from"./packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs";import Wo from"./packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs";import To from"./packem_shared/privilegedDispatchUnvalidatedPayload-C6Qtc8aT.mjs";import Io from"./packem_shared/privilegedFanoutFromPublicProcedure-Coqt23CQ.mjs";import No from"./packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs";import ko from"./packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs";import xo from"./packem_shared/publicMutationWithoutRatelimit-DbczxgpL.mjs";import Fo from"./packem_shared/publicTableRlsOptoutConfusion-C9wCtAfg.mjs";import Co from"./packem_shared/queueWithoutDlq-BvHz3Opg.mjs";import Oo from"./packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs";import Mo from"./packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs";import $o from"./packem_shared/ratelimitKeySpoofableOrGlobal-BzDzxnXm.mjs";import Eo from"./packem_shared/ratelimitMiddlewareFailOpen-iDJq68qx.mjs";import Po from"./packem_shared/relationReferencesUnknownField-BmWWEFsB.mjs";import Do from"./packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs";import Ko from"./packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs";import qo from"./packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs";import Lo from"./packem_shared/shapeUnknownTable-CREfNnWi.mjs";import Go from"./packem_shared/signupMutationWithoutDisposableGating-D9MFN8QY.mjs";import _o from"./packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs";import zo from"./packem_shared/sqlInjectionRisk-CslaRBxz.mjs";import Bo from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-CTIU_7Fj.mjs";import Ho from"./packem_shared/storageKeyFromUserArgs-C-QUcdJ3.mjs";import Vo from"./packem_shared/storagePresignedUrlForPrivateContent-yeu1s81k.mjs";import jo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-KSRG81Iu.mjs";import Qo from"./packem_shared/storageUploadWithoutMaxSize-BTiM9Q3A.mjs";import Xo from"./packem_shared/tableWithoutInsert-DQ-GxjFF.mjs";import Zo from"./packem_shared/ttlFieldNotTimestamp-E14I83wY.mjs";import{s as Jo,q as Yo}from"./packem_shared/helpers-DcWLLrH0.mjs";import oe from"./packem_shared/unboundedStringArgument-DzbFJc5q.mjs";import ee from"./packem_shared/unindexedForeignKey-Dypgn8uH.mjs";import re from"./packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs";import te from"./packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs";import ie from"./packem_shared/userCreatingMutationWithoutCaptcha-lGWw1o2a.mjs";import ne from"./packem_shared/vectorsNamespaceFromUserInput-CDSOrkyX.mjs";import ae from"./packem_shared/workflowDuplicateStepName-BU4rg5So.mjs";import me from"./packem_shared/workflowUnknownTarget-B8H7jwnH.mjs";import se from"./packem_shared/workflowUnused-BUSOPdHq.mjs";import{compareToBaseline as ut,parseAdvisorMap as ht}from"./packem_shared/compareToBaseline-DBgN5YqX.mjs";import{gradeFromScore as bt}from"./packem_shared/gradeFromScore-KSt58rj1.mjs";import{MAP_VERSION as wt,byCodepoint as St,scoreAdvisor as vt}from"./packem_shared/MAP_VERSION-DEJGm0wI.mjs";import{default as Ut}from"./packem_shared/classifySensitivity-JnjaTGYi.mjs";import{fromServerSchema as Wt}from"./packem_shared/fromServerSchema-w16riDj2.mjs";const le={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},m={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>a(m,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${le[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},pe=new Map([["global",{level:"WARN",scope:r=>`it reads the whole D1 table "${r}" over a cross-region round trip`}],["root",{level:"WARN",scope:r=>`it loads every row of "${r}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:r=>`"${r}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),fe={level:"WARN",scope:r=>`it loads every row of "${r}"`},s={categories:["PERFORMANCE"],description:"A query calls `.collect()` with no `.withIndex()` and no `.filter()`, so it materializes every row of the table. Any live subscription over it also re-sends that whole result to every subscribed client on every write to the table.",facing:"EXTERNAL",level:"WARN",name:"unbounded_collect",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))`, cap it with `.take(n)`, or page it with `.paginate(args.paginationOpts)` so neither the scan nor the subscription payload grows with the table.',run:r=>{const o=[],i=Jo(r.schema);for(const e of r.queries??[]){if(e.terminal!=="collect"||e.hasIndex||e.table===""||e.hasFilter)continue;const t=i.get(e.table),{level:n,scope:l}=pe.get(t??"")??fe,p=Yo(e),f=n==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${e.table}" re-runs it and re-sends the full result to each subscribed socket.`:" Cap it with `.take(n)` if that count can grow.";o.push(a(s,{cacheKey:`unbounded_collect:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${p} calls .collect() with no index and no filter — ${l(e.table)}.${f}`,level:n,metadata:{exportName:e.exportName,file:e.file,line:e.line,shardKind:t??"unknown",table:e.table}}))}return o},source:"static",title:"Unbounded collect"},ce=[ao,Do,Po,me,ae,Lo,B,H,V,G,Y,_,Z,J,z,Zo,$,ee,re,L,Xo,se,Co,j,Q,s,qo,ho,go,to,Oo,T,Wo,Ko,fo,co,P,D,xo,te,ie,Go,ko,oe,oo,zo,S,Ao,lo,Mo,O,Io,No,mo,R,y,w,vo,Ho,so,E,v,A,ne,po,M,To,q,K,k,C,I,x,N,F,no,$o,Fo,W,jo,Qo,Bo,Vo,eo,ro,Eo,X,U,io,Uo,_o,uo,So,bo,wo,yo,Ro,m],de=[g,b,d,u,h],ue=[...ce,...de],ft=(r,o={})=>{const i=o.lints??ue,e=[];for(const t of i)o.source!==void 0&&t.source!==o.source||e.push(...t.run(r));return c(e)};export{ue as ALL_LINTS,wt as MAP_VERSION,de as RUNTIME_LINTS,ce as STATIC_LINTS,y as actionFetchSsrf,w as actionWithoutErrorHandling,S as adminRouteWithoutGuard,v as aiRawRunEscapeHatch,A as aiRunWithoutLogging,U as aiToolSideEffectPromptInjection,R as aiUnboundedGenerationPublic,W as allowUnauthenticatedShardAccessEnabled,T as authApiCallWithoutHeaders,I as authCsrfCheckDisabled,N as authEmailVerificationDisabled,k as authScimWithoutTransactions,x as authSecureCookiesDisabled,F as authSessionFreshageZero,C as authTrustedOriginsWildcard,O as browserAllowPrivateTargets,M as browserUserUrlWithoutAllowlist,St as byCodepoint,$ as circularFk,Ut as classifySensitivity,ut as compareToBaseline,d as constraintValidator,E as containerInstanceKeyFromUserInput,P as containerOversizedInstance,D as containerPublicInternet,K as containerRuntimeEgressRelaxation,q as containerStartEnableInternetOverride,c as dedupeCacheKeys,L as duplicateIndex,G as emptyIndex,u as errorRateOutlier,_ as errorWithoutCatalog,z as exportSinkMisconfigured,B as externalSourceIncrementalNoDeletePath,H as externalSourceOnGlobal,V as externalSourceUnscoped,h as fanOutBreadth,j as filterOnPrimaryKey,Q as filterWithoutIndex,X as flagGatesSecurityWithUnsafeDefault,Wt as fromServerSchema,Z as geoIndexFieldNotGeopoint,J as geoIndexUnused,Y as globalTableNearColumnLimit,bt as gradeFromScore,oo as hardcodedSecret,g as hotShard,eo as httpActionMissingAuthGuard,ro as httpActionResponseHeaderInjection,to as hyperdriveOutsideAction,io as identityUndeclaredClaimTrusted,no as imagesUrlSourceFromUserInput,ao as indexReferencesUnknownField,b as indexUtilization,mo as insertManyUnsafeUserData,so as kvUnscopedUserKeyIdor,lo as mailInboundDispatchWithoutVerify,po as mailRecipientFromRequestInput,fo as maskUncoveredPiiColumn,co as maskWeakHashStrategyOnPii,uo as maskedRelationLeakViaWith,ho as mutatorFullRowReplace,go as nondeterministicQueryMutation,bo as normalizeIdUsedAsAuthorization,yo as notifyMissingPushConfig,wo as notifySendOutsideAction,So as outputProjectionMissingOnPublicRead,vo as ownerFieldFromArgsNotAuth,ht as parseAdvisorMap,Ao as paymentCreateWithoutAuthorize,Uo as paymentWebhookWideTolerance,Ro as plaintextSecretInWranglerVariables,Wo as policyReferencesUnknownTable,To as privilegedDispatchUnvalidatedPayload,Io as privilegedFanoutFromPublicProcedure,No as procedureWithoutStructuredEvent,ko as publicArgumentUsesAny,xo as publicMutationWithoutRatelimit,Fo as publicTableRlsOptoutConfusion,Co as queueWithoutDlq,Oo as r2sqlOutsideAction,Mo as ratelimitDefaultMemoryStore,$o as ratelimitKeySpoofableOrGlobal,Eo as ratelimitMiddlewareFailOpen,Po as relationReferencesUnknownField,Do as relationReferencesUnknownTable,Ko as rlsUncoveredTable,ft as runAdvisor,vt as scoreAdvisor,qo as shapeTargetsGlobalTable,Lo as shapeUnknownTable,Go as signupMutationWithoutDisposableGating,_o as softDeleteIncludeDeletedFromArgs,zo as sqlInjectionRisk,Bo as storageGenerateUploadUrlNoContentTypePin,Ho as storageKeyFromUserArgs,Vo as storagePresignedUrlForPrivateContent,jo as storageUploadWithoutContentTypeAllowlist,Qo as storageUploadWithoutMaxSize,Xo as tableWithoutInsert,Zo as ttlFieldNotTimestamp,oe as unboundedStringArgument,ee as unindexedForeignKey,re as unindexedRelationTarget,te as unrestrictedWhereBranch,ie as userCreatingMutationWithoutCaptcha,ne as vectorsNamespaceFromUserInput,ae as workflowDuplicateStepName,me as workflowUnknownTarget,se as workflowUnused};
1
+ import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-DtBOHffV.mjs";import d from"./packem_shared/constraintValidator-DoRJD9Is.mjs";import u from"./packem_shared/errorRateOutlier-pr3_ZKbq.mjs";import h from"./packem_shared/fanOutBreadth-CBtmZnoh.mjs";import g from"./packem_shared/hotShard-BwGYZ3Tq.mjs";import b from"./packem_shared/indexUtilization-CkVPZcVe.mjs";import y from"./packem_shared/actionFetchSsrf-Z61P0o8U.mjs";import w from"./packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs";import S from"./packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs";import v from"./packem_shared/aiRawRunEscapeHatch-Dq43DVD9.mjs";import A from"./packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs";import U from"./packem_shared/aiToolSideEffectPromptInjection-K42X3QzJ.mjs";import R from"./packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs";import W from"./packem_shared/allowUnauthenticatedShardAccessEnabled-CjXI-kda.mjs";import T from"./packem_shared/authApiCallWithoutHeaders-C1OOWML5.mjs";import I from"./packem_shared/authCsrfCheckDisabled-Dz27bDzp.mjs";import N from"./packem_shared/authEmailVerificationDisabled-QTDD7TAU.mjs";import k from"./packem_shared/authScimWithoutTransactions-FqdTmUJs.mjs";import x from"./packem_shared/authSecureCookiesDisabled-CrGYulfJ.mjs";import F from"./packem_shared/authSessionFreshageZero-yWE6CGYP.mjs";import C from"./packem_shared/authTrustedOriginsWildcard-xpeRXfGF.mjs";import O from"./packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs";import M from"./packem_shared/browserUserUrlWithoutAllowlist-Wl3xHr7v.mjs";import $ from"./packem_shared/circularFk-DtcWFJxK.mjs";import D from"./packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs";import E from"./packem_shared/containerInstanceKeyFromUserInput-uEQQVsEz.mjs";import P from"./packem_shared/containerOversizedInstance-Bx89uR7E.mjs";import K from"./packem_shared/containerPublicInternet-BFfZf_P4.mjs";import q from"./packem_shared/containerRuntimeEgressRelaxation-pkpyXNou.mjs";import L from"./packem_shared/containerStartEnableInternetOverride-BBY4PMG1.mjs";import G from"./packem_shared/duplicateIndex-Cip6-Rpu.mjs";import _ from"./packem_shared/emptyIndex-BnHDcXza.mjs";import z from"./packem_shared/errorWithoutCatalog-BTfvaXHR.mjs";import H from"./packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs";import B from"./packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs";import V from"./packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs";import j from"./packem_shared/externalSourceUnscoped-BxU2uSXk.mjs";import Q from"./packem_shared/filterOnPrimaryKey-COQRpmnu.mjs";import X from"./packem_shared/filterWithoutIndex-Cf-tXY_A.mjs";import Z from"./packem_shared/flagGatesSecurityWithUnsafeDefault-i-Befg8b.mjs";import J from"./packem_shared/geoIndexFieldNotGeopoint-D0lOTm-_.mjs";import Y from"./packem_shared/geoIndexUnused-D7C9Qr4U.mjs";import oo from"./packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs";import eo from"./packem_shared/hardcodedSecret-Bw_4FYrs.mjs";import ro from"./packem_shared/httpActionMissingAuthGuard-BS3JZgaz.mjs";import to from"./packem_shared/httpActionResponseHeaderInjection-DHnc8c9f.mjs";import io from"./packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs";import no from"./packem_shared/identityUndeclaredClaimTrusted-BDFqB7Dw.mjs";import ao from"./packem_shared/imagesUrlSourceFromUserInput-DizsRh3M.mjs";import mo from"./packem_shared/indexReferencesUnknownField-B_c3o9QR.mjs";import so from"./packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs";import lo from"./packem_shared/kvUnscopedUserKeyIdor-CIgMPzFj.mjs";import po from"./packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs";import fo from"./packem_shared/mailRecipientFromRequestInput-SDCchLP7.mjs";import co from"./packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs";import uo from"./packem_shared/maskWeakHashStrategyOnPii-BC1n0F4-.mjs";import ho from"./packem_shared/maskedRelationLeakViaWith-C8n-Mzb1.mjs";import{e as a}from"./packem_shared/finding-NrKO8idM.mjs";import go from"./packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs";import bo from"./packem_shared/nondeterministicQueryMutation-sxmixc0l.mjs";import yo from"./packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs";import wo from"./packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs";import So from"./packem_shared/notifySendOutsideAction-CU4T16cR.mjs";import vo from"./packem_shared/outputProjectionMissingOnPublicRead-BZI8Y7am.mjs";import Ao from"./packem_shared/ownerFieldFromArgsNotAuth-CjOSUDKi.mjs";import Uo from"./packem_shared/paymentCreateWithoutAuthorize-CICGmRFR.mjs";import Ro from"./packem_shared/paymentWebhookWideTolerance-B-F5jOeA.mjs";import Wo from"./packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs";import To from"./packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs";import Io from"./packem_shared/privilegedDispatchUnvalidatedPayload-C6Qtc8aT.mjs";import No from"./packem_shared/privilegedFanoutFromPublicProcedure-Coqt23CQ.mjs";import ko from"./packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs";import xo from"./packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs";import Fo from"./packem_shared/publicMutationWithoutRatelimit-Dsd4cSPD.mjs";import Co from"./packem_shared/publicTableRlsOptoutConfusion-Dz7lbKKe.mjs";import Oo from"./packem_shared/queueWithoutDlq-BvHz3Opg.mjs";import Mo from"./packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs";import $o from"./packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs";import Do from"./packem_shared/ratelimitKeySpoofableOrGlobal-BzDzxnXm.mjs";import Eo from"./packem_shared/ratelimitMiddlewareFailOpen-iDJq68qx.mjs";import Po from"./packem_shared/relationReferencesUnknownField-D8Qyth_P.mjs";import Ko from"./packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs";import qo from"./packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs";import Lo from"./packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs";import Go from"./packem_shared/shapeUnknownTable-CREfNnWi.mjs";import _o from"./packem_shared/signupMutationWithoutDisposableGating-BIsSLyhJ.mjs";import zo from"./packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs";import Ho from"./packem_shared/sqlInjectionRisk-CslaRBxz.mjs";import Bo from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-CTIU_7Fj.mjs";import Vo from"./packem_shared/storageKeyFromUserArgs-C-QUcdJ3.mjs";import jo from"./packem_shared/storagePresignedUrlForPrivateContent-yeu1s81k.mjs";import Qo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-KSRG81Iu.mjs";import Xo from"./packem_shared/storageUploadWithoutMaxSize-BTiM9Q3A.mjs";import Zo from"./packem_shared/tableWithoutInsert-DQ-GxjFF.mjs";import Jo from"./packem_shared/ttlFieldNotTimestamp-E14I83wY.mjs";import{s as Yo,q as oe}from"./packem_shared/helpers-CwSEZdku.mjs";import ee from"./packem_shared/unboundedStringArgument-DzbFJc5q.mjs";import re from"./packem_shared/unindexedForeignKey-Dypgn8uH.mjs";import te from"./packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs";import ie from"./packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs";import ne from"./packem_shared/userCreatingMutationWithoutCaptcha-B7l-S-rV.mjs";import ae from"./packem_shared/vectorsNamespaceFromUserInput-CDSOrkyX.mjs";import me from"./packem_shared/workflowDuplicateStepName-BU4rg5So.mjs";import se from"./packem_shared/workflowUnknownTarget-B8H7jwnH.mjs";import le from"./packem_shared/workflowUnused-BUSOPdHq.mjs";import{compareToBaseline as gt,parseAdvisorMap as bt}from"./packem_shared/compareToBaseline-DBgN5YqX.mjs";import{gradeFromScore as wt}from"./packem_shared/gradeFromScore-KSt58rj1.mjs";import{MAP_VERSION as vt,byCodepoint as At,scoreAdvisor as Ut}from"./packem_shared/MAP_VERSION-DEJGm0wI.mjs";import{default as Wt}from"./packem_shared/classifySensitivity-JnjaTGYi.mjs";import{fromServerSchema as It}from"./packem_shared/fromServerSchema-D2nTknz0.mjs";const pe={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},m={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>a(m,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${pe[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},fe=new Map([["global",{level:"WARN",scope:r=>`it reads the whole D1 table "${r}" over a cross-region round trip`}],["root",{level:"WARN",scope:r=>`it loads every row of "${r}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:r=>`"${r}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),ce={level:"WARN",scope:r=>`it loads every row of "${r}"`},s={categories:["PERFORMANCE"],description:"A query calls `.collect()` with no `.withIndex()` and no `.filter()`, so it materializes every row of the table. Any live subscription over it also re-sends that whole result to every subscribed client on every write to the table.",facing:"EXTERNAL",level:"WARN",name:"unbounded_collect",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))`, cap it with `.take(n)`, or page it with `.paginate(args.paginationOpts)` so neither the scan nor the subscription payload grows with the table.',run:r=>{const o=[],i=Yo(r.schema);for(const e of r.queries??[]){if(e.terminal!=="collect"||e.hasIndex||e.table===""||e.hasFilter)continue;const t=i.get(e.table),{level:n,scope:l}=fe.get(t??"")??ce,p=oe(e),f=n==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${e.table}" re-runs it and re-sends the full result to each subscribed socket.`:" Cap it with `.take(n)` if that count can grow.";o.push(a(s,{cacheKey:`unbounded_collect:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${p} calls .collect() with no index and no filter — ${l(e.table)}.${f}`,level:n,metadata:{exportName:e.exportName,file:e.file,line:e.line,shardKind:t??"unknown",table:e.table}}))}return o},source:"static",title:"Unbounded collect"},de=[mo,Ko,Po,se,me,Go,B,V,j,_,oo,z,J,Y,H,Jo,D,$,re,te,G,Zo,le,Oo,Q,X,s,Lo,go,bo,io,Mo,T,To,qo,co,uo,P,K,Fo,ie,ne,_o,xo,ee,eo,Ho,S,Uo,po,$o,O,No,ko,so,R,y,w,Ao,Vo,lo,E,v,A,ae,fo,M,Io,L,q,k,C,I,x,N,F,ao,Do,Co,W,Qo,Xo,Bo,jo,ro,to,Eo,Z,U,no,Ro,zo,ho,vo,yo,So,wo,Wo,m],ue=[g,b,d,u,h],he=[...de,...ue],dt=(r,o={})=>{const i=o.lints??he,e=[];for(const t of i)o.source!==void 0&&t.source!==o.source||e.push(...t.run(r));return c(e)};export{he as ALL_LINTS,vt as MAP_VERSION,ue as RUNTIME_LINTS,de as STATIC_LINTS,y as actionFetchSsrf,w as actionWithoutErrorHandling,S as adminRouteWithoutGuard,v as aiRawRunEscapeHatch,A as aiRunWithoutLogging,U as aiToolSideEffectPromptInjection,R as aiUnboundedGenerationPublic,W as allowUnauthenticatedShardAccessEnabled,T as authApiCallWithoutHeaders,I as authCsrfCheckDisabled,N as authEmailVerificationDisabled,k as authScimWithoutTransactions,x as authSecureCookiesDisabled,F as authSessionFreshageZero,C as authTrustedOriginsWildcard,O as browserAllowPrivateTargets,M as browserUserUrlWithoutAllowlist,At as byCodepoint,$ as circularFk,Wt as classifySensitivity,D as commitOrderedHardDelete,gt as compareToBaseline,d as constraintValidator,E as containerInstanceKeyFromUserInput,P as containerOversizedInstance,K as containerPublicInternet,q as containerRuntimeEgressRelaxation,L as containerStartEnableInternetOverride,c as dedupeCacheKeys,G as duplicateIndex,_ as emptyIndex,u as errorRateOutlier,z as errorWithoutCatalog,H as exportSinkMisconfigured,B as externalSourceIncrementalNoDeletePath,V as externalSourceOnGlobal,j as externalSourceUnscoped,h as fanOutBreadth,Q as filterOnPrimaryKey,X as filterWithoutIndex,Z as flagGatesSecurityWithUnsafeDefault,It as fromServerSchema,J as geoIndexFieldNotGeopoint,Y as geoIndexUnused,oo as globalTableNearColumnLimit,wt as gradeFromScore,eo as hardcodedSecret,g as hotShard,ro as httpActionMissingAuthGuard,to as httpActionResponseHeaderInjection,io as hyperdriveOutsideAction,no as identityUndeclaredClaimTrusted,ao as imagesUrlSourceFromUserInput,mo as indexReferencesUnknownField,b as indexUtilization,so as insertManyUnsafeUserData,lo as kvUnscopedUserKeyIdor,po as mailInboundDispatchWithoutVerify,fo as mailRecipientFromRequestInput,co as maskUncoveredPiiColumn,uo as maskWeakHashStrategyOnPii,ho as maskedRelationLeakViaWith,go as mutatorFullRowReplace,bo as nondeterministicQueryMutation,yo as normalizeIdUsedAsAuthorization,wo as notifyMissingPushConfig,So as notifySendOutsideAction,vo as outputProjectionMissingOnPublicRead,Ao as ownerFieldFromArgsNotAuth,bt as parseAdvisorMap,Uo as paymentCreateWithoutAuthorize,Ro as paymentWebhookWideTolerance,Wo as plaintextSecretInWranglerVariables,To as policyReferencesUnknownTable,Io as privilegedDispatchUnvalidatedPayload,No as privilegedFanoutFromPublicProcedure,ko as procedureWithoutStructuredEvent,xo as publicArgumentUsesAny,Fo as publicMutationWithoutRatelimit,Co as publicTableRlsOptoutConfusion,Oo as queueWithoutDlq,Mo as r2sqlOutsideAction,$o as ratelimitDefaultMemoryStore,Do as ratelimitKeySpoofableOrGlobal,Eo as ratelimitMiddlewareFailOpen,Po as relationReferencesUnknownField,Ko as relationReferencesUnknownTable,qo as rlsUncoveredTable,dt as runAdvisor,Ut as scoreAdvisor,Lo as shapeTargetsGlobalTable,Go as shapeUnknownTable,_o as signupMutationWithoutDisposableGating,zo as softDeleteIncludeDeletedFromArgs,Ho as sqlInjectionRisk,Bo as storageGenerateUploadUrlNoContentTypePin,Vo as storageKeyFromUserArgs,jo as storagePresignedUrlForPrivateContent,Qo as storageUploadWithoutContentTypeAllowlist,Xo as storageUploadWithoutMaxSize,Zo as tableWithoutInsert,Jo as ttlFieldNotTimestamp,ee as unboundedStringArgument,re as unindexedForeignKey,te as unindexedRelationTarget,ie as unrestrictedWhereBranch,ne as userCreatingMutationWithoutCaptcha,ae as vectorsNamespaceFromUserInput,me as workflowDuplicateStepName,se as workflowUnknownTarget,le as workflowUnused};
@@ -0,0 +1 @@
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const n={categories:["SCHEMA"],description:"A `.commitOrdered()` table without `.softDelete()` cannot express a delete in its `_commitSeq` feed: the sequence lives on the row, so a hard-deleted row vanishes with no event a consumer can observe.",facing:"INTERNAL",level:"WARN",name:"commit_ordered_hard_delete",remediation:"Add `.softDelete()` so the tombstone flip advances `_commitSeq` and pages through the feed — or, if the table is genuinely append-only and nothing is ever deleted from it, leave it as-is.",run:o=>{const t=[];for(const e of o.schema.tables)e.commitOrdered!==!0||e.softDelete!==void 0||t.push(a(n,{cacheKey:`commit_ordered_hard_delete:${e.name}`,detail:`Table "${e.name}" is \`.commitOrdered()\` but not \`.softDelete()\`. A hard delete removes the row and its \`_commitSeq\` together, so a changefeed paging on the sequence never learns the row is gone.`,metadata:{table:e.name}}));return t},source:"static",title:"Commit-ordered table cannot express deletes"};export{n as default};
@@ -1 +1 @@
1
- import{e as r}from"./finding-NrKO8idM.mjs";import{q as a}from"./helpers-DcWLLrH0.mjs";const l={categories:["PERFORMANCE"],description:"A query filters on `_id`, the primary key. That walks the table comparing every row's id, when `ctx.db.get(id)` fetches the row directly. There is no case where the scan is preferable.",facing:"EXTERNAL",level:"WARN",name:"filter_on_primary_key",remediation:'Replace `ctx.db.query("table").filter((d) => d._id === id).first()` with `ctx.db.get(id)`. Passing a typed `Id<"table">` also narrows the result to that table\'s `Doc`, where the scan form returns the shared row type.',run:t=>(t.queries??[]).filter(e=>e.filtersPrimaryKey===!0&&e.table!=="").map(e=>{const i=a(e);return r(l,{cacheKey:`filter_on_primary_key:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${i} filters on \`_id\` — it scans "${e.table}" to find a row \`ctx.db.get(id)\` addresses directly.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,table:e.table}})}),source:"static",title:"Filter on primary key instead of ctx.db.get"};export{l as default};
1
+ import{e as r}from"./finding-NrKO8idM.mjs";import{q as a}from"./helpers-CwSEZdku.mjs";const l={categories:["PERFORMANCE"],description:"A query filters on `_id`, the primary key. That walks the table comparing every row's id, when `ctx.db.get(id)` fetches the row directly. There is no case where the scan is preferable.",facing:"EXTERNAL",level:"WARN",name:"filter_on_primary_key",remediation:'Replace `ctx.db.query("table").filter((d) => d._id === id).first()` with `ctx.db.get(id)`. Passing a typed `Id<"table">` also narrows the result to that table\'s `Doc`, where the scan form returns the shared row type.',run:t=>(t.queries??[]).filter(e=>e.filtersPrimaryKey===!0&&e.table!=="").map(e=>{const i=a(e);return r(l,{cacheKey:`filter_on_primary_key:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${i} filters on \`_id\` — it scans "${e.table}" to find a row \`ctx.db.get(id)\` addresses directly.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,table:e.table}})}),source:"static",title:"Filter on primary key instead of ctx.db.get"};export{l as default};
@@ -1 +1 @@
1
- import{e as s}from"./finding-NrKO8idM.mjs";import{s as c,q as f}from"./helpers-DcWLLrH0.mjs";const l={categories:["PERFORMANCE"],description:"A query calls `.filter()` without a `.withIndex()` / `.withSearchIndex()`, so it loads every row in the table and filters in memory — a full table scan that gets linearly slower as the table grows.",facing:"EXTERNAL",level:"WARN",name:"filter_without_index",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))` first, then `.filter()` only for what the index cannot express.',run:i=>{const t=[],d=c(i.schema);for(const e of i.queries??[]){if(!e.hasFilter||e.hasIndex||e.table===""||e.filtersPrimaryKey===!0)continue;const n=f(e),a=d.get(e.table),o={exportName:e.exportName,file:e.file,line:e.line,shardKind:a??"unknown",table:e.table},r=`filter_without_index:${e.file}:${e.line.toString()}:${e.table}`;if(a==="shardBy"){t.push(s(l,{cacheKey:r,detail:`Query on "${e.table}" at ${n} calls .filter() without an index. "${e.table}" is \`.shardBy()\`, so the read is already scoped to one shard rather than the whole table — this is bounded by one tenant's row count, not the dataset. Add an index if that per-shard count grows large.`,level:"INFO",metadata:o}));continue}const h=new Map([["global",`it scans the whole D1 table "${e.table}" — unbounded, and the cost is a cross-region round trip`],["root",`it loads every row of "${e.table}" from the root Durable Object's SQLite and filters in memory`]]).get(a??"")??`it loads every row of "${e.table}" and filters in memory`;t.push(s(l,{cacheKey:r,detail:`Query on "${e.table}" at ${n} calls .filter() without an index — ${h}.`,metadata:o}))}return t},source:"static",title:"Filter without index"};export{l as default};
1
+ import{e as s}from"./finding-NrKO8idM.mjs";import{s as c,q as f}from"./helpers-CwSEZdku.mjs";const l={categories:["PERFORMANCE"],description:"A query calls `.filter()` without a `.withIndex()` / `.withSearchIndex()`, so it loads every row in the table and filters in memory — a full table scan that gets linearly slower as the table grows.",facing:"EXTERNAL",level:"WARN",name:"filter_without_index",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))` first, then `.filter()` only for what the index cannot express.',run:i=>{const t=[],d=c(i.schema);for(const e of i.queries??[]){if(!e.hasFilter||e.hasIndex||e.table===""||e.filtersPrimaryKey===!0)continue;const n=f(e),a=d.get(e.table),o={exportName:e.exportName,file:e.file,line:e.line,shardKind:a??"unknown",table:e.table},r=`filter_without_index:${e.file}:${e.line.toString()}:${e.table}`;if(a==="shardBy"){t.push(s(l,{cacheKey:r,detail:`Query on "${e.table}" at ${n} calls .filter() without an index. "${e.table}" is \`.shardBy()\`, so the read is already scoped to one shard rather than the whole table — this is bounded by one tenant's row count, not the dataset. Add an index if that per-shard count grows large.`,level:"INFO",metadata:o}));continue}const h=new Map([["global",`it scans the whole D1 table "${e.table}" — unbounded, and the cost is a cross-region round trip`],["root",`it loads every row of "${e.table}" from the root Durable Object's SQLite and filters in memory`]]).get(a??"")??`it loads every row of "${e.table}" and filters in memory`;t.push(s(l,{cacheKey:r,detail:`Query on "${e.table}" at ${n} calls .filter() without an index — ${h}.`,metadata:o}))}return t},source:"static",title:"Filter without index"};export{l as default};
@@ -1 +1 @@
1
- const l=i=>({rlsMode:i.rlsMode,tables:Object.entries(i.tables).map(([t,n])=>{const a=[...n.indexes.map(e=>({fields:e.fields,kind:"index",name:e.name,unique:e.unique})),...n.searchIndexes.map(e=>({fields:[e.field,...e.filterFields??[]],kind:"search",name:e.name})),...n.rankIndexes.map(e=>({fields:[...e.sortBy.map(r=>r.field),...e.partitionBy??[]],kind:"rank",name:e.name})),...n.vectorIndexes.map(e=>({fields:[e.field],kind:"vector",name:e.name})),...n.geoIndexes.map(e=>({fields:[e.field],kind:"geo",name:e.name}))],s={},o=new Set;for(const[e,r]of Object.entries(n.shape))if(r.kind==="optional"){const d=r._meta?.inner;s[e]=d?.kind??r.kind,o.add(e)}else s[e]=r.kind,r._meta?.column?.notNull===!1&&o.add(e);return{externallyManaged:n.isExternallyManaged??!1,externalSource:n.externalSource?{hasReconcile:n.externalSource.reconcileEveryMs!==void 0,hasSoftDelete:n.externalSource.softDeleteColumn!==void 0,hasTenantBy:n.externalSource.tenantBy!==void 0,mode:n.externalSource.mode}:void 0,columnKinds:s,fields:Object.keys(n.shape),indexes:a,isPublic:n.isPublic??!1,name:t,optionalFields:o,shardKind:n.shardMode.kind,softDelete:n.softDeleteMode,ttl:n.ttlPolicy,relations:Object.entries(n.relationMap).map(([e,r])=>({field:r.field,kind:r.kind,name:e,onDelete:r.onDelete,references:r.references,table:r.table}))}})});export{l as fromServerSchema};
1
+ const l=d=>({rlsMode:d.rlsMode,tables:Object.entries(d.tables).map(([t,n])=>{const a=[...n.indexes.map(e=>({fields:e.fields,kind:"index",name:e.name,unique:e.unique})),...n.searchIndexes.map(e=>({fields:[e.field,...e.filterFields??[]],kind:"search",name:e.name})),...n.rankIndexes.map(e=>({fields:[...e.sortBy.map(r=>r.field),...e.partitionBy??[]],kind:"rank",name:e.name})),...n.vectorIndexes.map(e=>({fields:[e.field],kind:"vector",name:e.name})),...n.geoIndexes.map(e=>({fields:[e.field],kind:"geo",name:e.name}))],o={},s=new Set;for(const[e,r]of Object.entries(n.shape))if(r.kind==="optional"){const i=r._meta?.inner;o[e]=i?.kind??r.kind,s.add(e)}else o[e]=r.kind,r._meta?.column?.notNull===!1&&s.add(e);return{externallyManaged:n.isExternallyManaged??!1,externalSource:n.externalSource?{hasReconcile:n.externalSource.reconcileEveryMs!==void 0,hasSoftDelete:n.externalSource.softDeleteColumn!==void 0,hasTenantBy:n.externalSource.tenantBy!==void 0,mode:n.externalSource.mode}:void 0,columnKinds:o,commitOrdered:n.commitOrderedMode,fields:Object.keys(n.shape),indexes:a,isPublic:n.isPublic??!1,name:t,optionalFields:s,shardKind:n.shardMode.kind,softDelete:n.softDeleteMode,ttl:n.ttlPolicy,relations:Object.entries(n.relationMap).map(([e,r])=>({field:r.field,kind:r.kind,name:e,onDelete:r.onDelete,references:r.references,table:r.table}))}})});export{l as fromServerSchema};
@@ -0,0 +1 @@
1
+ const s=["address","birthdate","creditCard","dateOfBirth","dob","driversLicense","email","firstName","fullName","lastName","nationalId","passport","phone","phoneNumber","socialSecurity","socialSecurityNumber","ssn","taxId"],i=/[^a-z0-9]/giu,o=/([a-z0-9])([A-Z])/gu,l=/[^a-zA-Z0-9]+/u,a=e=>e.replaceAll(i,"").toLowerCase(),n=e=>e.replaceAll(o,"$1_$2").split(l).filter(t=>t.length>0).map(t=>t.toLowerCase()),r=new Set(s.map(e=>a(e))),c=new Set(s.filter(e=>n(e).length===1).map(e=>e.toLowerCase())),d=new Set(["accountId","authorId","createdBy","createdById","organizationId","orgId","ownerId","tenantId","updatedBy","userId","workspaceId"]),I=e=>r.has(a(e))||n(e).some(t=>c.has(t));new Set(s);const S=new Set(["_creationTime","_id"]),m=e=>e.fields.filter(t=>d.has(t)||I(t)),u=e=>new Set([...S,...e.fields]),p=e=>e.visibility==="public"&&(e.kind==="mutation"||e.kind==="action"),N=e=>e.line>0?`${e.file}:${e.line.toString()}`:e.file,_=e=>new Map(e.tables.map(t=>[t.name,t.shardKind]));export{p as a,I as i,m as o,N as q,_ as s,u as t};
@@ -1 +1 @@
1
- import{e as i}from"./finding-NrKO8idM.mjs";import{t as r}from"./helpers-DcWLLrH0.mjs";const c={categories:["SCHEMA"],description:"An index references a column that is not declared on its table. The index can never match, and the typo would otherwise surface only at runtime.",facing:"INTERNAL",level:"ERROR",name:"index_references_unknown_field",remediation:"Fix the column name in the index declaration, or add the column to the table.",run:a=>a.schema.tables.flatMap(e=>{const o=r(e);return e.indexes.flatMap(n=>n.fields.filter(t=>!o.has(t)).map(t=>i(c,{cacheKey:`index_references_unknown_field:${e.name}:${n.name}:${t}`,detail:`Index "${n.name}" on table "${e.name}" references column "${t}", which is not declared on the table.`,metadata:{field:t,index:n.name,indexKind:n.kind,table:e.name}})))}),source:"static",title:"Index references unknown field"};export{c as default};
1
+ import{e as i}from"./finding-NrKO8idM.mjs";import{t as r}from"./helpers-CwSEZdku.mjs";const c={categories:["SCHEMA"],description:"An index references a column that is not declared on its table. The index can never match, and the typo would otherwise surface only at runtime.",facing:"INTERNAL",level:"ERROR",name:"index_references_unknown_field",remediation:"Fix the column name in the index declaration, or add the column to the table.",run:a=>a.schema.tables.flatMap(e=>{const o=r(e);return e.indexes.flatMap(n=>n.fields.filter(t=>!o.has(t)).map(t=>i(c,{cacheKey:`index_references_unknown_field:${e.name}:${n.name}:${t}`,detail:`Index "${n.name}" on table "${e.name}" references column "${t}", which is not declared on the table.`,metadata:{field:t,index:n.name,indexKind:n.kind,table:e.name}})))}),source:"static",title:"Index references unknown field"};export{c as default};
@@ -0,0 +1 @@
1
+ import{e as i}from"./finding-NrKO8idM.mjs";import{i as s}from"./helpers-CwSEZdku.mjs";const o={categories:["SECURITY"],description:'A `mask(policies)` column named like PII (email, SSN, phone, …) uses the `"hash"` strategy. `"hash"` is an unsalted 32-bit FNV-1a digest — brute-force-recoverable and cross-row-correlatable — not a confidentiality control, so it does not actually hide the value.',facing:"EXTERNAL",level:"WARN",name:"mask_weak_hash_strategy_on_pii",remediation:'Use `"redact"` instead of `"hash"` for this column — e.g. `mask({ <table>: { <column>: "redact" } })`. Reserve `"hash"` for columns where a stable, joinable pseudonym is the goal and the value itself is not sensitive.',run:a=>a.maskStrategies===void 0?[]:a.maskStrategies.filter(e=>e.strategy==="hash"&&s(e.column)).map(e=>i(o,{cacheKey:`mask_weak_hash_strategy_on_pii:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` in ${e.file} masks \`${e.table===""?e.column:`${e.table}.${e.column}`}\` with \`"hash"\` — its name suggests PII, and \`"hash"\` is brute-force-recoverable and leaks cross-row equality. Use \`"redact"\` instead.`,metadata:{column:e.column,exportName:e.exportName,file:e.file,table:e.table}})),source:"static",title:'Weak "hash" mask strategy on a PII column'};export{o as default};
@@ -0,0 +1 @@
1
+ import{e as l}from"./finding-NrKO8idM.mjs";import{i as r}from"./helpers-CwSEZdku.mjs";const s={categories:["SECURITY"],description:"A `.public()` `query` returns raw table rows (no `.output(...)` projection, no `.use(mask(...))`) from a table carrying PII-named columns (`email`, `phone`, `ssn`, …). Every column ships to the caller, and a column added to the table later leaks by default.",facing:"EXTERNAL",level:"INFO",name:"output_projection_missing_on_public_read",remediation:"Add an explicit return projection to the public query — `.output(v.object({ … }))` listing only the fields a client needs — or apply a `.use(mask(...))` policy to the PII columns. This makes the exposed shape intentional and stops a newly-added column from leaking through this query by default.",run:o=>{if(o.rawRowReturns===void 0)return[];const i=new Map;for(const e of o.schema.tables){const t=e.fields.filter(a=>r(a));t.length>0&&i.set(e.name,t)}const n=[];for(const e of o.rawRowReturns){if(e.visibility!=="public"||e.usesOutput||e.usesMask||e.table==="")continue;const t=i.get(e.table);t!==void 0&&n.push(l(s,{cacheKey:`output_projection_missing_on_public_read:${e.file}:${e.line.toString()}`,detail:`Public query \`${e.exportName}\` (${e.file}:${e.line.toString()}) returns raw \`${e.table}\` rows with no \`.output(...)\` projection — shipping PII column(s) ${t.join(", ")} to every caller, and any column added to \`${e.table}\` later leaks by default. Project the return with \`.output(v.object({ … }))\` or mask the PII columns.`,metadata:{columns:t,exportName:e.exportName,file:e.file,line:e.line,table:e.table}}))}return n},source:"static",title:"Public query returns raw rows with PII and no output projection"};export{s as default};
@@ -1 +1 @@
1
- import{e as a}from"./finding-NrKO8idM.mjs";import{i as r}from"./helpers-DcWLLrH0.mjs";const s=/contact|forgot|login|magic|otp|register|reset|signin|signup|subscribe|verify/iu,o={categories:["SECURITY"],description:"A public `mutation`/`action` has no `rateLimit` middleware. Publicly-callable writes are flood and brute-force targets — an attacker can exhaust writes, mail quota, or credits, or guess credentials on auth-shaped endpoints.",facing:"EXTERNAL",level:"WARN",name:"public_mutation_without_ratelimit",remediation:'Attach a rate limit: `.use(rateLimit(limiter, "<bucket>"))` from `@lunora/ratelimit`, or wrap the recommended public-procedure guards with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Genuinely-open writes can be acknowledged by adding a permissive limiter.',run:i=>i.procedureProtections===void 0?[]:i.procedureProtections.filter(t=>r(t)&&!t.usesRateLimit).map(t=>{const e=s.test(t.exportName);return a(o,{cacheKey:`public_mutation_without_ratelimit:${t.file}:${t.exportName}`,detail:`Public ${t.kind} \`${t.exportName}\` (${t.file}) has no rate limit${e?" — its name suggests an auth/abuse-sensitive endpoint, so this is high-risk":""}. Add \`.use(rateLimit(...))\` or \`.use(protectPublic({ rateLimit }))\`.`,metadata:{exportName:t.exportName,file:t.file,kind:t.kind,sensitive:e}})}),source:"static",title:"Public write without a rate limit"};export{o as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";import{a as r}from"./helpers-CwSEZdku.mjs";const s=/contact|forgot|login|magic|otp|register|reset|signin|signup|subscribe|verify/iu,o={categories:["SECURITY"],description:"A public `mutation`/`action` has no `rateLimit` middleware. Publicly-callable writes are flood and brute-force targets — an attacker can exhaust writes, mail quota, or credits, or guess credentials on auth-shaped endpoints.",facing:"EXTERNAL",level:"WARN",name:"public_mutation_without_ratelimit",remediation:'Attach a rate limit: `.use(rateLimit(limiter, "<bucket>"))` from `@lunora/ratelimit`, or wrap the recommended public-procedure guards with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Genuinely-open writes can be acknowledged by adding a permissive limiter.',run:i=>i.procedureProtections===void 0?[]:i.procedureProtections.filter(t=>r(t)&&!t.usesRateLimit).map(t=>{const e=s.test(t.exportName);return a(o,{cacheKey:`public_mutation_without_ratelimit:${t.file}:${t.exportName}`,detail:`Public ${t.kind} \`${t.exportName}\` (${t.file}) has no rate limit${e?" — its name suggests an auth/abuse-sensitive endpoint, so this is high-risk":""}. Add \`.use(rateLimit(...))\` or \`.use(protectPublic({ rateLimit }))\`.`,metadata:{exportName:t.exportName,file:t.file,kind:t.kind,sensitive:e}})}),source:"static",title:"Public write without a rate limit"};export{o as default};
@@ -1 +1 @@
1
- import{e as i}from"./finding-NrKO8idM.mjs";import{o as a}from"./helpers-DcWLLrH0.mjs";const n={categories:["SECURITY"],description:'`.public()` opts a table OUT of the schema\'s `.rls("required")` enforcement — it does not mean the table holds public data. A `.public()` table whose columns look ownership- or PII-shaped (`userId`, `email`, `ssn`, …) is exempt from the row-security guard, not "safe to expose".',facing:"EXTERNAL",level:"WARN",name:"public_table_rls_optout_confusion",remediation:'Double-check this table actually needs the RLS opt-out. If it does not, remove `.public()` so `.rls("required")` covers it like every other table. If it genuinely must stay open (a shared lookup table, for instance), rename or comment it so the exemption is intentional rather than a name-implies-safety mistake, and make sure no procedure trusts `.public()` as an authorization boundary.',run:o=>{if(o.schema.rlsMode!=="required")return[];const s=[];for(const e of o.schema.tables){if(!e.isPublic)continue;const t=a(e);t.length!==0&&s.push(i(n,{cacheKey:`public_table_rls_optout_confusion:${e.name}`,detail:`Table "${e.name}" is \`.public()\` (opted OUT of the schema's \`.rls("required")\` enforcement) but carries ownership/PII-shaped column(s): ${t.join(", ")}. \`.public()\` means "exempt from RLS", not "safe to expose" — confirm this table really needs the opt-out.`,metadata:{columns:t,table:e.name}}))}return s},source:"static",title:"Public table opts out of RLS but carries sensitive columns"};export{n as default};
1
+ import{e as i}from"./finding-NrKO8idM.mjs";import{o as a}from"./helpers-CwSEZdku.mjs";const n={categories:["SECURITY"],description:'`.public()` opts a table OUT of the schema\'s `.rls("required")` enforcement — it does not mean the table holds public data. A `.public()` table whose columns look ownership- or PII-shaped (`userId`, `email`, `ssn`, …) is exempt from the row-security guard, not "safe to expose".',facing:"EXTERNAL",level:"WARN",name:"public_table_rls_optout_confusion",remediation:'Double-check this table actually needs the RLS opt-out. If it does not, remove `.public()` so `.rls("required")` covers it like every other table. If it genuinely must stay open (a shared lookup table, for instance), rename or comment it so the exemption is intentional rather than a name-implies-safety mistake, and make sure no procedure trusts `.public()` as an authorization boundary.',run:o=>{if(o.schema.rlsMode!=="required")return[];const s=[];for(const e of o.schema.tables){if(!e.isPublic)continue;const t=a(e);t.length!==0&&s.push(i(n,{cacheKey:`public_table_rls_optout_confusion:${e.name}`,detail:`Table "${e.name}" is \`.public()\` (opted OUT of the schema's \`.rls("required")\` enforcement) but carries ownership/PII-shaped column(s): ${t.join(", ")}. \`.public()\` means "exempt from RLS", not "safe to expose" — confirm this table really needs the opt-out.`,metadata:{columns:t,table:e.name}}))}return s},source:"static",title:"Public table opts out of RLS but carries sensitive columns"};export{n as default};
@@ -1 +1 @@
1
- import{e as c}from"./finding-NrKO8idM.mjs";import{t as m}from"./helpers-DcWLLrH0.mjs";const f=(o,i,t,a)=>{const e=o.get(a.table);if(!e)return[];const n=a.kind==="one"?t:e,s=a.kind==="one"?e:t,r=[];return i(n).has(a.field)||r.push({column:a.field,owner:n.name,side:"field"}),i(s).has(a.references)||r.push({column:a.references,owner:s.name,side:"references"}),r},d={categories:["SCHEMA"],description:"A relation references a foreign-key or referenced column that is not declared on its table, so the join can never resolve.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_field",remediation:"Fix the `field` / `references` column name in the relation, or add the missing column.",run:o=>{const i=new Map(o.schema.tables.map(e=>[e.name,e])),t=new Map,a=e=>{let n=t.get(e);return n||(n=m(e),t.set(e,n)),n};return o.schema.tables.flatMap(e=>e.relations.flatMap(n=>f(i,a,e,n).map(s=>c(d,{cacheKey:`relation_references_unknown_field:${e.name}:${n.name}:${s.side}`,detail:`Relation "${n.name}" on table "${e.name}" uses ${s.side} "${s.column}", which is not declared on table "${s.owner}".`,metadata:{column:s.column,owner:s.owner,relation:n.name,side:s.side,table:e.name}}))))},source:"static",title:"Relation references unknown field"};export{d as default};
1
+ import{e as c}from"./finding-NrKO8idM.mjs";import{t as m}from"./helpers-CwSEZdku.mjs";const f=(o,i,t,a)=>{const e=o.get(a.table);if(!e)return[];const n=a.kind==="one"?t:e,s=a.kind==="one"?e:t,r=[];return i(n).has(a.field)||r.push({column:a.field,owner:n.name,side:"field"}),i(s).has(a.references)||r.push({column:a.references,owner:s.name,side:"references"}),r},d={categories:["SCHEMA"],description:"A relation references a foreign-key or referenced column that is not declared on its table, so the join can never resolve.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_field",remediation:"Fix the `field` / `references` column name in the relation, or add the missing column.",run:o=>{const i=new Map(o.schema.tables.map(e=>[e.name,e])),t=new Map,a=e=>{let n=t.get(e);return n||(n=m(e),t.set(e,n)),n};return o.schema.tables.flatMap(e=>e.relations.flatMap(n=>f(i,a,e,n).map(s=>c(d,{cacheKey:`relation_references_unknown_field:${e.name}:${n.name}:${s.side}`,detail:`Relation "${n.name}" on table "${e.name}" uses ${s.side} "${s.column}", which is not declared on table "${s.owner}".`,metadata:{column:s.column,owner:s.owner,relation:n.name,side:s.side,table:e.name}}))))},source:"static",title:"Relation references unknown field"};export{d as default};
@@ -1 +1 @@
1
- import{e as s}from"./finding-NrKO8idM.mjs";import{m as i}from"./procedure-protections-DRQotu9I.mjs";import{i as o}from"./helpers-DcWLLrH0.mjs";const r={categories:["SECURITY"],description:"A public `mutation`/`action` that creates a user/session/account row has no disposable-email gate. Throwaway mailboxes farm free trials, evade bans, and pollute the user table.",facing:"EXTERNAL",level:"WARN",name:"signup_mutation_without_disposable_gating",remediation:"Add `.use(emailGateMiddleware({ email: (ctx) => ctx.args.email }))` from `@lunora/auth/email-guard` to reject disposable domains at signup, or gate better-auth's native signup with `withEmailGate(...)` / `emailGateDatabaseHooks(...)`.",run:a=>{if(a.procedureProtections===void 0)return[];const t=[];for(const e of a.procedureProtections)!o(e)||!i(e.writesUserTable)||e.usesEmailGate||i(e.hasEmailArg)&&t.push(s(r,{cacheKey:`signup_mutation_without_disposable_gating:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) writes a user/session/account table but has no disposable-email gate. Add \`.use(emailGateMiddleware(...))\` from \`@lunora/auth/email-guard\`.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind,writesUserTable:e.writesUserTable}}));return t},source:"static",title:"Account-creating write without a disposable-email gate"};export{r as default};
1
+ import{e as s}from"./finding-NrKO8idM.mjs";import{m as i}from"./procedure-protections-DRQotu9I.mjs";import{a as o}from"./helpers-CwSEZdku.mjs";const r={categories:["SECURITY"],description:"A public `mutation`/`action` that creates a user/session/account row has no disposable-email gate. Throwaway mailboxes farm free trials, evade bans, and pollute the user table.",facing:"EXTERNAL",level:"WARN",name:"signup_mutation_without_disposable_gating",remediation:"Add `.use(emailGateMiddleware({ email: (ctx) => ctx.args.email }))` from `@lunora/auth/email-guard` to reject disposable domains at signup, or gate better-auth's native signup with `withEmailGate(...)` / `emailGateDatabaseHooks(...)`.",run:a=>{if(a.procedureProtections===void 0)return[];const t=[];for(const e of a.procedureProtections)!o(e)||!i(e.writesUserTable)||e.usesEmailGate||i(e.hasEmailArg)&&t.push(s(r,{cacheKey:`signup_mutation_without_disposable_gating:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) writes a user/session/account table but has no disposable-email gate. Add \`.use(emailGateMiddleware(...))\` from \`@lunora/auth/email-guard\`.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind,writesUserTable:e.writesUserTable}}));return t},source:"static",title:"Account-creating write without a disposable-email gate"};export{r as default};
@@ -1 +1 @@
1
- import{e as i}from"./finding-NrKO8idM.mjs";import{m as a}from"./procedure-protections-DRQotu9I.mjs";import{i as s}from"./helpers-DcWLLrH0.mjs";const r=e=>e.writesUserTable===!0?"writes a user/session table":e.callsMail===!0?"sends mail":e.analyzableBody===!1?"may write a user/session table or send mail — its handler body could not be read":"may write a user/session table or send mail — at least one could not be determined",n={categories:["SECURITY"],description:"A public `mutation`/`action` that creates a user/session or sends mail has no CAPTCHA / bot check. Account-creating and mail-sending endpoints are prime automated-abuse targets (credential stuffing, mailbox flooding, disposable-account farming).",facing:"EXTERNAL",level:"WARN",name:"user_creating_mutation_without_captcha",remediation:"Add a server-verified human check: `.use(verifyTurnstile({ secret, token }))` from `@lunora/auth`, or wrap it with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Pair with a rate limit for defense in depth.",run:e=>e.procedureProtections===void 0?[]:e.procedureProtections.filter(t=>s(t)&&(a(t.writesUserTable)||a(t.callsMail))&&!t.usesCaptcha).map(t=>i(n,{cacheKey:`user_creating_mutation_without_captcha:${t.file}:${t.exportName}`,detail:`Public ${t.kind} \`${t.exportName}\` (${t.file}) ${r(t)} but has no CAPTCHA check. Add \`.use(verifyTurnstile(...))\` or \`.use(protectPublic({ captcha }))\`.`,metadata:{callsMail:t.callsMail,exportName:t.exportName,file:t.file,kind:t.kind,writesUserTable:t.writesUserTable}})),source:"static",title:"Account-creating / mail-sending write without a CAPTCHA"};export{n as default};
1
+ import{e as i}from"./finding-NrKO8idM.mjs";import{m as a}from"./procedure-protections-DRQotu9I.mjs";import{a as s}from"./helpers-CwSEZdku.mjs";const r=e=>e.writesUserTable===!0?"writes a user/session table":e.callsMail===!0?"sends mail":e.analyzableBody===!1?"may write a user/session table or send mail — its handler body could not be read":"may write a user/session table or send mail — at least one could not be determined",n={categories:["SECURITY"],description:"A public `mutation`/`action` that creates a user/session or sends mail has no CAPTCHA / bot check. Account-creating and mail-sending endpoints are prime automated-abuse targets (credential stuffing, mailbox flooding, disposable-account farming).",facing:"EXTERNAL",level:"WARN",name:"user_creating_mutation_without_captcha",remediation:"Add a server-verified human check: `.use(verifyTurnstile({ secret, token }))` from `@lunora/auth`, or wrap it with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Pair with a rate limit for defense in depth.",run:e=>e.procedureProtections===void 0?[]:e.procedureProtections.filter(t=>s(t)&&(a(t.writesUserTable)||a(t.callsMail))&&!t.usesCaptcha).map(t=>i(n,{cacheKey:`user_creating_mutation_without_captcha:${t.file}:${t.exportName}`,detail:`Public ${t.kind} \`${t.exportName}\` (${t.file}) ${r(t)} but has no CAPTCHA check. Add \`.use(verifyTurnstile(...))\` or \`.use(protectPublic({ captcha }))\`.`,metadata:{callsMail:t.callsMail,exportName:t.exportName,file:t.file,kind:t.kind,writesUserTable:t.writesUserTable}})),source:"static",title:"Account-creating / mail-sending write without a CAPTCHA"};export{n as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/advisor",
3
- "version": "1.0.0-alpha.83",
3
+ "version": "1.0.0-alpha.85",
4
4
  "description": "Schema & query lints (splinter-style advisors) for Lunora, feeding the Studio Advisors view",
5
5
  "keywords": [
6
6
  "advisor",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@lunora/errors": "1.0.0-alpha.22",
50
- "@lunora/server": "1.0.0-alpha.77"
50
+ "@lunora/server": "1.0.0-alpha.79"
51
51
  },
52
52
  "engines": {
53
53
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
1
- const n=new Set(["accountId","authorId","createdBy","createdById","organizationId","orgId","ownerId","tenantId","updatedBy","userId","workspaceId"]),i=new Set(["address","dateOfBirth","dob","email","firstName","lastName","phone","phoneNumber","socialSecurityNumber","ssn"]),s=new Set(["_creationTime","_id"]),a=t=>t.fields.filter(e=>n.has(e)||i.has(e)),o=t=>new Set([...s,...t.fields]),d=t=>t.visibility==="public"&&(t.kind==="mutation"||t.kind==="action"),c=t=>t.line>0?`${t.file}:${t.line.toString()}`:t.file,r=t=>new Map(t.tables.map(e=>[e.name,e.shardKind]));export{i as P,d as i,a as o,c as q,r as s,o as t};
@@ -1 +0,0 @@
1
- import{e as s}from"./finding-NrKO8idM.mjs";const i=/address|birthdate|creditcard|dateofbirth|dob|driverslicense|email|firstname|fullname|lastname|nationalid|passport|phone|socialsecurity|ssn|taxid/u,t=a=>i.test(a.replaceAll(/[^a-z0-9]/giu,"").toLowerCase()),o={categories:["SECURITY"],description:'A `mask(policies)` column named like PII (email, SSN, phone, …) uses the `"hash"` strategy. `"hash"` is an unsalted 32-bit FNV-1a digest — brute-force-recoverable and cross-row-correlatable — not a confidentiality control, so it does not actually hide the value.',facing:"EXTERNAL",level:"WARN",name:"mask_weak_hash_strategy_on_pii",remediation:'Use `"redact"` instead of `"hash"` for this column — e.g. `mask({ <table>: { <column>: "redact" } })`. Reserve `"hash"` for columns where a stable, joinable pseudonym is the goal and the value itself is not sensitive.',run:a=>a.maskStrategies===void 0?[]:a.maskStrategies.filter(e=>e.strategy==="hash"&&t(e.column)).map(e=>s(o,{cacheKey:`mask_weak_hash_strategy_on_pii:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` in ${e.file} masks \`${e.table===""?e.column:`${e.table}.${e.column}`}\` with \`"hash"\` — its name suggests PII, and \`"hash"\` is brute-force-recoverable and leaks cross-row equality. Use \`"redact"\` instead.`,metadata:{column:e.column,exportName:e.exportName,file:e.file,table:e.table}})),source:"static",title:'Weak "hash" mask strategy on a PII column'};export{o as default};
@@ -1 +0,0 @@
1
- import{e as l}from"./finding-NrKO8idM.mjs";import{P as r}from"./helpers-DcWLLrH0.mjs";const s={categories:["SECURITY"],description:"A `.public()` `query` returns raw table rows (no `.output(...)` projection, no `.use(mask(...))`) from a table carrying PII-named columns (`email`, `phone`, `ssn`, …). Every column ships to the caller, and a column added to the table later leaks by default.",facing:"EXTERNAL",level:"INFO",name:"output_projection_missing_on_public_read",remediation:"Add an explicit return projection to the public query — `.output(v.object({ … }))` listing only the fields a client needs — or apply a `.use(mask(...))` policy to the PII columns. This makes the exposed shape intentional and stops a newly-added column from leaking through this query by default.",run:o=>{if(o.rawRowReturns===void 0)return[];const i=new Map;for(const e of o.schema.tables){const t=e.fields.filter(a=>r.has(a));t.length>0&&i.set(e.name,t)}const n=[];for(const e of o.rawRowReturns){if(e.visibility!=="public"||e.usesOutput||e.usesMask||e.table==="")continue;const t=i.get(e.table);t!==void 0&&n.push(l(s,{cacheKey:`output_projection_missing_on_public_read:${e.file}:${e.line.toString()}`,detail:`Public query \`${e.exportName}\` (${e.file}:${e.line.toString()}) returns raw \`${e.table}\` rows with no \`.output(...)\` projection — shipping PII column(s) ${t.join(", ")} to every caller, and any column added to \`${e.table}\` later leaks by default. Project the return with \`.output(v.object({ … }))\` or mask the PII columns.`,metadata:{columns:t,exportName:e.exportName,file:e.file,line:e.line,table:e.table}}))}return n},source:"static",title:"Public query returns raw rows with PII and no output projection"};export{s as default};