@lunora/advisor 1.0.0-alpha.67 → 1.0.0-alpha.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/actionWithoutErrorHandling-rhzjCEqm.mjs +1 -0
- package/dist/packem_shared/adminRouteWithoutGuard-BbscPp1m.mjs +1 -0
- package/dist/packem_shared/aiRunWithoutLogging-B0K93Pga.mjs +1 -0
- package/dist/packem_shared/authApiCallWithoutHeaders-BHkik-EF.mjs +1 -0
- package/dist/packem_shared/containerPublicInternet-g0TRsayn.mjs +1 -0
- package/dist/packem_shared/emptyIndex-Bp-Y81D0.mjs +1 -0
- package/dist/packem_shared/errorWithoutCatalog-0PbxiZL4.mjs +1 -0
- package/dist/packem_shared/externalSourceOnGlobal-IYnALPlO.mjs +1 -0
- package/dist/packem_shared/filterOnPrimaryKey-Db947tQY.mjs +1 -0
- package/dist/packem_shared/fromServerSchema-9aaK1J7g.mjs +1 -0
- package/dist/packem_shared/{geoIndexUnused-BgDE6_AR.mjs → geoIndexUnused-B5Oww9o2.mjs} +1 -1
- package/dist/packem_shared/hardcodedSecret-yu14ITlW.mjs +1 -0
- package/dist/packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs +1 -0
- package/dist/packem_shared/indexReferencesUnknownField-BPJjE0zg.mjs +1 -0
- package/dist/packem_shared/maskWeakHashStrategyOnPii-DcEwc4Sz.mjs +1 -0
- package/dist/packem_shared/notifySendOutsideAction-B9DcirhJ.mjs +1 -0
- package/dist/packem_shared/plaintextSecretInWranglerVariables-CmbygN6_.mjs +1 -0
- package/dist/packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs +1 -0
- package/dist/packem_shared/publicArgumentUsesAny-BzdcoJ9C.mjs +1 -0
- package/dist/packem_shared/publicMutationWithoutRatelimit-CozomYXw.mjs +1 -0
- package/dist/packem_shared/r2sqlOutsideAction-DCMAsAeM.mjs +1 -0
- package/dist/packem_shared/relationReferencesUnknownTable-EOtKyaHx.mjs +1 -0
- package/dist/packem_shared/shapeTargetsGlobalTable-SAlq2N-T.mjs +1 -0
- package/dist/packem_shared/shapeUnknownTable-DhvsbEtT.mjs +1 -0
- package/dist/packem_shared/tableWithoutInsert-BV7qsPKx.mjs +1 -0
- package/dist/packem_shared/unboundedStringArgument-DN8w1bg-.mjs +1 -0
- package/dist/packem_shared/userCreatingMutationWithoutCaptcha-DIeHIeTU.mjs +1 -0
- package/dist/packem_shared/workflowUnknownTarget-BzKlYSCg.mjs +1 -0
- package/dist/packem_shared/workflowUnused-D9fC9E9b.mjs +1 -0
- package/package.json +3 -3
- package/dist/packem_shared/actionWithoutErrorHandling-D1DqSPpg.mjs +0 -1
- package/dist/packem_shared/adminRouteWithoutGuard-DSo4t9j4.mjs +0 -1
- package/dist/packem_shared/aiRunWithoutLogging-CMvQPNBr.mjs +0 -1
- package/dist/packem_shared/authApiCallWithoutHeaders-BItMMdZP.mjs +0 -1
- package/dist/packem_shared/containerPublicInternet-Bx2J7tha.mjs +0 -1
- package/dist/packem_shared/emptyIndex-BiXEfyfN.mjs +0 -1
- package/dist/packem_shared/errorWithoutCatalog-DTDVKU4b.mjs +0 -1
- package/dist/packem_shared/externalSourceOnGlobal-DNPLoNUg.mjs +0 -1
- package/dist/packem_shared/filterOnPrimaryKey-CPPa7fog.mjs +0 -1
- package/dist/packem_shared/fromServerSchema-CJ-AQ2kw.mjs +0 -1
- package/dist/packem_shared/hardcodedSecret-rIRc3aNk.mjs +0 -1
- package/dist/packem_shared/hyperdriveOutsideAction-CRmOQz67.mjs +0 -1
- package/dist/packem_shared/indexReferencesUnknownField-r3QHlaz6.mjs +0 -1
- package/dist/packem_shared/maskWeakHashStrategyOnPii-CEiiGgT7.mjs +0 -1
- package/dist/packem_shared/notifySendOutsideAction-B0jZuUP3.mjs +0 -1
- package/dist/packem_shared/plaintextSecretInWranglerVariables-DXjKIm8H.mjs +0 -1
- package/dist/packem_shared/procedureWithoutStructuredEvent-D4Qi2vH7.mjs +0 -1
- package/dist/packem_shared/publicArgumentUsesAny-BJb4cTqL.mjs +0 -1
- package/dist/packem_shared/publicMutationWithoutRatelimit-D9phluHW.mjs +0 -1
- package/dist/packem_shared/r2sqlOutsideAction-CKK_I1Ms.mjs +0 -1
- package/dist/packem_shared/relationReferencesUnknownTable-CLdrhkmS.mjs +0 -1
- package/dist/packem_shared/shapeTargetsGlobalTable-Blz-aSkg.mjs +0 -1
- package/dist/packem_shared/shapeUnknownTable-DFxtyFio.mjs +0 -1
- package/dist/packem_shared/tableWithoutInsert-D3AhaNzR.mjs +0 -1
- package/dist/packem_shared/unboundedStringArgument-AkkIcr-S.mjs +0 -1
- package/dist/packem_shared/userCreatingMutationWithoutCaptcha-DGiZ3_W7.mjs +0 -1
- package/dist/packem_shared/workflowUnknownTarget-JF8rITLP.mjs +0 -1
- package/dist/packem_shared/workflowUnused-Dx_FQwGE.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1476,6 +1476,26 @@ interface AdvisorSqlInterpolation {
|
|
|
1476
1476
|
/** 1-based line of the interpolation, or `0` when unknown. */
|
|
1477
1477
|
line: number;
|
|
1478
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* One import of a migrated-away platform's SDK still present in `lunora/`
|
|
1481
|
+
* source — the input the `migration_stale_import` lint consumes.
|
|
1482
|
+
*
|
|
1483
|
+
* A half-finished port is the failure mode: the data moved, the functions were
|
|
1484
|
+
* rewritten, and one handler still calls the old client. It builds, it
|
|
1485
|
+
* typechecks, and at runtime it reads from the platform the app just left.
|
|
1486
|
+
* Produced by the codegen feeder; runtime callers don't supply it, so the lint
|
|
1487
|
+
* finds nothing there.
|
|
1488
|
+
*/
|
|
1489
|
+
interface AdvisorStaleMigrationImport {
|
|
1490
|
+
/** Source file relative to the lunora dir, no extension. */
|
|
1491
|
+
file: string;
|
|
1492
|
+
/** 1-based line of the import, or `0` when unknown. */
|
|
1493
|
+
line: number;
|
|
1494
|
+
/** The imported module, e.g. `@supabase/supabase-js`. */
|
|
1495
|
+
moduleSpecifier: string;
|
|
1496
|
+
/** Which migration guide covers this platform. */
|
|
1497
|
+
platform: "convex" | "firebase" | "supabase";
|
|
1498
|
+
}
|
|
1479
1499
|
/**
|
|
1480
1500
|
* One `ctx.storage.<bucket>.<method>(key, …)` call whose R2 object key is derived
|
|
1481
1501
|
* from the handler's `args` with no server-side scoping — the input the
|
|
@@ -2169,6 +2189,12 @@ interface LintContext {
|
|
|
2169
2189
|
* finds nothing.
|
|
2170
2190
|
*/
|
|
2171
2191
|
sqlInterpolations?: ReadonlyArray<AdvisorSqlInterpolation>;
|
|
2192
|
+
/**
|
|
2193
|
+
* Imports of a migrated-away platform's SDK still present in `lunora/` source
|
|
2194
|
+
* — the `migration_stale_import` input. Supplied by the codegen feeder; absent
|
|
2195
|
+
* for runtime callers, where the lint finds nothing.
|
|
2196
|
+
*/
|
|
2197
|
+
staleMigrationImports?: ReadonlyArray<AdvisorStaleMigrationImport>;
|
|
2172
2198
|
/**
|
|
2173
2199
|
* `ctx.storage.<bucket>.<method>(key, …)` calls whose R2 object key is derived
|
|
2174
2200
|
* from the handler's `args` with no server-side scoping — the
|
|
@@ -4461,4 +4487,4 @@ interface RunAdvisorOptions {
|
|
|
4461
4487
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
4462
4488
|
*/
|
|
4463
4489
|
declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
|
|
4464
|
-
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
|
4490
|
+
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
package/dist/index.d.ts
CHANGED
|
@@ -1476,6 +1476,26 @@ interface AdvisorSqlInterpolation {
|
|
|
1476
1476
|
/** 1-based line of the interpolation, or `0` when unknown. */
|
|
1477
1477
|
line: number;
|
|
1478
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* One import of a migrated-away platform's SDK still present in `lunora/`
|
|
1481
|
+
* source — the input the `migration_stale_import` lint consumes.
|
|
1482
|
+
*
|
|
1483
|
+
* A half-finished port is the failure mode: the data moved, the functions were
|
|
1484
|
+
* rewritten, and one handler still calls the old client. It builds, it
|
|
1485
|
+
* typechecks, and at runtime it reads from the platform the app just left.
|
|
1486
|
+
* Produced by the codegen feeder; runtime callers don't supply it, so the lint
|
|
1487
|
+
* finds nothing there.
|
|
1488
|
+
*/
|
|
1489
|
+
interface AdvisorStaleMigrationImport {
|
|
1490
|
+
/** Source file relative to the lunora dir, no extension. */
|
|
1491
|
+
file: string;
|
|
1492
|
+
/** 1-based line of the import, or `0` when unknown. */
|
|
1493
|
+
line: number;
|
|
1494
|
+
/** The imported module, e.g. `@supabase/supabase-js`. */
|
|
1495
|
+
moduleSpecifier: string;
|
|
1496
|
+
/** Which migration guide covers this platform. */
|
|
1497
|
+
platform: "convex" | "firebase" | "supabase";
|
|
1498
|
+
}
|
|
1479
1499
|
/**
|
|
1480
1500
|
* One `ctx.storage.<bucket>.<method>(key, …)` call whose R2 object key is derived
|
|
1481
1501
|
* from the handler's `args` with no server-side scoping — the input the
|
|
@@ -2169,6 +2189,12 @@ interface LintContext {
|
|
|
2169
2189
|
* finds nothing.
|
|
2170
2190
|
*/
|
|
2171
2191
|
sqlInterpolations?: ReadonlyArray<AdvisorSqlInterpolation>;
|
|
2192
|
+
/**
|
|
2193
|
+
* Imports of a migrated-away platform's SDK still present in `lunora/` source
|
|
2194
|
+
* — the `migration_stale_import` input. Supplied by the codegen feeder; absent
|
|
2195
|
+
* for runtime callers, where the lint finds nothing.
|
|
2196
|
+
*/
|
|
2197
|
+
staleMigrationImports?: ReadonlyArray<AdvisorStaleMigrationImport>;
|
|
2172
2198
|
/**
|
|
2173
2199
|
* `ctx.storage.<bucket>.<method>(key, …)` calls whose R2 object key is derived
|
|
2174
2200
|
* from the handler's `args` with no server-side scoping — the
|
|
@@ -4461,4 +4487,4 @@ interface RunAdvisorOptions {
|
|
|
4461
4487
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
4462
4488
|
*/
|
|
4463
4489
|
declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
|
|
4464
|
-
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
|
4490
|
+
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagSecurityDefault, type AdvisorFunctionMetrics, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, circularFk, classifySensitivity, compareToBaseline, constraintValidator, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, dedupeCacheKeys, duplicateIndex, emptyIndex, errorRateOutlier, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, gradeFromScore, hardcodedSecret, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, ttlFieldNotTimestamp, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dedupeCacheKeys as 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 m}from"./packem_shared/dedupeCacheKeys-7JgxaQWV.mjs";import s from"./packem_shared/constraintValidator-N339mfyx.mjs";import n from"./packem_shared/errorRateOutlier-CgK42sq0.mjs";import p from"./packem_shared/hotShard-io-eF2Sx.mjs";import f from"./packem_shared/indexUtilization-QfbIH_w4.mjs";import l from"./packem_shared/actionFetchSsrf-BbWxy1Yf.mjs";import u from"./packem_shared/actionWithoutErrorHandling-rhzjCEqm.mjs";import d from"./packem_shared/adminRouteWithoutGuard-BbscPp1m.mjs";import c from"./packem_shared/aiRawRunEscapeHatch-DfhjYm43.mjs";import $ from"./packem_shared/aiRunWithoutLogging-B0K93Pga.mjs";import h from"./packem_shared/aiToolSideEffectPromptInjection-DnWQOoKi.mjs";import g from"./packem_shared/aiUnboundedGenerationPublic-iu7crnSa.mjs";import S from"./packem_shared/allowUnauthenticatedShardAccessEnabled-D4aMNBb0.mjs";import b from"./packem_shared/authApiCallWithoutHeaders-BHkik-EF.mjs";import y from"./packem_shared/authCsrfCheckDisabled-De9ammtV.mjs";import U from"./packem_shared/authEmailVerificationDisabled-BFKdJcx4.mjs";import I from"./packem_shared/authScimWithoutTransactions-DCtLXzks.mjs";import A from"./packem_shared/authSecureCookiesDisabled-B4WIzvMm.mjs";import R from"./packem_shared/authSessionFreshageZero-Bpcaf_Pe.mjs";import T from"./packem_shared/authTrustedOriginsWildcard-DjU9NXEm.mjs";import W from"./packem_shared/browserAllowPrivateTargets-Dxyeh6lg.mjs";import v 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 C from"./packem_shared/containerRuntimeEgressRelaxation-BQ2ZD1aV.mjs";import M from"./packem_shared/containerStartEnableInternetOverride-B62WUura.mjs";import P from"./packem_shared/duplicateIndex-DFT7qLQD.mjs";import D from"./packem_shared/emptyIndex-Bp-Y81D0.mjs";import N from"./packem_shared/errorWithoutCatalog-0PbxiZL4.mjs";import O from"./packem_shared/exportSinkMisconfigured-DvpAaa6j.mjs";import E from"./packem_shared/externalSourceIncrementalNoDeletePath-DnoL64YY.mjs";import G from"./packem_shared/externalSourceOnGlobal-IYnALPlO.mjs";import K from"./packem_shared/externalSourceUnscoped-DqBp5jYr.mjs";import L from"./packem_shared/filterOnPrimaryKey-Db947tQY.mjs";import _ from"./packem_shared/filterWithoutIndex-pIpqk-O8.mjs";import j from"./packem_shared/flagGatesSecurityWithUnsafeDefault-DPGFEHTt.mjs";import q from"./packem_shared/geoIndexFieldNotGeopoint-DAGLZtaH.mjs";import z from"./packem_shared/geoIndexUnused-B5Oww9o2.mjs";import H from"./packem_shared/hardcodedSecret-yu14ITlW.mjs";import V from"./packem_shared/httpActionMissingAuthGuard-BZDdeW_D.mjs";import B from"./packem_shared/httpActionResponseHeaderInjection-D063rB7U.mjs";import Q from"./packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs";import Z from"./packem_shared/identityUndeclaredClaimTrusted-BYRYGQ74.mjs";import J from"./packem_shared/imagesUrlSourceFromUserInput-Cy5pc_dA.mjs";import X from"./packem_shared/indexReferencesUnknownField-BPJjE0zg.mjs";import Y from"./packem_shared/insertManyUnsafeUserData-xLYAkcKj.mjs";import oo from"./packem_shared/kvUnscopedUserKeyIdor-B4lGxRxh.mjs";import ro from"./packem_shared/mailInboundDispatchWithoutVerify-BivYJ5F9.mjs";import to from"./packem_shared/mailRecipientFromRequestInput-BR_oSh65.mjs";import eo from"./packem_shared/maskUncoveredPiiColumn-DqPfZM2O.mjs";import io from"./packem_shared/maskWeakHashStrategyOnPii-DcEwc4Sz.mjs";import ao from"./packem_shared/maskedRelationLeakViaWith-CxYk0MDW.mjs";import{i as mo}from"./packem_shared/finding-BAVgqnCF.mjs";import so from"./packem_shared/mutatorFullRowReplace-CEgecSGm.mjs";import no from"./packem_shared/nondeterministicQueryMutation-BZV_biUV.mjs";import po from"./packem_shared/normalizeIdUsedAsAuthorization-FjISSXJm.mjs";import fo from"./packem_shared/notifyMissingPushConfig-C_GbVw3S.mjs";import lo from"./packem_shared/notifySendOutsideAction-B9DcirhJ.mjs";import uo from"./packem_shared/outputProjectionMissingOnPublicRead-9Pm9766s.mjs";import co from"./packem_shared/ownerFieldFromArgsNotAuth-D4K-_0aD.mjs";import $o from"./packem_shared/paymentCreateWithoutAuthorize-CurtqN8K.mjs";import ho from"./packem_shared/paymentWebhookWideTolerance-I7ingfR8.mjs";import go from"./packem_shared/plaintextSecretInWranglerVariables-CmbygN6_.mjs";import So from"./packem_shared/policyReferencesUnknownTable-D3WelAQa.mjs";import bo from"./packem_shared/privilegedDispatchUnvalidatedPayload-74GuGye1.mjs";import yo from"./packem_shared/privilegedFanoutFromPublicProcedure-sMsZN4uP.mjs";import Uo from"./packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs";import Io from"./packem_shared/publicArgumentUsesAny-BzdcoJ9C.mjs";import Ao from"./packem_shared/publicMutationWithoutRatelimit-CozomYXw.mjs";import Ro from"./packem_shared/publicTableRlsOptoutConfusion-CaWtPGAn.mjs";import To from"./packem_shared/queueWithoutDlq-BLY3iy9Q.mjs";import Wo from"./packem_shared/r2sqlOutsideAction-DCMAsAeM.mjs";import vo from"./packem_shared/ratelimitDefaultMemoryStore-mOdI3Jmb.mjs";import ko from"./packem_shared/ratelimitKeySpoofableOrGlobal-DaGId_Fh.mjs";import wo from"./packem_shared/ratelimitMiddlewareFailOpen-CTWVosEc.mjs";import xo from"./packem_shared/relationReferencesUnknownField-clqyB69Y.mjs";import Fo from"./packem_shared/relationReferencesUnknownTable-EOtKyaHx.mjs";import Co from"./packem_shared/rlsUncoveredTable-DYHgnoos.mjs";import Mo from"./packem_shared/shapeTargetsGlobalTable-SAlq2N-T.mjs";import Po from"./packem_shared/shapeUnknownTable-DhvsbEtT.mjs";import Do from"./packem_shared/signupMutationWithoutDisposableGating-Tgv9SbsY.mjs";import No from"./packem_shared/softDeleteIncludeDeletedFromArgs-DgxpQHRA.mjs";import Oo from"./packem_shared/sqlInjectionRisk-D2NaT-5c.mjs";import Eo from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-C9cjl6Jh.mjs";import Go from"./packem_shared/storageKeyFromUserArgs-CI-6kyte.mjs";import Ko from"./packem_shared/storagePresignedUrlForPrivateContent-j7VhwIdu.mjs";import Lo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-oxKLecuf.mjs";import _o from"./packem_shared/storageUploadWithoutMaxSize-DUDxPutk.mjs";import jo from"./packem_shared/tableWithoutInsert-BV7qsPKx.mjs";import qo from"./packem_shared/ttlFieldNotTimestamp-BFiRkSZD.mjs";import zo from"./packem_shared/unboundedStringArgument-DN8w1bg-.mjs";import Ho from"./packem_shared/unindexedForeignKey-DK4eWXkb.mjs";import Vo from"./packem_shared/unindexedRelationTarget-B5fFq1E8.mjs";import Bo from"./packem_shared/unrestrictedWhereBranch-YKiydie4.mjs";import Qo from"./packem_shared/userCreatingMutationWithoutCaptcha-DIeHIeTU.mjs";import Zo from"./packem_shared/vectorsNamespaceFromUserInput-Cyq5B2kF.mjs";import Jo from"./packem_shared/workflowDuplicateStepName-cXF6Rpvg.mjs";import Xo from"./packem_shared/workflowUnknownTarget-BzKlYSCg.mjs";import Yo from"./packem_shared/workflowUnused-D9fC9E9b.mjs";import{compareToBaseline as oe,parseAdvisorMap as re}from"./packem_shared/compareToBaseline-BwXFusQG.mjs";import{gradeFromScore as ee}from"./packem_shared/gradeFromScore-DW82YDVY.mjs";import{MAP_VERSION as ae,byCodepoint as me,scoreAdvisor as se}from"./packem_shared/MAP_VERSION-DxkFCiLX.mjs";import{default as pe}from"./packem_shared/classifySensitivity-B1kkm44P.mjs";import{fromServerSchema as le}from"./packem_shared/fromServerSchema-9aaK1J7g.mjs";const or={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},i={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>mo(i,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${or[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},rr=[X,Fo,xo,Xo,Jo,Po,E,G,K,D,N,q,z,O,qo,k,Ho,Vo,P,jo,Yo,To,L,_,Mo,so,no,Q,Wo,b,So,Co,eo,io,x,F,Ao,Bo,Qo,Do,Io,zo,H,Oo,d,$o,ro,vo,W,yo,Uo,Y,g,l,u,co,Go,oo,w,c,$,Zo,to,v,bo,M,C,I,T,y,A,U,R,J,ko,Ro,S,Lo,_o,Eo,Ko,V,B,wo,j,h,Z,ho,No,ao,uo,po,lo,fo,go,i],tr=[p,f,s,n],er=[...rr,...tr],Jt=(r,o={})=>{const a=o.lints??er,t=[];for(const e of a)o.source!==void 0&&e.source!==o.source||t.push(...e.run(r));return m(t)};export{er as ALL_LINTS,ae as MAP_VERSION,tr as RUNTIME_LINTS,rr as STATIC_LINTS,l as actionFetchSsrf,u as actionWithoutErrorHandling,d as adminRouteWithoutGuard,c as aiRawRunEscapeHatch,$ as aiRunWithoutLogging,h as aiToolSideEffectPromptInjection,g as aiUnboundedGenerationPublic,S as allowUnauthenticatedShardAccessEnabled,b as authApiCallWithoutHeaders,y as authCsrfCheckDisabled,U as authEmailVerificationDisabled,I as authScimWithoutTransactions,A as authSecureCookiesDisabled,R as authSessionFreshageZero,T as authTrustedOriginsWildcard,W as browserAllowPrivateTargets,v as browserUserUrlWithoutAllowlist,me as byCodepoint,k as circularFk,pe as classifySensitivity,oe as compareToBaseline,s as constraintValidator,w as containerInstanceKeyFromUserInput,x as containerOversizedInstance,F as containerPublicInternet,C as containerRuntimeEgressRelaxation,M as containerStartEnableInternetOverride,m as dedupeCacheKeys,P as duplicateIndex,D as emptyIndex,n as errorRateOutlier,N as errorWithoutCatalog,O as exportSinkMisconfigured,E as externalSourceIncrementalNoDeletePath,G as externalSourceOnGlobal,K as externalSourceUnscoped,L as filterOnPrimaryKey,_ as filterWithoutIndex,j as flagGatesSecurityWithUnsafeDefault,le as fromServerSchema,q as geoIndexFieldNotGeopoint,z as geoIndexUnused,ee as gradeFromScore,H as hardcodedSecret,p as hotShard,V as httpActionMissingAuthGuard,B as httpActionResponseHeaderInjection,Q as hyperdriveOutsideAction,Z as identityUndeclaredClaimTrusted,J as imagesUrlSourceFromUserInput,X as indexReferencesUnknownField,f as indexUtilization,Y as insertManyUnsafeUserData,oo as kvUnscopedUserKeyIdor,ro as mailInboundDispatchWithoutVerify,to as mailRecipientFromRequestInput,eo as maskUncoveredPiiColumn,io as maskWeakHashStrategyOnPii,ao as maskedRelationLeakViaWith,so as mutatorFullRowReplace,no as nondeterministicQueryMutation,po as normalizeIdUsedAsAuthorization,fo as notifyMissingPushConfig,lo as notifySendOutsideAction,uo as outputProjectionMissingOnPublicRead,co as ownerFieldFromArgsNotAuth,re as parseAdvisorMap,$o as paymentCreateWithoutAuthorize,ho as paymentWebhookWideTolerance,go as plaintextSecretInWranglerVariables,So as policyReferencesUnknownTable,bo as privilegedDispatchUnvalidatedPayload,yo as privilegedFanoutFromPublicProcedure,Uo as procedureWithoutStructuredEvent,Io as publicArgumentUsesAny,Ao as publicMutationWithoutRatelimit,Ro as publicTableRlsOptoutConfusion,To as queueWithoutDlq,Wo as r2sqlOutsideAction,vo as ratelimitDefaultMemoryStore,ko as ratelimitKeySpoofableOrGlobal,wo as ratelimitMiddlewareFailOpen,xo as relationReferencesUnknownField,Fo as relationReferencesUnknownTable,Co as rlsUncoveredTable,Jt as runAdvisor,se as scoreAdvisor,Mo as shapeTargetsGlobalTable,Po as shapeUnknownTable,Do as signupMutationWithoutDisposableGating,No as softDeleteIncludeDeletedFromArgs,Oo as sqlInjectionRisk,Eo as storageGenerateUploadUrlNoContentTypePin,Go as storageKeyFromUserArgs,Ko as storagePresignedUrlForPrivateContent,Lo as storageUploadWithoutContentTypeAllowlist,_o as storageUploadWithoutMaxSize,jo as tableWithoutInsert,qo as ttlFieldNotTimestamp,zo as unboundedStringArgument,Ho as unindexedForeignKey,Vo as unindexedRelationTarget,Bo as unrestrictedWhereBranch,Qo as userCreatingMutationWithoutCaptcha,Zo as vectorsNamespaceFromUserInput,Jo as workflowDuplicateStepName,Xo as workflowUnknownTarget,Yo 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
|
|
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.
|
|
3
|
+
"version": "1.0.0-alpha.69",
|
|
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.
|
|
50
|
-
"@lunora/server": "1.0.0-alpha.
|
|
49
|
+
"@lunora/errors": "1.0.0-alpha.16",
|
|
50
|
+
"@lunora/server": "1.0.0-alpha.64"
|
|
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};
|