@lunora/advisor 1.0.0-alpha.77 → 1.0.0-alpha.79

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 (142) hide show
  1. package/dist/index.mjs +1 -1
  2. package/dist/packem_shared/MAP_VERSION-DEJGm0wI.mjs +1 -0
  3. package/dist/packem_shared/{actionFetchSsrf-BbWxy1Yf.mjs → actionFetchSsrf-Z61P0o8U.mjs} +1 -1
  4. package/dist/packem_shared/{actionWithoutErrorHandling-rhzjCEqm.mjs → actionWithoutErrorHandling-4GCBT0_z.mjs} +1 -1
  5. package/dist/packem_shared/{adminRouteWithoutGuard-BbscPp1m.mjs → adminRouteWithoutGuard-DPE7LuNh.mjs} +1 -1
  6. package/dist/packem_shared/{aiRawRunEscapeHatch-DfhjYm43.mjs → aiRawRunEscapeHatch-Dq43DVD9.mjs} +1 -1
  7. package/dist/packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs +1 -0
  8. package/dist/packem_shared/{aiToolSideEffectPromptInjection-DnWQOoKi.mjs → aiToolSideEffectPromptInjection-K42X3QzJ.mjs} +1 -1
  9. package/dist/packem_shared/{aiUnboundedGenerationPublic-iu7crnSa.mjs → aiUnboundedGenerationPublic-C17h6sMV.mjs} +1 -1
  10. package/dist/packem_shared/{allowUnauthenticatedShardAccessEnabled-D4aMNBb0.mjs → allowUnauthenticatedShardAccessEnabled-CjXI-kda.mjs} +1 -1
  11. package/dist/packem_shared/argument-derived-sink-DLXFC79t.mjs +1 -0
  12. package/dist/packem_shared/{authApiCallWithoutHeaders-BHkik-EF.mjs → authApiCallWithoutHeaders-C1OOWML5.mjs} +1 -1
  13. package/dist/packem_shared/{authCsrfCheckDisabled-De9ammtV.mjs → authCsrfCheckDisabled-Dz27bDzp.mjs} +1 -1
  14. package/dist/packem_shared/{authEmailVerificationDisabled-BFKdJcx4.mjs → authEmailVerificationDisabled-QTDD7TAU.mjs} +1 -1
  15. package/dist/packem_shared/authScimWithoutTransactions-FqdTmUJs.mjs +1 -0
  16. package/dist/packem_shared/{authSecureCookiesDisabled-B4WIzvMm.mjs → authSecureCookiesDisabled-CrGYulfJ.mjs} +1 -1
  17. package/dist/packem_shared/{authSessionFreshageZero-Bpcaf_Pe.mjs → authSessionFreshageZero-yWE6CGYP.mjs} +1 -1
  18. package/dist/packem_shared/{authTrustedOriginsWildcard-DjU9NXEm.mjs → authTrustedOriginsWildcard-xpeRXfGF.mjs} +1 -1
  19. package/dist/packem_shared/{browserAllowPrivateTargets-Dxyeh6lg.mjs → browserAllowPrivateTargets-Cj5sizhv.mjs} +1 -1
  20. package/dist/packem_shared/{browserUserUrlWithoutAllowlist-BHBS6b0N.mjs → browserUserUrlWithoutAllowlist-Wl3xHr7v.mjs} +1 -1
  21. package/dist/packem_shared/circularFk-DtcWFJxK.mjs +1 -0
  22. package/dist/packem_shared/classifySensitivity-JnjaTGYi.mjs +1 -0
  23. package/dist/packem_shared/compareToBaseline-DBgN5YqX.mjs +1 -0
  24. package/dist/packem_shared/constraintValidator-DoRJD9Is.mjs +1 -0
  25. package/dist/packem_shared/{containerInstanceKeyFromUserInput-C8S4T3ty.mjs → containerInstanceKeyFromUserInput-uEQQVsEz.mjs} +1 -1
  26. package/dist/packem_shared/{containerOversizedInstance-B9wW_sU5.mjs → containerOversizedInstance-Bx89uR7E.mjs} +1 -1
  27. package/dist/packem_shared/{containerPublicInternet-g0TRsayn.mjs → containerPublicInternet-BFfZf_P4.mjs} +1 -1
  28. package/dist/packem_shared/{containerRuntimeEgressRelaxation-BQ2ZD1aV.mjs → containerRuntimeEgressRelaxation-pkpyXNou.mjs} +1 -1
  29. package/dist/packem_shared/{containerStartEnableInternetOverride-B62WUura.mjs → containerStartEnableInternetOverride-BBY4PMG1.mjs} +1 -1
  30. package/dist/packem_shared/dedupeCacheKeys-DtBOHffV.mjs +1 -0
  31. package/dist/packem_shared/{duplicateIndex-DFT7qLQD.mjs → duplicateIndex-Cip6-Rpu.mjs} +1 -1
  32. package/dist/packem_shared/{emptyIndex-Bp-Y81D0.mjs → emptyIndex-BnHDcXza.mjs} +1 -1
  33. package/dist/packem_shared/{errorRateOutlier-CgK42sq0.mjs → errorRateOutlier-pr3_ZKbq.mjs} +1 -1
  34. package/dist/packem_shared/{errorWithoutCatalog-0PbxiZL4.mjs → errorWithoutCatalog-BTfvaXHR.mjs} +1 -1
  35. package/dist/packem_shared/{exportSinkMisconfigured-DvpAaa6j.mjs → exportSinkMisconfigured-JfbAx9AI.mjs} +1 -1
  36. package/dist/packem_shared/{externalSourceIncrementalNoDeletePath-DnoL64YY.mjs → externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs} +1 -1
  37. package/dist/packem_shared/{externalSourceOnGlobal-IYnALPlO.mjs → externalSourceOnGlobal-CH7xbJ49.mjs} +1 -1
  38. package/dist/packem_shared/{externalSourceUnscoped-DqBp5jYr.mjs → externalSourceUnscoped-BxU2uSXk.mjs} +1 -1
  39. package/dist/packem_shared/fanOutBreadth-CBtmZnoh.mjs +1 -0
  40. package/dist/packem_shared/filterOnPrimaryKey-c37rjqBk.mjs +1 -0
  41. package/dist/packem_shared/{filterWithoutIndex-CyHJwVhc.mjs → filterWithoutIndex-DXb__LGI.mjs} +1 -1
  42. package/dist/packem_shared/finding-NrKO8idM.mjs +1 -0
  43. package/dist/packem_shared/fk-index-DXYS17cc.mjs +1 -0
  44. package/dist/packem_shared/{flagGatesSecurityWithUnsafeDefault-DPGFEHTt.mjs → flagGatesSecurityWithUnsafeDefault-i-Befg8b.mjs} +1 -1
  45. package/dist/packem_shared/fromServerSchema-w16riDj2.mjs +1 -0
  46. package/dist/packem_shared/{geoIndexFieldNotGeopoint-DAGLZtaH.mjs → geoIndexFieldNotGeopoint-D0lOTm-_.mjs} +1 -1
  47. package/dist/packem_shared/{geoIndexUnused-B5Oww9o2.mjs → geoIndexUnused-D7C9Qr4U.mjs} +1 -1
  48. package/dist/packem_shared/{globalTableNearColumnLimit-DBQwb2MS.mjs → globalTableNearColumnLimit-BFbBBd6A.mjs} +1 -1
  49. package/dist/packem_shared/gradeFromScore-KSt58rj1.mjs +1 -0
  50. package/dist/packem_shared/hardcodedSecret-Bw_4FYrs.mjs +1 -0
  51. package/dist/packem_shared/helpers-DcWLLrH0.mjs +1 -0
  52. package/dist/packem_shared/hotShard-BwGYZ3Tq.mjs +1 -0
  53. package/dist/packem_shared/httpActionMissingAuthGuard-BS3JZgaz.mjs +1 -0
  54. package/dist/packem_shared/{httpActionResponseHeaderInjection-D063rB7U.mjs → httpActionResponseHeaderInjection-DHnc8c9f.mjs} +1 -1
  55. package/dist/packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs +1 -0
  56. package/dist/packem_shared/{identityUndeclaredClaimTrusted-BYRYGQ74.mjs → identityUndeclaredClaimTrusted-BDFqB7Dw.mjs} +1 -1
  57. package/dist/packem_shared/{imagesUrlSourceFromUserInput-Cy5pc_dA.mjs → imagesUrlSourceFromUserInput-DizsRh3M.mjs} +1 -1
  58. package/dist/packem_shared/indexReferencesUnknownField-Dxd5jcVh.mjs +1 -0
  59. package/dist/packem_shared/{indexUtilization-QfbIH_w4.mjs → indexUtilization-CkVPZcVe.mjs} +1 -1
  60. package/dist/packem_shared/{insertManyUnsafeUserData-xLYAkcKj.mjs → insertManyUnsafeUserData-DWE8d_DF.mjs} +1 -1
  61. package/dist/packem_shared/{kvUnscopedUserKeyIdor-B4lGxRxh.mjs → kvUnscopedUserKeyIdor-CIgMPzFj.mjs} +1 -1
  62. package/dist/packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs +1 -0
  63. package/dist/packem_shared/{mailRecipientFromRequestInput-BR_oSh65.mjs → mailRecipientFromRequestInput-SDCchLP7.mjs} +1 -1
  64. package/dist/packem_shared/{maskUncoveredPiiColumn-DqPfZM2O.mjs → maskUncoveredPiiColumn-axEka4nd.mjs} +1 -1
  65. package/dist/packem_shared/{maskWeakHashStrategyOnPii-DcEwc4Sz.mjs → maskWeakHashStrategyOnPii-d9BGImzK.mjs} +1 -1
  66. package/dist/packem_shared/{maskedRelationLeakViaWith-CxYk0MDW.mjs → maskedRelationLeakViaWith-C8n-Mzb1.mjs} +1 -1
  67. package/dist/packem_shared/{mutatorFullRowReplace-CEgecSGm.mjs → mutatorFullRowReplace-BzRpZ47r.mjs} +1 -1
  68. package/dist/packem_shared/{nondeterministicQueryMutation-BZV_biUV.mjs → nondeterministicQueryMutation-sxmixc0l.mjs} +1 -1
  69. package/dist/packem_shared/{normalizeIdUsedAsAuthorization-FjISSXJm.mjs → normalizeIdUsedAsAuthorization-BXN-Can6.mjs} +1 -1
  70. package/dist/packem_shared/{notifyMissingPushConfig-C_GbVw3S.mjs → notifyMissingPushConfig-DeQMoNwm.mjs} +1 -1
  71. package/dist/packem_shared/{notifySendOutsideAction-B9DcirhJ.mjs → notifySendOutsideAction-CU4T16cR.mjs} +1 -1
  72. package/dist/packem_shared/{outputProjectionMissingOnPublicRead-CjLp7ggc.mjs → outputProjectionMissingOnPublicRead-BN5-uW0p.mjs} +1 -1
  73. package/dist/packem_shared/{ownerFieldFromArgsNotAuth-D4K-_0aD.mjs → ownerFieldFromArgsNotAuth-CjOSUDKi.mjs} +1 -1
  74. package/dist/packem_shared/{paymentCreateWithoutAuthorize-CurtqN8K.mjs → paymentCreateWithoutAuthorize-CICGmRFR.mjs} +1 -1
  75. package/dist/packem_shared/{paymentWebhookWideTolerance-I7ingfR8.mjs → paymentWebhookWideTolerance-B-F5jOeA.mjs} +1 -1
  76. package/dist/packem_shared/{plaintextSecretInWranglerVariables-CmbygN6_.mjs → plaintextSecretInWranglerVariables-bNkqmNqV.mjs} +1 -1
  77. package/dist/packem_shared/{policyReferencesUnknownTable-D3WelAQa.mjs → policyReferencesUnknownTable-CIKuRZ5Y.mjs} +1 -1
  78. package/dist/packem_shared/{privilegedDispatchUnvalidatedPayload-74GuGye1.mjs → privilegedDispatchUnvalidatedPayload-C6Qtc8aT.mjs} +1 -1
  79. package/dist/packem_shared/{privilegedFanoutFromPublicProcedure-sMsZN4uP.mjs → privilegedFanoutFromPublicProcedure-Coqt23CQ.mjs} +1 -1
  80. package/dist/packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs +1 -0
  81. package/dist/packem_shared/{publicArgumentUsesAny-BzdcoJ9C.mjs → publicArgumentUsesAny-BDEvMA8Q.mjs} +1 -1
  82. package/dist/packem_shared/{publicMutationWithoutRatelimit-DgE1OCbv.mjs → publicMutationWithoutRatelimit-DbczxgpL.mjs} +1 -1
  83. package/dist/packem_shared/{publicTableRlsOptoutConfusion-C2s9mIhH.mjs → publicTableRlsOptoutConfusion-C9wCtAfg.mjs} +1 -1
  84. package/dist/packem_shared/{queueWithoutDlq-BLY3iy9Q.mjs → queueWithoutDlq-BvHz3Opg.mjs} +1 -1
  85. package/dist/packem_shared/{r2sqlOutsideAction-DCMAsAeM.mjs → r2sqlOutsideAction-BdLiLOAX.mjs} +1 -1
  86. package/dist/packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs +1 -0
  87. package/dist/packem_shared/ratelimitKeySpoofableOrGlobal-BzDzxnXm.mjs +1 -0
  88. package/dist/packem_shared/{ratelimitMiddlewareFailOpen-CTWVosEc.mjs → ratelimitMiddlewareFailOpen-iDJq68qx.mjs} +1 -1
  89. package/dist/packem_shared/relationReferencesUnknownField-BmWWEFsB.mjs +1 -0
  90. package/dist/packem_shared/{relationReferencesUnknownTable-EOtKyaHx.mjs → relationReferencesUnknownTable-CP4aWtAJ.mjs} +1 -1
  91. package/dist/packem_shared/{rlsUncoveredTable-DYHgnoos.mjs → rlsUncoveredTable-CZ4ie4gX.mjs} +1 -1
  92. package/dist/packem_shared/{shapeTargetsGlobalTable-SAlq2N-T.mjs → shapeTargetsGlobalTable-Bu3eEDic.mjs} +1 -1
  93. package/dist/packem_shared/{shapeUnknownTable-DhvsbEtT.mjs → shapeUnknownTable-CREfNnWi.mjs} +1 -1
  94. package/dist/packem_shared/{signupMutationWithoutDisposableGating-CqIn86bT.mjs → signupMutationWithoutDisposableGating-D9MFN8QY.mjs} +1 -1
  95. package/dist/packem_shared/{softDeleteIncludeDeletedFromArgs-DgxpQHRA.mjs → softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs} +1 -1
  96. package/dist/packem_shared/sqlInjectionRisk-CslaRBxz.mjs +1 -0
  97. package/dist/packem_shared/{storageGenerateUploadUrlNoContentTypePin-C9cjl6Jh.mjs → storageGenerateUploadUrlNoContentTypePin-CTIU_7Fj.mjs} +1 -1
  98. package/dist/packem_shared/storageKeyFromUserArgs-C-QUcdJ3.mjs +1 -0
  99. package/dist/packem_shared/{storagePresignedUrlForPrivateContent-j7VhwIdu.mjs → storagePresignedUrlForPrivateContent-yeu1s81k.mjs} +1 -1
  100. package/dist/packem_shared/{storageUploadWithoutContentTypeAllowlist-oxKLecuf.mjs → storageUploadWithoutContentTypeAllowlist-KSRG81Iu.mjs} +1 -1
  101. package/dist/packem_shared/{storageUploadWithoutMaxSize-DUDxPutk.mjs → storageUploadWithoutMaxSize-BTiM9Q3A.mjs} +1 -1
  102. package/dist/packem_shared/tableWithoutInsert-DQ-GxjFF.mjs +1 -0
  103. package/dist/packem_shared/{ttlFieldNotTimestamp-BFiRkSZD.mjs → ttlFieldNotTimestamp-E14I83wY.mjs} +1 -1
  104. package/dist/packem_shared/{unboundedStringArgument-DN8w1bg-.mjs → unboundedStringArgument-DzbFJc5q.mjs} +1 -1
  105. package/dist/packem_shared/{unindexedForeignKey-DK4eWXkb.mjs → unindexedForeignKey-Dypgn8uH.mjs} +1 -1
  106. package/dist/packem_shared/{unindexedRelationTarget-B5fFq1E8.mjs → unindexedRelationTarget-CSqRJWYZ.mjs} +1 -1
  107. package/dist/packem_shared/{unrestrictedWhereBranch-YKiydie4.mjs → unrestrictedWhereBranch-CcIBmHik.mjs} +1 -1
  108. package/dist/packem_shared/{userCreatingMutationWithoutCaptcha-CYgKnJ9j.mjs → userCreatingMutationWithoutCaptcha-lGWw1o2a.mjs} +1 -1
  109. package/dist/packem_shared/{vectorsNamespaceFromUserInput-Cyq5B2kF.mjs → vectorsNamespaceFromUserInput-CDSOrkyX.mjs} +1 -1
  110. package/dist/packem_shared/{workflowDuplicateStepName-cXF6Rpvg.mjs → workflowDuplicateStepName-BU4rg5So.mjs} +1 -1
  111. package/dist/packem_shared/{workflowUnknownTarget-BzKlYSCg.mjs → workflowUnknownTarget-B8H7jwnH.mjs} +1 -1
  112. package/dist/packem_shared/{workflowUnused-D9fC9E9b.mjs → workflowUnused-BUSOPdHq.mjs} +1 -1
  113. package/package.json +2 -2
  114. package/dist/packem_shared/MAP_VERSION-DxkFCiLX.mjs +0 -1
  115. package/dist/packem_shared/aiRunWithoutLogging-B0K93Pga.mjs +0 -1
  116. package/dist/packem_shared/argument-derived-sink-BpO3NPPM.mjs +0 -1
  117. package/dist/packem_shared/authScimWithoutTransactions-DCtLXzks.mjs +0 -1
  118. package/dist/packem_shared/circularFk-ZNjkhLaE.mjs +0 -1
  119. package/dist/packem_shared/classifySensitivity-B1kkm44P.mjs +0 -1
  120. package/dist/packem_shared/compareToBaseline-BwXFusQG.mjs +0 -1
  121. package/dist/packem_shared/constraintValidator-N339mfyx.mjs +0 -1
  122. package/dist/packem_shared/dedupeCacheKeys-7JgxaQWV.mjs +0 -1
  123. package/dist/packem_shared/fanOutBreadth-DkloezYX.mjs +0 -1
  124. package/dist/packem_shared/filterOnPrimaryKey-C7SeWewK.mjs +0 -1
  125. package/dist/packem_shared/finding-BAVgqnCF.mjs +0 -1
  126. package/dist/packem_shared/fk-index-Bmk19RvQ.mjs +0 -1
  127. package/dist/packem_shared/fromServerSchema-9aaK1J7g.mjs +0 -1
  128. package/dist/packem_shared/gradeFromScore-DW82YDVY.mjs +0 -1
  129. package/dist/packem_shared/hardcodedSecret-yu14ITlW.mjs +0 -1
  130. package/dist/packem_shared/helpers-yHInjqeD.mjs +0 -1
  131. package/dist/packem_shared/hotShard-io-eF2Sx.mjs +0 -1
  132. package/dist/packem_shared/httpActionMissingAuthGuard-BZDdeW_D.mjs +0 -1
  133. package/dist/packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs +0 -1
  134. package/dist/packem_shared/indexReferencesUnknownField-EIL4UArJ.mjs +0 -1
  135. package/dist/packem_shared/mailInboundDispatchWithoutVerify-BivYJ5F9.mjs +0 -1
  136. package/dist/packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs +0 -1
  137. package/dist/packem_shared/ratelimitDefaultMemoryStore-mOdI3Jmb.mjs +0 -1
  138. package/dist/packem_shared/ratelimitKeySpoofableOrGlobal-DaGId_Fh.mjs +0 -1
  139. package/dist/packem_shared/relationReferencesUnknownField-C_3oUop_.mjs +0 -1
  140. package/dist/packem_shared/sqlInjectionRisk-D2NaT-5c.mjs +0 -1
  141. package/dist/packem_shared/storageKeyFromUserArgs-CI-6kyte.mjs +0 -1
  142. package/dist/packem_shared/tableWithoutInsert-BV7qsPKx.mjs +0 -1
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-7JgxaQWV.mjs";import d from"./packem_shared/constraintValidator-N339mfyx.mjs";import u from"./packem_shared/errorRateOutlier-CgK42sq0.mjs";import h from"./packem_shared/fanOutBreadth-DkloezYX.mjs";import $ from"./packem_shared/hotShard-io-eF2Sx.mjs";import g from"./packem_shared/indexUtilization-QfbIH_w4.mjs";import b from"./packem_shared/actionFetchSsrf-BbWxy1Yf.mjs";import y from"./packem_shared/actionWithoutErrorHandling-rhzjCEqm.mjs";import w from"./packem_shared/adminRouteWithoutGuard-BbscPp1m.mjs";import v from"./packem_shared/aiRawRunEscapeHatch-DfhjYm43.mjs";import S from"./packem_shared/aiRunWithoutLogging-B0K93Pga.mjs";import A from"./packem_shared/aiToolSideEffectPromptInjection-DnWQOoKi.mjs";import I from"./packem_shared/aiUnboundedGenerationPublic-iu7crnSa.mjs";import U from"./packem_shared/allowUnauthenticatedShardAccessEnabled-D4aMNBb0.mjs";import R from"./packem_shared/authApiCallWithoutHeaders-BHkik-EF.mjs";import W from"./packem_shared/authCsrfCheckDisabled-De9ammtV.mjs";import k from"./packem_shared/authEmailVerificationDisabled-BFKdJcx4.mjs";import x from"./packem_shared/authScimWithoutTransactions-DCtLXzks.mjs";import T from"./packem_shared/authSecureCookiesDisabled-B4WIzvMm.mjs";import N from"./packem_shared/authSessionFreshageZero-Bpcaf_Pe.mjs";import F from"./packem_shared/authTrustedOriginsWildcard-DjU9NXEm.mjs";import C from"./packem_shared/browserAllowPrivateTargets-Dxyeh6lg.mjs";import M from"./packem_shared/browserUserUrlWithoutAllowlist-BHBS6b0N.mjs";import O from"./packem_shared/circularFk-ZNjkhLaE.mjs";import P from"./packem_shared/containerInstanceKeyFromUserInput-C8S4T3ty.mjs";import D from"./packem_shared/containerOversizedInstance-B9wW_sU5.mjs";import E from"./packem_shared/containerPublicInternet-g0TRsayn.mjs";import q from"./packem_shared/containerRuntimeEgressRelaxation-BQ2ZD1aV.mjs";import L from"./packem_shared/containerStartEnableInternetOverride-B62WUura.mjs";import K from"./packem_shared/duplicateIndex-DFT7qLQD.mjs";import G from"./packem_shared/emptyIndex-Bp-Y81D0.mjs";import _ from"./packem_shared/errorWithoutCatalog-0PbxiZL4.mjs";import j from"./packem_shared/exportSinkMisconfigured-DvpAaa6j.mjs";import z from"./packem_shared/externalSourceIncrementalNoDeletePath-DnoL64YY.mjs";import B from"./packem_shared/externalSourceOnGlobal-IYnALPlO.mjs";import H from"./packem_shared/externalSourceUnscoped-DqBp5jYr.mjs";import V from"./packem_shared/filterOnPrimaryKey-C7SeWewK.mjs";import Q from"./packem_shared/filterWithoutIndex-CyHJwVhc.mjs";import X from"./packem_shared/flagGatesSecurityWithUnsafeDefault-DPGFEHTt.mjs";import Z from"./packem_shared/geoIndexFieldNotGeopoint-DAGLZtaH.mjs";import J from"./packem_shared/geoIndexUnused-B5Oww9o2.mjs";import Y from"./packem_shared/globalTableNearColumnLimit-DBQwb2MS.mjs";import oo from"./packem_shared/hardcodedSecret-yu14ITlW.mjs";import ro from"./packem_shared/httpActionMissingAuthGuard-BZDdeW_D.mjs";import eo from"./packem_shared/httpActionResponseHeaderInjection-D063rB7U.mjs";import to from"./packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs";import io from"./packem_shared/identityUndeclaredClaimTrusted-BYRYGQ74.mjs";import ao from"./packem_shared/imagesUrlSourceFromUserInput-Cy5pc_dA.mjs";import so from"./packem_shared/indexReferencesUnknownField-EIL4UArJ.mjs";import no from"./packem_shared/insertManyUnsafeUserData-xLYAkcKj.mjs";import mo from"./packem_shared/kvUnscopedUserKeyIdor-B4lGxRxh.mjs";import lo from"./packem_shared/mailInboundDispatchWithoutVerify-BivYJ5F9.mjs";import po from"./packem_shared/mailRecipientFromRequestInput-BR_oSh65.mjs";import fo from"./packem_shared/maskUncoveredPiiColumn-DqPfZM2O.mjs";import co from"./packem_shared/maskWeakHashStrategyOnPii-DcEwc4Sz.mjs";import uo from"./packem_shared/maskedRelationLeakViaWith-CxYk0MDW.mjs";import{i as s}from"./packem_shared/finding-BAVgqnCF.mjs";import ho from"./packem_shared/mutatorFullRowReplace-CEgecSGm.mjs";import $o from"./packem_shared/nondeterministicQueryMutation-BZV_biUV.mjs";import go from"./packem_shared/normalizeIdUsedAsAuthorization-FjISSXJm.mjs";import bo from"./packem_shared/notifyMissingPushConfig-C_GbVw3S.mjs";import yo from"./packem_shared/notifySendOutsideAction-B9DcirhJ.mjs";import wo from"./packem_shared/outputProjectionMissingOnPublicRead-CjLp7ggc.mjs";import vo from"./packem_shared/ownerFieldFromArgsNotAuth-D4K-_0aD.mjs";import So from"./packem_shared/paymentCreateWithoutAuthorize-CurtqN8K.mjs";import Ao from"./packem_shared/paymentWebhookWideTolerance-I7ingfR8.mjs";import Io from"./packem_shared/plaintextSecretInWranglerVariables-CmbygN6_.mjs";import Uo from"./packem_shared/policyReferencesUnknownTable-D3WelAQa.mjs";import Ro from"./packem_shared/privilegedDispatchUnvalidatedPayload-74GuGye1.mjs";import Wo from"./packem_shared/privilegedFanoutFromPublicProcedure-sMsZN4uP.mjs";import ko from"./packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs";import xo from"./packem_shared/publicArgumentUsesAny-BzdcoJ9C.mjs";import To from"./packem_shared/publicMutationWithoutRatelimit-DgE1OCbv.mjs";import No from"./packem_shared/publicTableRlsOptoutConfusion-C2s9mIhH.mjs";import Fo from"./packem_shared/queueWithoutDlq-BLY3iy9Q.mjs";import Co from"./packem_shared/r2sqlOutsideAction-DCMAsAeM.mjs";import Mo from"./packem_shared/ratelimitDefaultMemoryStore-mOdI3Jmb.mjs";import Oo from"./packem_shared/ratelimitKeySpoofableOrGlobal-DaGId_Fh.mjs";import Po from"./packem_shared/ratelimitMiddlewareFailOpen-CTWVosEc.mjs";import Do from"./packem_shared/relationReferencesUnknownField-C_3oUop_.mjs";import Eo from"./packem_shared/relationReferencesUnknownTable-EOtKyaHx.mjs";import qo from"./packem_shared/rlsUncoveredTable-DYHgnoos.mjs";import Lo from"./packem_shared/shapeTargetsGlobalTable-SAlq2N-T.mjs";import Ko from"./packem_shared/shapeUnknownTable-DhvsbEtT.mjs";import Go from"./packem_shared/signupMutationWithoutDisposableGating-CqIn86bT.mjs";import _o from"./packem_shared/softDeleteIncludeDeletedFromArgs-DgxpQHRA.mjs";import jo from"./packem_shared/sqlInjectionRisk-D2NaT-5c.mjs";import zo from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-C9cjl6Jh.mjs";import Bo from"./packem_shared/storageKeyFromUserArgs-CI-6kyte.mjs";import Ho from"./packem_shared/storagePresignedUrlForPrivateContent-j7VhwIdu.mjs";import Vo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-oxKLecuf.mjs";import Qo from"./packem_shared/storageUploadWithoutMaxSize-DUDxPutk.mjs";import Xo from"./packem_shared/tableWithoutInsert-BV7qsPKx.mjs";import Zo from"./packem_shared/ttlFieldNotTimestamp-BFiRkSZD.mjs";import{s as Jo,q as Yo}from"./packem_shared/helpers-yHInjqeD.mjs";import or from"./packem_shared/unboundedStringArgument-DN8w1bg-.mjs";import rr from"./packem_shared/unindexedForeignKey-DK4eWXkb.mjs";import er from"./packem_shared/unindexedRelationTarget-B5fFq1E8.mjs";import tr from"./packem_shared/unrestrictedWhereBranch-YKiydie4.mjs";import ir from"./packem_shared/userCreatingMutationWithoutCaptcha-CYgKnJ9j.mjs";import ar from"./packem_shared/vectorsNamespaceFromUserInput-Cyq5B2kF.mjs";import sr from"./packem_shared/workflowDuplicateStepName-cXF6Rpvg.mjs";import nr from"./packem_shared/workflowUnknownTarget-BzKlYSCg.mjs";import mr from"./packem_shared/workflowUnused-D9fC9E9b.mjs";import{compareToBaseline as ut,parseAdvisorMap as ht}from"./packem_shared/compareToBaseline-BwXFusQG.mjs";import{gradeFromScore as gt}from"./packem_shared/gradeFromScore-DW82YDVY.mjs";import{MAP_VERSION as yt,byCodepoint as wt,scoreAdvisor as vt}from"./packem_shared/MAP_VERSION-DxkFCiLX.mjs";import{default as At}from"./packem_shared/classifySensitivity-B1kkm44P.mjs";import{fromServerSchema as Ut}from"./packem_shared/fromServerSchema-9aaK1J7g.mjs";const lr={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},n={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:e=>e.staleMigrationImports===void 0?[]:e.staleMigrationImports.map(o=>s(n,{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 \`${lr[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"},pr=new Map([["global",{level:"WARN",scope:e=>`it reads the whole D1 table "${e}" over a cross-region round trip`}],["root",{level:"WARN",scope:e=>`it loads every row of "${e}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:e=>`"${e}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),fr={level:"WARN",scope:e=>`it loads every row of "${e}"`},m={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:e=>{const o=[],i=Jo(e.schema);for(const r of e.queries??[]){if(r.terminal!=="collect"||r.hasIndex||r.table===""||r.hasFilter)continue;const t=i.get(r.table),{level:a,scope:l}=pr.get(t??"")??fr,p=Yo(r),f=a==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${r.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(s(m,{cacheKey:`unbounded_collect:${r.file}:${r.line.toString()}:${r.table}`,detail:`Query on "${r.table}" at ${p} calls .collect() with no index and no filter — ${l(r.table)}.${f}`,level:a,metadata:{exportName:r.exportName,file:r.file,line:r.line,shardKind:t??"unknown",table:r.table}}))}return o},source:"static",title:"Unbounded collect"},cr=[so,Eo,Do,nr,sr,Ko,z,B,H,G,Y,_,Z,J,j,Zo,O,rr,er,K,Xo,mr,Fo,V,Q,m,Lo,ho,$o,to,Co,R,Uo,qo,fo,co,D,E,To,tr,ir,Go,xo,or,oo,jo,w,So,lo,Mo,C,Wo,ko,no,I,b,y,vo,Bo,mo,P,v,S,ar,po,M,Ro,L,q,x,F,W,T,k,N,ao,Oo,No,U,Vo,Qo,zo,Ho,ro,eo,Po,X,A,io,Ao,_o,uo,wo,go,yo,bo,Io,n],dr=[$,g,d,u,h],ur=[...cr,...dr],ft=(e,o={})=>{const i=o.lints??ur,r=[];for(const t of i)o.source!==void 0&&t.source!==o.source||r.push(...t.run(e));return c(r)};export{ur as ALL_LINTS,yt as MAP_VERSION,dr as RUNTIME_LINTS,cr as STATIC_LINTS,b as actionFetchSsrf,y as actionWithoutErrorHandling,w as adminRouteWithoutGuard,v as aiRawRunEscapeHatch,S as aiRunWithoutLogging,A as aiToolSideEffectPromptInjection,I as aiUnboundedGenerationPublic,U as allowUnauthenticatedShardAccessEnabled,R as authApiCallWithoutHeaders,W as authCsrfCheckDisabled,k as authEmailVerificationDisabled,x as authScimWithoutTransactions,T as authSecureCookiesDisabled,N as authSessionFreshageZero,F as authTrustedOriginsWildcard,C as browserAllowPrivateTargets,M as browserUserUrlWithoutAllowlist,wt as byCodepoint,O as circularFk,At as classifySensitivity,ut as compareToBaseline,d as constraintValidator,P as containerInstanceKeyFromUserInput,D as containerOversizedInstance,E as containerPublicInternet,q as containerRuntimeEgressRelaxation,L as containerStartEnableInternetOverride,c as dedupeCacheKeys,K as duplicateIndex,G as emptyIndex,u as errorRateOutlier,_ as errorWithoutCatalog,j as exportSinkMisconfigured,z as externalSourceIncrementalNoDeletePath,B as externalSourceOnGlobal,H as externalSourceUnscoped,h as fanOutBreadth,V as filterOnPrimaryKey,Q as filterWithoutIndex,X as flagGatesSecurityWithUnsafeDefault,Ut as fromServerSchema,Z as geoIndexFieldNotGeopoint,J as geoIndexUnused,Y as globalTableNearColumnLimit,gt as gradeFromScore,oo as hardcodedSecret,$ as hotShard,ro as httpActionMissingAuthGuard,eo as httpActionResponseHeaderInjection,to as hyperdriveOutsideAction,io as identityUndeclaredClaimTrusted,ao as imagesUrlSourceFromUserInput,so as indexReferencesUnknownField,g as indexUtilization,no as insertManyUnsafeUserData,mo as kvUnscopedUserKeyIdor,lo as mailInboundDispatchWithoutVerify,po as mailRecipientFromRequestInput,fo as maskUncoveredPiiColumn,co as maskWeakHashStrategyOnPii,uo as maskedRelationLeakViaWith,ho as mutatorFullRowReplace,$o as nondeterministicQueryMutation,go as normalizeIdUsedAsAuthorization,bo as notifyMissingPushConfig,yo as notifySendOutsideAction,wo as outputProjectionMissingOnPublicRead,vo as ownerFieldFromArgsNotAuth,ht as parseAdvisorMap,So as paymentCreateWithoutAuthorize,Ao as paymentWebhookWideTolerance,Io as plaintextSecretInWranglerVariables,Uo as policyReferencesUnknownTable,Ro as privilegedDispatchUnvalidatedPayload,Wo as privilegedFanoutFromPublicProcedure,ko as procedureWithoutStructuredEvent,xo as publicArgumentUsesAny,To as publicMutationWithoutRatelimit,No as publicTableRlsOptoutConfusion,Fo as queueWithoutDlq,Co as r2sqlOutsideAction,Mo as ratelimitDefaultMemoryStore,Oo as ratelimitKeySpoofableOrGlobal,Po as ratelimitMiddlewareFailOpen,Do as relationReferencesUnknownField,Eo as relationReferencesUnknownTable,qo as rlsUncoveredTable,ft as runAdvisor,vt as scoreAdvisor,Lo as shapeTargetsGlobalTable,Ko as shapeUnknownTable,Go as signupMutationWithoutDisposableGating,_o as softDeleteIncludeDeletedFromArgs,jo as sqlInjectionRisk,zo as storageGenerateUploadUrlNoContentTypePin,Bo as storageKeyFromUserArgs,Ho as storagePresignedUrlForPrivateContent,Vo as storageUploadWithoutContentTypeAllowlist,Qo as storageUploadWithoutMaxSize,Xo as tableWithoutInsert,Zo as ttlFieldNotTimestamp,or as unboundedStringArgument,rr as unindexedForeignKey,er as unindexedRelationTarget,tr as unrestrictedWhereBranch,ir as userCreatingMutationWithoutCaptcha,ar as vectorsNamespaceFromUserInput,sr as workflowDuplicateStepName,nr as workflowUnknownTarget,mr 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 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};
@@ -0,0 +1 @@
1
+ import{scoreProcedure as h,procedureWeight as S,coverageFromScore as b,scoreGlobal as k,projectWeight as N,gradeFromScore as j,weightFor as M,worstLevel as F}from"./gradeFromScore-KSt58rj1.mjs";import P from"./classifySensitivity-JnjaTGYi.mjs";const y=(e,o)=>e===o?0:e<o?-1:1,w=(e,o)=>y(e.name,o.name),x=(e,o)=>{const r=e[o];return typeof r=="string"?r:void 0},g=(e,o)=>`${e}#${o}`,A=(e,o,r,c)=>e===void 0?{level:o,name:r,occurrences:1,weight:c}:{level:F(e.level,o),name:r,occurrences:e.occurrences+1,weight:Math.max(e.weight,c)},I=(e,o,r)=>{const c=new Map;for(const n of e)c.set(g(n.file,n.exportName),new Map);const s=new Map;for(const n of o){const a=x(n.metadata,"file"),l=x(n.metadata,"exportName"),m=(a!==void 0&&l!==void 0?c.get(g(a,l)):void 0)??s;m.set(n.name,A(m.get(n.name),n.level,n.name,r(n)))}return{byProcedure:new Map([...c].map(([n,a])=>[n,[...a.values()].toSorted(w)])),project:[...s.values()].toSorted(w)}},C=(e,o)=>{const r={clean:0,exempt:0,failing:0,warned:0};let c=o.checks.length;for(const s of e)r[s.coverage]+=1,c+=s.checks.length;return{clean:r.clean,exempt:r.exempt,failing:r.failing,procedures:e.length,rulesFired:c,warned:r.warned}},R=1,O=(e,o,r={})=>{const c=new Set(r.exempt),s=I(e,o,t=>M(t.level)),n=e.map(t=>{const i=g(t.file,t.exportName),f=s.byProcedure.get(i)??[],u=h(f),d=c.has(i)||t.exempt===!0,v=P(t);return{checks:f,coverage:d?"exempt":b(u),...d?{exemptReason:t.exemptReason??""}:{},exportName:t.exportName,file:t.file,id:i,kind:t.kind,score:u,sensitivity:v,visibility:t.visibility,weight:d?0:S(t,v.level)}}).toSorted((t,i)=>y(t.id,i.id)),a={checks:s.project,score:h(s.project)},l=n.filter(t=>t.coverage!=="exempt"),p=l.reduce((t,i)=>t+i.weight,0),m=k([...l,{score:a.score,weight:N(p)}]);return{generatedAt:r.generatedAt??new Date().toISOString(),grade:j(m),procedures:n,project:a,score:m,summary:C(n,a),version:R}};export{R as MAP_VERSION,y as byCodepoint,O as scoreAdvisor};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const r={categories:["SECURITY"],description:"An action's `ctx.fetch(url)` fetches a URL derived from the handler's `args`. `ctx.fetch` applies no host allowlist, so request-controlled URLs let a caller reach cloud metadata endpoints or internal services — server-side request forgery.",facing:"EXTERNAL",level:"ERROR",name:"action_fetch_ssrf",remediation:"Validate the URL against an allowlist of expected hosts before fetching, and reject private, loopback, and link-local addresses (notably `169.254.169.254`). Never pass request input straight to `ctx.fetch`.",run:t=>t.argumentDerivedFetches===void 0?[]:t.argumentDerivedFetches.map(e=>a(r,{cacheKey:`action_fetch_ssrf:${e.file}:${e.line.toString()}`,detail:`\`ctx.fetch\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) fetches a URL derived from \`args\` — a server-side request forgery vector. Validate the URL against a host allowlist and reject private/link-local targets before fetching.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"Possible SSRF from arg-derived ctx.fetch URL"};export{r as default};
1
+ import{e as r}from"./finding-NrKO8idM.mjs";const a={categories:["SECURITY"],description:"An action's `ctx.fetch(url)` fetches a URL derived from the handler's `args`. `ctx.fetch` applies no host allowlist, so request-controlled URLs let a caller reach cloud metadata endpoints or internal services — server-side request forgery.",facing:"EXTERNAL",level:"ERROR",name:"action_fetch_ssrf",remediation:"Validate the URL against an allowlist of expected hosts before fetching, and reject private, loopback, and link-local addresses (notably `169.254.169.254`). Never pass request input straight to `ctx.fetch`.",run:t=>t.argumentDerivedFetches===void 0?[]:t.argumentDerivedFetches.map(e=>r(a,{cacheKey:`action_fetch_ssrf:${e.file}:${e.line.toString()}`,detail:`\`ctx.fetch\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) fetches a URL derived from \`args\` — a server-side request forgery vector. Validate the URL against a host allowlist and reject private/link-local targets before fetching.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"Possible SSRF from arg-derived ctx.fetch URL"};export{a as default};
@@ -1 +1 @@
1
- import{i as r}from"./finding-BAVgqnCF.mjs";const a={categories:["SCHEMA"],description:"An `action` calls an outbound surface (fetch, mail, queues, storage, sql, ai) with no `try`/`catch`. Those fail routinely, and an uncaught rejection reaches the caller with no indication of which dependency broke.",facing:"EXTERNAL",level:"WARN",name:"action_without_error_handling",remediation:"Wrap the outbound call in `try`/`catch`, then either degrade or rethrow a coded `LunoraError` naming the dependency that failed.",run:t=>t.procedureProtections===void 0?[]:t.procedureProtections.filter(e=>e.kind==="action"&&e.reachesOutbound===!0&&e.handlesErrors===!1).map(e=>r(a,{cacheKey:`action_without_error_handling:${e.file}:${e.exportName}`,detail:`Action \`${e.exportName}\` (${e.file}) calls an outbound surface with no \`try\`/\`catch\`. A dependency failure will surface to the caller unexplained.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}})),source:"static",title:"Action performs outbound I/O with no error handling"};export{a as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const n={categories:["SCHEMA"],description:"An `action` calls an outbound surface (fetch, mail, queues, storage, sql, ai) with no `try`/`catch`. Those fail routinely, and an uncaught rejection reaches the caller with no indication of which dependency broke.",facing:"EXTERNAL",level:"WARN",name:"action_without_error_handling",remediation:"Wrap the outbound call in `try`/`catch`, then either degrade or rethrow a coded `LunoraError` naming the dependency that failed.",run:t=>t.procedureProtections===void 0?[]:t.procedureProtections.filter(e=>e.kind==="action"&&e.reachesOutbound===!0&&e.handlesErrors===!1).map(e=>a(n,{cacheKey:`action_without_error_handling:${e.file}:${e.exportName}`,detail:`Action \`${e.exportName}\` (${e.file}) calls an outbound surface with no \`try\`/\`catch\`. A dependency failure will surface to the caller unexplained.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}})),source:"static",title:"Action performs outbound I/O with no error handling"};export{n as default};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"An `httpRoute` on an admin/privileged path has no visible auth/admin guard. REST routes aren't covered by RLS, so an unguarded `/admin/*` route is callable by anyone who reaches the URL.",facing:"EXTERNAL",level:"WARN",name:"admin_route_without_guard",remediation:"Assert an authenticated, authorized caller at the top of the handler — check `ctx.auth` / `getSession(...)`, or a `requireAdmin`-style guard — and reject (401/403) before doing privileged work. For machine callers, verify a secret with a timing-safe compare.",run:t=>t.adminRoutes===void 0?[]:t.adminRoutes.filter(e=>!e.usesGuard).map(e=>a(i,{cacheKey:`admin_route_without_guard:${e.file}:${e.method}:${e.path}`,detail:`Route \`${e.method} ${e.path}\` (\`${e.exportName}\` in ${e.file}) is on an admin/privileged path but its handler shows no auth/admin guard. Assert an authorized caller before doing privileged work.`,metadata:{exportName:e.exportName,file:e.file,method:e.method,path:e.path}})),source:"static",title:"Admin route without an auth guard"};export{i as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const i={categories:["SECURITY"],description:"An `httpRoute` on an admin/privileged path has no visible auth/admin guard. REST routes aren't covered by RLS, so an unguarded `/admin/*` route is callable by anyone who reaches the URL.",facing:"EXTERNAL",level:"WARN",name:"admin_route_without_guard",remediation:"Assert an authenticated, authorized caller at the top of the handler — check `ctx.auth` / `getSession(...)`, or a `requireAdmin`-style guard — and reject (401/403) before doing privileged work. For machine callers, verify a secret with a timing-safe compare.",run:a=>a.adminRoutes===void 0?[]:a.adminRoutes.filter(e=>!e.usesGuard).map(e=>t(i,{cacheKey:`admin_route_without_guard:${e.file}:${e.method}:${e.path}`,detail:`Route \`${e.method} ${e.path}\` (\`${e.exportName}\` in ${e.file}) is on an admin/privileged path but its handler shows no auth/admin guard. Assert an authorized caller before doing privileged work.`,metadata:{exportName:e.exportName,file:e.file,method:e.method,path:e.path}})),source:"static",title:"Admin route without an auth guard"};export{i as default};
@@ -1 +1 @@
1
- import{i as r}from"./finding-BAVgqnCF.mjs";const t={categories:["SECURITY"],description:"A `ctx.ai.run(model, …)` call selects its model from the handler's `args` with no server-side scoping. `ctx.ai.run` is the raw Workers AI binding passthrough, so an arg-derived model id lets any caller pick an arbitrary model — bypassing the typed `ctx.ai.model(...)` + AI-SDK layer's cap/schema.",facing:"EXTERNAL",level:"WARN",name:"ai_raw_run_escape_hatch",remediation:"Select the model from a fixed server-side allowlist — never from `args`. Prefer the typed `ctx.ai.model(...)` + `generateText`/`streamText` path with an output cap over the raw `ctx.ai.run` escape hatch.",run:a=>a.aiRawRuns===void 0?[]:a.aiRawRuns.map(e=>r(t,{cacheKey:`ai_raw_run_escape_hatch:${e.file}:${e.line.toString()}`,detail:`\`ctx.ai.run\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) selects its model from \`args\` with no server-side scoping — any caller can pick an arbitrary model, bypassing the typed AI-SDK layer's cap/schema. Select the model from a fixed server-side allowlist instead.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"Possible arbitrary model selection from arg-derived ctx.ai.run"};export{t as default};
1
+ import{e as r}from"./finding-NrKO8idM.mjs";const t={categories:["SECURITY"],description:"A `ctx.ai.run(model, …)` call selects its model from the handler's `args` with no server-side scoping. `ctx.ai.run` is the raw Workers AI binding passthrough, so an arg-derived model id lets any caller pick an arbitrary model — bypassing the typed `ctx.ai.model(...)` + AI-SDK layer's cap/schema.",facing:"EXTERNAL",level:"WARN",name:"ai_raw_run_escape_hatch",remediation:"Select the model from a fixed server-side allowlist — never from `args`. Prefer the typed `ctx.ai.model(...)` + `generateText`/`streamText` path with an output cap over the raw `ctx.ai.run` escape hatch.",run:a=>a.aiRawRuns===void 0?[]:a.aiRawRuns.map(e=>r(t,{cacheKey:`ai_raw_run_escape_hatch:${e.file}:${e.line.toString()}`,detail:`\`ctx.ai.run\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) selects its model from \`args\` with no server-side scoping — any caller can pick an arbitrary model, bypassing the typed AI-SDK layer's cap/schema. Select the model from a fixed server-side allowlist instead.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"Possible arbitrary model selection from arg-derived ctx.ai.run"};export{t as default};
@@ -0,0 +1 @@
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const i={categories:["PERFORMANCE"],description:"A procedure runs an AI generation but emits no structured event. Model calls bill per invocation and are not reproducible, so without one there is no way to attribute spend or trace a bad answer back to its prompt.",facing:"INTERNAL",level:"INFO",name:"ai_run_without_logging",remediation:'Emit an event around the generation — `ctx.span("<name>", …)`, or a `ctx.log` line carrying the model and token usage.',run:e=>e.procedureProtections===void 0?[]:e.procedureProtections.filter(t=>t.runsAiGeneration===!0&&t.emitsEvent===!1).map(t=>n(i,{cacheKey:`ai_run_without_logging:${t.file}:${t.exportName}`,detail:`${t.kind} \`${t.exportName}\` (${t.file}) runs an AI generation with no structured event, so its cost and output cannot be attributed later.`,metadata:{exportName:t.exportName,file:t.file,kind:t.kind}})),source:"static",title:"AI generation with no structured event"};export{i as default};
@@ -1 +1 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";const n={categories:["SECURITY"],description:"A `generateText` / `streamText` call whose prompt/messages carry user-supplied text and whose model-callable `tools` perform a privileged side effect (DB write, `ctx.run` dispatch, or outbound send). An injected instruction in the user text can steer the model into firing that action — the confused-deputy prompt-injection hazard.",facing:"EXTERNAL",level:"WARN",name:"ai_tool_side_effect_prompt_injection",remediation:"Don't let a model with user-derived input directly trigger a privileged action. Have the tool return a proposal the server validates and authorizes before executing, gate the side effect behind an explicit human/step confirmation, or keep side-effecting tools out of any generation whose prompt includes untrusted input.",run:t=>t.aiToolSideEffects===void 0?[]:t.aiToolSideEffects.filter(e=>e.userInputDerived).map(e=>{const i=`\`${e.exportName}\` (${e.file}:${e.line.toString()})`;return o(n,{cacheKey:`ai_tool_side_effect_prompt_injection:${e.file}:${e.line.toString()}`,detail:`\`${e.method}\` in ${i} feeds user-derived input to a model whose tools reach \`${e.sideEffect}\`. An injected instruction can steer the model into firing that side effect.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,method:e.method,sideEffect:e.sideEffect}})}),source:"static",title:"AI tool side effect reachable via prompt injection"};export{n as default};
1
+ import{e as o}from"./finding-NrKO8idM.mjs";const n={categories:["SECURITY"],description:"A `generateText` / `streamText` call whose prompt/messages carry user-supplied text and whose model-callable `tools` perform a privileged side effect (DB write, `ctx.run` dispatch, or outbound send). An injected instruction in the user text can steer the model into firing that action — the confused-deputy prompt-injection hazard.",facing:"EXTERNAL",level:"WARN",name:"ai_tool_side_effect_prompt_injection",remediation:"Don't let a model with user-derived input directly trigger a privileged action. Have the tool return a proposal the server validates and authorizes before executing, gate the side effect behind an explicit human/step confirmation, or keep side-effecting tools out of any generation whose prompt includes untrusted input.",run:t=>t.aiToolSideEffects===void 0?[]:t.aiToolSideEffects.filter(e=>e.userInputDerived).map(e=>{const i=`\`${e.exportName}\` (${e.file}:${e.line.toString()})`;return o(n,{cacheKey:`ai_tool_side_effect_prompt_injection:${e.file}:${e.line.toString()}`,detail:`\`${e.method}\` in ${i} feeds user-derived input to a model whose tools reach \`${e.sideEffect}\`. An injected instruction can steer the model into firing that side effect.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,method:e.method,sideEffect:e.sideEffect}})}),source:"static",title:"AI tool side effect reachable via prompt injection"};export{n as default};
@@ -1 +1 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";import{m as i}from"./procedure-protections-DRQotu9I.mjs";const o={categories:["SECURITY"],description:"A public procedure runs an AI generation (`generateText`/`streamText`/`generateObject`/`streamObject`) with no `maxOutputTokens` bound. Generation bills per output token, so an anonymous caller can request arbitrarily long completions in a loop — a denial-of-wallet vector that also ties up worker time on long streams.",facing:"EXTERNAL",level:"WARN",name:"ai_unbounded_generation_public",remediation:"Cap the completion with `maxOutputTokens` in the generation config, and rate-limit the public entry point (`.use(rateLimit(...))`). For expensive models, prefer running generation from an internal, authenticated path rather than exposing it directly to anonymous callers.",run:t=>t.procedureProtections===void 0?[]:t.procedureProtections.filter(e=>i(e.unboundedAiGeneration)&&e.visibility==="public").map(e=>n(o,{cacheKey:`ai_unbounded_generation_public:${e.file}:${e.exportName}`,detail:`\`${e.exportName}\` (${e.file}) is public and runs an AI generation with no \`maxOutputTokens\` bound — an anonymous caller can drive unbounded, billable completions in a loop. Add a \`maxOutputTokens\` cap and rate-limit the entry point.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}})),source:"static",title:"Public procedure runs unbounded AI generation (no maxOutputTokens)"};export{o as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";import{m as i}from"./procedure-protections-DRQotu9I.mjs";const a={categories:["SECURITY"],description:"A public procedure runs an AI generation (`generateText`/`streamText`/`generateObject`/`streamObject`) with no `maxOutputTokens` bound. Generation bills per output token, so an anonymous caller can request arbitrarily long completions in a loop — a denial-of-wallet vector that also ties up worker time on long streams.",facing:"EXTERNAL",level:"WARN",name:"ai_unbounded_generation_public",remediation:"Cap the completion with `maxOutputTokens` in the generation config, and rate-limit the public entry point (`.use(rateLimit(...))`). For expensive models, prefer running generation from an internal, authenticated path rather than exposing it directly to anonymous callers.",run:n=>n.procedureProtections===void 0?[]:n.procedureProtections.filter(e=>i(e.unboundedAiGeneration)&&e.visibility==="public").map(e=>t(a,{cacheKey:`ai_unbounded_generation_public:${e.file}:${e.exportName}`,detail:`\`${e.exportName}\` (${e.file}) is public and runs an AI generation with no \`maxOutputTokens\` bound — an anonymous caller can drive unbounded, billable completions in a loop. Add a \`maxOutputTokens\` cap and rate-limit the entry point.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}})),source:"static",title:"Public procedure runs unbounded AI generation (no maxOutputTokens)"};export{a as default};
@@ -1 +1 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const n={categories:["SECURITY"],description:"`.extend(() => ({ allowUnauthenticatedShardAccess: true }))` disables the fail-closed default that denies an unauthenticated shard lookup. Combined with a schema that doesn't enforce `.rls(\"required\")` everywhere (or that leaves a table `.public()`), an unauthenticated caller can shard-hop into another tenant's rows with no row-security guard behind the door.",facing:"EXTERNAL",level:"WARN",name:"allow_unauthenticated_shard_access_enabled",remediation:"Prefer `authorizeShard` / `authorizeFanOut` (which take precedence over `allowUnauthenticatedShardAccess` and let you allow specific unauthenticated cases) over the blanket opt-out. If the app genuinely needs open shard access, enforce `.rls(\"required\")` on the schema with no `.public()` tables so a missing identity still can't read another tenant's rows.",run:a=>a.configCalls===void 0?[]:a.schema.rlsMode!=="required"||a.schema.tables.some(e=>e.isPublic)?a.configCalls.filter(e=>e.callee==="extend"&&e.trueKeys.includes("allowUnauthenticatedShardAccess")).map(e=>t(n,{cacheKey:`allow_unauthenticated_shard_access_enabled:${e.file}:${e.line.toString()}`,detail:`\`.extend(...)\` in ${e.file}:${e.line.toString()} sets \`allowUnauthenticatedShardAccess: true\`, and the schema has an RLS gap (no \`.rls("required")\`, or a \`.public()\` table) — an unauthenticated caller can shard-hop into another tenant's rows with no row-security guard behind the door.`,metadata:{callee:e.callee,file:e.file,line:e.line}})):[],source:"static",title:"Unauthenticated shard access enabled on an RLS-gapped schema"};export{n as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const n={categories:["SECURITY"],description:"`.extend(() => ({ allowUnauthenticatedShardAccess: true }))` disables the fail-closed default that denies an unauthenticated shard lookup. Combined with a schema that doesn't enforce `.rls(\"required\")` everywhere (or that leaves a table `.public()`), an unauthenticated caller can shard-hop into another tenant's rows with no row-security guard behind the door.",facing:"EXTERNAL",level:"WARN",name:"allow_unauthenticated_shard_access_enabled",remediation:"Prefer `authorizeShard` / `authorizeFanOut` (which take precedence over `allowUnauthenticatedShardAccess` and let you allow specific unauthenticated cases) over the blanket opt-out. If the app genuinely needs open shard access, enforce `.rls(\"required\")` on the schema with no `.public()` tables so a missing identity still can't read another tenant's rows.",run:a=>a.configCalls===void 0?[]:a.schema.rlsMode!=="required"||a.schema.tables.some(e=>e.isPublic)?a.configCalls.filter(e=>e.callee==="extend"&&e.trueKeys.includes("allowUnauthenticatedShardAccess")).map(e=>t(n,{cacheKey:`allow_unauthenticated_shard_access_enabled:${e.file}:${e.line.toString()}`,detail:`\`.extend(...)\` in ${e.file}:${e.line.toString()} sets \`allowUnauthenticatedShardAccess: true\`, and the schema has an RLS gap (no \`.rls("required")\`, or a \`.public()\` table) — an unauthenticated caller can shard-hop into another tenant's rows with no row-security guard behind the door.`,metadata:{callee:e.callee,file:e.file,line:e.line}})):[],source:"static",title:"Unauthenticated shard access enabled on an RLS-gapped schema"};export{n as default};
@@ -0,0 +1 @@
1
+ import{e as i}from"./finding-NrKO8idM.mjs";const d=e=>{const a={categories:e.categories,description:e.description,facing:e.facing,level:e.level,name:e.name,remediation:e.remediation,run:r=>{const s=e.getAccesses(r);return s===void 0||e.suppressWhen?.(r)===!0?[]:s.map(t=>i(a,{cacheKey:e.cacheKey(t),detail:e.detail(t),metadata:e.metadata(t)}))},source:"static",title:e.title};return a};export{d as m};
@@ -1 +1 @@
1
- import{dedupeCacheKeys as i}from"./dedupeCacheKeys-7JgxaQWV.mjs";import{i as a}from"./finding-BAVgqnCF.mjs";const s={categories:["SECURITY"],description:"A `ctx.authApi.<method>(...)` call omits `headers` — better-auth skips session authorization and runs the call with full server-to-server privileges, regardless of the caller's identity.",facing:"EXTERNAL",level:"WARN",name:"auth_api_call_without_headers",remediation:"Pass the inbound `headers` from the incoming request into every `ctx.authApi.*` call: `ctx.authApi.<method>({ body, headers: request.headers })`. See the `withAuthPlugins` middleware doc for details.",run:t=>t.authApiCalls===void 0?[]:i(t.authApiCalls.filter(e=>!e.hasHeaders).map(e=>a(s,{cacheKey:`auth_api_call_without_headers:${e.file}:${e.line.toString()}:${e.method}`,detail:`\`ctx.authApi.${e.method||"<method>"}(…)\` in ${e.exportName} (${e.file}:${e.line.toString()}) is called without \`headers\` — better-auth skips session authorization, so this runs with full privileges. Pass the inbound \`headers\`.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,method:e.method}}))),source:"static",title:"Privileged ctx.authApi call missing headers"};export{s as default};
1
+ import{dedupeCacheKeys as i}from"./dedupeCacheKeys-DtBOHffV.mjs";import{e as a}from"./finding-NrKO8idM.mjs";const s={categories:["SECURITY"],description:"A `ctx.authApi.<method>(...)` call omits `headers` — better-auth skips session authorization and runs the call with full server-to-server privileges, regardless of the caller's identity.",facing:"EXTERNAL",level:"WARN",name:"auth_api_call_without_headers",remediation:"Pass the inbound `headers` from the incoming request into every `ctx.authApi.*` call: `ctx.authApi.<method>({ body, headers: request.headers })`. See the `withAuthPlugins` middleware doc for details.",run:t=>t.authApiCalls===void 0?[]:i(t.authApiCalls.filter(e=>!e.hasHeaders).map(e=>a(s,{cacheKey:`auth_api_call_without_headers:${e.file}:${e.line.toString()}:${e.method}`,detail:`\`ctx.authApi.${e.method||"<method>"}(…)\` in ${e.exportName} (${e.file}:${e.line.toString()}) is called without \`headers\` — better-auth skips session authorization, so this runs with full privileges. Pass the inbound \`headers\`.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,method:e.method}}))),source:"static",title:"Privileged ctx.authApi call missing headers"};export{s as default};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A `createAuth({...})` call sets `advanced.disableCSRFCheck: true`, turning off better-auth's origin validation for state-changing auth requests — a cross-site request riding the ambient auth cookie is processed like a same-origin one.",facing:"EXTERNAL",level:"ERROR",name:"auth_csrf_check_disabled",remediation:"Remove `advanced.disableCSRFCheck: true` from `createAuth({...})` and rely on `trustedOrigins` for origin validation instead of disabling the check.",run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(e=>e.analyzable&&e.disableCsrfCheck).map(e=>a(i,{cacheKey:`auth_csrf_check_disabled:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`advanced.disableCSRFCheck: true\`, turning off origin validation for state-changing auth requests. Remove the flag and rely on \`trustedOrigins\` instead.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth CSRF check disabled"};export{i as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const i={categories:["SECURITY"],description:"A `createAuth({...})` call sets `advanced.disableCSRFCheck: true`, turning off better-auth's origin validation for state-changing auth requests — a cross-site request riding the ambient auth cookie is processed like a same-origin one.",facing:"EXTERNAL",level:"ERROR",name:"auth_csrf_check_disabled",remediation:"Remove `advanced.disableCSRFCheck: true` from `createAuth({...})` and rely on `trustedOrigins` for origin validation instead of disabling the check.",run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(e=>e.analyzable&&e.disableCsrfCheck).map(e=>a(i,{cacheKey:`auth_csrf_check_disabled:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`advanced.disableCSRFCheck: true\`, turning off origin validation for state-changing auth requests. Remove the flag and rely on \`trustedOrigins\` instead.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth CSRF check disabled"};export{i as default};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const t={categories:["SECURITY"],description:"A `createAuth({...})` call enables `emailAndPassword` with no `requireEmailVerification: true`. better-auth defaults email verification off, so an account is usable before the caller has proven ownership of the email address.",facing:"EXTERNAL",level:"WARN",name:"auth_email_verification_disabled",remediation:"Set `emailAndPassword.requireEmailVerification: true` on `createAuth({...})` so a new account can't act until its email address is verified.",run:i=>i.authConfigs===void 0?[]:i.authConfigs.filter(e=>e.analyzable&&e.emailPasswordEnabled&&!e.requireEmailVerification).map(e=>a(t,{cacheKey:`auth_email_verification_disabled:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) enables \`emailAndPassword\` with no \`requireEmailVerification: true\`, so an account is usable before its email is proven. Set \`emailAndPassword.requireEmailVerification: true\`.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth email verification disabled"};export{t as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const t={categories:["SECURITY"],description:"A `createAuth({...})` call enables `emailAndPassword` with no `requireEmailVerification: true`. better-auth defaults email verification off, so an account is usable before the caller has proven ownership of the email address.",facing:"EXTERNAL",level:"WARN",name:"auth_email_verification_disabled",remediation:"Set `emailAndPassword.requireEmailVerification: true` on `createAuth({...})` so a new account can't act until its email address is verified.",run:i=>i.authConfigs===void 0?[]:i.authConfigs.filter(e=>e.analyzable&&e.emailPasswordEnabled&&!e.requireEmailVerification).map(e=>a(t,{cacheKey:`auth_email_verification_disabled:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) enables \`emailAndPassword\` with no \`requireEmailVerification: true\`, so an account is usable before its email is proven. Set \`emailAndPassword.requireEmailVerification: true\`.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth email verification disabled"};export{t as default};
@@ -0,0 +1 @@
1
+ import{e}from"./finding-NrKO8idM.mjs";const r={categories:["SECURITY"],description:"A `createAuth({...})` call loads `scim()` with a `database` that has no native transactions (`lunoraD1Adapter` / `lunoraAuthAdapter`). `@better-auth/scim` refuses to serve on such an adapter, so every SCIM request fails.",facing:"EXTERNAL",level:"ERROR",name:"auth_scim_without_transactions",remediation:"Move the auth tables somewhere with real transactions: `.auth({ namespace })` with a `LunoraAuthDO` (Durable Object storage), or a Postgres/MySQL kysely dialect over `@lunora/hyperdrive` with `transaction: true`. D1 cannot host SCIM.",run:a=>a.authConfigs===void 0?[]:a.authConfigs.filter(t=>t.analyzable&&t.scimOnNonTransactionalAdapter).map(t=>e(r,{cacheKey:`auth_scim_without_transactions:${t.file}:${t.line.toString()}`,detail:`\`createAuth\` in \`${t.exportName}\` (${t.file}:${t.line.toString()}) loads \`scim()\` on an adapter without native transactions, so the SCIM plugin will refuse to serve and every provisioning request from your IdP fails. Host the auth tables in a Durable Object (\`.auth({ namespace })\`) or on Postgres/MySQL via \`@lunora/hyperdrive\`.`,metadata:{exportName:t.exportName,file:t.file,line:t.line}})),source:"static",title:"SCIM configured on an adapter without transactions"};export{r as default};
@@ -1 +1 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";const s={categories:["SECURITY"],description:"A `createAuth({...})` call sets `advanced.useSecureCookies: false`, overriding Lunora's secure-by-default posture — the session cookie ships without `Secure` even on an HTTPS deployment, so it can be sent over a plaintext connection.",facing:"EXTERNAL",level:"ERROR",name:"auth_secure_cookies_disabled",remediation:"Remove `advanced.useSecureCookies: false` from `createAuth({...})` — let Lunora's secure-by-default posture set the `Secure` cookie attribute for an HTTPS deployment.",run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(e=>e.analyzable&&e.secureCookiesDisabled).map(e=>o(s,{cacheKey:`auth_secure_cookies_disabled:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`advanced.useSecureCookies: false\`, so the session cookie ships without \`Secure\` even on an HTTPS deployment. Remove the override and let Lunora's secure-by-default posture apply.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth secure cookies disabled"};export{s as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const s={categories:["SECURITY"],description:"A `createAuth({...})` call sets `advanced.useSecureCookies: false`, overriding Lunora's secure-by-default posture — the session cookie ships without `Secure` even on an HTTPS deployment, so it can be sent over a plaintext connection.",facing:"EXTERNAL",level:"ERROR",name:"auth_secure_cookies_disabled",remediation:"Remove `advanced.useSecureCookies: false` from `createAuth({...})` — let Lunora's secure-by-default posture set the `Secure` cookie attribute for an HTTPS deployment.",run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(e=>e.analyzable&&e.secureCookiesDisabled).map(e=>a(s,{cacheKey:`auth_secure_cookies_disabled:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`advanced.useSecureCookies: false\`, so the session cookie ships without \`Secure\` even on an HTTPS deployment. Remove the override and let Lunora's secure-by-default posture apply.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth secure cookies disabled"};export{s as default};
@@ -1 +1 @@
1
- import{i as s}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A `createAuth({...})` call sets `session.freshAge: 0`, disabling better-auth's recent-reauth check for sensitive operations — a long-lived stolen session can perform them without proving the caller still controls the credentials.",facing:"EXTERNAL",level:"WARN",name:"auth_session_freshage_zero",remediation:"Remove `session.freshAge: 0` from `createAuth({...})` (or set it to a real window) so sensitive operations require a recently re-authenticated session.",run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(e=>e.analyzable&&e.sessionFreshAgeZero).map(e=>s(i,{cacheKey:`auth_session_freshage_zero:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`session.freshAge: 0\`, disabling the recent-reauth check for sensitive operations. Remove the override or set a real window.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth session freshAge zero"};export{i as default};
1
+ import{e as s}from"./finding-NrKO8idM.mjs";const r={categories:["SECURITY"],description:"A `createAuth({...})` call sets `session.freshAge: 0`, disabling better-auth's recent-reauth check for sensitive operations — a long-lived stolen session can perform them without proving the caller still controls the credentials.",facing:"EXTERNAL",level:"WARN",name:"auth_session_freshage_zero",remediation:"Remove `session.freshAge: 0` from `createAuth({...})` (or set it to a real window) so sensitive operations require a recently re-authenticated session.",run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(e=>e.analyzable&&e.sessionFreshAgeZero).map(e=>s(r,{cacheKey:`auth_session_freshage_zero:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`session.freshAge: 0\`, disabling the recent-reauth check for sensitive operations. Remove the override or set a real window.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth session freshAge zero"};export{r as default};
@@ -1 +1 @@
1
- import{i as e}from"./finding-BAVgqnCF.mjs";const r={categories:["SECURITY"],description:"A `createAuth({...})` call's `trustedOrigins` array contains a `\"*\"` entry, disabling better-auth's CSRF/origin validation entirely — any origin is accepted for state-changing auth requests.",facing:"EXTERNAL",level:"ERROR",name:"auth_trusted_origins_wildcard",remediation:'List the specific origins your deployment serves in `trustedOrigins` instead of `"*"` — a wildcard disables origin validation for every caller.',run:t=>t.authConfigs===void 0?[]:t.authConfigs.filter(i=>i.analyzable&&i.trustedOriginsWildcard).map(i=>e(r,{cacheKey:`auth_trusted_origins_wildcard:${i.file}:${i.line.toString()}`,detail:`\`createAuth\` in \`${i.exportName}\` (${i.file}:${i.line.toString()}) sets \`trustedOrigins\` to include \`"*"\`, disabling CSRF/origin validation for every request. List the specific origins your deployment serves instead.`,metadata:{exportName:i.exportName,file:i.file,line:i.line}})),source:"static",title:"createAuth trustedOrigins wildcard"};export{r as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const r={categories:["SECURITY"],description:"A `createAuth({...})` call's `trustedOrigins` array contains a `\"*\"` entry, disabling better-auth's CSRF/origin validation entirely — any origin is accepted for state-changing auth requests.",facing:"EXTERNAL",level:"ERROR",name:"auth_trusted_origins_wildcard",remediation:'List the specific origins your deployment serves in `trustedOrigins` instead of `"*"` — a wildcard disables origin validation for every caller.',run:i=>i.authConfigs===void 0?[]:i.authConfigs.filter(e=>e.analyzable&&e.trustedOriginsWildcard).map(e=>t(r,{cacheKey:`auth_trusted_origins_wildcard:${e.file}:${e.line.toString()}`,detail:`\`createAuth\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) sets \`trustedOrigins\` to include \`"*"\`, disabling CSRF/origin validation for every request. List the specific origins your deployment serves instead.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"createAuth trustedOrigins wildcard"};export{r as default};
@@ -1 +1 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const r={categories:["SECURITY"],description:"`createBrowser({ allowPrivateTargets: true })` disables the private/internal-IP SSRF guard, letting a navigated URL reach cloud metadata endpoints, internal services, or loopback. Combined with a request-supplied URL this is a server-side request forgery path.",facing:"EXTERNAL",level:"ERROR",name:"browser_allow_private_targets",remediation:"Remove `allowPrivateTargets: true` from `createBrowser({...})` so the private/internal-address guard stays on. If a specific internal host is genuinely required, pin it with an `allowedHosts` allowlist instead of opening all private targets.",run:a=>a.configCalls===void 0?[]:a.configCalls.filter(e=>e.callee==="createBrowser"&&e.trueKeys.includes("allowPrivateTargets")).map(e=>t(r,{cacheKey:`browser_allow_private_targets:${e.file}:${e.line.toString()}`,detail:`\`createBrowser({ allowPrivateTargets: true })\` in ${e.file}:${e.line.toString()} disables the private/internal-address SSRF guard — a navigated URL can then reach cloud metadata, internal services, or loopback. Remove the override, or pin the specific host with \`allowedHosts\` instead.`,metadata:{callee:e.callee,file:e.file,line:e.line}})),source:"static",title:"Browser with private-target SSRF guard disabled"};export{r as default};
1
+ import{e as r}from"./finding-NrKO8idM.mjs";const a={categories:["SECURITY"],description:"`createBrowser({ allowPrivateTargets: true })` disables the private/internal-IP SSRF guard, letting a navigated URL reach cloud metadata endpoints, internal services, or loopback. Combined with a request-supplied URL this is a server-side request forgery path.",facing:"EXTERNAL",level:"ERROR",name:"browser_allow_private_targets",remediation:"Remove `allowPrivateTargets: true` from `createBrowser({...})` so the private/internal-address guard stays on. If a specific internal host is genuinely required, pin it with an `allowedHosts` allowlist instead of opening all private targets.",run:t=>t.configCalls===void 0?[]:t.configCalls.filter(e=>e.callee==="createBrowser"&&e.trueKeys.includes("allowPrivateTargets")).map(e=>r(a,{cacheKey:`browser_allow_private_targets:${e.file}:${e.line.toString()}`,detail:`\`createBrowser({ allowPrivateTargets: true })\` in ${e.file}:${e.line.toString()} disables the private/internal-address SSRF guard — a navigated URL can then reach cloud metadata, internal services, or loopback. Remove the override, or pin the specific host with \`allowedHosts\` instead.`,metadata:{callee:e.callee,file:e.file,line:e.line}})),source:"static",title:"Browser with private-target SSRF guard disabled"};export{a as default};
@@ -1 +1 @@
1
- import{m as t}from"./argument-derived-sink-BpO3NPPM.mjs";const a=t({cacheKey:e=>`browser_user_url_without_allowlist:${e.file}:${e.line.toString()}`,categories:["SECURITY"],description:"A `ctx.browser.<method>(url, …)` call navigates to a URL derived from the handler's `args` with no server-side scoping, and no `createBrowser` allowlist contains it. The default guard blocks private targets but not arbitrary public URLs, so any caller can make the headless browser an open-proxy / SSRF tool (fetch arbitrary third-party URLs, DNS-rebind to internal hosts).",detail:e=>`\`ctx.browser.${e.method}\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) navigates to a URL derived from \`args\` with no server-side scoping, and no \`createBrowser\` allowlist contains it — the default guard blocks private targets but not arbitrary public URLs, so any caller can turn the headless browser into an open-proxy / SSRF tool. Pin \`allowedHosts\` (and/or \`resolveDns\`) on \`createBrowser({...})\`, and derive the URL from server-trusted state where possible.`,facing:"EXTERNAL",getAccesses:e=>e.browserUrlAccesses,level:"WARN",metadata:e=>({exportName:e.exportName,file:e.file,line:e.line,method:e.method}),name:"browser_user_url_without_allowlist",remediation:"Pin the browser with an `allowedHosts` allowlist (and/or `resolveDns`) on `createBrowser({...})`, and derive the navigation URL from server-trusted state where possible rather than passing `args` straight to `ctx.browser`.",suppressWhen:e=>(e.configCalls??[]).some(r=>r.callee==="createBrowser"&&r.analyzable&&(r.presentKeys.includes("allowedHosts")||r.presentKeys.includes("resolveDns"))),title:"Browser navigates to arg-derived URL with no allowlist"});export{a as default};
1
+ import{m as t}from"./argument-derived-sink-DLXFC79t.mjs";const s=t({cacheKey:e=>`browser_user_url_without_allowlist:${e.file}:${e.line.toString()}`,categories:["SECURITY"],description:"A `ctx.browser.<method>(url, …)` call navigates to a URL derived from the handler's `args` with no server-side scoping, and no `createBrowser` allowlist contains it. The default guard blocks private targets but not arbitrary public URLs, so any caller can make the headless browser an open-proxy / SSRF tool (fetch arbitrary third-party URLs, DNS-rebind to internal hosts).",detail:e=>`\`ctx.browser.${e.method}\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) navigates to a URL derived from \`args\` with no server-side scoping, and no \`createBrowser\` allowlist contains it — the default guard blocks private targets but not arbitrary public URLs, so any caller can turn the headless browser into an open-proxy / SSRF tool. Pin \`allowedHosts\` (and/or \`resolveDns\`) on \`createBrowser({...})\`, and derive the URL from server-trusted state where possible.`,facing:"EXTERNAL",getAccesses:e=>e.browserUrlAccesses,level:"WARN",metadata:e=>({exportName:e.exportName,file:e.file,line:e.line,method:e.method}),name:"browser_user_url_without_allowlist",remediation:"Pin the browser with an `allowedHosts` allowlist (and/or `resolveDns`) on `createBrowser({...})`, and derive the navigation URL from server-trusted state where possible rather than passing `args` straight to `ctx.browser`.",suppressWhen:e=>(e.configCalls??[]).some(r=>r.callee==="createBrowser"&&r.analyzable&&(r.presentKeys.includes("allowedHosts")||r.presentKeys.includes("resolveDns"))),title:"Browser navigates to arg-derived URL with no allowlist"});export{s as default};
@@ -0,0 +1 @@
1
+ import{e as w}from"./finding-NrKO8idM.mjs";const E=100,S={categories:["SCHEMA"],description:"A chain of foreign-key relations forms a cycle (e.g. A → B → C → A). Circular FK dependencies can cause unexpected behavior during DELETE operations — a CASCADE chain may loop indefinitely, and even a RESTRICT cycle prevents deletion of any row in the loop without temporarily disabling constraints.",facing:"INTERNAL",level:"WARN",name:"circular_fk",remediation:"Remove or break the cycle by dropping at least one FK relation from the loop. Consider replacing the circular dependency with a nullable FK and explicit application logic, or restructure the schema using a junction table.",run:u=>{const h=[],i=[...new Set(u.schema.tables.map(e=>e.name))].toSorted((e,n)=>e.localeCompare(n)),g=new Map(i.map((e,n)=>[e,n])),l=new Map;for(const e of u.schema.tables)for(const n of e.relations){if(n.kind!=="one"||!g.has(n.table))continue;let t=l.get(e.name);t===void 0&&(t=new Set,l.set(e.name,t)),t.add(n.table)}const a=new Set,r=new Map,d=[],f=new Set;let p=!1;const b=e=>{a.delete(e);const n=r.get(e);if(n!==void 0)for(const t of n)n.delete(t),a.has(t)&&b(t)},k=()=>{const e=[...d];if(e.length<2)return;let n=0;for(let s=1;s<e.length;s+=1)e[s]<e[n]&&(n=s);const t=[...e.slice(n),...e.slice(0,n)],o=t.join("→");if(f.has(o))return;f.add(o);const c=[...t,t[0]].join(" → ");h.push(w(S,{cacheKey:`circular_fk:${o}`,detail:`Circular foreign-key dependency detected: ${c}. This cycle can cause unexpected behavior during DELETE operations.`,metadata:{cycle:t,path:c,tables:t}})),f.size>=E&&(p=!0)},y=(e,n)=>[...l.get(e)??[]].filter(t=>g.get(t)>=n),C=(e,n)=>{for(const t of y(e,n)){let o=r.get(t);o===void 0&&(o=new Set,r.set(t,o)),o.add(e)}},m=(e,n,t)=>{let o=!1;d.push(e),a.add(e);for(const c of y(e,t)){if(p)break;c===n?(k(),o=!0):!a.has(c)&&m(c,n,t)&&(o=!0)}return o?b(e):C(e,t),d.pop(),o};for(let e=0;e<i.length&&!p;e+=1)a.clear(),r.clear(),m(i[e],i[e],e);return h},source:"static",title:"Circular foreign-key dependency"};export{S as default};
@@ -0,0 +1 @@
1
+ const i=[{fact:e=>e.writesUserTable,reason:"writes an identity table"},{fact:e=>e.callsMail,reason:"sends mail"},{fact:e=>e.fanOut,reason:"fans out to a privileged dispatch surface"},{fact:e=>e.usesInsertManyUnsafe,reason:"bypasses validators with insertManyUnsafe"},{fact:e=>e.unboundedAiGeneration,reason:"runs an unbounded AI generation"}],o="may exhibit sensitive behaviour — its handler body could not be read",l=e=>{const a=[];let s=!1;for(const n of i){const t=n.fact(e);t===!0?a.push(n.reason):t===void 0&&(s=!0)}return s&&a.push(o),{level:a.length===0?"none":"high",reasons:a}};export{l as default};
@@ -0,0 +1 @@
1
+ import{byCodepoint as d}from"./MAP_VERSION-DEJGm0wI.mjs";const g=new Set(["clean","exempt","failing","warned"]),u=(e,r)=>{const t=new Map(e.map(s=>[s.name,s.occurrences]));return r.some(s=>s.occurrences>(t.get(s.name)??0))},p=e=>typeof e=="number"&&Number.isFinite(e),m=e=>{if(typeof e!="object"||e===null)return!1;const r=e;return typeof r.id=="string"&&p(r.score)&&typeof r.coverage=="string"&&g.has(r.coverage)},h=e=>{if(typeof e!="object"||e===null)return!1;const r=e;return Array.isArray(r.checks)&&p(r.score)},v=(e,r)=>{if(r.version!==e.version)return{comparable:!1,reason:"version-mismatch"};const t=new Map(r.procedures.map(o=>[o.id,o])),s=[],n=[],i=[];for(const o of e.procedures){if(o.coverage==="exempt")continue;const c=t.get(o.id);c!==void 0&&c.coverage!=="exempt"&&o.score<c.score&&s.push({after:o.score,before:c.score,id:o.id}),o.coverage==="failing"&&c?.coverage!=="failing"&&n.push(o.id),u(c?.checks??[],o.checks)&&i.push(o.id)}s.sort((o,c)=>d(o.id,c.id)),n.sort(d),i.sort(d);const a=e.score-r.score,f=u(r.project.checks,e.project.checks);return{comparable:!0,dropped:s,newFailing:n,projectRegressed:f,regressed:a<0||s.length>0||n.length>0||i.length>0||f,scoreDelta:a,worsened:i}},l=e=>{if(typeof e!="object"||e===null)return;const r=e;if(!(!p(r.version)||!p(r.score))&&!(!Array.isArray(r.procedures)||!r.procedures.every(t=>m(t)))&&h(r.project))return e};export{v as compareToBaseline,l as parseAdvisorMap};
@@ -0,0 +1 @@
1
+ import{e as f}from"./finding-NrKO8idM.mjs";const d=5,h=o=>typeof o._id=="string"?o._id:"?",g=(o,e,t)=>o?` (sample capped at ${e.toString()} rows — ${t})`:"",b=(o,e)=>{const t=o.slice(0,d),i=e-t.length,n=t.map(s=>`"${s}"`).join(", ");return i>0?`${n} (+${i.toString()} more)`:n},$=(o,e)=>{if(o==="_id")return e.existingIds;const t=new Set;for(const i of e.rows){const n=i[o];n!=null&&t.add(String(n))}return t},k=(o,e,t,i)=>{if(t.kind!=="one"||i.truncated)return;const n=t.references,s=$(n,i),r=t.field,a=[];for(const m of e.rows){const u=m[r];if(u==null)continue;const p=String(u);s.has(p)||a.push(h(m))}if(a.length===0)return;const c=e.truncated,{cap:l}=e,v=n==="_id"?`"${t.table}"`:`"${t.table}"."${n}"`,w=`Table "${e.table}": ${a.length.toString()} sampled row(s) have a dangling FK value in column "${r}" — the referenced ${v} row does not exist. Row ids: ${b(a,a.length)}${g(c,l,"more rows may exist beyond the window")}.`;return f(o,{cacheKey:`constraint_validator:fk:${e.table}:${r}`,detail:w,metadata:{cap:l,column:r,count:a.length,examples:a.slice(0,d),kind:"fk",references:n,referencesTable:t.table,table:e.table,truncated:c}})},S=(o,e,t,i)=>{if(!i)return;const n=[];for(const r of e.rows){const a=r[t];a==null&&n.push(h(r))}if(n.length===0)return;const s=`Table "${e.table}": ${n.length.toString()} sampled row(s) have a null/missing value in column "${t}", which is declared as non-optional. Row ids: ${b(n,n.length)}${g(e.truncated,e.cap,"more rows may exist beyond the window")}.`;return f(o,{cacheKey:`constraint_validator:null:${e.table}:${t}`,detail:s,metadata:{cap:e.cap,column:t,count:n.length,examples:n.slice(0,d),kind:"null",table:e.table,truncated:e.truncated}})},y=(o,e)=>{const t=[];for(const i of e){const n=o[i];if(n==null)return;t.push(JSON.stringify(n))}return t.join("|")},x=(o,e,t)=>{t.includes(e)||t.push(e),t.push(o)},T=(o,e,t)=>{if(t.kind!=="index"||t.unique!==!0)return;const i=new Map,n=[];for(const r of e.rows){const a=h(r),c=y(r,t.fields);if(c===void 0)continue;const l=i.get(c);l===void 0?i.set(c,a):x(a,l,n)}if(n.length===0)return;const s=`Table "${e.table}": ${n.length.toString()} sampled row(s) share duplicate values on unique index "${t.name}" (${t.fields.join(", ")}). Row ids: ${b(n,n.length)}${g(e.truncated,e.cap,"duplicates beyond the window may exist")}.`;return f(o,{cacheKey:`constraint_validator:unique:${e.table}:${t.name}`,detail:s,metadata:{cap:e.cap,count:n.length,examples:n.slice(0,d),fields:t.fields,index:t.name,kind:"unique",table:e.table,truncated:e.truncated}})},_=(o,e,t,i)=>{const n=i.get(e.table);if(!n)return[];const s=[];for(const r of n.relations){const a=t.get(r.table);if(!a)continue;const c=k(o,e,r,a);c&&s.push(c)}for(const r of n.fields){const a=!n.optionalFields?.has(r),c=S(o,e,r,a);c&&s.push(c)}for(const r of n.indexes){const a=T(o,e,r);a&&s.push(a)}return s},R={categories:["SCHEMA"],description:"Sampled rows violate one or more declared constraints: a foreign-key column references a non-existent row, a non-optional column contains null, or a unique-indexed column has duplicate values. These violations indicate data inserted before a constraint was enforced (e.g. a schema migration, a raw import, or a bug in a past data-migration transform).",facing:"INTERNAL",level:"WARN",name:"constraint_validator",remediation:"Inspect the listed rows and correct or remove the violating values. Run a data migration to backfill nulls, dedup unique violations, or re-link dangling FK references. For FK violations, confirm the target table rows exist before fixing the referencing rows.",run:o=>{if(!o.tableSamples||o.tableSamples.length===0)return[];const e=new Map;for(const i of o.tableSamples)e.set(i.table,i);const t=new Map(o.schema.tables.map(i=>[i.name,i]));return o.tableSamples.flatMap(i=>_(R,i,e,t))},source:"runtime",title:"Constraint violation"};export{R as default};
@@ -1 +1 @@
1
- import{m as t}from"./argument-derived-sink-BpO3NPPM.mjs";const r=t({cacheKey:e=>`container_instance_key_from_user_input:${e.file}:${e.line.toString()}`,categories:["SECURITY"],description:"A `ctx.containers.<name>.get` call routes to a container instance using a key derived from the handler's `args` with no server-side scoping. Any caller can supply another tenant's key and reach that tenant's container instance — a cross-tenant insecure direct object reference (IDOR).",detail:e=>`\`ctx.containers.*.${e.method}\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) routes to a container instance using a key derived from \`args\` with no server-side scoping — any caller can reach another tenant's container (IDOR). Derive the key from a server-trusted identity (e.g. \`\${ctx.auth.userId}\`).`,facing:"EXTERNAL",getAccesses:e=>e.containerKeyAccesses,level:"WARN",metadata:e=>({exportName:e.exportName,file:e.file,line:e.line,method:e.method}),name:"container_instance_key_from_user_input",remediation:"Derive the container instance key from a server-trusted identity (e.g. `${ctx.auth.userId}`) or a record the caller owns — never pass request input straight to `ctx.containers.<name>.get`.",title:"Possible cross-tenant IDOR from arg-derived unscoped container key"});export{r as default};
1
+ import{m as t}from"./argument-derived-sink-DLXFC79t.mjs";const r=t({cacheKey:e=>`container_instance_key_from_user_input:${e.file}:${e.line.toString()}`,categories:["SECURITY"],description:"A `ctx.containers.<name>.get` call routes to a container instance using a key derived from the handler's `args` with no server-side scoping. Any caller can supply another tenant's key and reach that tenant's container instance — a cross-tenant insecure direct object reference (IDOR).",detail:e=>`\`ctx.containers.*.${e.method}\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) routes to a container instance using a key derived from \`args\` with no server-side scoping — any caller can reach another tenant's container (IDOR). Derive the key from a server-trusted identity (e.g. \`\${ctx.auth.userId}\`).`,facing:"EXTERNAL",getAccesses:e=>e.containerKeyAccesses,level:"WARN",metadata:e=>({exportName:e.exportName,file:e.file,line:e.line,method:e.method}),name:"container_instance_key_from_user_input",remediation:"Derive the container instance key from a server-trusted identity (e.g. `${ctx.auth.userId}`) or a record the caller owns — never pass request input straight to `ctx.containers.<name>.get`.",title:"Possible cross-tenant IDOR from arg-derived unscoped container key"});export{r as default};
@@ -1 +1 @@
1
- import{i as s}from"./finding-BAVgqnCF.mjs";const r=new Set(["standard-3","standard-4"]),o=2,c=4096,d=e=>typeof e=="string"?r.has(e):(e.vcpu??0)>o||(e.memoryMib??0)>c,l={categories:["PERFORMANCE"],description:"A container is declared on a large instance type, whose provisioned memory and disk are billed for the lifetime of every running instance.",facing:"INTERNAL",level:"INFO",name:"container_oversized_instance",remediation:"Confirm the workload needs this size; a smaller instanceType (lite/basic/standard-1) costs less while idle-but-running.",run:e=>{const t=[];for(const i of e.containers??[]){const{instanceType:n}=i;if(n===void 0||!d(n))continue;const a=typeof n=="string"?n:JSON.stringify(n);t.push(s(l,{cacheKey:`container_oversized_instance:${i.exportName}`,detail:`Container "${i.exportName}" uses a large instance type (${a}); its memory + disk are billed while any instance runs.`,metadata:{container:i.exportName,instanceType:n}}))}return t},source:"static",title:"Oversized container instance"};export{l as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const r=new Set(["standard-3","standard-4"]),o=2,c=4096,d=e=>typeof e=="string"?r.has(e):(e.vcpu??0)>o||(e.memoryMib??0)>c,l={categories:["PERFORMANCE"],description:"A container is declared on a large instance type, whose provisioned memory and disk are billed for the lifetime of every running instance.",facing:"INTERNAL",level:"INFO",name:"container_oversized_instance",remediation:"Confirm the workload needs this size; a smaller instanceType (lite/basic/standard-1) costs less while idle-but-running.",run:e=>{const t=[];for(const i of e.containers??[]){const{instanceType:n}=i;if(n===void 0||!d(n))continue;const s=typeof n=="string"?n:JSON.stringify(n);t.push(a(l,{cacheKey:`container_oversized_instance:${i.exportName}`,detail:`Container "${i.exportName}" uses a large instance type (${s}); its memory + disk are billed while any instance runs.`,metadata:{container:i.exportName,instanceType:n}}))}return t},source:"static",title:"Oversized container instance"};export{l as default};
@@ -1 +1 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const a={categories:["SECURITY"],description:"A container leaves outbound internet access at the default (enabled), which is billed per GB of egress and widens the attack surface.",facing:"INTERNAL",level:"INFO",name:"container_public_internet",remediation:"Set `enableInternet: false` on the container if it doesn't call external services, or `true` to opt in deliberately.",run:t=>(t.containers??[]).filter(e=>e.enableInternet===void 0).map(e=>n(a,{cacheKey:`container_public_internet:${e.exportName}`,detail:`Container "${e.exportName}" doesn't set enableInternet, so outbound internet is on by default (egress is billed).`,metadata:{container:e.exportName}})),source:"static",title:"Container egress enabled by default"};export{a as default};
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const a={categories:["SECURITY"],description:"A container leaves outbound internet access at the default (enabled), which is billed per GB of egress and widens the attack surface.",facing:"INTERNAL",level:"INFO",name:"container_public_internet",remediation:"Set `enableInternet: false` on the container if it doesn't call external services, or `true` to opt in deliberately.",run:t=>(t.containers??[]).filter(e=>e.enableInternet===void 0).map(e=>n(a,{cacheKey:`container_public_internet:${e.exportName}`,detail:`Container "${e.exportName}" doesn't set enableInternet, so outbound internet is on by default (egress is billed).`,metadata:{container:e.exportName}})),source:"static",title:"Container egress enabled by default"};export{a as default};
@@ -1 +1 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A runtime `.egress.allow(...)` / `.egress.deny(...)` / `.egress.setAllowed(...)` call mutates the container's egress firewall at runtime, re-opening a network surface the static analysis assumes is locked to the `defineContainer` declaration.",facing:"EXTERNAL",level:"WARN",name:"container_runtime_egress_relaxation",remediation:"Prefer declaring the egress policy statically in `lunora/containers.ts` where it is auditable. When a runtime mutation is unavoidable, scope it behind a server-trusted check and keep the change narrow (a specific host, not a broad allow) rather than an unconditional relaxation.",run:a=>a.containerOverrides===void 0?[]:a.containerOverrides.filter(e=>e.kind==="egress_relaxation").map(e=>t(i,{cacheKey:`container_runtime_egress_relaxation:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` calls \`.egress.${e.detail}(...)\` at runtime, mutating the container's egress firewall.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,method:e.detail}})),source:"static",title:"Runtime egress mutation relaxes the container firewall"};export{i as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const n={categories:["SECURITY"],description:"A runtime `.egress.allow(...)` / `.egress.deny(...)` / `.egress.setAllowed(...)` call mutates the container's egress firewall at runtime, re-opening a network surface the static analysis assumes is locked to the `defineContainer` declaration.",facing:"EXTERNAL",level:"WARN",name:"container_runtime_egress_relaxation",remediation:"Prefer declaring the egress policy statically in `lunora/containers.ts` where it is auditable. When a runtime mutation is unavoidable, scope it behind a server-trusted check and keep the change narrow (a specific host, not a broad allow) rather than an unconditional relaxation.",run:t=>t.containerOverrides===void 0?[]:t.containerOverrides.filter(e=>e.kind==="egress_relaxation").map(e=>a(n,{cacheKey:`container_runtime_egress_relaxation:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` calls \`.egress.${e.detail}(...)\` at runtime, mutating the container's egress firewall.`,metadata:{exportName:e.exportName,file:e.file,line:e.line,method:e.detail}})),source:"static",title:"Runtime egress mutation relaxes the container firewall"};export{n as default};
@@ -1 +1 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A runtime `.start({ enableInternet: true, … })` call overrides the static `defineContainer` declaration and re-opens public internet egress for that container instance, defeating the `container_public_internet` lint's assumption that the declaration is authoritative.",facing:"EXTERNAL",level:"WARN",name:"container_start_enable_internet_override",remediation:"Avoid overriding `enableInternet` at `.start()` time. Set it once on the `defineContainer` declaration in `lunora/containers.ts` so the static advisors can reason about it, and gate any runtime opt-in behind a server-trusted check rather than an unconditional literal.",run:t=>t.containerOverrides===void 0?[]:t.containerOverrides.filter(e=>e.kind==="enable_internet").map(e=>n(i,{cacheKey:`container_start_enable_internet_override:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` calls \`.start({ enableInternet: true })\`, overriding the container's static egress posture.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"Runtime .start() override re-enables container internet access"};export{i as default};
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const a={categories:["SECURITY"],description:"A runtime `.start({ enableInternet: true, … })` call overrides the static `defineContainer` declaration and re-opens public internet egress for that container instance, defeating the `container_public_internet` lint's assumption that the declaration is authoritative.",facing:"EXTERNAL",level:"WARN",name:"container_start_enable_internet_override",remediation:"Avoid overriding `enableInternet` at `.start()` time. Set it once on the `defineContainer` declaration in `lunora/containers.ts` so the static advisors can reason about it, and gate any runtime opt-in behind a server-trusted check rather than an unconditional literal.",run:t=>t.containerOverrides===void 0?[]:t.containerOverrides.filter(e=>e.kind==="enable_internet").map(e=>n(a,{cacheKey:`container_start_enable_internet_override:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` calls \`.start({ enableInternet: true })\`, overriding the container's static egress posture.`,metadata:{exportName:e.exportName,file:e.file,line:e.line}})),source:"static",title:"Runtime .start() override re-enables container internet access"};export{a as default};
@@ -0,0 +1 @@
1
+ const a=r=>{const t=new Map;return r.map(e=>{const c=(t.get(e.cacheKey)??0)+1;return t.set(e.cacheKey,c),c===1?e:{...e,cacheKey:`${e.cacheKey}:${c.toString()}`}})};export{a as dedupeCacheKeys};
@@ -1 +1 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";const r=(a,i)=>a.length>i.length?!1:a.every((n,d)=>n===i[d]),l={categories:["PERFORMANCE"],description:"A secondary index is redundant because another index already covers every lookup it serves (its columns are a leading prefix of the other's). The redundant index costs storage and is maintained on every write for no read benefit.",facing:"INTERNAL",level:"INFO",name:"duplicate_index",remediation:"Drop the redundant index; the covering index already serves its lookups.",run:a=>{const i=[];for(const n of a.schema.tables){const d=n.indexes.filter(e=>e.kind==="index");for(const e of d){if(e.unique===!0)continue;const t=d.find(s=>s===e||!r(e.fields,s.fields)?!1:e.fields.length===s.fields.length?e.name>s.name:!0);t&&i.push(o(l,{cacheKey:`duplicate_index:${n.name}:${e.name}`,detail:`Index "${e.name}" on table "${n.name}" (${e.fields.join(", ")}) is redundant — index "${t.name}" (${t.fields.join(", ")}) already covers its lookups.`,metadata:{coveredBy:{fields:t.fields,name:t.name},fields:e.fields,index:e.name,table:n.name}}))}}return i},source:"static",title:"Duplicate / redundant index"};export{l as default};
1
+ import{e as r}from"./finding-NrKO8idM.mjs";const o=(a,i)=>a.length>i.length?!1:a.every((n,d)=>n===i[d]),l={categories:["PERFORMANCE"],description:"A secondary index is redundant because another index already covers every lookup it serves (its columns are a leading prefix of the other's). The redundant index costs storage and is maintained on every write for no read benefit.",facing:"INTERNAL",level:"INFO",name:"duplicate_index",remediation:"Drop the redundant index; the covering index already serves its lookups.",run:a=>{const i=[];for(const n of a.schema.tables){const d=n.indexes.filter(e=>e.kind==="index");for(const e of d){if(e.unique===!0)continue;const t=d.find(s=>s===e||!o(e.fields,s.fields)?!1:e.fields.length===s.fields.length?e.name>s.name:!0);t&&i.push(r(l,{cacheKey:`duplicate_index:${n.name}:${e.name}`,detail:`Index "${e.name}" on table "${n.name}" (${e.fields.join(", ")}) is redundant — index "${t.name}" (${t.fields.join(", ")}) already covers its lookups.`,metadata:{coveredBy:{fields:t.fields,name:t.name},fields:e.fields,index:e.name,table:n.name}}))}}return i},source:"static",title:"Duplicate / redundant index"};export{l as default};
@@ -1 +1 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const i={categories:["SCHEMA"],description:"An index declares no columns, so it indexes nothing and can never narrow a read.",facing:"INTERNAL",level:"WARN",name:"empty_index",remediation:"Give the index its columns, or remove it.",run:a=>a.schema.tables.flatMap(n=>n.indexes.filter(e=>e.kind==="index"&&e.fields.length===0).map(e=>t(i,{cacheKey:`empty_index:${n.name}:${e.name}`,detail:`Index "${e.name}" on table "${n.name}" declares no columns.`,metadata:{index:e.name,table:n.name}}))),source:"static",title:"Empty index"};export{i as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const i={categories:["SCHEMA"],description:"An index declares no columns, so it indexes nothing and can never narrow a read.",facing:"INTERNAL",level:"WARN",name:"empty_index",remediation:"Give the index its columns, or remove it.",run:a=>a.schema.tables.flatMap(n=>n.indexes.filter(e=>e.kind==="index"&&e.fields.length===0).map(e=>t(i,{cacheKey:`empty_index:${n.name}:${e.name}`,detail:`Index "${e.name}" on table "${n.name}" declares no columns.`,metadata:{index:e.name,table:n.name}}))),source:"static",title:"Empty index"};export{i as default};
@@ -1 +1 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const i=20,s=.1,l={categories:["PERFORMANCE"],description:"A function's observed error rate has cleared a threshold high enough to be a genuine reliability signal — the same errorHotspots signal `lunora insights` reports, now gated in `lunora advisor`.",facing:"EXTERNAL",level:"WARN",name:"error_rate_outlier",remediation:"Read the function's `lastErrorMessage` / traces to find the failing dependency or input shape, then fix or guard it; `lunora insights` ranks every function by error rate for the same window.",run:o=>{const t=[];for(const r of o.functionMetrics??[]){if(r.calls<i)continue;const e=r.errors/r.calls;if(e<s)continue;const a=Math.round(e*100);t.push(n(l,{cacheKey:`error_rate_outlier:${r.path}`,detail:`"${r.path}" errored on ${r.errors.toString()} of ${r.calls.toString()} calls (${a.toString()}%) over the observed window.`,metadata:{calls:r.calls,errors:r.errors,path:r.path,rate:e}}))}return t},source:"runtime",title:"Error-rate outlier"};export{l as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const i=20,s=.1,l={categories:["PERFORMANCE"],description:"A function's observed error rate has cleared a threshold high enough to be a genuine reliability signal — the same errorHotspots signal `lunora insights` reports, now gated in `lunora advisor`.",facing:"EXTERNAL",level:"WARN",name:"error_rate_outlier",remediation:"Read the function's `lastErrorMessage` / traces to find the failing dependency or input shape, then fix or guard it; `lunora insights` ranks every function by error rate for the same window.",run:o=>{const t=[];for(const r of o.functionMetrics??[]){if(r.calls<i)continue;const e=r.errors/r.calls;if(e<s)continue;const n=Math.round(e*100);t.push(a(l,{cacheKey:`error_rate_outlier:${r.path}`,detail:`"${r.path}" errored on ${r.errors.toString()} of ${r.calls.toString()} calls (${n.toString()}%) over the observed window.`,metadata:{calls:r.calls,errors:r.errors,path:r.path,rate:e}}))}return t},source:"runtime",title:"Error-rate outlier"};export{l as default};
@@ -1 +1 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";const t={categories:["SCHEMA"],description:"A procedure throws a bare `new Error(...)`. It reaches the client as an opaque message the caller cannot branch on, and error grouping cannot fingerprint it into a stable issue.",facing:"EXTERNAL",level:"WARN",name:"error_without_catalog",remediation:'Throw a coded error instead: `throw new LunoraError("<CODE>", { … })` from `@lunora/errors`, adding the code to `ERROR_CATALOG` if it is new.',run:e=>e.procedureProtections===void 0?[]:e.procedureProtections.filter(r=>r.throwsBareError===!0).map(r=>o(t,{cacheKey:`error_without_catalog:${r.file}:${r.exportName}`,detail:`${r.kind} \`${r.exportName}\` (${r.file}) throws a bare \`new Error(...)\`. Use \`LunoraError\` with a catalog code so the client can branch on it and Studio can group it.`,metadata:{exportName:r.exportName,file:r.file,kind:r.kind}})),source:"static",title:"Bare Error thrown instead of a coded LunoraError"};export{t as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const o={categories:["SCHEMA"],description:"A procedure throws a bare `new Error(...)`. It reaches the client as an opaque message the caller cannot branch on, and error grouping cannot fingerprint it into a stable issue.",facing:"EXTERNAL",level:"WARN",name:"error_without_catalog",remediation:'Throw a coded error instead: `throw new LunoraError("<CODE>", { … })` from `@lunora/errors`, adding the code to `ERROR_CATALOG` if it is new.',run:e=>e.procedureProtections===void 0?[]:e.procedureProtections.filter(r=>r.throwsBareError===!0).map(r=>t(o,{cacheKey:`error_without_catalog:${r.file}:${r.exportName}`,detail:`${r.kind} \`${r.exportName}\` (${r.file}) throws a bare \`new Error(...)\`. Use \`LunoraError\` with a catalog code so the client can branch on it and Studio can group it.`,metadata:{exportName:r.exportName,file:r.file,kind:r.kind}})),source:"static",title:"Bare Error thrown instead of a coded LunoraError"};export{o as default};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const c={defineExportSink:["name","deliver"],r2Sink:["bucket","name"],webhookExportSink:["name","url"]},f={categories:["SCHEMA"],description:"A CDC export sink (`defineExportSink` / `webhookExportSink` / `r2Sink`) is missing a required config field (a webhook with no `url`, an R2 sink with no `bucket`, or a sink with no `name`/`deliver`). The sink can never deliver a change batch, so the export tap silently drains nothing.",facing:"INTERNAL",level:"ERROR",name:"export_sink_misconfigured",remediation:"Supply the missing field: a non-empty `url` for `webhookExportSink`, a `bucket` R2 binding for `r2Sink`, or `name` + `deliver` for `defineExportSink`.",run:n=>{if(n.exportSinks===void 0)return[];const t=[];for(const e of n.exportSinks){if(!e.analyzable)continue;const r=new Set(e.presentKeys),o=new Set(e.emptyKeys);for(const i of c[e.factory]??[]){if(r.has(i)&&!o.has(i))continue;const s=o.has(i)?"is set to an empty string":"is missing";t.push(a(f,{cacheKey:`export_sink_misconfigured:${e.file}:${e.line.toString()}:${i}`,detail:`\`${e.factory}({ … })\` in ${e.file}:${e.line.toString()} ${s} its required \`${i}\` field — the sink can never deliver a change batch, so the export tap drains nothing.`,metadata:{factory:e.factory,field:i,file:e.file,line:e.line}}))}}return t},source:"static",title:"Export sink is misconfigured"};export{f as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const c={defineExportSink:["name","deliver"],r2Sink:["bucket","name"],webhookExportSink:["name","url"]},f={categories:["SCHEMA"],description:"A CDC export sink (`defineExportSink` / `webhookExportSink` / `r2Sink`) is missing a required config field (a webhook with no `url`, an R2 sink with no `bucket`, or a sink with no `name`/`deliver`). The sink can never deliver a change batch, so the export tap silently drains nothing.",facing:"INTERNAL",level:"ERROR",name:"export_sink_misconfigured",remediation:"Supply the missing field: a non-empty `url` for `webhookExportSink`, a `bucket` R2 binding for `r2Sink`, or `name` + `deliver` for `defineExportSink`.",run:n=>{if(n.exportSinks===void 0)return[];const t=[];for(const e of n.exportSinks){if(!e.analyzable)continue;const r=new Set(e.presentKeys),o=new Set(e.emptyKeys);for(const i of c[e.factory]??[]){if(r.has(i)&&!o.has(i))continue;const s=o.has(i)?"is set to an empty string":"is missing";t.push(a(f,{cacheKey:`export_sink_misconfigured:${e.file}:${e.line.toString()}:${i}`,detail:`\`${e.factory}({ … })\` in ${e.file}:${e.line.toString()} ${s} its required \`${i}\` field — the sink can never deliver a change batch, so the export tap drains nothing.`,metadata:{factory:e.factory,field:i,file:e.file,line:e.line}}))}}return t},source:"static",title:"Export sink is misconfigured"};export{f as default};
@@ -1 +1 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const l={categories:["SCHEMA"],description:"A `.source({ mode: 'incremental' })` table declares no delete-visibility path (`reconcileEveryMs` or `softDeleteColumn`), so upstream deletes are never applied and the materialized table accumulates phantom rows.",facing:"EXTERNAL",level:"ERROR",name:"external_source_incremental_no_delete_path",remediation:"Add `reconcileEveryMs` (a periodic full-pull sweep that GCs vanished rows) or `softDeleteColumn` (an upstream tombstone column the incremental pull returns). Incremental ingest can't see a delete otherwise.",run:o=>{const a=[];for(const e of o.schema.tables){const t=e.externalSource;if(t!==void 0){if(t.unanalyzable){a.push(n(l,{cacheKey:`external_source_incremental_no_delete_path:${e.name}`,detail:`Table \`${e.name}\`'s \`.source(...)\` config is not a static object literal, so its \`mode\`/delete-visibility can't be verified. If it is \`mode: "incremental"\`, confirm it declares \`reconcileEveryMs\` or \`softDeleteColumn\` — an incremental source without one accumulates phantom rows on every upstream delete.`,level:"WARN",metadata:{table:e.name}}));continue}t.mode!=="incremental"||t.hasReconcile||t.hasSoftDelete||a.push(n(l,{cacheKey:`external_source_incremental_no_delete_path:${e.name}`,detail:`Table \`${e.name}\` is \`.source({ mode: "incremental" })\` but declares neither \`reconcileEveryMs\` nor \`softDeleteColumn\` — an incremental pull never sees upstream deletes, so the materialized table would accumulate phantom rows. Add one of the two delete-visibility paths.`,metadata:{table:e.name}}))}}return a},source:"static",title:"Incremental sourced table has no delete-visibility path"};export{l as default};
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const l={categories:["SCHEMA"],description:"A `.source({ mode: 'incremental' })` table declares no delete-visibility path (`reconcileEveryMs` or `softDeleteColumn`), so upstream deletes are never applied and the materialized table accumulates phantom rows.",facing:"EXTERNAL",level:"ERROR",name:"external_source_incremental_no_delete_path",remediation:"Add `reconcileEveryMs` (a periodic full-pull sweep that GCs vanished rows) or `softDeleteColumn` (an upstream tombstone column the incremental pull returns). Incremental ingest can't see a delete otherwise.",run:o=>{const a=[];for(const e of o.schema.tables){const t=e.externalSource;if(t!==void 0){if(t.unanalyzable){a.push(n(l,{cacheKey:`external_source_incremental_no_delete_path:${e.name}`,detail:`Table \`${e.name}\`'s \`.source(...)\` config is not a static object literal, so its \`mode\`/delete-visibility can't be verified. If it is \`mode: "incremental"\`, confirm it declares \`reconcileEveryMs\` or \`softDeleteColumn\` — an incremental source without one accumulates phantom rows on every upstream delete.`,level:"WARN",metadata:{table:e.name}}));continue}t.mode!=="incremental"||t.hasReconcile||t.hasSoftDelete||a.push(n(l,{cacheKey:`external_source_incremental_no_delete_path:${e.name}`,detail:`Table \`${e.name}\` is \`.source({ mode: "incremental" })\` but declares neither \`reconcileEveryMs\` nor \`softDeleteColumn\` — an incremental pull never sees upstream deletes, so the materialized table would accumulate phantom rows. Add one of the two delete-visibility paths.`,metadata:{table:e.name}}))}}return a},source:"static",title:"Incremental sourced table has no delete-visibility path"};export{l as default};
@@ -1 +1 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const l={categories:["SCHEMA"],description:"A table is both `.source(...)` and `.global()`. A global table lives in the external/global tier; a sourced table is materialized into a shard DO's SQLite — the two are mutually exclusive.",facing:"INTERNAL",level:"ERROR",name:"external_source_on_global",remediation:'Drop either `.source(...)` or `.global()`. To ingest an external database into per-tenant DOs, use `.source(...)` with `.shardBy(...)`; to read it in place as a global table, use `.global({ backend: "hyperdrive" })`.',run:a=>a.schema.tables.filter(e=>e.externalSource!==void 0&&e.shardKind==="global").map(e=>t(l,{cacheKey:`external_source_on_global:${e.name}`,detail:`Table \`${e.name}\` is both \`.source(...)\` and \`.global()\` — contradictory. A sourced table materializes into a shard DO's SQLite; a global table lives in the external tier.`,metadata:{table:e.name}})),source:"static",title:"Sourced table cannot also be global"};export{l as default};
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const l={categories:["SCHEMA"],description:"A table is both `.source(...)` and `.global()`. A global table lives in the external/global tier; a sourced table is materialized into a shard DO's SQLite — the two are mutually exclusive.",facing:"INTERNAL",level:"ERROR",name:"external_source_on_global",remediation:'Drop either `.source(...)` or `.global()`. To ingest an external database into per-tenant DOs, use `.source(...)` with `.shardBy(...)`; to read it in place as a global table, use `.global({ backend: "hyperdrive" })`.',run:a=>a.schema.tables.filter(e=>e.externalSource!==void 0&&e.shardKind==="global").map(e=>t(l,{cacheKey:`external_source_on_global:${e.name}`,detail:`Table \`${e.name}\` is both \`.source(...)\` and \`.global()\` — contradictory. A sourced table materializes into a shard DO's SQLite; a global table lives in the external tier.`,metadata:{table:e.name}})),source:"static",title:"Sourced table cannot also be global"};export{l as default};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const n={categories:["SECURITY"],description:"A `.source(...)` table that is also `.shardBy(...)` has no `tenantBy` mapper, so every tenant's Durable Object runs the same unscoped query and replicates the whole multitenant table — a cross-tenant data leak.",facing:"EXTERNAL",level:"ERROR",name:"external_source_unscoped",remediation:"Add `tenantBy: (shardKey) => [shardKey]` (matching the query's bound parameters) so each shard DO pulls only its own tenant's rows. The shard key MUST bind into the source `WHERE`.",run:s=>{const t=[];for(const e of s.schema.tables)if(!(e.externalSource===void 0||e.shardKind!=="shardBy")){if(e.externalSource.unanalyzable){t.push(a(n,{cacheKey:`external_source_unscoped:${e.name}`,detail:`Table \`${e.name}\` is \`.source(...)\` + \`.shardBy(...)\` but its source config is not a static object literal, so \`tenantBy\` can't be verified. Confirm \`tenantBy\` binds the shard key into the query, or inline the config so the linter can check it — an unscoped query leaks every tenant's rows into each DO.`,level:"WARN",metadata:{table:e.name}}));continue}e.externalSource.hasTenantBy||t.push(a(n,{cacheKey:`external_source_unscoped:${e.name}`,detail:`Table \`${e.name}\` is \`.source(...)\` + \`.shardBy(...)\` but declares no \`tenantBy\` — every tenant DO would pull the whole table. Add a \`tenantBy\` that binds the shard key into the query.`,metadata:{table:e.name}}))}return t},source:"static",title:"Sourced + sharded table is not tenant-scoped"};export{n as default};
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const n={categories:["SECURITY"],description:"A `.source(...)` table that is also `.shardBy(...)` has no `tenantBy` mapper, so every tenant's Durable Object runs the same unscoped query and replicates the whole multitenant table — a cross-tenant data leak.",facing:"EXTERNAL",level:"ERROR",name:"external_source_unscoped",remediation:"Add `tenantBy: (shardKey) => [shardKey]` (matching the query's bound parameters) so each shard DO pulls only its own tenant's rows. The shard key MUST bind into the source `WHERE`.",run:s=>{const t=[];for(const e of s.schema.tables)if(!(e.externalSource===void 0||e.shardKind!=="shardBy")){if(e.externalSource.unanalyzable){t.push(a(n,{cacheKey:`external_source_unscoped:${e.name}`,detail:`Table \`${e.name}\` is \`.source(...)\` + \`.shardBy(...)\` but its source config is not a static object literal, so \`tenantBy\` can't be verified. Confirm \`tenantBy\` binds the shard key into the query, or inline the config so the linter can check it — an unscoped query leaks every tenant's rows into each DO.`,level:"WARN",metadata:{table:e.name}}));continue}e.externalSource.hasTenantBy||t.push(a(n,{cacheKey:`external_source_unscoped:${e.name}`,detail:`Table \`${e.name}\` is \`.source(...)\` + \`.shardBy(...)\` but declares no \`tenantBy\` — every tenant DO would pull the whole table. Add a \`tenantBy\` that binds the shard key into the query.`,metadata:{table:e.name}}))}return t},source:"static",title:"Sourced + sharded table is not tenant-scoped"};export{n as default};
@@ -0,0 +1 @@
1
+ import{e as i}from"./finding-NrKO8idM.mjs";const n=1e3,o=500,d={categories:["PERFORMANCE"],description:"A shard set is wide enough that a cross-shard read over it would approach the per-invocation subrequest ceiling.",facing:"EXTERNAL",level:"WARN",name:"fan_out_breadth",remediation:"Keep reads shard-pinned, or roll cross-shard answers up through a `.global()` table.",run:s=>{const a=(s.shardTraffic??[]).filter(e=>e.requests>0);if(a.length===0)return[];const r=new Map;for(const e of a){const t=e.group??"";r.set(t,(r.get(t)??0)+1)}return[...r].filter(([,e])=>e>=o).map(([e,t])=>i(d,{cacheKey:`fan_out_breadth:${e}`,detail:`${e===""?"This deployment":`Shard group "${e}"`} has ${String(t)} active shards, so a cross-shard read over it would issue ${String(t)} Durable Object subrequests — against a ceiling of ${String(n)} per invocation on the Free plan (higher on Paid, but finite). Shard-pinned reads are unaffected.`,metadata:{freePlanCeiling:n,group:e,shards:t}}))},source:"runtime",title:"Shard set wide enough to strain a cross-shard read"};export{d as default};
@@ -0,0 +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 +1 @@
1
- import{i as l}from"./finding-BAVgqnCF.mjs";import{s as c,q as u}from"./helpers-yHInjqeD.mjs";const s={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 r=u(e),a=d.get(e.table),o={exportName:e.exportName,file:e.file,line:e.line,shardKind:a??"unknown",table:e.table},n=`filter_without_index:${e.file}:${e.line.toString()}:${e.table}`;if(a==="shardBy"){t.push(l(s,{cacheKey:n,detail:`Query on "${e.table}" at ${r} 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(l(s,{cacheKey:n,detail:`Query on "${e.table}" at ${r} calls .filter() without an index — ${h}.`,metadata:o}))}return t},source:"static",title:"Filter without index"};export{s as default};
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};
@@ -0,0 +1 @@
1
+ const t=(e,a)=>({cacheKey:a.cacheKey,categories:a.categories??e.categories,description:e.description,detail:a.detail,facing:a.facing??e.facing,level:a.level??e.level,metadata:a.metadata,name:e.name,remediation:e.remediation,title:e.title});export{t as e};
@@ -0,0 +1 @@
1
+ const n="_id",t=s=>`by${s.charAt(0).toUpperCase()}${s.slice(1)}`,d=s=>new Set(s.indexes.filter(e=>e.kind==="index").map(e=>e.fields[0]).filter(e=>e!==void 0));export{n as P,d as l,t as s};
@@ -1 +1 @@
1
- import{i as s}from"./finding-BAVgqnCF.mjs";const i=new Set(["enforce","gate","lockdown","rls"]),o=new Set(["allow","bypass","permit"]),n=/(?<=[A-Z])(?=[A-Z][a-z])/gu,u=/(?<=[a-z0-9])(?=[A-Z])/gu,f=/[^a-z0-9]+/u,d=a=>a.replaceAll(n," ").replaceAll(u," ").toLowerCase().split(f).filter(e=>e.length>0),c=a=>{const e=d(a.key),l=e.some(t=>i.has(t)),r=e.some(t=>o.has(t));return l===r?!1:l?!a.defaultValue:a.defaultValue},p={categories:["SECURITY"],description:"A `ctx.flags.boolean(key, default)` read on a security-shaped key has a fail-open default that selects the permissive branch. OpenFeature returns the default when the provider errors, so an outage silently disables a protection (a `false` default on an `enforce`/`rls`/`gate`/`lockdown` key) or grants a permission (a `true` default on an `allow`/`permit`/`bypass` key).",facing:"EXTERNAL",level:"WARN",name:"flag_gates_security_with_unsafe_default",remediation:"Flip the default so a provider outage fails closed: default a protection flag (`enforce*`/`rls*`/`gate*`/`lockdown*`) to `true`, and a permission/bypass flag (`allow*`/`permit*`/`bypass*`) to `false`. The safe default is always the restrictive branch — never let an unreachable flag backend open access.",run:a=>a.flagSecurityDefaults===void 0?[]:a.flagSecurityDefaults.filter(e=>c(e)).map(e=>s(p,{cacheKey:`flag_gates_security_with_unsafe_default:${e.file}:${e.line.toString()}`,detail:`\`ctx.flags.boolean("${e.key}", ${String(e.defaultValue)})\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) fails open to the permissive branch — a provider outage returns \`${String(e.defaultValue)}\`, ${e.defaultValue?"granting the guarded permission":"disabling the guarded protection"}.`,metadata:{defaultValue:e.defaultValue,exportName:e.exportName,file:e.file,key:e.key,line:e.line}})),source:"static",title:"Security flag fails open to the permissive branch"};export{p as default};
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const i=new Set(["enforce","gate","lockdown","rls"]),r=new Set(["allow","bypass","permit"]),o=/(?<=[A-Z])(?=[A-Z][a-z])/gu,f=/(?<=[a-z0-9])(?=[A-Z])/gu,u=/[^a-z0-9]+/u,c=a=>a.replaceAll(o," ").replaceAll(f," ").toLowerCase().split(u).filter(e=>e.length>0),d=a=>{const e=c(a.key),l=e.some(t=>i.has(t)),s=e.some(t=>r.has(t));return l===s?!1:l?!a.defaultValue:a.defaultValue},p={categories:["SECURITY"],description:"A `ctx.flags.boolean(key, default)` read on a security-shaped key has a fail-open default that selects the permissive branch. OpenFeature returns the default when the provider errors, so an outage silently disables a protection (a `false` default on an `enforce`/`rls`/`gate`/`lockdown` key) or grants a permission (a `true` default on an `allow`/`permit`/`bypass` key).",facing:"EXTERNAL",level:"WARN",name:"flag_gates_security_with_unsafe_default",remediation:"Flip the default so a provider outage fails closed: default a protection flag (`enforce*`/`rls*`/`gate*`/`lockdown*`) to `true`, and a permission/bypass flag (`allow*`/`permit*`/`bypass*`) to `false`. The safe default is always the restrictive branch — never let an unreachable flag backend open access.",run:a=>a.flagSecurityDefaults===void 0?[]:a.flagSecurityDefaults.filter(e=>d(e)).map(e=>n(p,{cacheKey:`flag_gates_security_with_unsafe_default:${e.file}:${e.line.toString()}`,detail:`\`ctx.flags.boolean("${e.key}", ${String(e.defaultValue)})\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) fails open to the permissive branch — a provider outage returns \`${String(e.defaultValue)}\`, ${e.defaultValue?"granting the guarded permission":"disabling the guarded protection"}.`,metadata:{defaultValue:e.defaultValue,exportName:e.exportName,file:e.file,key:e.key,line:e.line}})),source:"static",title:"Security flag fails open to the permissive branch"};export{p as default};
@@ -0,0 +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 +1 @@
1
- import{i as c}from"./finding-BAVgqnCF.mjs";const d={categories:["SCHEMA"],description:"A `.geoIndex(name, { field })` points at a column that is not a `v.geoPoint()`, so its geohash companion can't be built and `withGeoIndex(...)` can never match.",facing:"INTERNAL",level:"ERROR",name:"geo_index_field_not_geopoint",remediation:"Point the geo index at a `v.geoPoint()` column, or change the column's type to `v.geoPoint()`.",run:a=>{const i=[];for(const e of a.schema.tables)for(const n of e.indexes){if(n.kind!=="geo")continue;const o=n.fields[0];if(o===void 0)continue;const t=e.columnKinds?.[o];t===void 0||t==="geoPoint"||i.push(c(d,{cacheKey:`geo_index_field_not_geopoint:${e.name}:${n.name}:${o}`,detail:`Geo index "${n.name}" on table "${e.name}" indexes column "${o}", which is a ${t}, not a v.geoPoint().`,metadata:{field:o,index:n.name,kind:t,table:e.name}}))}return i},source:"static",title:"Geo index field is not a geoPoint"};export{d as default};
1
+ import{e as d}from"./finding-NrKO8idM.mjs";const c={categories:["SCHEMA"],description:"A `.geoIndex(name, { field })` points at a column that is not a `v.geoPoint()`, so its geohash companion can't be built and `withGeoIndex(...)` can never match.",facing:"INTERNAL",level:"ERROR",name:"geo_index_field_not_geopoint",remediation:"Point the geo index at a `v.geoPoint()` column, or change the column's type to `v.geoPoint()`.",run:a=>{const t=[];for(const e of a.schema.tables)for(const n of e.indexes){if(n.kind!=="geo")continue;const o=n.fields[0];if(o===void 0)continue;const i=e.columnKinds?.[o];i===void 0||i==="geoPoint"||t.push(d(c,{cacheKey:`geo_index_field_not_geopoint:${e.name}:${n.name}:${o}`,detail:`Geo index "${n.name}" on table "${e.name}" indexes column "${o}", which is a ${i}, not a v.geoPoint().`,metadata:{field:o,index:n.name,kind:i,table:e.name}}))}return t},source:"static",title:"Geo index field is not a geoPoint"};export{c as default};
@@ -1 +1 @@
1
- import{i as d}from"./finding-BAVgqnCF.mjs";const t={categories:["SCHEMA"],description:"A `.geoIndex(name, { field })` is declared but no handler reads it via `withGeoIndex(name, …)`. The geohash companion column is maintained on every write and read by nothing — dead overhead, the geo analogue of a dead index.",facing:"INTERNAL",level:"INFO",name:"geo_index_unused",remediation:'Query the index from a handler with `ctx.db.query("<table>").withGeoIndex("<name>", q => q.near(point, radius))`, or drop the `.geoIndex(...)` declaration so writes stop maintaining its geohash companion.',run:a=>{if(a.geoIndexUsages===void 0)return[];const i=a.geoIndexUsages;if(i.some(e=>e.indexName===""))return[];const o=new Set(i.map(e=>e.indexName));return a.schema.tables.flatMap(e=>e.indexes.filter(n=>n.kind==="geo"&&!o.has(n.name)).map(n=>d(t,{cacheKey:`geo_index_unused:${e.name}:${n.name}`,detail:`No handler reads geo index "${n.name}" on table "${e.name}" via \`withGeoIndex("${n.name}", …)\` — it's declared but never queried, so its geohash companion is maintained on every write for nothing.`,metadata:{index:n.name,table:e.name}})))},source:"static",title:"Geo index is declared but never queried"};export{t as default};
1
+ import{e as d}from"./finding-NrKO8idM.mjs";const t={categories:["SCHEMA"],description:"A `.geoIndex(name, { field })` is declared but no handler reads it via `withGeoIndex(name, …)`. The geohash companion column is maintained on every write and read by nothing — dead overhead, the geo analogue of a dead index.",facing:"INTERNAL",level:"INFO",name:"geo_index_unused",remediation:'Query the index from a handler with `ctx.db.query("<table>").withGeoIndex("<name>", q => q.near(point, radius))`, or drop the `.geoIndex(...)` declaration so writes stop maintaining its geohash companion.',run:n=>{if(n.geoIndexUsages===void 0)return[];const i=n.geoIndexUsages;if(i.some(e=>e.indexName===""))return[];const o=new Set(i.map(e=>e.indexName));return n.schema.tables.flatMap(e=>e.indexes.filter(a=>a.kind==="geo"&&!o.has(a.name)).map(a=>d(t,{cacheKey:`geo_index_unused:${e.name}:${a.name}`,detail:`No handler reads geo index "${a.name}" on table "${e.name}" via \`withGeoIndex("${a.name}", …)\` — it's declared but never queried, so its geohash companion is maintained on every write for nothing.`,metadata:{index:a.name,table:e.name}})))},source:"static",title:"Geo index is declared but never queried"};export{t as default};
@@ -1 +1 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const t=100,i=2,o=90,s={categories:["SCHEMA"],description:"A `.global()` table is approaching D1's 100-column ceiling, past which it cannot be created.",facing:"INTERNAL",level:"WARN",name:"global_table_near_column_limit",remediation:"Split the table, or move the tail of its fields into one object field.",run:a=>a.schema.tables.filter(e=>e.shardKind==="global"&&e.fields.length+i>=o).map(e=>{const l=e.fields.length+i;return n(s,{cacheKey:`global_table_near_column_limit:${e.name}`,detail:`Global table "${e.name}" needs ${String(l)} columns (${String(e.fields.length)} fields plus id and _creationTime), against D1's limit of ${String(t)}.`,metadata:{columns:l,limit:t,table:e.name}})}),source:"static",title:"Global table near the column limit"};export{s as default};
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const l=100,i=2,o=90,s={categories:["SCHEMA"],description:"A `.global()` table is approaching D1's 100-column ceiling, past which it cannot be created.",facing:"INTERNAL",level:"WARN",name:"global_table_near_column_limit",remediation:"Split the table, or move the tail of its fields into one object field.",run:a=>a.schema.tables.filter(e=>e.shardKind==="global"&&e.fields.length+i>=o).map(e=>{const t=e.fields.length+i;return n(s,{cacheKey:`global_table_near_column_limit:${e.name}`,detail:`Global table "${e.name}" needs ${String(t)} columns (${String(e.fields.length)} fields plus id and _creationTime), against D1's limit of ${String(l)}.`,metadata:{columns:t,limit:l,table:e.name}})}),source:"static",title:"Global table near the column limit"};export{s as default};
@@ -0,0 +1 @@
1
+ const R={ERROR:20,INFO:5,WARN:10},r={ERROR:3,INFO:1,WARN:2},c=t=>Number.isFinite(t)&&t>=0?t:0,I=t=>R[t],s=(t,E)=>r[t]>=r[E]?t:E,_=(t,E="none")=>{const e=t.visibility==="public"?2:.5,n=t.kind==="query"?.5:e;return E==="high"?n*2:n},O=t=>Math.max(1,t*.2),i=t=>{const E=t.reduce((e,n)=>e+c(n.weight),0);return Math.min(100,Math.max(0,100-E))},T=t=>{const E=t.reduce((n,o)=>n+o.weight,0);if(E===0)return 100;const e=t.reduce((n,o)=>n+o.score*o.weight,0);return Math.round(e/E)},L=t=>t>=90?"excellent":t>=70?"good":t>=50?"needs-work":"at-risk",F=t=>t===100?"clean":t>=50?"warned":"failing";export{R as DEFAULT_WEIGHT_BY_LEVEL,F as coverageFromScore,L as gradeFromScore,c as normalizeWeight,_ as procedureWeight,O as projectWeight,T as scoreGlobal,i as scoreProcedure,I as weightFor,s as worstLevel};
@@ -0,0 +1 @@
1
+ import{dedupeCacheKeys as i}from"./dedupeCacheKeys-DtBOHffV.mjs";import{e as r}from"./finding-NrKO8idM.mjs";const a={categories:["SECURITY"],description:"A secret-shaped string literal (live API key, access key, private key, or high-entropy token) is hard-coded in the source. Committed secrets leak via clone/fork/history and force a redeploy to rotate.",facing:"INTERNAL",level:"ERROR",name:"hardcoded_secret",remediation:'Move the secret out of source: set it locally in `.dev.vars` (`lunora env set <NAME> "<value>"`) and in production with `wrangler secret put <NAME>`, then read it at runtime from `env.<NAME>`. Rotate the exposed value — assume it is already compromised.',run:t=>t.secretLiterals===void 0?[]:i(t.secretLiterals.map(e=>r(a,{cacheKey:`hardcoded_secret:${e.file}:${e.line.toString()}:${e.kind}`,detail:`A ${e.kind.replaceAll("_"," ")} (${e.preview}) is hard-coded at ${e.file}:${e.line.toString()}. Move it to \`.dev.vars\` / \`wrangler secret put\` and read it from \`env\`. Rotate the exposed value.`,metadata:{file:e.file,kind:e.kind,line:e.line,preview:e.preview}}))),source:"static",title:"Hard-coded secret in source"};export{a as default};
@@ -0,0 +1 @@
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};
@@ -0,0 +1 @@
1
+ import{e as u}from"./finding-NrKO8idM.mjs";const h=.5,d=50,l=r=>{const o=new Map;for(const t of r){const s=t.group??"",e=o.get(s);e===void 0?o.set(s,[t]):e.push(t)}return o},g=(r,o,t)=>{const s=t.reduce((e,n)=>e+n.requests,0);return t.length<2||s<d?[]:t.filter(e=>e.requests/s>=h).map(e=>{const n=e.requests/s,a=e.group!==void 0&&e.group!==""?`"${e.group}" `:"",i=e.shardKey===""?"the root shard":`shard "${e.shardKey}"`,c=Math.round(n*100);return u(r,{cacheKey:`hot_shard:${o}:${e.shardKey}`,detail:`${a}${i} handled ${e.requests.toString()} of ${s.toString()} requests (${c.toString()}%) across ${t.length.toString()} shards — a hot-key skew. Re-shard on a more evenly-distributed key.`,metadata:{group:e.group,requests:e.requests,shardCount:t.length,shardKey:e.shardKey,share:n,totalRequests:s}})})},p={categories:["PERFORMANCE"],description:"One shard is taking a disproportionate share of a sharded function's traffic, so the busy shard becomes a single-DO bottleneck while its siblings sit idle — the hot-key skew sharding is meant to avoid.",facing:"EXTERNAL",level:"WARN",name:"hot_shard",remediation:"Re-shard on a higher-cardinality / more evenly-distributed key, or split the hot entity's state, so request volume spreads across shards instead of concentrating on one.",run:r=>{const o=(r.shardTraffic??[]).filter(t=>t.requests>0);return[...l(o)].flatMap(([t,s])=>g(p,t,s))},source:"runtime",title:"Hot shard"};export{p as default};
@@ -0,0 +1 @@
1
+ import{e as a}from"./finding-NrKO8idM.mjs";const n=(e,t)=>e==="httpRoute"&&t?`\`httpRoute\` ${t} handler`:"`httpAction` handler",c={categories:["SECURITY"],description:"An `httpAction`/`httpRoute` handler performs a side effect (`ctx.runMutation`/`ctx.runAction`/a `ctx.db` write) but never reads `ctx.auth` — an unauthenticated HTTP endpoint driving a state change, bypassing the identity/RLS checks that guard the rest of the app.",facing:"EXTERNAL",level:"WARN",name:"http_action_missing_auth_guard",remediation:"Read `ctx.auth` in the handler before the side effect — call `await ctx.auth.getIdentity()` (or check `ctx.auth.userId`) and reject unauthenticated/unauthorized requests, or forward through a `mutation`/`action` whose RLS policies enforce access. If the endpoint is intentionally public (e.g. a signed webhook), verify the provider signature before the write.",run:e=>e.httpActionGuards===void 0?[]:e.httpActionGuards.filter(t=>!t.readsAuth).map(t=>{const i=n(t.kind,t.method);return a(c,{cacheKey:`http_action_missing_auth_guard:${t.file}:${t.line.toString()}`,detail:`${i} \`${t.exportName}\` (${t.file}:${t.line.toString()}) calls \`ctx.${t.sideEffect}\` but never reads \`ctx.auth\` — an anonymous caller can drive this write. Authenticate the request before the side effect.`,metadata:{exportName:t.exportName,file:t.file,kind:t.kind,line:t.line,sideEffect:t.sideEffect,...t.method?{method:t.method}:{}}})}),source:"static",title:"Unauthenticated HTTP handler performs a side effect"};export{c as default};
@@ -1 +1 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const i={"headers-append":"a `headers.append(...)` call","headers-ctor":"a `new Headers({...})` initializer","headers-set":"a `headers.set(...)` call","response-init":"a `Response` init `headers` object"},n={categories:["SECURITY"],description:"An `httpAction` handler writes a response-header value derived from raw request input (`request.headers`/URL/query/body) with no CR/LF sanitizer. A `Request`-derived string placed verbatim into a response header lets a caller smuggle `\\r\\n` and inject extra headers or split the response (HTTP response splitting / header injection).",facing:"EXTERNAL",level:"WARN",name:"http_action_response_header_injection",remediation:"Never place a raw request value into a response header. Route it through a CR/LF guard (`isSafeHeaderValue` from `@lunora/server`, rejecting values containing `\\r`/`\\n`/`\\0`), URL-encode it (`encodeURIComponent`), or coerce it to a number — before writing the header. `String(...)` / `.toString()` do NOT strip CR/LF and are not sufficient.",run:r=>r.httpHeaderWrites===void 0?[]:r.httpHeaderWrites.map(e=>{const t=e.headerName===""?"a response header":`the \`${e.headerName}\` response header`;return a(n,{cacheKey:`http_action_response_header_injection:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` (${e.file}:${e.line.toString()}) writes ${t} from raw request input via ${i[e.via]} with no CR/LF guard — a caller can smuggle \`\\r\\n\` to inject headers or split the response. Route the value through \`isSafeHeaderValue\` or \`encodeURIComponent\` first.`,metadata:{exportName:e.exportName,file:e.file,headerName:e.headerName,line:e.line,via:e.via}})}),source:"static",title:"Response header written from unsanitized request input"};export{n as default};
1
+ import{e as r}from"./finding-NrKO8idM.mjs";const n={"headers-append":"a `headers.append(...)` call","headers-ctor":"a `new Headers({...})` initializer","headers-set":"a `headers.set(...)` call","response-init":"a `Response` init `headers` object"},i={categories:["SECURITY"],description:"An `httpAction` handler writes a response-header value derived from raw request input (`request.headers`/URL/query/body) with no CR/LF sanitizer. A `Request`-derived string placed verbatim into a response header lets a caller smuggle `\\r\\n` and inject extra headers or split the response (HTTP response splitting / header injection).",facing:"EXTERNAL",level:"WARN",name:"http_action_response_header_injection",remediation:"Never place a raw request value into a response header. Route it through a CR/LF guard (`isSafeHeaderValue` from `@lunora/server`, rejecting values containing `\\r`/`\\n`/`\\0`), URL-encode it (`encodeURIComponent`), or coerce it to a number — before writing the header. `String(...)` / `.toString()` do NOT strip CR/LF and are not sufficient.",run:t=>t.httpHeaderWrites===void 0?[]:t.httpHeaderWrites.map(e=>{const a=e.headerName===""?"a response header":`the \`${e.headerName}\` response header`;return r(i,{cacheKey:`http_action_response_header_injection:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` (${e.file}:${e.line.toString()}) writes ${a} from raw request input via ${n[e.via]} with no CR/LF guard — a caller can smuggle \`\\r\\n\` to inject headers or split the response. Route the value through \`isSafeHeaderValue\` or \`encodeURIComponent\` first.`,metadata:{exportName:e.exportName,file:e.file,headerName:e.headerName,line:e.line,via:e.via}})}),source:"static",title:"Response header written from unsanitized request input"};export{i as default};
@@ -0,0 +1 @@
1
+ import{e as t}from"./finding-NrKO8idM.mjs";const n={categories:["SCHEMA"],description:"A `query`/`mutation` handler accesses Hyperdrive via `ctx.sql`. Hyperdrive hits an external database Lunora does not own: queries are non-deterministic (like `fetch`) and external writes are invisible to live queries. `ctx.sql` is available on `ActionCtx` only and must be confined to `action` handlers.",facing:"EXTERNAL",level:"WARN",name:"hyperdrive_outside_action",remediation:"Move the `ctx.sql` access into an `action(...)` (the only context where it is typed), where external I/O is allowed. If a query/mutation needs the data, have the action read it via `ctx.sql` and write a projection into a `defineSchema` DO/D1 table — that write is tracked by live queries, whereas the external DB is not.",run:i=>i.hyperdriveCalls===void 0?[]:i.hyperdriveCalls.map(e=>t(n,{cacheKey:`hyperdrive_outside_action:${e.file}:${e.line.toString()}:${e.callee}`,detail:`\`${e.callee}(…)\` in ${e.exportName} (${e.file}:${e.line.toString()}) runs inside a ${e.kind} handler — Hyperdrive's \`ctx.sql\` is non-deterministic and non-reactive, so it is available only in actions. Move the external SQL into an \`action\` and project the result into a Lunora table if a query/mutation needs it.`,metadata:{callee:e.callee,exportName:e.exportName,file:e.file,kind:e.kind,line:e.line}})),source:"static",title:"Hyperdrive `ctx.sql` used outside an action"};export{n as default};