@lunora/advisor 1.0.0-alpha.67 → 1.0.0-alpha.68

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 (59) hide show
  1. package/dist/index.mjs +1 -1
  2. package/dist/packem_shared/actionWithoutErrorHandling-rhzjCEqm.mjs +1 -0
  3. package/dist/packem_shared/adminRouteWithoutGuard-BbscPp1m.mjs +1 -0
  4. package/dist/packem_shared/aiRunWithoutLogging-B0K93Pga.mjs +1 -0
  5. package/dist/packem_shared/authApiCallWithoutHeaders-BHkik-EF.mjs +1 -0
  6. package/dist/packem_shared/containerPublicInternet-g0TRsayn.mjs +1 -0
  7. package/dist/packem_shared/emptyIndex-Bp-Y81D0.mjs +1 -0
  8. package/dist/packem_shared/errorWithoutCatalog-0PbxiZL4.mjs +1 -0
  9. package/dist/packem_shared/externalSourceOnGlobal-IYnALPlO.mjs +1 -0
  10. package/dist/packem_shared/filterOnPrimaryKey-Db947tQY.mjs +1 -0
  11. package/dist/packem_shared/fromServerSchema-9aaK1J7g.mjs +1 -0
  12. package/dist/packem_shared/{geoIndexUnused-BgDE6_AR.mjs → geoIndexUnused-B5Oww9o2.mjs} +1 -1
  13. package/dist/packem_shared/hardcodedSecret-yu14ITlW.mjs +1 -0
  14. package/dist/packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs +1 -0
  15. package/dist/packem_shared/indexReferencesUnknownField-BPJjE0zg.mjs +1 -0
  16. package/dist/packem_shared/maskWeakHashStrategyOnPii-DcEwc4Sz.mjs +1 -0
  17. package/dist/packem_shared/notifySendOutsideAction-B9DcirhJ.mjs +1 -0
  18. package/dist/packem_shared/plaintextSecretInWranglerVariables-CmbygN6_.mjs +1 -0
  19. package/dist/packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs +1 -0
  20. package/dist/packem_shared/publicArgumentUsesAny-BzdcoJ9C.mjs +1 -0
  21. package/dist/packem_shared/publicMutationWithoutRatelimit-CozomYXw.mjs +1 -0
  22. package/dist/packem_shared/r2sqlOutsideAction-DCMAsAeM.mjs +1 -0
  23. package/dist/packem_shared/relationReferencesUnknownTable-EOtKyaHx.mjs +1 -0
  24. package/dist/packem_shared/shapeTargetsGlobalTable-SAlq2N-T.mjs +1 -0
  25. package/dist/packem_shared/shapeUnknownTable-DhvsbEtT.mjs +1 -0
  26. package/dist/packem_shared/tableWithoutInsert-BV7qsPKx.mjs +1 -0
  27. package/dist/packem_shared/unboundedStringArgument-DN8w1bg-.mjs +1 -0
  28. package/dist/packem_shared/userCreatingMutationWithoutCaptcha-DIeHIeTU.mjs +1 -0
  29. package/dist/packem_shared/workflowUnknownTarget-BzKlYSCg.mjs +1 -0
  30. package/dist/packem_shared/workflowUnused-D9fC9E9b.mjs +1 -0
  31. package/package.json +3 -3
  32. package/dist/packem_shared/actionWithoutErrorHandling-D1DqSPpg.mjs +0 -1
  33. package/dist/packem_shared/adminRouteWithoutGuard-DSo4t9j4.mjs +0 -1
  34. package/dist/packem_shared/aiRunWithoutLogging-CMvQPNBr.mjs +0 -1
  35. package/dist/packem_shared/authApiCallWithoutHeaders-BItMMdZP.mjs +0 -1
  36. package/dist/packem_shared/containerPublicInternet-Bx2J7tha.mjs +0 -1
  37. package/dist/packem_shared/emptyIndex-BiXEfyfN.mjs +0 -1
  38. package/dist/packem_shared/errorWithoutCatalog-DTDVKU4b.mjs +0 -1
  39. package/dist/packem_shared/externalSourceOnGlobal-DNPLoNUg.mjs +0 -1
  40. package/dist/packem_shared/filterOnPrimaryKey-CPPa7fog.mjs +0 -1
  41. package/dist/packem_shared/fromServerSchema-CJ-AQ2kw.mjs +0 -1
  42. package/dist/packem_shared/hardcodedSecret-rIRc3aNk.mjs +0 -1
  43. package/dist/packem_shared/hyperdriveOutsideAction-CRmOQz67.mjs +0 -1
  44. package/dist/packem_shared/indexReferencesUnknownField-r3QHlaz6.mjs +0 -1
  45. package/dist/packem_shared/maskWeakHashStrategyOnPii-CEiiGgT7.mjs +0 -1
  46. package/dist/packem_shared/notifySendOutsideAction-B0jZuUP3.mjs +0 -1
  47. package/dist/packem_shared/plaintextSecretInWranglerVariables-DXjKIm8H.mjs +0 -1
  48. package/dist/packem_shared/procedureWithoutStructuredEvent-D4Qi2vH7.mjs +0 -1
  49. package/dist/packem_shared/publicArgumentUsesAny-BJb4cTqL.mjs +0 -1
  50. package/dist/packem_shared/publicMutationWithoutRatelimit-D9phluHW.mjs +0 -1
  51. package/dist/packem_shared/r2sqlOutsideAction-CKK_I1Ms.mjs +0 -1
  52. package/dist/packem_shared/relationReferencesUnknownTable-CLdrhkmS.mjs +0 -1
  53. package/dist/packem_shared/shapeTargetsGlobalTable-Blz-aSkg.mjs +0 -1
  54. package/dist/packem_shared/shapeUnknownTable-DFxtyFio.mjs +0 -1
  55. package/dist/packem_shared/tableWithoutInsert-D3AhaNzR.mjs +0 -1
  56. package/dist/packem_shared/unboundedStringArgument-AkkIcr-S.mjs +0 -1
  57. package/dist/packem_shared/userCreatingMutationWithoutCaptcha-DGiZ3_W7.mjs +0 -1
  58. package/dist/packem_shared/workflowUnknownTarget-JF8rITLP.mjs +0 -1
  59. package/dist/packem_shared/workflowUnused-Dx_FQwGE.mjs +0 -1
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{dedupeCacheKeys as a}from"./packem_shared/dedupeCacheKeys-7JgxaQWV.mjs";import m from"./packem_shared/constraintValidator-N339mfyx.mjs";import s from"./packem_shared/errorRateOutlier-CgK42sq0.mjs";import n from"./packem_shared/hotShard-io-eF2Sx.mjs";import p from"./packem_shared/indexUtilization-QfbIH_w4.mjs";import f from"./packem_shared/actionFetchSsrf-BbWxy1Yf.mjs";import l from"./packem_shared/actionWithoutErrorHandling-D1DqSPpg.mjs";import u from"./packem_shared/adminRouteWithoutGuard-DSo4t9j4.mjs";import d from"./packem_shared/aiRawRunEscapeHatch-DfhjYm43.mjs";import c from"./packem_shared/aiRunWithoutLogging-CMvQPNBr.mjs";import $ from"./packem_shared/aiToolSideEffectPromptInjection-DnWQOoKi.mjs";import h from"./packem_shared/aiUnboundedGenerationPublic-iu7crnSa.mjs";import g from"./packem_shared/allowUnauthenticatedShardAccessEnabled-D4aMNBb0.mjs";import b from"./packem_shared/authApiCallWithoutHeaders-BItMMdZP.mjs";import U from"./packem_shared/authCsrfCheckDisabled-De9ammtV.mjs";import S from"./packem_shared/authEmailVerificationDisabled-BFKdJcx4.mjs";import y from"./packem_shared/authScimWithoutTransactions-DCtLXzks.mjs";import I from"./packem_shared/authSecureCookiesDisabled-B4WIzvMm.mjs";import A from"./packem_shared/authSessionFreshageZero-Bpcaf_Pe.mjs";import W from"./packem_shared/authTrustedOriginsWildcard-DjU9NXEm.mjs";import T from"./packem_shared/browserAllowPrivateTargets-Dxyeh6lg.mjs";import R from"./packem_shared/browserUserUrlWithoutAllowlist-BHBS6b0N.mjs";import w from"./packem_shared/circularFk-ZNjkhLaE.mjs";import k from"./packem_shared/containerInstanceKeyFromUserInput-C8S4T3ty.mjs";import x from"./packem_shared/containerOversizedInstance-B9wW_sU5.mjs";import F from"./packem_shared/containerPublicInternet-Bx2J7tha.mjs";import v from"./packem_shared/containerRuntimeEgressRelaxation-BQ2ZD1aV.mjs";import C from"./packem_shared/containerStartEnableInternetOverride-B62WUura.mjs";import P from"./packem_shared/duplicateIndex-DFT7qLQD.mjs";import M from"./packem_shared/emptyIndex-BiXEfyfN.mjs";import O from"./packem_shared/errorWithoutCatalog-DTDVKU4b.mjs";import D from"./packem_shared/exportSinkMisconfigured-DvpAaa6j.mjs";import N from"./packem_shared/externalSourceIncrementalNoDeletePath-DnoL64YY.mjs";import E from"./packem_shared/externalSourceOnGlobal-DNPLoNUg.mjs";import G from"./packem_shared/externalSourceUnscoped-DqBp5jYr.mjs";import K from"./packem_shared/filterOnPrimaryKey-CPPa7fog.mjs";import L from"./packem_shared/filterWithoutIndex-pIpqk-O8.mjs";import z from"./packem_shared/flagGatesSecurityWithUnsafeDefault-DPGFEHTt.mjs";import V from"./packem_shared/geoIndexFieldNotGeopoint-DAGLZtaH.mjs";import j from"./packem_shared/geoIndexUnused-BgDE6_AR.mjs";import q from"./packem_shared/hardcodedSecret-rIRc3aNk.mjs";import H from"./packem_shared/httpActionMissingAuthGuard-BZDdeW_D.mjs";import _ from"./packem_shared/httpActionResponseHeaderInjection-D063rB7U.mjs";import B from"./packem_shared/hyperdriveOutsideAction-CRmOQz67.mjs";import Q from"./packem_shared/identityUndeclaredClaimTrusted-BYRYGQ74.mjs";import Z from"./packem_shared/imagesUrlSourceFromUserInput-Cy5pc_dA.mjs";import J from"./packem_shared/indexReferencesUnknownField-r3QHlaz6.mjs";import X from"./packem_shared/insertManyUnsafeUserData-xLYAkcKj.mjs";import Y from"./packem_shared/kvUnscopedUserKeyIdor-B4lGxRxh.mjs";import rr from"./packem_shared/mailInboundDispatchWithoutVerify-BivYJ5F9.mjs";import or from"./packem_shared/mailRecipientFromRequestInput-BR_oSh65.mjs";import tr from"./packem_shared/maskUncoveredPiiColumn-DqPfZM2O.mjs";import ir from"./packem_shared/maskWeakHashStrategyOnPii-CEiiGgT7.mjs";import er from"./packem_shared/maskedRelationLeakViaWith-CxYk0MDW.mjs";import ar from"./packem_shared/mutatorFullRowReplace-CEgecSGm.mjs";import mr from"./packem_shared/nondeterministicQueryMutation-BZV_biUV.mjs";import sr from"./packem_shared/normalizeIdUsedAsAuthorization-FjISSXJm.mjs";import nr from"./packem_shared/notifyMissingPushConfig-C_GbVw3S.mjs";import pr from"./packem_shared/notifySendOutsideAction-B0jZuUP3.mjs";import fr from"./packem_shared/outputProjectionMissingOnPublicRead-9Pm9766s.mjs";import lr from"./packem_shared/ownerFieldFromArgsNotAuth-D4K-_0aD.mjs";import ur from"./packem_shared/paymentCreateWithoutAuthorize-CurtqN8K.mjs";import dr from"./packem_shared/paymentWebhookWideTolerance-I7ingfR8.mjs";import cr from"./packem_shared/plaintextSecretInWranglerVariables-DXjKIm8H.mjs";import $r from"./packem_shared/policyReferencesUnknownTable-D3WelAQa.mjs";import hr from"./packem_shared/privilegedDispatchUnvalidatedPayload-74GuGye1.mjs";import gr from"./packem_shared/privilegedFanoutFromPublicProcedure-sMsZN4uP.mjs";import br from"./packem_shared/procedureWithoutStructuredEvent-D4Qi2vH7.mjs";import Ur from"./packem_shared/publicArgumentUsesAny-BJb4cTqL.mjs";import Sr from"./packem_shared/publicMutationWithoutRatelimit-D9phluHW.mjs";import yr from"./packem_shared/publicTableRlsOptoutConfusion-CaWtPGAn.mjs";import Ir from"./packem_shared/queueWithoutDlq-BLY3iy9Q.mjs";import Ar from"./packem_shared/r2sqlOutsideAction-CKK_I1Ms.mjs";import Wr from"./packem_shared/ratelimitDefaultMemoryStore-mOdI3Jmb.mjs";import Tr from"./packem_shared/ratelimitKeySpoofableOrGlobal-DaGId_Fh.mjs";import Rr from"./packem_shared/ratelimitMiddlewareFailOpen-CTWVosEc.mjs";import wr from"./packem_shared/relationReferencesUnknownField-clqyB69Y.mjs";import kr from"./packem_shared/relationReferencesUnknownTable-CLdrhkmS.mjs";import xr from"./packem_shared/rlsUncoveredTable-DYHgnoos.mjs";import Fr from"./packem_shared/shapeTargetsGlobalTable-Blz-aSkg.mjs";import vr from"./packem_shared/shapeUnknownTable-DFxtyFio.mjs";import Cr from"./packem_shared/signupMutationWithoutDisposableGating-Tgv9SbsY.mjs";import Pr from"./packem_shared/softDeleteIncludeDeletedFromArgs-DgxpQHRA.mjs";import Mr from"./packem_shared/sqlInjectionRisk-D2NaT-5c.mjs";import Or from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-C9cjl6Jh.mjs";import Dr from"./packem_shared/storageKeyFromUserArgs-CI-6kyte.mjs";import Nr from"./packem_shared/storagePresignedUrlForPrivateContent-j7VhwIdu.mjs";import Er from"./packem_shared/storageUploadWithoutContentTypeAllowlist-oxKLecuf.mjs";import Gr from"./packem_shared/storageUploadWithoutMaxSize-DUDxPutk.mjs";import Kr from"./packem_shared/tableWithoutInsert-D3AhaNzR.mjs";import Lr from"./packem_shared/ttlFieldNotTimestamp-BFiRkSZD.mjs";import zr from"./packem_shared/unboundedStringArgument-AkkIcr-S.mjs";import Vr from"./packem_shared/unindexedForeignKey-DK4eWXkb.mjs";import jr from"./packem_shared/unindexedRelationTarget-B5fFq1E8.mjs";import qr from"./packem_shared/unrestrictedWhereBranch-YKiydie4.mjs";import Hr from"./packem_shared/userCreatingMutationWithoutCaptcha-DGiZ3_W7.mjs";import _r from"./packem_shared/vectorsNamespaceFromUserInput-Cyq5B2kF.mjs";import Br from"./packem_shared/workflowDuplicateStepName-cXF6Rpvg.mjs";import Qr from"./packem_shared/workflowUnknownTarget-JF8rITLP.mjs";import Zr from"./packem_shared/workflowUnused-Dx_FQwGE.mjs";import{compareToBaseline as Zt,parseAdvisorMap as Jt}from"./packem_shared/compareToBaseline-BwXFusQG.mjs";import{gradeFromScore as Yt}from"./packem_shared/gradeFromScore-DW82YDVY.mjs";import{MAP_VERSION as oi,byCodepoint as ti,scoreAdvisor as ii}from"./packem_shared/MAP_VERSION-DxkFCiLX.mjs";import{default as ai}from"./packem_shared/classifySensitivity-B1kkm44P.mjs";import{fromServerSchema as si}from"./packem_shared/fromServerSchema-CJ-AQ2kw.mjs";const Jr=[J,kr,wr,Qr,Br,vr,N,E,G,M,O,V,j,D,Lr,w,Vr,jr,P,Kr,Zr,Ir,K,L,Fr,ar,mr,B,Ar,b,$r,xr,tr,ir,x,F,Sr,qr,Hr,Cr,Ur,zr,q,Mr,u,ur,rr,Wr,T,gr,br,X,h,f,l,lr,Dr,Y,k,d,c,_r,or,R,hr,C,v,y,W,U,I,S,A,Z,Tr,yr,g,Er,Gr,Or,Nr,H,_,Rr,z,$,Q,dr,Pr,er,fr,sr,pr,nr,cr],Xr=[n,p,m,s],Yr=[...Jr,...Xr],_t=(i,r={})=>{const e=r.lints??Yr,o=[];for(const t of e)r.source!==void 0&&t.source!==r.source||o.push(...t.run(i));return a(o)};export{Yr as ALL_LINTS,oi as MAP_VERSION,Xr as RUNTIME_LINTS,Jr as STATIC_LINTS,f as actionFetchSsrf,l as actionWithoutErrorHandling,u as adminRouteWithoutGuard,d as aiRawRunEscapeHatch,c as aiRunWithoutLogging,$ as aiToolSideEffectPromptInjection,h as aiUnboundedGenerationPublic,g as allowUnauthenticatedShardAccessEnabled,b as authApiCallWithoutHeaders,U as authCsrfCheckDisabled,S as authEmailVerificationDisabled,y as authScimWithoutTransactions,I as authSecureCookiesDisabled,A as authSessionFreshageZero,W as authTrustedOriginsWildcard,T as browserAllowPrivateTargets,R as browserUserUrlWithoutAllowlist,ti as byCodepoint,w as circularFk,ai as classifySensitivity,Zt as compareToBaseline,m as constraintValidator,k as containerInstanceKeyFromUserInput,x as containerOversizedInstance,F as containerPublicInternet,v as containerRuntimeEgressRelaxation,C as containerStartEnableInternetOverride,a as dedupeCacheKeys,P as duplicateIndex,M as emptyIndex,s as errorRateOutlier,O as errorWithoutCatalog,D as exportSinkMisconfigured,N as externalSourceIncrementalNoDeletePath,E as externalSourceOnGlobal,G as externalSourceUnscoped,K as filterOnPrimaryKey,L as filterWithoutIndex,z as flagGatesSecurityWithUnsafeDefault,si as fromServerSchema,V as geoIndexFieldNotGeopoint,j as geoIndexUnused,Yt as gradeFromScore,q as hardcodedSecret,n as hotShard,H as httpActionMissingAuthGuard,_ as httpActionResponseHeaderInjection,B as hyperdriveOutsideAction,Q as identityUndeclaredClaimTrusted,Z as imagesUrlSourceFromUserInput,J as indexReferencesUnknownField,p as indexUtilization,X as insertManyUnsafeUserData,Y as kvUnscopedUserKeyIdor,rr as mailInboundDispatchWithoutVerify,or as mailRecipientFromRequestInput,tr as maskUncoveredPiiColumn,ir as maskWeakHashStrategyOnPii,er as maskedRelationLeakViaWith,ar as mutatorFullRowReplace,mr as nondeterministicQueryMutation,sr as normalizeIdUsedAsAuthorization,nr as notifyMissingPushConfig,pr as notifySendOutsideAction,fr as outputProjectionMissingOnPublicRead,lr as ownerFieldFromArgsNotAuth,Jt as parseAdvisorMap,ur as paymentCreateWithoutAuthorize,dr as paymentWebhookWideTolerance,cr as plaintextSecretInWranglerVariables,$r as policyReferencesUnknownTable,hr as privilegedDispatchUnvalidatedPayload,gr as privilegedFanoutFromPublicProcedure,br as procedureWithoutStructuredEvent,Ur as publicArgumentUsesAny,Sr as publicMutationWithoutRatelimit,yr as publicTableRlsOptoutConfusion,Ir as queueWithoutDlq,Ar as r2sqlOutsideAction,Wr as ratelimitDefaultMemoryStore,Tr as ratelimitKeySpoofableOrGlobal,Rr as ratelimitMiddlewareFailOpen,wr as relationReferencesUnknownField,kr as relationReferencesUnknownTable,xr as rlsUncoveredTable,_t as runAdvisor,ii as scoreAdvisor,Fr as shapeTargetsGlobalTable,vr as shapeUnknownTable,Cr as signupMutationWithoutDisposableGating,Pr as softDeleteIncludeDeletedFromArgs,Mr as sqlInjectionRisk,Or as storageGenerateUploadUrlNoContentTypePin,Dr as storageKeyFromUserArgs,Nr as storagePresignedUrlForPrivateContent,Er as storageUploadWithoutContentTypeAllowlist,Gr as storageUploadWithoutMaxSize,Kr as tableWithoutInsert,Lr as ttlFieldNotTimestamp,zr as unboundedStringArgument,Vr as unindexedForeignKey,jr as unindexedRelationTarget,qr as unrestrictedWhereBranch,Hr as userCreatingMutationWithoutCaptcha,_r as vectorsNamespaceFromUserInput,Br as workflowDuplicateStepName,Qr as workflowUnknownTarget,Zr as workflowUnused};
1
+ import{dedupeCacheKeys as a}from"./packem_shared/dedupeCacheKeys-7JgxaQWV.mjs";import m from"./packem_shared/constraintValidator-N339mfyx.mjs";import s from"./packem_shared/errorRateOutlier-CgK42sq0.mjs";import n from"./packem_shared/hotShard-io-eF2Sx.mjs";import p from"./packem_shared/indexUtilization-QfbIH_w4.mjs";import f from"./packem_shared/actionFetchSsrf-BbWxy1Yf.mjs";import l from"./packem_shared/actionWithoutErrorHandling-rhzjCEqm.mjs";import u from"./packem_shared/adminRouteWithoutGuard-BbscPp1m.mjs";import d from"./packem_shared/aiRawRunEscapeHatch-DfhjYm43.mjs";import c from"./packem_shared/aiRunWithoutLogging-B0K93Pga.mjs";import $ from"./packem_shared/aiToolSideEffectPromptInjection-DnWQOoKi.mjs";import h from"./packem_shared/aiUnboundedGenerationPublic-iu7crnSa.mjs";import g from"./packem_shared/allowUnauthenticatedShardAccessEnabled-D4aMNBb0.mjs";import b from"./packem_shared/authApiCallWithoutHeaders-BHkik-EF.mjs";import U from"./packem_shared/authCsrfCheckDisabled-De9ammtV.mjs";import S from"./packem_shared/authEmailVerificationDisabled-BFKdJcx4.mjs";import y from"./packem_shared/authScimWithoutTransactions-DCtLXzks.mjs";import I from"./packem_shared/authSecureCookiesDisabled-B4WIzvMm.mjs";import A from"./packem_shared/authSessionFreshageZero-Bpcaf_Pe.mjs";import W from"./packem_shared/authTrustedOriginsWildcard-DjU9NXEm.mjs";import T from"./packem_shared/browserAllowPrivateTargets-Dxyeh6lg.mjs";import R from"./packem_shared/browserUserUrlWithoutAllowlist-BHBS6b0N.mjs";import k from"./packem_shared/circularFk-ZNjkhLaE.mjs";import w from"./packem_shared/containerInstanceKeyFromUserInput-C8S4T3ty.mjs";import x from"./packem_shared/containerOversizedInstance-B9wW_sU5.mjs";import F from"./packem_shared/containerPublicInternet-g0TRsayn.mjs";import v from"./packem_shared/containerRuntimeEgressRelaxation-BQ2ZD1aV.mjs";import C from"./packem_shared/containerStartEnableInternetOverride-B62WUura.mjs";import P from"./packem_shared/duplicateIndex-DFT7qLQD.mjs";import M from"./packem_shared/emptyIndex-Bp-Y81D0.mjs";import O from"./packem_shared/errorWithoutCatalog-0PbxiZL4.mjs";import D from"./packem_shared/exportSinkMisconfigured-DvpAaa6j.mjs";import N from"./packem_shared/externalSourceIncrementalNoDeletePath-DnoL64YY.mjs";import E from"./packem_shared/externalSourceOnGlobal-IYnALPlO.mjs";import G from"./packem_shared/externalSourceUnscoped-DqBp5jYr.mjs";import K from"./packem_shared/filterOnPrimaryKey-Db947tQY.mjs";import L from"./packem_shared/filterWithoutIndex-pIpqk-O8.mjs";import z from"./packem_shared/flagGatesSecurityWithUnsafeDefault-DPGFEHTt.mjs";import V from"./packem_shared/geoIndexFieldNotGeopoint-DAGLZtaH.mjs";import j from"./packem_shared/geoIndexUnused-B5Oww9o2.mjs";import q from"./packem_shared/hardcodedSecret-yu14ITlW.mjs";import H from"./packem_shared/httpActionMissingAuthGuard-BZDdeW_D.mjs";import _ from"./packem_shared/httpActionResponseHeaderInjection-D063rB7U.mjs";import B from"./packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs";import Q from"./packem_shared/identityUndeclaredClaimTrusted-BYRYGQ74.mjs";import Z from"./packem_shared/imagesUrlSourceFromUserInput-Cy5pc_dA.mjs";import J from"./packem_shared/indexReferencesUnknownField-BPJjE0zg.mjs";import X from"./packem_shared/insertManyUnsafeUserData-xLYAkcKj.mjs";import Y from"./packem_shared/kvUnscopedUserKeyIdor-B4lGxRxh.mjs";import rr from"./packem_shared/mailInboundDispatchWithoutVerify-BivYJ5F9.mjs";import or from"./packem_shared/mailRecipientFromRequestInput-BR_oSh65.mjs";import tr from"./packem_shared/maskUncoveredPiiColumn-DqPfZM2O.mjs";import ir from"./packem_shared/maskWeakHashStrategyOnPii-DcEwc4Sz.mjs";import er from"./packem_shared/maskedRelationLeakViaWith-CxYk0MDW.mjs";import ar from"./packem_shared/mutatorFullRowReplace-CEgecSGm.mjs";import mr from"./packem_shared/nondeterministicQueryMutation-BZV_biUV.mjs";import sr from"./packem_shared/normalizeIdUsedAsAuthorization-FjISSXJm.mjs";import nr from"./packem_shared/notifyMissingPushConfig-C_GbVw3S.mjs";import pr from"./packem_shared/notifySendOutsideAction-B9DcirhJ.mjs";import fr from"./packem_shared/outputProjectionMissingOnPublicRead-9Pm9766s.mjs";import lr from"./packem_shared/ownerFieldFromArgsNotAuth-D4K-_0aD.mjs";import ur from"./packem_shared/paymentCreateWithoutAuthorize-CurtqN8K.mjs";import dr from"./packem_shared/paymentWebhookWideTolerance-I7ingfR8.mjs";import cr from"./packem_shared/plaintextSecretInWranglerVariables-CmbygN6_.mjs";import $r from"./packem_shared/policyReferencesUnknownTable-D3WelAQa.mjs";import hr from"./packem_shared/privilegedDispatchUnvalidatedPayload-74GuGye1.mjs";import gr from"./packem_shared/privilegedFanoutFromPublicProcedure-sMsZN4uP.mjs";import br from"./packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs";import Ur from"./packem_shared/publicArgumentUsesAny-BzdcoJ9C.mjs";import Sr from"./packem_shared/publicMutationWithoutRatelimit-CozomYXw.mjs";import yr from"./packem_shared/publicTableRlsOptoutConfusion-CaWtPGAn.mjs";import Ir from"./packem_shared/queueWithoutDlq-BLY3iy9Q.mjs";import Ar from"./packem_shared/r2sqlOutsideAction-DCMAsAeM.mjs";import Wr from"./packem_shared/ratelimitDefaultMemoryStore-mOdI3Jmb.mjs";import Tr from"./packem_shared/ratelimitKeySpoofableOrGlobal-DaGId_Fh.mjs";import Rr from"./packem_shared/ratelimitMiddlewareFailOpen-CTWVosEc.mjs";import kr from"./packem_shared/relationReferencesUnknownField-clqyB69Y.mjs";import wr from"./packem_shared/relationReferencesUnknownTable-EOtKyaHx.mjs";import xr from"./packem_shared/rlsUncoveredTable-DYHgnoos.mjs";import Fr from"./packem_shared/shapeTargetsGlobalTable-SAlq2N-T.mjs";import vr from"./packem_shared/shapeUnknownTable-DhvsbEtT.mjs";import Cr from"./packem_shared/signupMutationWithoutDisposableGating-Tgv9SbsY.mjs";import Pr from"./packem_shared/softDeleteIncludeDeletedFromArgs-DgxpQHRA.mjs";import Mr from"./packem_shared/sqlInjectionRisk-D2NaT-5c.mjs";import Or from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-C9cjl6Jh.mjs";import Dr from"./packem_shared/storageKeyFromUserArgs-CI-6kyte.mjs";import Nr from"./packem_shared/storagePresignedUrlForPrivateContent-j7VhwIdu.mjs";import Er from"./packem_shared/storageUploadWithoutContentTypeAllowlist-oxKLecuf.mjs";import Gr from"./packem_shared/storageUploadWithoutMaxSize-DUDxPutk.mjs";import Kr from"./packem_shared/tableWithoutInsert-BV7qsPKx.mjs";import Lr from"./packem_shared/ttlFieldNotTimestamp-BFiRkSZD.mjs";import zr from"./packem_shared/unboundedStringArgument-DN8w1bg-.mjs";import Vr from"./packem_shared/unindexedForeignKey-DK4eWXkb.mjs";import jr from"./packem_shared/unindexedRelationTarget-B5fFq1E8.mjs";import qr from"./packem_shared/unrestrictedWhereBranch-YKiydie4.mjs";import Hr from"./packem_shared/userCreatingMutationWithoutCaptcha-DIeHIeTU.mjs";import _r from"./packem_shared/vectorsNamespaceFromUserInput-Cyq5B2kF.mjs";import Br from"./packem_shared/workflowDuplicateStepName-cXF6Rpvg.mjs";import Qr from"./packem_shared/workflowUnknownTarget-BzKlYSCg.mjs";import Zr from"./packem_shared/workflowUnused-D9fC9E9b.mjs";import{compareToBaseline as Zt,parseAdvisorMap as Jt}from"./packem_shared/compareToBaseline-BwXFusQG.mjs";import{gradeFromScore as Yt}from"./packem_shared/gradeFromScore-DW82YDVY.mjs";import{MAP_VERSION as oi,byCodepoint as ti,scoreAdvisor as ii}from"./packem_shared/MAP_VERSION-DxkFCiLX.mjs";import{default as ai}from"./packem_shared/classifySensitivity-B1kkm44P.mjs";import{fromServerSchema as si}from"./packem_shared/fromServerSchema-9aaK1J7g.mjs";const Jr=[J,wr,kr,Qr,Br,vr,N,E,G,M,O,V,j,D,Lr,k,Vr,jr,P,Kr,Zr,Ir,K,L,Fr,ar,mr,B,Ar,b,$r,xr,tr,ir,x,F,Sr,qr,Hr,Cr,Ur,zr,q,Mr,u,ur,rr,Wr,T,gr,br,X,h,f,l,lr,Dr,Y,w,d,c,_r,or,R,hr,C,v,y,W,U,I,S,A,Z,Tr,yr,g,Er,Gr,Or,Nr,H,_,Rr,z,$,Q,dr,Pr,er,fr,sr,pr,nr,cr],Xr=[n,p,m,s],Yr=[...Jr,...Xr],_t=(i,r={})=>{const e=r.lints??Yr,o=[];for(const t of e)r.source!==void 0&&t.source!==r.source||o.push(...t.run(i));return a(o)};export{Yr as ALL_LINTS,oi as MAP_VERSION,Xr as RUNTIME_LINTS,Jr as STATIC_LINTS,f as actionFetchSsrf,l as actionWithoutErrorHandling,u as adminRouteWithoutGuard,d as aiRawRunEscapeHatch,c as aiRunWithoutLogging,$ as aiToolSideEffectPromptInjection,h as aiUnboundedGenerationPublic,g as allowUnauthenticatedShardAccessEnabled,b as authApiCallWithoutHeaders,U as authCsrfCheckDisabled,S as authEmailVerificationDisabled,y as authScimWithoutTransactions,I as authSecureCookiesDisabled,A as authSessionFreshageZero,W as authTrustedOriginsWildcard,T as browserAllowPrivateTargets,R as browserUserUrlWithoutAllowlist,ti as byCodepoint,k as circularFk,ai as classifySensitivity,Zt as compareToBaseline,m as constraintValidator,w as containerInstanceKeyFromUserInput,x as containerOversizedInstance,F as containerPublicInternet,v as containerRuntimeEgressRelaxation,C as containerStartEnableInternetOverride,a as dedupeCacheKeys,P as duplicateIndex,M as emptyIndex,s as errorRateOutlier,O as errorWithoutCatalog,D as exportSinkMisconfigured,N as externalSourceIncrementalNoDeletePath,E as externalSourceOnGlobal,G as externalSourceUnscoped,K as filterOnPrimaryKey,L as filterWithoutIndex,z as flagGatesSecurityWithUnsafeDefault,si as fromServerSchema,V as geoIndexFieldNotGeopoint,j as geoIndexUnused,Yt as gradeFromScore,q as hardcodedSecret,n as hotShard,H as httpActionMissingAuthGuard,_ as httpActionResponseHeaderInjection,B as hyperdriveOutsideAction,Q as identityUndeclaredClaimTrusted,Z as imagesUrlSourceFromUserInput,J as indexReferencesUnknownField,p as indexUtilization,X as insertManyUnsafeUserData,Y as kvUnscopedUserKeyIdor,rr as mailInboundDispatchWithoutVerify,or as mailRecipientFromRequestInput,tr as maskUncoveredPiiColumn,ir as maskWeakHashStrategyOnPii,er as maskedRelationLeakViaWith,ar as mutatorFullRowReplace,mr as nondeterministicQueryMutation,sr as normalizeIdUsedAsAuthorization,nr as notifyMissingPushConfig,pr as notifySendOutsideAction,fr as outputProjectionMissingOnPublicRead,lr as ownerFieldFromArgsNotAuth,Jt as parseAdvisorMap,ur as paymentCreateWithoutAuthorize,dr as paymentWebhookWideTolerance,cr as plaintextSecretInWranglerVariables,$r as policyReferencesUnknownTable,hr as privilegedDispatchUnvalidatedPayload,gr as privilegedFanoutFromPublicProcedure,br as procedureWithoutStructuredEvent,Ur as publicArgumentUsesAny,Sr as publicMutationWithoutRatelimit,yr as publicTableRlsOptoutConfusion,Ir as queueWithoutDlq,Ar as r2sqlOutsideAction,Wr as ratelimitDefaultMemoryStore,Tr as ratelimitKeySpoofableOrGlobal,Rr as ratelimitMiddlewareFailOpen,kr as relationReferencesUnknownField,wr as relationReferencesUnknownTable,xr as rlsUncoveredTable,_t as runAdvisor,ii as scoreAdvisor,Fr as shapeTargetsGlobalTable,vr as shapeUnknownTable,Cr as signupMutationWithoutDisposableGating,Pr as softDeleteIncludeDeletedFromArgs,Mr as sqlInjectionRisk,Or as storageGenerateUploadUrlNoContentTypePin,Dr as storageKeyFromUserArgs,Nr as storagePresignedUrlForPrivateContent,Er as storageUploadWithoutContentTypeAllowlist,Gr as storageUploadWithoutMaxSize,Kr as tableWithoutInsert,Lr as ttlFieldNotTimestamp,zr as unboundedStringArgument,Vr as unindexedForeignKey,jr as unindexedRelationTarget,qr as unrestrictedWhereBranch,Hr as userCreatingMutationWithoutCaptcha,_r as vectorsNamespaceFromUserInput,Br as workflowDuplicateStepName,Qr as workflowUnknownTarget,Zr as workflowUnused};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +1 @@
1
+ import{i as n}from"./finding-BAVgqnCF.mjs";const o={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:t=>t.procedureProtections===void 0?[]:t.procedureProtections.filter(e=>e.runsAiGeneration===!0&&e.emitsEvent===!1).map(e=>n(o,{cacheKey:`ai_run_without_logging:${e.file}:${e.exportName}`,detail:`${e.kind} \`${e.exportName}\` (${e.file}) runs an AI generation with no structured event, so its cost and output cannot be attributed later.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}})),source:"static",title:"AI generation with no structured event"};export{o as default};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +1 @@
1
+ import{i}from"./finding-BAVgqnCF.mjs";const a={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 r=e.line>0?`${e.file}:${e.line.toString()}`:e.file;return i(a,{cacheKey:`filter_on_primary_key:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${r} 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{a as default};
@@ -0,0 +1 @@
1
+ const r=t=>({rlsMode:t.rlsMode,tables:Object.entries(t.tables).map(([d,a])=>{const s=[...a.indexes.map(e=>({fields:e.fields,kind:"index",name:e.name,unique:e.unique})),...a.searchIndexes.map(e=>({fields:[e.field,...e.filterFields??[]],kind:"search",name:e.name})),...a.rankIndexes.map(e=>({fields:[...e.sortBy.map(n=>n.field),...e.partitionBy??[]],kind:"rank",name:e.name})),...a.vectorIndexes.map(e=>({fields:[e.field],kind:"vector",name:e.name})),...a.geoIndexes.map(e=>({fields:[e.field],kind:"geo",name:e.name}))],i={},l=new Set;for(const[e,n]of Object.entries(a.shape))if(n.kind==="optional"){const o=n._meta?.inner;i[e]=o?.kind??n.kind,l.add(e)}else i[e]=n.kind,n._meta?.column?.notNull===!1&&l.add(e);return{externallyManaged:a.isExternallyManaged??!1,externalSource:a.externalSource?{hasReconcile:a.externalSource.reconcileEveryMs!==void 0,hasSoftDelete:a.externalSource.softDeleteColumn!==void 0,hasTenantBy:a.externalSource.tenantBy!==void 0,mode:a.externalSource.mode}:void 0,columnKinds:i,fields:Object.keys(a.shape),indexes:s,isPublic:a.isPublic??!1,name:d,optionalFields:l,shardKind:a.shardMode.kind,softDelete:a.softDeleteMode,ttl:a.ttlPolicy,relations:Object.entries(a.relationMap).map(([e,n])=>({field:n.field,kind:n.kind,name:e,onDelete:n.onDelete,references:n.references,table:n.table}))}})});export{r as fromServerSchema};
@@ -1 +1 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const r={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 d=new Set(i.map(e=>e.indexName)),o=[];for(const e of a.schema.tables)for(const n of e.indexes)n.kind!=="geo"||d.has(n.name)||o.push(t(r,{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}}));return o},source:"static",title:"Geo index is declared but never queried"};export{r as default};
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};
@@ -0,0 +1 @@
1
+ import{dedupeCacheKeys as r}from"./dedupeCacheKeys-7JgxaQWV.mjs";import{i}from"./finding-BAVgqnCF.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?[]:r(t.secretLiterals.map(e=>i(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
+ import{i}from"./finding-BAVgqnCF.mjs";const a={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:t=>t.hyperdriveCalls===void 0?[]:t.hyperdriveCalls.map(e=>i(a,{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{a as default};
@@ -0,0 +1 @@
1
+ import{i as o}from"./finding-BAVgqnCF.mjs";import{t as d}from"./helpers-DwlvEUCh.mjs";const r={categories:["SCHEMA"],description:"An index references a column that is not declared on its table. The index can never match, and the typo would otherwise surface only at runtime.",facing:"INTERNAL",level:"ERROR",name:"index_references_unknown_field",remediation:"Fix the column name in the index declaration, or add the column to the table.",run:t=>t.schema.tables.flatMap(e=>{const i=d(e);return e.indexes.flatMap(n=>n.fields.filter(a=>!i.has(a)).map(a=>o(r,{cacheKey:`index_references_unknown_field:${e.name}:${n.name}:${a}`,detail:`Index "${n.name}" on table "${e.name}" references column "${a}", which is not declared on the table.`,metadata:{field:a,index:n.name,indexKind:n.kind,table:e.name}})))}),source:"static",title:"Index references unknown field"};export{r as default};
@@ -0,0 +1 @@
1
+ import{i as t}from"./finding-BAVgqnCF.mjs";const s=/address|birthdate|creditcard|dateofbirth|dob|driverslicense|email|firstname|fullname|lastname|nationalid|passport|phone|socialsecurity|ssn|taxid/u,i=a=>s.test(a.replaceAll(/[^a-z0-9]/giu,"").toLowerCase()),o={categories:["SECURITY"],description:'A `mask(policies)` column named like PII (email, SSN, phone, …) uses the `"hash"` strategy. `"hash"` is an unsalted 32-bit FNV-1a digest — brute-force-recoverable and cross-row-correlatable — not a confidentiality control, so it does not actually hide the value.',facing:"EXTERNAL",level:"WARN",name:"mask_weak_hash_strategy_on_pii",remediation:'Use `"redact"` instead of `"hash"` for this column — e.g. `mask({ <table>: { <column>: "redact" } })`. Reserve `"hash"` for columns where a stable, joinable pseudonym is the goal and the value itself is not sensitive.',run:a=>a.maskStrategies===void 0?[]:a.maskStrategies.filter(e=>e.strategy==="hash"&&i(e.column)).map(e=>t(o,{cacheKey:`mask_weak_hash_strategy_on_pii:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` in ${e.file} masks \`${e.table===""?e.column:`${e.table}.${e.column}`}\` with \`"hash"\` — its name suggests PII, and \`"hash"\` is brute-force-recoverable and leaks cross-row equality. Use \`"redact"\` instead.`,metadata:{column:e.column,exportName:e.exportName,file:e.file,table:e.table}})),source:"static",title:'Weak "hash" mask strategy on a PII column'};export{o as default};
@@ -0,0 +1 @@
1
+ import{i as t}from"./finding-BAVgqnCF.mjs";const i={categories:["SCHEMA"],description:"A `query`/`mutation` handler sends a notification via `ctx.notify`/`ctx.push`. A send is external I/O (a `fetch` to a push service / FCM): it is non-deterministic like `fetch`, and a mutation re-run on OCC retry would re-send it (duplicate pushes). These facades are available on `ActionCtx` only and must be confined to `action` handlers.",facing:"EXTERNAL",level:"WARN",name:"notify_send_outside_action",remediation:"Move the `ctx.notify`/`ctx.push` send into an `action(...)`, where external I/O is allowed. If a query/mutation must trigger a notification, have it enqueue the send (`enqueuePushBroadcast` via `@lunora/queue`) or schedule an action — the queue/scheduler runs the send exactly once, off the transactional path.",run:n=>n.notifyCalls===void 0?[]:n.notifyCalls.map(e=>t(i,{cacheKey:`notify_send_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 — a notification send is non-deterministic external I/O and a retried ${e.kind} would re-send it. Move it into an \`action\`, or enqueue/schedule the send.`,metadata:{callee:e.callee,exportName:e.exportName,file:e.file,kind:e.kind,line:e.line}})),source:"static",title:"Notification send used outside an action"};export{i as default};
@@ -0,0 +1 @@
1
+ import{dedupeCacheKeys as r}from"./dedupeCacheKeys-7JgxaQWV.mjs";import{i as a}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A plaintext secret is committed to `wrangler.jsonc`'s `vars` block. `vars` are cleartext — baked into the deployed Worker and checked into source control — so the secret leaks via the repo and the bundle, and rotating it needs a config edit plus a redeploy.",facing:"INTERNAL",level:"ERROR",name:"plaintext_secret_in_wrangler_vars",remediation:"Move the value out of `vars`: bind it through a Secrets Store (`ctx.secrets.get(<NAME>)`) or set it with `wrangler secret put <NAME>` and read it from `env.<NAME>`. Delete the `vars` entry and rotate the exposed value — assume it is already compromised. Public/publishable keys (name them `*_PUBLIC` / `*_PUBLISHABLE`) are exempt.",run:t=>t.wranglerVariables===void 0?[]:r(t.wranglerVariables.map(e=>a(i,{cacheKey:`plaintext_secret_in_wrangler_vars:${e.file}:${e.key}`,detail:`The \`vars\` entry \`${e.key}\` in ${e.file} holds a plaintext ${e.kind.replaceAll("_"," ")} (${e.preview}). \`vars\` ship in cleartext to the deployed Worker and are committed to source control — move it to a Secrets Store binding or \`wrangler secret put\` and rotate the exposed value.`,metadata:{file:e.file,key:e.key,kind:e.kind,preview:e.preview}}))),source:"static",title:"Plaintext secret in wrangler vars"};export{i as default};
@@ -0,0 +1 @@
1
+ import{i}from"./finding-BAVgqnCF.mjs";const o={categories:["SCHEMA"],description:"A public `mutation`/`action` emits no structured event. When it fails you get a stack trace with no request context — no ids, no tenant, no outcome — so the failure is visible but not searchable.",facing:"INTERNAL",level:"INFO",name:"procedure_without_structured_event",remediation:'Emit one event on the primary path: `ctx.log.info("<verb>", { … })`, or wrap the handler in `ctx.span("<name>", …)` to attach timing too.',run:t=>t.procedureProtections===void 0?[]:t.procedureProtections.filter(e=>e.visibility==="public"&&e.kind!=="query"&&e.emitsEvent===!1).map(e=>i(o,{cacheKey:`procedure_without_structured_event:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) emits no structured event. Add a \`ctx.log\` line or a \`ctx.span\` so a failure carries its request context.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}})),source:"static",title:"Public write emits no structured event"};export{o as default};
@@ -0,0 +1 @@
1
+ import{i as t}from"./finding-BAVgqnCF.mjs";const n={categories:["SECURITY"],description:"A public procedure declares a `v.any()` argument. `v.any()` accepts arbitrary untyped input from an untrusted client — defeating validation and opening injection / oversized-payload abuse.",facing:"EXTERNAL",level:"WARN",name:"public_arg_uses_any",remediation:"Replace `v.any()` with a precise validator — `v.object({...})`, `v.string()`, `v.union(...)`, etc. If the shape is genuinely dynamic, model the known variants with `v.union` rather than accepting anything.",run:i=>i.argValidators===void 0?[]:i.argValidators.flatMap(e=>e.anyArgs.map(a=>t(n,{cacheKey:`public_arg_uses_any:${e.file}:${e.exportName}:${a}`,detail:`Arg \`${a}\` of public procedure \`${e.exportName}\` (${e.file}:${e.line.toString()}) is \`v.any()\` — untrusted input with no validation. Give it a precise validator.`,metadata:{argument:a,exportName:e.exportName,file:e.file,line:e.line}}))),source:"static",title:"Public argument uses v.any()"};export{n as default};
@@ -0,0 +1 @@
1
+ import{i as r}from"./finding-BAVgqnCF.mjs";import{i as a}from"./helpers-DwlvEUCh.mjs";const o=/contact|forgot|login|magic|otp|register|reset|signin|signup|subscribe|verify/iu,s={categories:["SECURITY"],description:"A public `mutation`/`action` has no `rateLimit` middleware. Publicly-callable writes are flood and brute-force targets — an attacker can exhaust writes, mail quota, or credits, or guess credentials on auth-shaped endpoints.",facing:"EXTERNAL",level:"WARN",name:"public_mutation_without_ratelimit",remediation:'Attach a rate limit: `.use(rateLimit(limiter, "<bucket>"))` from `@lunora/ratelimit`, or wrap the recommended public-procedure guards with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Genuinely-open writes can be acknowledged by adding a permissive limiter.',run:i=>i.procedureProtections===void 0?[]:i.procedureProtections.filter(e=>a(e)&&!e.usesRateLimit).map(e=>{const t=o.test(e.exportName);return r(s,{cacheKey:`public_mutation_without_ratelimit:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) has no rate limit${t?" — its name suggests an auth/abuse-sensitive endpoint, so this is high-risk":""}. Add \`.use(rateLimit(...))\` or \`.use(protectPublic({ rateLimit }))\`.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind,sensitive:t}})}),source:"static",title:"Public write without a rate limit"};export{s as default};
@@ -0,0 +1 @@
1
+ import{i as t}from"./finding-BAVgqnCF.mjs";const a={categories:["SCHEMA"],description:"A `query`/`mutation` handler accesses R2 SQL via `ctx.r2sql`. R2 SQL queries external Apache Iceberg tables over REST (no Workers binding): queries are non-deterministic (like `fetch`) and the reads are invisible to live queries. `ctx.r2sql` is available on `ActionCtx` only and must be confined to `action` handlers.",facing:"EXTERNAL",level:"WARN",name:"r2sql_outside_action",remediation:"Move the `ctx.r2sql` 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.r2sql` and write a projection into a `defineSchema` DO/D1 table — that write is tracked by live queries, whereas R2 SQL is not.",run:i=>i.r2sqlCalls===void 0?[]:i.r2sqlCalls.map(e=>t(a,{cacheKey:`r2sql_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 — R2 SQL's \`ctx.r2sql\` is non-deterministic and non-reactive, so it is available only in actions. Move the external query 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:"R2 SQL `ctx.r2sql` used outside an action"};export{a as default};
@@ -0,0 +1 @@
1
+ import{i as r}from"./finding-BAVgqnCF.mjs";const l={categories:["SCHEMA"],description:"A relation targets a table that does not exist in the schema. The relation can never load — the target is a typo or a removed/renamed table.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_table",remediation:"Fix the target table name in the relation, or add the missing table to the schema.",run:t=>{const n=new Set(t.schema.tables.map(a=>a.name));return t.schema.tables.flatMap(a=>a.relations.filter(e=>!n.has(e.table)).map(e=>r(l,{cacheKey:`relation_references_unknown_table:${a.name}:${e.name}`,detail:`Relation "${e.name}" on table "${a.name}" targets table "${e.table}", which does not exist in the schema.`,metadata:{relation:e.name,table:a.name,target:e.table}})))},source:"static",title:"Relation references unknown table"};export{l as default};
@@ -0,0 +1 @@
1
+ import{i as l}from"./finding-BAVgqnCF.mjs";const s={categories:["PERFORMANCE"],description:"A `defineShape` replicates from a `.global()` table. Global tables live outside the shard DO's SQLite op-log, so the shape is served through the cross-shard tier — coordinator/poll-refreshed and latency-tiered, not poke-live like a sharded shape.",facing:"EXTERNAL",level:"WARN",name:"shape_targets_global_table",remediation:"Expected if you want the cross-shard tier — just don't assume poke-live freshness; global shapes refresh on a poll. For live updates, replicate from a sharded table instead (denormalize the columns you need into the shard).",run:a=>{if(a.shapes===void 0)return[];const t=new Set(a.schema.tables.filter(e=>e.shardKind==="global").map(e=>e.name));return a.shapes.filter(e=>e.table!==void 0&&t.has(e.table)).map(e=>l(s,{cacheKey:`shape_targets_global_table:${e.exportName}`,detail:`Shape \`${e.exportName}\` (${e.file}) replicates from the \`.global()\` table \`${e.table}\`. It is served through the cross-shard global tier — poll-refreshed and latency-tiered, not poke-live.`,metadata:{exportName:e.exportName,file:e.file,table:e.table}}))},source:"static",title:"Shape replicates from a global (cross-shard) table"};export{s as default};
@@ -0,0 +1 @@
1
+ import{i as s}from"./finding-BAVgqnCF.mjs";const n={categories:["SCHEMA"],description:"A `defineShape` is bound to a `table` name that does not exist in the schema. The shape can never resolve a rowset — its subscription seeds empty and errors at the first flush.",facing:"INTERNAL",level:"ERROR",name:"shape_unknown_table",remediation:"Fix the shape's `table` to a real table name (check for a typo or a table that was renamed/removed).",run:a=>{if(a.shapes===void 0)return[];const t=new Set(a.schema.tables.map(e=>e.name));return a.shapes.filter(e=>e.table!==void 0&&!t.has(e.table)).map(e=>s(n,{cacheKey:`shape_unknown_table:${e.exportName}`,detail:`Shape \`${e.exportName}\` (${e.file}) replicates from table \`${e.table}\`, which is not declared in the schema. The shape can never resolve a rowset.`,metadata:{exportName:e.exportName,file:e.file,table:e.table}}))},source:"static",title:"Shape bound to an unknown table"};export{n as default};
@@ -0,0 +1 @@
1
+ import{i}from"./finding-BAVgqnCF.mjs";const n={categories:["SCHEMA"],description:'No function inserts into this table via `ctx.db.insert("<table>", …)`. It may be read-only by design (seeded by a migration, replicated, or written through a path the advisor can\'t see) — or it may be dead schema.',facing:"INTERNAL",level:"INFO",name:"table_without_insert",remediation:'If the table should be writable, add a mutation that calls `ctx.db.insert("<table>", …)`. If it is read-only or seeded elsewhere, this advisory can be ignored.',run:t=>{if(t.inserts===void 0)return[];const a=new Set(t.inserts.filter(e=>e.table!=="").map(e=>e.table));return t.schema.tables.filter(e=>!a.has(e.name)&&e.externallyManaged!==!0).map(e=>i(n,{cacheKey:`table_without_insert:${e.name}`,detail:`No function calls \`ctx.db.insert("${e.name}", …)\` — table "${e.name}" has no discovered insert path.`,metadata:{table:e.name}}))},source:"static",title:"Table has no insert path"};export{n as default};
@@ -0,0 +1 @@
1
+ import{i as t}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A public `v.string()` argument has no maximum-length bound. An unbounded string lets a client submit arbitrarily large input — abusing storage and CPU on every request that processes it.",facing:"EXTERNAL",level:"INFO",name:"unbounded_string_arg",remediation:"Add a max-length bound via `.check(...)` / `.meta({ maxLength })` on the string validator (e.g. cap a name at 256, a body at a few KB). Size the cap to the field's real-world maximum.",run:n=>n.argValidators===void 0?[]:n.argValidators.flatMap(e=>e.unboundedStringArgs.map(a=>t(i,{cacheKey:`unbounded_string_arg:${e.file}:${e.exportName}:${a}`,detail:`Arg \`${a}\` of public procedure \`${e.exportName}\` (${e.file}:${e.line.toString()}) is an unbounded \`v.string()\`. Add a max-length bound to cap payload size.`,metadata:{argument:a,exportName:e.exportName,file:e.file,line:e.line}}))),source:"static",title:"Public string argument has no length bound"};export{i as default};
@@ -0,0 +1 @@
1
+ import{i as t}from"./finding-BAVgqnCF.mjs";import{m as i}from"./procedure-protections-DRQotu9I.mjs";import{i as r}from"./helpers-DwlvEUCh.mjs";const s=a=>a.writesUserTable===!0?"writes a user/session table":a.callsMail===!0?"sends mail":a.analyzableBody===!1?"may write a user/session table or send mail — its handler body could not be read":"may write a user/session table or send mail — at least one could not be determined",o={categories:["SECURITY"],description:"A public `mutation`/`action` that creates a user/session or sends mail has no CAPTCHA / bot check. Account-creating and mail-sending endpoints are prime automated-abuse targets (credential stuffing, mailbox flooding, disposable-account farming).",facing:"EXTERNAL",level:"WARN",name:"user_creating_mutation_without_captcha",remediation:"Add a server-verified human check: `.use(verifyTurnstile({ secret, token }))` from `@lunora/auth`, or wrap it with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Pair with a rate limit for defense in depth.",run:a=>a.procedureProtections===void 0?[]:a.procedureProtections.filter(e=>r(e)&&(i(e.writesUserTable)||i(e.callsMail))&&!e.usesCaptcha).map(e=>t(o,{cacheKey:`user_creating_mutation_without_captcha:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) ${s(e)} but has no CAPTCHA check. Add \`.use(verifyTurnstile(...))\` or \`.use(protectPublic({ captcha }))\`.`,metadata:{callsMail:e.callsMail,exportName:e.exportName,file:e.file,kind:e.kind,writesUserTable:e.writesUserTable}})),source:"static",title:"Account-creating / mail-sending write without a CAPTCHA"};export{o as default};
@@ -0,0 +1 @@
1
+ import{i as w}from"./finding-BAVgqnCF.mjs";const l={categories:["SCHEMA"],description:'A `ctx.workflows.get("<name>")` call references a workflow that is not declared by any `defineWorkflow` export in `lunora/workflows.ts`. The name is a typo or points at a removed/renamed workflow — the call throws at runtime.',facing:"INTERNAL",level:"ERROR",name:"workflow_unknown_target",remediation:'Fix the workflow name in the `ctx.workflows.get("…")` call, or add the missing `defineWorkflow` export to `lunora/workflows.ts`.',run:e=>{if(e.workflows===void 0||e.workflowCalls===void 0)return[];const r=new Set(e.workflows.map(o=>o.exportName));return e.workflowCalls.filter(o=>o.workflow!==""&&!r.has(o.workflow)).map(o=>w(l,{cacheKey:`workflow_unknown_target:${o.file}:${o.exportName}:${o.workflow}`,detail:`\`ctx.workflows.get("${o.workflow}")\` in "${o.exportName}" (${o.file}) references workflow "${o.workflow}", which is not declared in lunora/workflows.ts.`,metadata:{exportName:o.exportName,file:o.file,line:o.line,workflow:o.workflow}}))},source:"static",title:"Workflow call references unknown workflow"};export{l as default};
@@ -0,0 +1 @@
1
+ import{i as a}from"./finding-BAVgqnCF.mjs";const i={categories:["SCHEMA"],description:'No function starts this workflow via `ctx.workflows.get("<name>")`. It may be triggered through a path the advisor can\'t see (the Cloudflare API, a wrangler invocation, a cross-service binding) — or it may be dead code that still deploys as a billable WorkflowEntrypoint.',facing:"INTERNAL",level:"INFO",name:"workflow_unused",remediation:'If the workflow should be triggered in-app, start it from a mutation/action with `ctx.workflows.get("<name>").create({ params })`. If it is started externally or is no longer needed, this advisory can be ignored (or remove the `defineWorkflow` export).',run:o=>{if(o.workflows===void 0)return[];const t=o.workflowCalls??[];if(t.some(e=>e.workflow===""))return[];const r=new Set(t.map(e=>e.workflow));return o.workflows.filter(e=>!r.has(e.exportName)).map(e=>a(i,{cacheKey:`workflow_unused:${e.exportName}`,detail:`No function calls \`ctx.workflows.get("${e.exportName}")\` — workflow "${e.exportName}" is declared but never started in app code.`,metadata:{workflow:e.exportName}}))},source:"static",title:"Workflow is never started"};export{i as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/advisor",
3
- "version": "1.0.0-alpha.67",
3
+ "version": "1.0.0-alpha.68",
4
4
  "description": "Schema & query lints (splinter-style advisors) for Lunora, feeding the Studio Advisors view",
5
5
  "keywords": [
6
6
  "advisor",
@@ -46,8 +46,8 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@lunora/errors": "1.0.0-alpha.14",
50
- "@lunora/server": "1.0.0-alpha.62"
49
+ "@lunora/errors": "1.0.0-alpha.15",
50
+ "@lunora/server": "1.0.0-alpha.63"
51
51
  },
52
52
  "engines": {
53
53
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
1
- import{i as o}from"./finding-BAVgqnCF.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=>{if(t.procedureProtections===void 0)return[];const r=[];for(const e of t.procedureProtections)e.kind!=="action"||e.reachesOutbound!==!0||e.handlesErrors!==!1||r.push(o(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}}));return r},source:"static",title:"Action performs outbound I/O with no error handling"};export{n as default};
@@ -1 +0,0 @@
1
- import{i}from"./finding-BAVgqnCF.mjs";const r={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=>{if(a.adminRoutes===void 0)return[];const t=[];for(const e of a.adminRoutes)e.usesGuard||t.push(i(r,{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}}));return t},source:"static",title:"Admin route without an auth guard"};export{r as default};
@@ -1 +0,0 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";const r={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:t=>{if(t.procedureProtections===void 0)return[];const n=[];for(const e of t.procedureProtections)e.runsAiGeneration!==!0||e.emitsEvent!==!1||n.push(o(r,{cacheKey:`ai_run_without_logging:${e.file}:${e.exportName}`,detail:`${e.kind} \`${e.exportName}\` (${e.file}) runs an AI generation with no structured event, so its cost and output cannot be attributed later.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}}));return n},source:"static",title:"AI generation with no structured event"};export{r as default};
@@ -1 +0,0 @@
1
- import{i as h}from"./finding-BAVgqnCF.mjs";const l={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:a=>{if(a.authApiCalls===void 0)return[];const s=[],r=new Map;for(const e of a.authApiCalls){if(e.hasHeaders)continue;const t=`${e.file}:${e.line.toString()}:${e.method}`,i=(r.get(t)??0)+1;r.set(t,i);const o=i>1?`:${i.toString()}`:"";s.push(h(l,{cacheKey:`auth_api_call_without_headers:${t}${o}`,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}}))}return s},source:"static",title:"Privileged ctx.authApi call missing headers"};export{l as default};
@@ -1 +0,0 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const r={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:n=>{const t=[];for(const e of n.containers??[])e.enableInternet===void 0&&t.push(a(r,{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}}));return t},source:"static",title:"Container egress enabled by default"};export{r as default};
@@ -1 +0,0 @@
1
- import{i}from"./finding-BAVgqnCF.mjs";const o={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=>{const t=[];for(const n of a.schema.tables)for(const e of n.indexes)e.kind!=="index"||e.fields.length>0||t.push(i(o,{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}}));return t},source:"static",title:"Empty index"};export{o as default};
@@ -1 +0,0 @@
1
- import{i as t}from"./finding-BAVgqnCF.mjs";const a={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=>{if(e.procedureProtections===void 0)return[];const o=[];for(const r of e.procedureProtections)r.throwsBareError===!0&&o.push(t(a,{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}}));return o},source:"static",title:"Bare Error thrown instead of a coded LunoraError"};export{a as default};
@@ -1 +0,0 @@
1
- import{i as l}from"./finding-BAVgqnCF.mjs";const o={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:t=>{const a=[];for(const e of t.schema.tables)e.externalSource===void 0||e.shardKind!=="global"||a.push(l(o,{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}}));return a},source:"static",title:"Sourced table cannot also be global"};export{o as default};
@@ -1 +0,0 @@
1
- import{i as a}from"./finding-BAVgqnCF.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:i=>{const t=[];for(const e of i.queries??[]){if(e.filtersPrimaryKey!==!0||e.table==="")continue;const r=e.line>0?`${e.file}:${e.line.toString()}`:e.file;t.push(a(l,{cacheKey:`filter_on_primary_key:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${r} 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}}))}return t},source:"static",title:"Filter on primary key instead of ctx.db.get"};export{l as default};
@@ -1 +0,0 @@
1
- const r=t=>({rlsMode:t.rlsMode,tables:Object.entries(t.tables).map(([o,a])=>{const s=[...a.indexes.map(e=>({fields:e.fields,kind:"index",name:e.name,unique:e.unique})),...a.searchIndexes.map(e=>({fields:[e.field,...e.filterFields??[]],kind:"search",name:e.name})),...a.rankIndexes.map(e=>({fields:[...e.sortBy.map(n=>n.field),...e.partitionBy??[]],kind:"rank",name:e.name})),...a.vectorIndexes.map(e=>({fields:[e.field],kind:"vector",name:e.name})),...a.geoIndexes.map(e=>({fields:[e.field],kind:"geo",name:e.name}))],i={};for(const[e,n]of Object.entries(a.shape)){const d=n.kind==="optional"?n._meta?.inner:n;i[e]=d?.kind??n.kind}const l=new Set;for(const[e,n]of Object.entries(a.shape))(n.kind==="optional"||n._meta?.column?.notNull===!1)&&l.add(e);return{externallyManaged:a.isExternallyManaged??!1,externalSource:a.externalSource?{hasReconcile:a.externalSource.reconcileEveryMs!==void 0,hasSoftDelete:a.externalSource.softDeleteColumn!==void 0,hasTenantBy:a.externalSource.tenantBy!==void 0,mode:a.externalSource.mode}:void 0,columnKinds:i,fields:Object.keys(a.shape),indexes:s,isPublic:a.isPublic??!1,name:o,optionalFields:l,shardKind:a.shardMode.kind,softDelete:a.softDeleteMode,ttl:a.ttlPolicy,relations:Object.entries(a.relationMap).map(([e,n])=>({field:n.field,kind:n.kind,name:e,onDelete:n.onDelete,references:n.references,table:n.table}))}})});export{r as fromServerSchema};
@@ -1 +0,0 @@
1
- import{dedupeCacheKeys as i}from"./dedupeCacheKeys-7JgxaQWV.mjs";import{i as o}from"./finding-BAVgqnCF.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=>{if(t.secretLiterals===void 0)return[];const r=[];for(const e of t.secretLiterals)r.push(o(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}}));return i(r)},source:"static",title:"Hard-coded secret in source"};export{a as default};
@@ -1 +0,0 @@
1
- import{i as a}from"./finding-BAVgqnCF.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:t=>{if(t.hyperdriveCalls===void 0)return[];const i=[];for(const e of t.hyperdriveCalls)i.push(a(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}}));return i},source:"static",title:"Hyperdrive `ctx.sql` used outside an action"};export{n as default};
@@ -1 +0,0 @@
1
- import{i as r}from"./finding-BAVgqnCF.mjs";import{t as d}from"./helpers-DwlvEUCh.mjs";const c={categories:["SCHEMA"],description:"An index references a column that is not declared on its table. The index can never match, and the typo would otherwise surface only at runtime.",facing:"INTERNAL",level:"ERROR",name:"index_references_unknown_field",remediation:"Fix the column name in the index declaration, or add the column to the table.",run:a=>{const o=[];for(const e of a.schema.tables){const i=d(e);for(const n of e.indexes)for(const t of n.fields)i.has(t)||o.push(r(c,{cacheKey:`index_references_unknown_field:${e.name}:${n.name}:${t}`,detail:`Index "${n.name}" on table "${e.name}" references column "${t}", which is not declared on the table.`,metadata:{field:t,index:n.name,indexKind:n.kind,table:e.name}}))}return o},source:"static",title:"Index references unknown field"};export{c as default};
@@ -1 +0,0 @@
1
- import{i as s}from"./finding-BAVgqnCF.mjs";const i=/address|birthdate|creditcard|dateofbirth|dob|driverslicense|email|firstname|fullname|lastname|nationalid|passport|phone|socialsecurity|ssn|taxid/u,o=a=>i.test(a.replaceAll(/[^a-z0-9]/giu,"").toLowerCase()),r={categories:["SECURITY"],description:'A `mask(policies)` column named like PII (email, SSN, phone, …) uses the `"hash"` strategy. `"hash"` is an unsalted 32-bit FNV-1a digest — brute-force-recoverable and cross-row-correlatable — not a confidentiality control, so it does not actually hide the value.',facing:"EXTERNAL",level:"WARN",name:"mask_weak_hash_strategy_on_pii",remediation:'Use `"redact"` instead of `"hash"` for this column — e.g. `mask({ <table>: { <column>: "redact" } })`. Reserve `"hash"` for columns where a stable, joinable pseudonym is the goal and the value itself is not sensitive.',run:a=>{if(a.maskStrategies===void 0)return[];const t=[];for(const e of a.maskStrategies)e.strategy!=="hash"||!o(e.column)||t.push(s(r,{cacheKey:`mask_weak_hash_strategy_on_pii:${e.file}:${e.line.toString()}`,detail:`\`${e.exportName}\` in ${e.file} masks \`${e.table===""?e.column:`${e.table}.${e.column}`}\` with \`"hash"\` — its name suggests PII, and \`"hash"\` is brute-force-recoverable and leaks cross-row equality. Use \`"redact"\` instead.`,metadata:{column:e.column,exportName:e.exportName,file:e.file,table:e.table}}));return t},source:"static",title:'Weak "hash" mask strategy on a PII column'};export{r as default};
@@ -1 +0,0 @@
1
- import{i}from"./finding-BAVgqnCF.mjs";const a={categories:["SCHEMA"],description:"A `query`/`mutation` handler sends a notification via `ctx.notify`/`ctx.push`. A send is external I/O (a `fetch` to a push service / FCM): it is non-deterministic like `fetch`, and a mutation re-run on OCC retry would re-send it (duplicate pushes). These facades are available on `ActionCtx` only and must be confined to `action` handlers.",facing:"EXTERNAL",level:"WARN",name:"notify_send_outside_action",remediation:"Move the `ctx.notify`/`ctx.push` send into an `action(...)`, where external I/O is allowed. If a query/mutation must trigger a notification, have it enqueue the send (`enqueuePushBroadcast` via `@lunora/queue`) or schedule an action — the queue/scheduler runs the send exactly once, off the transactional path.",run:n=>{if(n.notifyCalls===void 0)return[];const t=[];for(const e of n.notifyCalls)t.push(i(a,{cacheKey:`notify_send_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 — a notification send is non-deterministic external I/O and a retried ${e.kind} would re-send it. Move it into an \`action\`, or enqueue/schedule the send.`,metadata:{callee:e.callee,exportName:e.exportName,file:e.file,kind:e.kind,line:e.line}}));return t},source:"static",title:"Notification send used outside an action"};export{a as default};
@@ -1 +0,0 @@
1
- import{i as s}from"./finding-BAVgqnCF.mjs";const l={categories:["SECURITY"],description:"A plaintext secret is committed to `wrangler.jsonc`'s `vars` block. `vars` are cleartext — baked into the deployed Worker and checked into source control — so the secret leaks via the repo and the bundle, and rotating it needs a config edit plus a redeploy.",facing:"INTERNAL",level:"ERROR",name:"plaintext_secret_in_wrangler_vars",remediation:"Move the value out of `vars`: bind it through a Secrets Store (`ctx.secrets.get(<NAME>)`) or set it with `wrangler secret put <NAME>` and read it from `env.<NAME>`. Delete the `vars` entry and rotate the exposed value — assume it is already compromised. Public/publishable keys (name them `*_PUBLIC` / `*_PUBLISHABLE`) are exempt.",run:a=>{if(a.wranglerVariables===void 0)return[];const i=[],n=new Map;for(const e of a.wranglerVariables){const t=`${e.file}:${e.key}`,r=(n.get(t)??0)+1;n.set(t,r);const o=r>1?`:${r.toString()}`:"";i.push(s(l,{cacheKey:`plaintext_secret_in_wrangler_vars:${t}${o}`,detail:`The \`vars\` entry \`${e.key}\` in ${e.file} holds a plaintext ${e.kind.replaceAll("_"," ")} (${e.preview}). \`vars\` ship in cleartext to the deployed Worker and are committed to source control — move it to a Secrets Store binding or \`wrangler secret put\` and rotate the exposed value.`,metadata:{file:e.file,key:e.key,kind:e.kind,preview:e.preview}}))}return i},source:"static",title:"Plaintext secret in wrangler vars"};export{l as default};
@@ -1 +0,0 @@
1
- import{i as r}from"./finding-BAVgqnCF.mjs";const o={categories:["SCHEMA"],description:"A public `mutation`/`action` emits no structured event. When it fails you get a stack trace with no request context — no ids, no tenant, no outcome — so the failure is visible but not searchable.",facing:"INTERNAL",level:"INFO",name:"procedure_without_structured_event",remediation:'Emit one event on the primary path: `ctx.log.info("<verb>", { … })`, or wrap the handler in `ctx.span("<name>", …)` to attach timing too.',run:t=>{if(t.procedureProtections===void 0)return[];const i=[];for(const e of t.procedureProtections)e.visibility!=="public"||e.kind==="query"||e.emitsEvent!==!1||i.push(r(o,{cacheKey:`procedure_without_structured_event:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) emits no structured event. Add a \`ctx.log\` line or a \`ctx.span\` so a failure carries its request context.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind}}));return i},source:"static",title:"Public write emits no structured event"};export{o as default};
@@ -1 +0,0 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const r={categories:["SECURITY"],description:"A public procedure declares a `v.any()` argument. `v.any()` accepts arbitrary untyped input from an untrusted client — defeating validation and opening injection / oversized-payload abuse.",facing:"EXTERNAL",level:"WARN",name:"public_arg_uses_any",remediation:"Replace `v.any()` with a precise validator — `v.object({...})`, `v.string()`, `v.union(...)`, etc. If the shape is genuinely dynamic, model the known variants with `v.union` rather than accepting anything.",run:i=>{if(i.argValidators===void 0)return[];const t=[];for(const e of i.argValidators)for(const a of e.anyArgs)t.push(n(r,{cacheKey:`public_arg_uses_any:${e.file}:${e.exportName}:${a}`,detail:`Arg \`${a}\` of public procedure \`${e.exportName}\` (${e.file}:${e.line.toString()}) is \`v.any()\` — untrusted input with no validation. Give it a precise validator.`,metadata:{argument:a,exportName:e.exportName,file:e.file,line:e.line}}));return t},source:"static",title:"Public argument uses v.any()"};export{r as default};
@@ -1 +0,0 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";import{i as o}from"./helpers-DwlvEUCh.mjs";const s=/contact|forgot|login|magic|otp|register|reset|signin|signup|subscribe|verify/iu,n={categories:["SECURITY"],description:"A public `mutation`/`action` has no `rateLimit` middleware. Publicly-callable writes are flood and brute-force targets — an attacker can exhaust writes, mail quota, or credits, or guess credentials on auth-shaped endpoints.",facing:"EXTERNAL",level:"WARN",name:"public_mutation_without_ratelimit",remediation:'Attach a rate limit: `.use(rateLimit(limiter, "<bucket>"))` from `@lunora/ratelimit`, or wrap the recommended public-procedure guards with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Genuinely-open writes can be acknowledged by adding a permissive limiter.',run:t=>{if(t.procedureProtections===void 0)return[];const i=[];for(const e of t.procedureProtections){if(!o(e)||e.usesRateLimit)continue;const r=s.test(e.exportName);i.push(a(n,{cacheKey:`public_mutation_without_ratelimit:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) has no rate limit${r?" — its name suggests an auth/abuse-sensitive endpoint, so this is high-risk":""}. Add \`.use(rateLimit(...))\` or \`.use(protectPublic({ rateLimit }))\`.`,metadata:{exportName:e.exportName,file:e.file,kind:e.kind,sensitive:r}}))}return i},source:"static",title:"Public write without a rate limit"};export{n as default};
@@ -1 +0,0 @@
1
- import{i as a}from"./finding-BAVgqnCF.mjs";const n={categories:["SCHEMA"],description:"A `query`/`mutation` handler accesses R2 SQL via `ctx.r2sql`. R2 SQL queries external Apache Iceberg tables over REST (no Workers binding): queries are non-deterministic (like `fetch`) and the reads are invisible to live queries. `ctx.r2sql` is available on `ActionCtx` only and must be confined to `action` handlers.",facing:"EXTERNAL",level:"WARN",name:"r2sql_outside_action",remediation:"Move the `ctx.r2sql` 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.r2sql` and write a projection into a `defineSchema` DO/D1 table — that write is tracked by live queries, whereas R2 SQL is not.",run:t=>{if(t.r2sqlCalls===void 0)return[];const i=[];for(const e of t.r2sqlCalls)i.push(a(n,{cacheKey:`r2sql_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 — R2 SQL's \`ctx.r2sql\` is non-deterministic and non-reactive, so it is available only in actions. Move the external query 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}}));return i},source:"static",title:"R2 SQL `ctx.r2sql` used outside an action"};export{n as default};
@@ -1 +0,0 @@
1
- import{i as r}from"./finding-BAVgqnCF.mjs";const s={categories:["SCHEMA"],description:"A relation targets a table that does not exist in the schema. The relation can never load — the target is a typo or a removed/renamed table.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_table",remediation:"Fix the target table name in the relation, or add the missing table to the schema.",run:a=>{const n=[],o=new Set(a.schema.tables.map(e=>e.name));for(const e of a.schema.tables)for(const t of e.relations)o.has(t.table)||n.push(r(s,{cacheKey:`relation_references_unknown_table:${e.name}:${t.name}`,detail:`Relation "${t.name}" on table "${e.name}" targets table "${t.table}", which does not exist in the schema.`,metadata:{relation:t.name,table:e.name,target:t.table}}));return n},source:"static",title:"Relation references unknown table"};export{s as default};
@@ -1 +0,0 @@
1
- import{i as l}from"./finding-BAVgqnCF.mjs";const r={categories:["PERFORMANCE"],description:"A `defineShape` replicates from a `.global()` table. Global tables live outside the shard DO's SQLite op-log, so the shape is served through the cross-shard tier — coordinator/poll-refreshed and latency-tiered, not poke-live like a sharded shape.",facing:"EXTERNAL",level:"WARN",name:"shape_targets_global_table",remediation:"Expected if you want the cross-shard tier — just don't assume poke-live freshness; global shapes refresh on a poll. For live updates, replicate from a sharded table instead (denormalize the columns you need into the shard).",run:a=>{if(a.shapes===void 0)return[];const s=new Set(a.schema.tables.filter(e=>e.shardKind==="global").map(e=>e.name)),t=[];for(const e of a.shapes)e.table===void 0||!s.has(e.table)||t.push(l(r,{cacheKey:`shape_targets_global_table:${e.exportName}`,detail:`Shape \`${e.exportName}\` (${e.file}) replicates from the \`.global()\` table \`${e.table}\`. It is served through the cross-shard global tier — poll-refreshed and latency-tiered, not poke-live.`,metadata:{exportName:e.exportName,file:e.file,table:e.table}}));return t},source:"static",title:"Shape replicates from a global (cross-shard) table"};export{r as default};
@@ -1 +0,0 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";const n={categories:["SCHEMA"],description:"A `defineShape` is bound to a `table` name that does not exist in the schema. The shape can never resolve a rowset — its subscription seeds empty and errors at the first flush.",facing:"INTERNAL",level:"ERROR",name:"shape_unknown_table",remediation:"Fix the shape's `table` to a real table name (check for a typo or a table that was renamed/removed).",run:a=>{if(a.shapes===void 0)return[];const s=new Set(a.schema.tables.map(e=>e.name)),t=[];for(const e of a.shapes)e.table===void 0||s.has(e.table)||t.push(o(n,{cacheKey:`shape_unknown_table:${e.exportName}`,detail:`Shape \`${e.exportName}\` (${e.file}) replicates from table \`${e.table}\`, which is not declared in the schema. The shape can never resolve a rowset.`,metadata:{exportName:e.exportName,file:e.file,table:e.table}}));return t},source:"static",title:"Shape bound to an unknown table"};export{n as default};
@@ -1 +0,0 @@
1
- import{i as n}from"./finding-BAVgqnCF.mjs";const s={categories:["SCHEMA"],description:'No function inserts into this table via `ctx.db.insert("<table>", …)`. It may be read-only by design (seeded by a migration, replicated, or written through a path the advisor can\'t see) — or it may be dead schema.',facing:"INTERNAL",level:"INFO",name:"table_without_insert",remediation:'If the table should be writable, add a mutation that calls `ctx.db.insert("<table>", …)`. If it is read-only or seeded elsewhere, this advisory can be ignored.',run:t=>{if(t.inserts===void 0)return[];const i=new Set(t.inserts.filter(e=>e.table!=="").map(e=>e.table)),a=[];for(const e of t.schema.tables)i.has(e.name)||e.externallyManaged===!0||a.push(n(s,{cacheKey:`table_without_insert:${e.name}`,detail:`No function calls \`ctx.db.insert("${e.name}", …)\` — table "${e.name}" has no discovered insert path.`,metadata:{table:e.name}}));return a},source:"static",title:"Table has no insert path"};export{s as default};
@@ -1 +0,0 @@
1
- import{i as r}from"./finding-BAVgqnCF.mjs";const i={categories:["SECURITY"],description:"A public `v.string()` argument has no maximum-length bound. An unbounded string lets a client submit arbitrarily large input — abusing storage and CPU on every request that processes it.",facing:"EXTERNAL",level:"INFO",name:"unbounded_string_arg",remediation:"Add a max-length bound via `.check(...)` / `.meta({ maxLength })` on the string validator (e.g. cap a name at 256, a body at a few KB). Size the cap to the field's real-world maximum.",run:a=>{if(a.argValidators===void 0)return[];const n=[];for(const e of a.argValidators)for(const t of e.unboundedStringArgs)n.push(r(i,{cacheKey:`unbounded_string_arg:${e.file}:${e.exportName}:${t}`,detail:`Arg \`${t}\` of public procedure \`${e.exportName}\` (${e.file}:${e.line.toString()}) is an unbounded \`v.string()\`. Add a max-length bound to cap payload size.`,metadata:{argument:t,exportName:e.exportName,file:e.file,line:e.line}}));return n},source:"static",title:"Public string argument has no length bound"};export{i as default};
@@ -1 +0,0 @@
1
- import{i as o}from"./finding-BAVgqnCF.mjs";import{m as i}from"./procedure-protections-DRQotu9I.mjs";import{i as n}from"./helpers-DwlvEUCh.mjs";const c=t=>t.writesUserTable===!0?"writes a user/session table":t.callsMail===!0?"sends mail":t.analyzableBody===!1?"may write a user/session table or send mail — its handler body could not be read":"may write a user/session table or send mail — at least one could not be determined",l={categories:["SECURITY"],description:"A public `mutation`/`action` that creates a user/session or sends mail has no CAPTCHA / bot check. Account-creating and mail-sending endpoints are prime automated-abuse targets (credential stuffing, mailbox flooding, disposable-account farming).",facing:"EXTERNAL",level:"WARN",name:"user_creating_mutation_without_captcha",remediation:"Add a server-verified human check: `.use(verifyTurnstile({ secret, token }))` from `@lunora/auth`, or wrap it with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Pair with a rate limit for defense in depth.",run:t=>{if(t.procedureProtections===void 0)return[];const a=[];for(const e of t.procedureProtections){const r=i(e.writesUserTable)||i(e.callsMail);if(!n(e)||!r||e.usesCaptcha)continue;const s=c(e);a.push(o(l,{cacheKey:`user_creating_mutation_without_captcha:${e.file}:${e.exportName}`,detail:`Public ${e.kind} \`${e.exportName}\` (${e.file}) ${s} but has no CAPTCHA check. Add \`.use(verifyTurnstile(...))\` or \`.use(protectPublic({ captcha }))\`.`,metadata:{callsMail:e.callsMail,exportName:e.exportName,file:e.file,kind:e.kind,writesUserTable:e.writesUserTable}}))}return a},source:"static",title:"Account-creating / mail-sending write without a CAPTCHA"};export{l as default};
@@ -1 +0,0 @@
1
- import{i as l}from"./finding-BAVgqnCF.mjs";const t={categories:["SCHEMA"],description:'A `ctx.workflows.get("<name>")` call references a workflow that is not declared by any `defineWorkflow` export in `lunora/workflows.ts`. The name is a typo or points at a removed/renamed workflow — the call throws at runtime.',facing:"INTERNAL",level:"ERROR",name:"workflow_unknown_target",remediation:'Fix the workflow name in the `ctx.workflows.get("…")` call, or add the missing `defineWorkflow` export to `lunora/workflows.ts`.',run:e=>{if(e.workflows===void 0||e.workflowCalls===void 0)return[];const w=new Set(e.workflows.map(o=>o.exportName)),r=[];for(const o of e.workflowCalls)o.workflow===""||w.has(o.workflow)||r.push(l(t,{cacheKey:`workflow_unknown_target:${o.file}:${o.exportName}:${o.workflow}`,detail:`\`ctx.workflows.get("${o.workflow}")\` in "${o.exportName}" (${o.file}) references workflow "${o.workflow}", which is not declared in lunora/workflows.ts.`,metadata:{exportName:o.exportName,file:o.file,line:o.line,workflow:o.workflow}}));return r},source:"static",title:"Workflow call references unknown workflow"};export{t as default};
@@ -1 +0,0 @@
1
- import{i}from"./finding-BAVgqnCF.mjs";const s={categories:["SCHEMA"],description:'No function starts this workflow via `ctx.workflows.get("<name>")`. It may be triggered through a path the advisor can\'t see (the Cloudflare API, a wrangler invocation, a cross-service binding) — or it may be dead code that still deploys as a billable WorkflowEntrypoint.',facing:"INTERNAL",level:"INFO",name:"workflow_unused",remediation:'If the workflow should be triggered in-app, start it from a mutation/action with `ctx.workflows.get("<name>").create({ params })`. If it is started externally or is no longer needed, this advisory can be ignored (or remove the `defineWorkflow` export).',run:o=>{if(o.workflows===void 0)return[];const t=o.workflowCalls??[];if(t.some(e=>e.workflow===""))return[];const a=new Set(t.map(e=>e.workflow)),r=[];for(const e of o.workflows)a.has(e.exportName)||r.push(i(s,{cacheKey:`workflow_unused:${e.exportName}`,detail:`No function calls \`ctx.workflows.get("${e.exportName}")\` — workflow "${e.exportName}" is declared but never started in app code.`,metadata:{workflow:e.exportName}}));return r},source:"static",title:"Workflow is never started"};export{s as default};