@lunora/advisor 1.0.0-alpha.107 → 1.0.0-alpha.109

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 CHANGED
@@ -620,6 +620,15 @@ interface AdvisorKvKeyAccess {
620
620
  line: number;
621
621
  /** The `ctx.kv` method invoked: `get` / `getRaw` / `getWithMetadata` / `put` / `delete`. */
622
622
  method: string;
623
+ /**
624
+ * Visibility of the enclosing procedure. `internal` procedures are not
625
+ * reachable by a caller, so the "any caller can read/overwrite/delete
626
+ * another user's entry" premise does not hold there and the finding drops
627
+ * to `INFO` (mirrors `AdvisorStorageKeyAccess.visibility` and
628
+ * `AdvisorOwnerFieldWrite.visibility`). `undefined` when the feeder could
629
+ * not attribute the access to a registered procedure.
630
+ */
631
+ visibility?: "internal" | "public";
623
632
  }
624
633
  /**
625
634
  * One `ctx.mail`/`ctx.email` `send`/`queue` call whose recipient field
@@ -1163,13 +1172,17 @@ interface AdvisorRawRowReturn {
1163
1172
  /**
1164
1173
  * One `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list read
1165
1174
  * — the shared input for the `masked_relation_leak_via_with` lint. Column
1166
- * masking is applied per-procedure to the top-level rows of the table named in
1167
- * the read; it does **not** descend into `with`-hydrated relations, so a masked
1168
- * table surfaced only through a `with` on an unprotected parent read is returned
1169
- * in the clear. The lint resolves each relation accessor to its target table and
1170
- * joins it against the discovered mask evidence before flagging. Produced by the
1171
- * codegen feeder; runtime callers don't supply it, so the lint finds nothing
1172
- * there. Structurally identical to `@lunora/codegen`'s `RelationLoadIR`.
1175
+ * masking is **per-procedure**: `.use(mask(policies))` installs a `relationMask`
1176
+ * hook on the read's args (`@lunora/server`'s `mask/middleware`) and the relation
1177
+ * loader applies it to the target table of every hop, at every nesting depth
1178
+ * (`@lunora/shard-engine`'s `relations`), so a procedure that masks a table gets
1179
+ * it masked through `with` too. What leaks is a read whose OWN procedure declares
1180
+ * no policy for the related table a mask declared on that table's other
1181
+ * procedures does not carry over. The lint resolves each relation accessor to its
1182
+ * target table and joins it against the discovered mask evidence before flagging.
1183
+ * Produced by the codegen feeder; runtime callers don't supply it, so the lint
1184
+ * finds nothing there. Structurally identical to `@lunora/codegen`'s
1185
+ * `RelationLoadIR`.
1173
1186
  */
1174
1187
  interface AdvisorRelationLoad {
1175
1188
  /** The exported binding name of the procedure performing the read. */
@@ -2120,11 +2133,11 @@ interface LintContext {
2120
2133
  rawRowReturns?: ReadonlyArray<AdvisorRawRowReturn>;
2121
2134
  /**
2122
2135
  * `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list reads
2123
- * — the `masked_relation_leak_via_with` input. Column masking is applied to a
2124
- * read's top-level rows but does not descend into `with`-hydrated relations,
2125
- * so a masked table surfaced only through a `with` on an unprotected public
2126
- * read is returned in the clear. Supplied by the codegen feeder; absent for
2127
- * runtime callers, where the lint finds nothing.
2136
+ * — the `masked_relation_leak_via_with` input. Column masking is
2137
+ * per-procedure and the relation loader applies the READING procedure's
2138
+ * policy to every `with` hop, so what leaks is a public read whose own
2139
+ * procedure declares no policy for the related table. Supplied by the codegen
2140
+ * feeder; absent for runtime callers, where the lint finds nothing.
2128
2141
  */
2129
2142
  relationLoads?: ReadonlyArray<AdvisorRelationLoad>;
2130
2143
  /**
@@ -2733,8 +2746,11 @@ declare const browserUserUrlWithoutAllowlist: Lint;
2733
2746
  * blowup on acyclic input. Each circuit is enumerated exactly once from its
2734
2747
  * lowest-indexed member (vertices are ordered lexicographically), so overlapping
2735
2748
  * / chord cycles that share interior nodes are each detected independently. The
2736
- * emitted cycle is still canonicalized to its lexicographically smallest
2737
- * rotation for a stable cacheKey.
2749
+ * emitted cycle is then canonicalized to its lexicographically smallest rotation
2750
+ * for a stable cacheKey: the search order is locale-collated (`localeCompare`)
2751
+ * while the rotation compares by codepoint, and the two disagree on mixed-case
2752
+ * table names — so the rotation, not the start vertex, is what pins the cacheKey
2753
+ * across ICU builds.
2738
2754
  */
2739
2755
  declare const circularFk: Lint;
2740
2756
  /**
@@ -2957,7 +2973,7 @@ declare const filterWithoutIndex: Lint;
2957
2973
  * a flag-backend outage silently disables the protection or grants the
2958
2974
  * permission for every request. A negating token in the key
2959
2975
  * (`disable*`/`*Disabled`/`skip*`/`no*`) inverts that — see
2960
- * {@link safeDefaultFor}.
2976
+ * {@link polarityOf}.
2961
2977
  *
2962
2978
  * Runs only when the codegen feeder supplies flag-default evidence
2963
2979
  * (`context.flagSecurityDefaults`); a runtime caller flags nothing. Deliberately
@@ -4269,8 +4285,8 @@ declare const workflowUnknownTarget: Lint;
4269
4285
  * Suppressed entirely when any call uses a non-literal name
4270
4286
  * (`ctx.workflows.get(someVariable)`), because a dynamic dispatch could target
4271
4287
  * any declared workflow — flagging "unused" workflows then would be a false
4272
- * positive. Only runs when the declaration feeder supplied evidence
4273
- * (`context.workflows` present); a runtime caller flags nothing.
4288
+ * positive. Only runs when BOTH feeders supplied evidence (`context.workflows`
4289
+ * and `context.workflowCalls` present); a runtime caller flags nothing.
4274
4290
  */
4275
4291
  declare const workflowUnused: Lint;
4276
4292
  /** Letter band for a 0–100 score. */
@@ -4454,10 +4470,10 @@ declare const compareToBaseline: (current: AdvisorMap, baseline: AdvisorMap) =>
4454
4470
  *
4455
4471
  * Validates *shape* — the header and every procedure row — because
4456
4472
  * `compareToBaseline` dereferences `entry.id` / `entry.score` /
4457
- * `entry.coverage`: a truncated or merge-conflicted baseline with a `null` row
4458
- * would otherwise crash the gate, and a row of `{}` would compare as a silent
4459
- * no-op. Non-finite scores are rejected for the same reason — `NaN < 0` is
4460
- * `false`, which reads as "no regression".
4473
+ * `entry.coverage` / `entry.checks`: a truncated or merge-conflicted baseline
4474
+ * with a `null` row would otherwise crash the gate, and a row of `{}` would
4475
+ * compare as a silent no-op. Non-finite scores are rejected for the same
4476
+ * reason — `NaN < 0` is `false`, which reads as "no regression".
4461
4477
  *
4462
4478
  * Version *policy* deliberately lives in {@link compareToBaseline}, not here.
4463
4479
  * Rejecting a mismatch in both places made that function's `comparable: false`
package/dist/index.d.ts CHANGED
@@ -620,6 +620,15 @@ interface AdvisorKvKeyAccess {
620
620
  line: number;
621
621
  /** The `ctx.kv` method invoked: `get` / `getRaw` / `getWithMetadata` / `put` / `delete`. */
622
622
  method: string;
623
+ /**
624
+ * Visibility of the enclosing procedure. `internal` procedures are not
625
+ * reachable by a caller, so the "any caller can read/overwrite/delete
626
+ * another user's entry" premise does not hold there and the finding drops
627
+ * to `INFO` (mirrors `AdvisorStorageKeyAccess.visibility` and
628
+ * `AdvisorOwnerFieldWrite.visibility`). `undefined` when the feeder could
629
+ * not attribute the access to a registered procedure.
630
+ */
631
+ visibility?: "internal" | "public";
623
632
  }
624
633
  /**
625
634
  * One `ctx.mail`/`ctx.email` `send`/`queue` call whose recipient field
@@ -1163,13 +1172,17 @@ interface AdvisorRawRowReturn {
1163
1172
  /**
1164
1173
  * One `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list read
1165
1174
  * — the shared input for the `masked_relation_leak_via_with` lint. Column
1166
- * masking is applied per-procedure to the top-level rows of the table named in
1167
- * the read; it does **not** descend into `with`-hydrated relations, so a masked
1168
- * table surfaced only through a `with` on an unprotected parent read is returned
1169
- * in the clear. The lint resolves each relation accessor to its target table and
1170
- * joins it against the discovered mask evidence before flagging. Produced by the
1171
- * codegen feeder; runtime callers don't supply it, so the lint finds nothing
1172
- * there. Structurally identical to `@lunora/codegen`'s `RelationLoadIR`.
1175
+ * masking is **per-procedure**: `.use(mask(policies))` installs a `relationMask`
1176
+ * hook on the read's args (`@lunora/server`'s `mask/middleware`) and the relation
1177
+ * loader applies it to the target table of every hop, at every nesting depth
1178
+ * (`@lunora/shard-engine`'s `relations`), so a procedure that masks a table gets
1179
+ * it masked through `with` too. What leaks is a read whose OWN procedure declares
1180
+ * no policy for the related table a mask declared on that table's other
1181
+ * procedures does not carry over. The lint resolves each relation accessor to its
1182
+ * target table and joins it against the discovered mask evidence before flagging.
1183
+ * Produced by the codegen feeder; runtime callers don't supply it, so the lint
1184
+ * finds nothing there. Structurally identical to `@lunora/codegen`'s
1185
+ * `RelationLoadIR`.
1173
1186
  */
1174
1187
  interface AdvisorRelationLoad {
1175
1188
  /** The exported binding name of the procedure performing the read. */
@@ -2120,11 +2133,11 @@ interface LintContext {
2120
2133
  rawRowReturns?: ReadonlyArray<AdvisorRawRowReturn>;
2121
2134
  /**
2122
2135
  * `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list reads
2123
- * — the `masked_relation_leak_via_with` input. Column masking is applied to a
2124
- * read's top-level rows but does not descend into `with`-hydrated relations,
2125
- * so a masked table surfaced only through a `with` on an unprotected public
2126
- * read is returned in the clear. Supplied by the codegen feeder; absent for
2127
- * runtime callers, where the lint finds nothing.
2136
+ * — the `masked_relation_leak_via_with` input. Column masking is
2137
+ * per-procedure and the relation loader applies the READING procedure's
2138
+ * policy to every `with` hop, so what leaks is a public read whose own
2139
+ * procedure declares no policy for the related table. Supplied by the codegen
2140
+ * feeder; absent for runtime callers, where the lint finds nothing.
2128
2141
  */
2129
2142
  relationLoads?: ReadonlyArray<AdvisorRelationLoad>;
2130
2143
  /**
@@ -2733,8 +2746,11 @@ declare const browserUserUrlWithoutAllowlist: Lint;
2733
2746
  * blowup on acyclic input. Each circuit is enumerated exactly once from its
2734
2747
  * lowest-indexed member (vertices are ordered lexicographically), so overlapping
2735
2748
  * / chord cycles that share interior nodes are each detected independently. The
2736
- * emitted cycle is still canonicalized to its lexicographically smallest
2737
- * rotation for a stable cacheKey.
2749
+ * emitted cycle is then canonicalized to its lexicographically smallest rotation
2750
+ * for a stable cacheKey: the search order is locale-collated (`localeCompare`)
2751
+ * while the rotation compares by codepoint, and the two disagree on mixed-case
2752
+ * table names — so the rotation, not the start vertex, is what pins the cacheKey
2753
+ * across ICU builds.
2738
2754
  */
2739
2755
  declare const circularFk: Lint;
2740
2756
  /**
@@ -2957,7 +2973,7 @@ declare const filterWithoutIndex: Lint;
2957
2973
  * a flag-backend outage silently disables the protection or grants the
2958
2974
  * permission for every request. A negating token in the key
2959
2975
  * (`disable*`/`*Disabled`/`skip*`/`no*`) inverts that — see
2960
- * {@link safeDefaultFor}.
2976
+ * {@link polarityOf}.
2961
2977
  *
2962
2978
  * Runs only when the codegen feeder supplies flag-default evidence
2963
2979
  * (`context.flagSecurityDefaults`); a runtime caller flags nothing. Deliberately
@@ -4269,8 +4285,8 @@ declare const workflowUnknownTarget: Lint;
4269
4285
  * Suppressed entirely when any call uses a non-literal name
4270
4286
  * (`ctx.workflows.get(someVariable)`), because a dynamic dispatch could target
4271
4287
  * any declared workflow — flagging "unused" workflows then would be a false
4272
- * positive. Only runs when the declaration feeder supplied evidence
4273
- * (`context.workflows` present); a runtime caller flags nothing.
4288
+ * positive. Only runs when BOTH feeders supplied evidence (`context.workflows`
4289
+ * and `context.workflowCalls` present); a runtime caller flags nothing.
4274
4290
  */
4275
4291
  declare const workflowUnused: Lint;
4276
4292
  /** Letter band for a 0–100 score. */
@@ -4454,10 +4470,10 @@ declare const compareToBaseline: (current: AdvisorMap, baseline: AdvisorMap) =>
4454
4470
  *
4455
4471
  * Validates *shape* — the header and every procedure row — because
4456
4472
  * `compareToBaseline` dereferences `entry.id` / `entry.score` /
4457
- * `entry.coverage`: a truncated or merge-conflicted baseline with a `null` row
4458
- * would otherwise crash the gate, and a row of `{}` would compare as a silent
4459
- * no-op. Non-finite scores are rejected for the same reason — `NaN < 0` is
4460
- * `false`, which reads as "no regression".
4473
+ * `entry.coverage` / `entry.checks`: a truncated or merge-conflicted baseline
4474
+ * with a `null` row would otherwise crash the gate, and a row of `{}` would
4475
+ * compare as a silent no-op. Non-finite scores are rejected for the same
4476
+ * reason — `NaN < 0` is `false`, which reads as "no regression".
4461
4477
  *
4462
4478
  * Version *policy* deliberately lives in {@link compareToBaseline}, not here.
4463
4479
  * Rejecting a mismatch in both places made that function's `comparable: false`
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-DtBOHffV.mjs";import d from"./packem_shared/fanOutBreadth-CBtmZnoh.mjs";import u from"./packem_shared/hotShard-BwGYZ3Tq.mjs";import h from"./packem_shared/indexUtilization-CkVPZcVe.mjs";import g from"./packem_shared/actionFetchSsrf-Z61P0o8U.mjs";import b from"./packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs";import y from"./packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs";import w from"./packem_shared/aiRawRunEscapeHatch-Dq43DVD9.mjs";import S from"./packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs";import v from"./packem_shared/aiToolSideEffectPromptInjection-K42X3QzJ.mjs";import A from"./packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs";import U from"./packem_shared/allowUnauthenticatedShardAccessEnabled-BQufF5Kb.mjs";import R from"./packem_shared/authApiCallWithoutHeaders-C1OOWML5.mjs";import W from"./packem_shared/authCsrfCheckDisabled-Dz27bDzp.mjs";import T from"./packem_shared/authEmailVerificationDisabled-QTDD7TAU.mjs";import I from"./packem_shared/authScimWithoutTransactions-FqdTmUJs.mjs";import N from"./packem_shared/authSecureCookiesDisabled-CrGYulfJ.mjs";import k from"./packem_shared/authSessionFreshageZero-yWE6CGYP.mjs";import x from"./packem_shared/authTrustedOriginsWildcard-xpeRXfGF.mjs";import F from"./packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs";import C from"./packem_shared/browserUserUrlWithoutAllowlist-Wl3xHr7v.mjs";import O from"./packem_shared/circularFk-DtcWFJxK.mjs";import M from"./packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs";import $ from"./packem_shared/containerInstanceKeyFromUserInput-uEQQVsEz.mjs";import D from"./packem_shared/containerOversizedInstance-Bx89uR7E.mjs";import E from"./packem_shared/containerPublicInternet-BFfZf_P4.mjs";import P from"./packem_shared/containerRuntimeEgressRelaxation-pkpyXNou.mjs";import K from"./packem_shared/containerStartEnableInternetOverride-BBY4PMG1.mjs";import q from"./packem_shared/duplicateIndex-Cip6-Rpu.mjs";import L from"./packem_shared/emptyIndex-BnHDcXza.mjs";import G from"./packem_shared/errorWithoutCatalog-BTfvaXHR.mjs";import _ from"./packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs";import z from"./packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs";import H from"./packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs";import B from"./packem_shared/externalSourceUnscoped-BxU2uSXk.mjs";import j from"./packem_shared/filterOnPrimaryKey-jmz_ApDa.mjs";import V from"./packem_shared/filterWithoutIndex-9kAguEVb.mjs";import Q from"./packem_shared/flagGatesSecurityWithUnsafeDefault-20ccteHn.mjs";import X from"./packem_shared/flagReadInSubscription-DabIhYGD.mjs";import Z from"./packem_shared/geoIndexFieldNotGeopoint-Cd5mywEg.mjs";import J from"./packem_shared/geoIndexUnused-D7C9Qr4U.mjs";import Y from"./packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs";import oo from"./packem_shared/hardcodedSecret-Bw_4FYrs.mjs";import eo from"./packem_shared/httpActionMissingAuthGuard-BS3JZgaz.mjs";import ro from"./packem_shared/httpActionResponseHeaderInjection-DHnc8c9f.mjs";import to from"./packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs";import io from"./packem_shared/identityUndeclaredClaimTrusted-BDFqB7Dw.mjs";import no from"./packem_shared/imagesUrlSourceFromUserInput-DizsRh3M.mjs";import ao from"./packem_shared/indexReferencesUnknownField-DiC2WN4V.mjs";import mo from"./packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs";import so from"./packem_shared/kvUnscopedUserKeyIdor-CIgMPzFj.mjs";import lo from"./packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs";import po from"./packem_shared/mailRecipientFromRequestInput-SDCchLP7.mjs";import fo from"./packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs";import co from"./packem_shared/maskWeakHashStrategyOnPii-DXhCr75r.mjs";import uo from"./packem_shared/maskedRelationLeakViaWith-CIFij6vE.mjs";import{e as a}from"./packem_shared/finding-NrKO8idM.mjs";import ho from"./packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs";import go from"./packem_shared/nondeterministicQueryMutation-sxmixc0l.mjs";import bo from"./packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs";import yo from"./packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs";import wo from"./packem_shared/notifySendOutsideAction-CWx7KuJS.mjs";import So from"./packem_shared/outputProjectionMissingOnPublicRead-HwweA4NI.mjs";import vo from"./packem_shared/ownerFieldFromArgsNotAuth-CjOSUDKi.mjs";import Ao from"./packem_shared/paymentCreateWithoutAuthorize-CICGmRFR.mjs";import Uo from"./packem_shared/paymentWebhookWideTolerance-B-F5jOeA.mjs";import Ro from"./packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs";import Wo from"./packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs";import To from"./packem_shared/privilegedDispatchUnvalidatedPayload-C6Qtc8aT.mjs";import Io from"./packem_shared/privilegedFanoutFromPublicProcedure-Coqt23CQ.mjs";import No from"./packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs";import ko from"./packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs";import xo from"./packem_shared/publicMutationWithoutRatelimit-cDJph2zk.mjs";import Fo from"./packem_shared/publicTableRlsOptoutConfusion-B4zbtE9m.mjs";import Co from"./packem_shared/queueWithoutDlq-BvHz3Opg.mjs";import Oo from"./packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs";import Mo from"./packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs";import $o from"./packem_shared/ratelimitKeySpoofableOrGlobal-BzDzxnXm.mjs";import Do from"./packem_shared/ratelimitMiddlewareFailOpen-eOeyAyC-.mjs";import Eo from"./packem_shared/relationReferencesUnknownField-GpJPLGDE.mjs";import Po from"./packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs";import Ko from"./packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs";import qo from"./packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs";import Lo from"./packem_shared/shapeUnknownTable-CREfNnWi.mjs";import Go from"./packem_shared/signupMutationWithoutDisposableGating-BMM2vTNu.mjs";import _o from"./packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs";import zo from"./packem_shared/sqlInjectionRisk-CslaRBxz.mjs";import Ho from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-DBq_DeHO.mjs";import Bo from"./packem_shared/storageKeyFromUserArgs-C-QUcdJ3.mjs";import jo from"./packem_shared/storagePresignedUrlForPrivateContent-DZdUkDdW.mjs";import Vo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-KSRG81Iu.mjs";import Qo from"./packem_shared/storageUploadWithoutMaxSize-BTiM9Q3A.mjs";import Xo from"./packem_shared/tableWithoutInsert-DQ-GxjFF.mjs";import Zo from"./packem_shared/ttlFieldNotTimestamp-CixnZ0Ii.mjs";import{s as Jo,q as Yo}from"./packem_shared/helpers-BCqZbKga.mjs";import oe from"./packem_shared/unboundedStringArgument-jZnCbwB0.mjs";import ee from"./packem_shared/unindexedForeignKey-Dypgn8uH.mjs";import re from"./packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs";import te from"./packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs";import ie from"./packem_shared/userCreatingMutationWithoutCaptcha-CGpMm45R.mjs";import ne from"./packem_shared/vectorsNamespaceFromUserInput-CDSOrkyX.mjs";import ae from"./packem_shared/workflowDuplicateStepName-BU4rg5So.mjs";import me from"./packem_shared/workflowUnknownTarget-B8H7jwnH.mjs";import se from"./packem_shared/workflowUnused-BUSOPdHq.mjs";import{compareToBaseline as ut,parseAdvisorMap as ht}from"./packem_shared/compareToBaseline-DBgN5YqX.mjs";import{gradeFromScore as bt}from"./packem_shared/gradeFromScore-KSt58rj1.mjs";import{MAP_VERSION as wt,byCodepoint as St,scoreAdvisor as vt}from"./packem_shared/MAP_VERSION-DEJGm0wI.mjs";import{default as Ut}from"./packem_shared/classifySensitivity-JnjaTGYi.mjs";import{fromServerSchema as Wt}from"./packem_shared/fromServerSchema-fAymN8o2.mjs";const le={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},m={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>a(m,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${le[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},pe=new Map([["global",{level:"WARN",scope:r=>`it reads the whole D1 table "${r}" over a cross-region round trip`}],["root",{level:"WARN",scope:r=>`it loads every row of "${r}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:r=>`"${r}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),fe={level:"WARN",scope:r=>`it loads every row of "${r}"`},s={categories:["PERFORMANCE"],description:"A query calls `.collect()` with no `.withIndex()` and no `.filter()`, so it materializes every row of the table. Any live subscription over it also re-sends that whole result to every subscribed client on every write to the table.",facing:"EXTERNAL",level:"WARN",name:"unbounded_collect",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))`, cap it with `.take(n)`, or page it with `.paginate(args.paginationOpts)` so neither the scan nor the subscription payload grows with the table.',run:r=>{const o=[],i=Jo(r.schema);for(const e of r.queries??[]){if(e.terminal!=="collect"||e.hasIndex||e.table===""||e.hasFilter)continue;const t=i.get(e.table),{level:n,scope:l}=pe.get(t??"")??fe,p=Yo(e),f=n==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${e.table}" re-runs it and re-sends the full result to each subscribed socket.`:" Cap it with `.take(n)` if that count can grow.";o.push(a(s,{cacheKey:`unbounded_collect:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${p} calls .collect() with no index and no filter — ${l(e.table)}.${f}`,level:n,metadata:{exportName:e.exportName,file:e.file,line:e.line,shardKind:t??"unknown",table:e.table}}))}return o},source:"static",title:"Unbounded collect"},ce=[ao,Po,Eo,me,ae,Lo,z,H,B,L,Y,G,Z,J,_,Zo,M,O,ee,re,q,Xo,se,Co,j,V,s,qo,ho,go,to,Oo,R,Wo,Ko,fo,co,D,E,xo,te,ie,Go,ko,oe,oo,zo,y,Ao,lo,Mo,F,Io,No,mo,A,g,b,vo,Bo,so,$,w,S,ne,po,C,To,K,P,I,x,W,N,T,k,no,$o,Fo,U,Vo,Qo,Ho,jo,eo,ro,Do,Q,X,v,io,Uo,_o,uo,So,bo,wo,yo,Ro,m],de=[u,h,d],ue=[...ce,...de],ft=(r,o={})=>{const i=o.lints??ue,e=[];for(const t of i)o.source!==void 0&&t.source!==o.source||e.push(...t.run(r));return c(e)};export{ue as ALL_LINTS,wt as MAP_VERSION,de as RUNTIME_LINTS,ce as STATIC_LINTS,g as actionFetchSsrf,b as actionWithoutErrorHandling,y as adminRouteWithoutGuard,w as aiRawRunEscapeHatch,S as aiRunWithoutLogging,v as aiToolSideEffectPromptInjection,A as aiUnboundedGenerationPublic,U as allowUnauthenticatedShardAccessEnabled,R as authApiCallWithoutHeaders,W as authCsrfCheckDisabled,T as authEmailVerificationDisabled,I as authScimWithoutTransactions,N as authSecureCookiesDisabled,k as authSessionFreshageZero,x as authTrustedOriginsWildcard,F as browserAllowPrivateTargets,C as browserUserUrlWithoutAllowlist,St as byCodepoint,O as circularFk,Ut as classifySensitivity,M as commitOrderedHardDelete,ut as compareToBaseline,$ as containerInstanceKeyFromUserInput,D as containerOversizedInstance,E as containerPublicInternet,P as containerRuntimeEgressRelaxation,K as containerStartEnableInternetOverride,c as dedupeCacheKeys,q as duplicateIndex,L as emptyIndex,G as errorWithoutCatalog,_ as exportSinkMisconfigured,z as externalSourceIncrementalNoDeletePath,H as externalSourceOnGlobal,B as externalSourceUnscoped,d as fanOutBreadth,j as filterOnPrimaryKey,V as filterWithoutIndex,Q as flagGatesSecurityWithUnsafeDefault,X as flagReadInSubscription,Wt as fromServerSchema,Z as geoIndexFieldNotGeopoint,J as geoIndexUnused,Y as globalTableNearColumnLimit,bt as gradeFromScore,oo as hardcodedSecret,u as hotShard,eo as httpActionMissingAuthGuard,ro as httpActionResponseHeaderInjection,to as hyperdriveOutsideAction,io as identityUndeclaredClaimTrusted,no as imagesUrlSourceFromUserInput,ao as indexReferencesUnknownField,h as indexUtilization,mo as insertManyUnsafeUserData,so as kvUnscopedUserKeyIdor,lo as mailInboundDispatchWithoutVerify,po as mailRecipientFromRequestInput,fo as maskUncoveredPiiColumn,co as maskWeakHashStrategyOnPii,uo as maskedRelationLeakViaWith,ho as mutatorFullRowReplace,go as nondeterministicQueryMutation,bo as normalizeIdUsedAsAuthorization,yo as notifyMissingPushConfig,wo as notifySendOutsideAction,So as outputProjectionMissingOnPublicRead,vo as ownerFieldFromArgsNotAuth,ht as parseAdvisorMap,Ao as paymentCreateWithoutAuthorize,Uo as paymentWebhookWideTolerance,Ro as plaintextSecretInWranglerVariables,Wo as policyReferencesUnknownTable,To as privilegedDispatchUnvalidatedPayload,Io as privilegedFanoutFromPublicProcedure,No as procedureWithoutStructuredEvent,ko as publicArgumentUsesAny,xo as publicMutationWithoutRatelimit,Fo as publicTableRlsOptoutConfusion,Co as queueWithoutDlq,Oo as r2sqlOutsideAction,Mo as ratelimitDefaultMemoryStore,$o as ratelimitKeySpoofableOrGlobal,Do as ratelimitMiddlewareFailOpen,Eo as relationReferencesUnknownField,Po as relationReferencesUnknownTable,Ko as rlsUncoveredTable,ft as runAdvisor,vt as scoreAdvisor,qo as shapeTargetsGlobalTable,Lo as shapeUnknownTable,Go as signupMutationWithoutDisposableGating,_o as softDeleteIncludeDeletedFromArgs,zo as sqlInjectionRisk,Ho as storageGenerateUploadUrlNoContentTypePin,Bo as storageKeyFromUserArgs,jo as storagePresignedUrlForPrivateContent,Vo as storageUploadWithoutContentTypeAllowlist,Qo as storageUploadWithoutMaxSize,Xo as tableWithoutInsert,Zo as ttlFieldNotTimestamp,oe as unboundedStringArgument,ee as unindexedForeignKey,re as unindexedRelationTarget,te as unrestrictedWhereBranch,ie as userCreatingMutationWithoutCaptcha,ne as vectorsNamespaceFromUserInput,ae as workflowDuplicateStepName,me as workflowUnknownTarget,se as workflowUnused};
1
+ import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-DtBOHffV.mjs";import d from"./packem_shared/fanOutBreadth-CBtmZnoh.mjs";import u from"./packem_shared/hotShard-BwGYZ3Tq.mjs";import h from"./packem_shared/indexUtilization-CkVPZcVe.mjs";import g from"./packem_shared/actionFetchSsrf-Z61P0o8U.mjs";import b from"./packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs";import y from"./packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs";import w from"./packem_shared/aiRawRunEscapeHatch-Dq43DVD9.mjs";import S from"./packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs";import v from"./packem_shared/aiToolSideEffectPromptInjection-K42X3QzJ.mjs";import A from"./packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs";import U from"./packem_shared/allowUnauthenticatedShardAccessEnabled-BQufF5Kb.mjs";import R from"./packem_shared/authApiCallWithoutHeaders-C1OOWML5.mjs";import W from"./packem_shared/authCsrfCheckDisabled-Dz27bDzp.mjs";import T from"./packem_shared/authEmailVerificationDisabled-QTDD7TAU.mjs";import I from"./packem_shared/authScimWithoutTransactions-FqdTmUJs.mjs";import N from"./packem_shared/authSecureCookiesDisabled-CrGYulfJ.mjs";import k from"./packem_shared/authSessionFreshageZero-yWE6CGYP.mjs";import x from"./packem_shared/authTrustedOriginsWildcard-xpeRXfGF.mjs";import F from"./packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs";import C from"./packem_shared/browserUserUrlWithoutAllowlist-Wl3xHr7v.mjs";import O from"./packem_shared/circularFk-DtcWFJxK.mjs";import M from"./packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs";import $ from"./packem_shared/containerInstanceKeyFromUserInput-uEQQVsEz.mjs";import D from"./packem_shared/containerOversizedInstance-Bx89uR7E.mjs";import E from"./packem_shared/containerPublicInternet-BFfZf_P4.mjs";import P from"./packem_shared/containerRuntimeEgressRelaxation-pkpyXNou.mjs";import K from"./packem_shared/containerStartEnableInternetOverride-BBY4PMG1.mjs";import q from"./packem_shared/duplicateIndex-Cip6-Rpu.mjs";import L from"./packem_shared/emptyIndex-BnHDcXza.mjs";import G from"./packem_shared/errorWithoutCatalog-BTfvaXHR.mjs";import _ from"./packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs";import z from"./packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs";import H from"./packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs";import B from"./packem_shared/externalSourceUnscoped-BxU2uSXk.mjs";import j from"./packem_shared/filterOnPrimaryKey-jmz_ApDa.mjs";import V from"./packem_shared/filterWithoutIndex-9kAguEVb.mjs";import Q from"./packem_shared/flagGatesSecurityWithUnsafeDefault-CcOU6_tc.mjs";import X from"./packem_shared/flagReadInSubscription-DabIhYGD.mjs";import Z from"./packem_shared/geoIndexFieldNotGeopoint-Cd5mywEg.mjs";import J from"./packem_shared/geoIndexUnused-D7C9Qr4U.mjs";import Y from"./packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs";import oo from"./packem_shared/hardcodedSecret-Bw_4FYrs.mjs";import eo from"./packem_shared/httpActionMissingAuthGuard-BS3JZgaz.mjs";import ro from"./packem_shared/httpActionResponseHeaderInjection-DHnc8c9f.mjs";import to from"./packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs";import io from"./packem_shared/identityUndeclaredClaimTrusted-BDFqB7Dw.mjs";import no from"./packem_shared/imagesUrlSourceFromUserInput-DizsRh3M.mjs";import ao from"./packem_shared/indexReferencesUnknownField-DiC2WN4V.mjs";import mo from"./packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs";import so from"./packem_shared/kvUnscopedUserKeyIdor-BbKx1xPL.mjs";import lo from"./packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs";import po from"./packem_shared/mailRecipientFromRequestInput-SDCchLP7.mjs";import fo from"./packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs";import co from"./packem_shared/maskWeakHashStrategyOnPii-DXhCr75r.mjs";import uo from"./packem_shared/maskedRelationLeakViaWith-CIFij6vE.mjs";import{e as a}from"./packem_shared/finding-NrKO8idM.mjs";import ho from"./packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs";import go from"./packem_shared/nondeterministicQueryMutation-sxmixc0l.mjs";import bo from"./packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs";import yo from"./packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs";import wo from"./packem_shared/notifySendOutsideAction-CWx7KuJS.mjs";import So from"./packem_shared/outputProjectionMissingOnPublicRead-HwweA4NI.mjs";import vo from"./packem_shared/ownerFieldFromArgsNotAuth-CjOSUDKi.mjs";import Ao from"./packem_shared/paymentCreateWithoutAuthorize-CICGmRFR.mjs";import Uo from"./packem_shared/paymentWebhookWideTolerance-B-F5jOeA.mjs";import Ro from"./packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs";import Wo from"./packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs";import To from"./packem_shared/privilegedDispatchUnvalidatedPayload-C6Qtc8aT.mjs";import Io from"./packem_shared/privilegedFanoutFromPublicProcedure-Coqt23CQ.mjs";import No from"./packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs";import ko from"./packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs";import xo from"./packem_shared/publicMutationWithoutRatelimit-cDJph2zk.mjs";import Fo from"./packem_shared/publicTableRlsOptoutConfusion-B4zbtE9m.mjs";import Co from"./packem_shared/queueWithoutDlq-BvHz3Opg.mjs";import Oo from"./packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs";import Mo from"./packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs";import $o from"./packem_shared/ratelimitKeySpoofableOrGlobal-BzDzxnXm.mjs";import Do from"./packem_shared/ratelimitMiddlewareFailOpen-eOeyAyC-.mjs";import Eo from"./packem_shared/relationReferencesUnknownField-GpJPLGDE.mjs";import Po from"./packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs";import Ko from"./packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs";import qo from"./packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs";import Lo from"./packem_shared/shapeUnknownTable-CREfNnWi.mjs";import Go from"./packem_shared/signupMutationWithoutDisposableGating-BMM2vTNu.mjs";import _o from"./packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs";import zo from"./packem_shared/sqlInjectionRisk-CslaRBxz.mjs";import Ho from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-DBq_DeHO.mjs";import Bo from"./packem_shared/storageKeyFromUserArgs-C-QUcdJ3.mjs";import jo from"./packem_shared/storagePresignedUrlForPrivateContent-DZdUkDdW.mjs";import Vo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-KSRG81Iu.mjs";import Qo from"./packem_shared/storageUploadWithoutMaxSize-BTiM9Q3A.mjs";import Xo from"./packem_shared/tableWithoutInsert-DQ-GxjFF.mjs";import Zo from"./packem_shared/ttlFieldNotTimestamp-CixnZ0Ii.mjs";import{s as Jo,q as Yo}from"./packem_shared/helpers-BCqZbKga.mjs";import oe from"./packem_shared/unboundedStringArgument-jZnCbwB0.mjs";import ee from"./packem_shared/unindexedForeignKey-Dypgn8uH.mjs";import re from"./packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs";import te from"./packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs";import ie from"./packem_shared/userCreatingMutationWithoutCaptcha-CGpMm45R.mjs";import ne from"./packem_shared/vectorsNamespaceFromUserInput-CDSOrkyX.mjs";import ae from"./packem_shared/workflowDuplicateStepName-BU4rg5So.mjs";import me from"./packem_shared/workflowUnknownTarget-B8H7jwnH.mjs";import se from"./packem_shared/workflowUnused-Cog-2sf5.mjs";import{compareToBaseline as ut,parseAdvisorMap as ht}from"./packem_shared/compareToBaseline-Dfc50M_I.mjs";import{gradeFromScore as bt}from"./packem_shared/gradeFromScore-KSt58rj1.mjs";import{MAP_VERSION as wt,byCodepoint as St,scoreAdvisor as vt}from"./packem_shared/MAP_VERSION-Bnr7lVVy.mjs";import{default as Ut}from"./packem_shared/classifySensitivity-JnjaTGYi.mjs";import{fromServerSchema as Wt}from"./packem_shared/fromServerSchema-fAymN8o2.mjs";const le={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},m={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>a(m,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${le[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},pe=new Map([["global",{level:"WARN",scope:r=>`it reads the whole D1 table "${r}" over a cross-region round trip`}],["root",{level:"WARN",scope:r=>`it loads every row of "${r}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:r=>`"${r}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),fe={level:"WARN",scope:r=>`it loads every row of "${r}"`},s={categories:["PERFORMANCE"],description:"A query calls `.collect()` with no `.withIndex()` and no `.filter()`, so it materializes every row of the table. Any live subscription over it also re-sends that whole result to every subscribed client on every write to the table.",facing:"EXTERNAL",level:"WARN",name:"unbounded_collect",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))`, cap it with `.take(n)`, or page it with `.paginate(args.paginationOpts)` so neither the scan nor the subscription payload grows with the table.',run:r=>{const o=[],i=Jo(r.schema);for(const e of r.queries??[]){if(e.terminal!=="collect"||e.hasIndex||e.table===""||e.hasFilter)continue;const t=i.get(e.table),{level:n,scope:l}=pe.get(t??"")??fe,p=Yo(e),f=n==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${e.table}" re-runs it and re-sends the full result to each subscribed socket.`:" Cap it with `.take(n)` if that count can grow.";o.push(a(s,{cacheKey:`unbounded_collect:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${p} calls .collect() with no index and no filter — ${l(e.table)}.${f}`,level:n,metadata:{exportName:e.exportName,file:e.file,line:e.line,shardKind:t??"unknown",table:e.table}}))}return o},source:"static",title:"Unbounded collect"},ce=[ao,Po,Eo,me,ae,Lo,z,H,B,L,Y,G,Z,J,_,Zo,M,O,ee,re,q,Xo,se,Co,j,V,s,qo,ho,go,to,Oo,R,Wo,Ko,fo,co,D,E,xo,te,ie,Go,ko,oe,oo,zo,y,Ao,lo,Mo,F,Io,No,mo,A,g,b,vo,Bo,so,$,w,S,ne,po,C,To,K,P,I,x,W,N,T,k,no,$o,Fo,U,Vo,Qo,Ho,jo,eo,ro,Do,Q,X,v,io,Uo,_o,uo,So,bo,wo,yo,Ro,m],de=[u,h,d],ue=[...ce,...de],ft=(r,o={})=>{const i=o.lints??ue,e=[];for(const t of i)o.source!==void 0&&t.source!==o.source||e.push(...t.run(r));return c(e)};export{ue as ALL_LINTS,wt as MAP_VERSION,de as RUNTIME_LINTS,ce as STATIC_LINTS,g as actionFetchSsrf,b as actionWithoutErrorHandling,y as adminRouteWithoutGuard,w as aiRawRunEscapeHatch,S as aiRunWithoutLogging,v as aiToolSideEffectPromptInjection,A as aiUnboundedGenerationPublic,U as allowUnauthenticatedShardAccessEnabled,R as authApiCallWithoutHeaders,W as authCsrfCheckDisabled,T as authEmailVerificationDisabled,I as authScimWithoutTransactions,N as authSecureCookiesDisabled,k as authSessionFreshageZero,x as authTrustedOriginsWildcard,F as browserAllowPrivateTargets,C as browserUserUrlWithoutAllowlist,St as byCodepoint,O as circularFk,Ut as classifySensitivity,M as commitOrderedHardDelete,ut as compareToBaseline,$ as containerInstanceKeyFromUserInput,D as containerOversizedInstance,E as containerPublicInternet,P as containerRuntimeEgressRelaxation,K as containerStartEnableInternetOverride,c as dedupeCacheKeys,q as duplicateIndex,L as emptyIndex,G as errorWithoutCatalog,_ as exportSinkMisconfigured,z as externalSourceIncrementalNoDeletePath,H as externalSourceOnGlobal,B as externalSourceUnscoped,d as fanOutBreadth,j as filterOnPrimaryKey,V as filterWithoutIndex,Q as flagGatesSecurityWithUnsafeDefault,X as flagReadInSubscription,Wt as fromServerSchema,Z as geoIndexFieldNotGeopoint,J as geoIndexUnused,Y as globalTableNearColumnLimit,bt as gradeFromScore,oo as hardcodedSecret,u as hotShard,eo as httpActionMissingAuthGuard,ro as httpActionResponseHeaderInjection,to as hyperdriveOutsideAction,io as identityUndeclaredClaimTrusted,no as imagesUrlSourceFromUserInput,ao as indexReferencesUnknownField,h as indexUtilization,mo as insertManyUnsafeUserData,so as kvUnscopedUserKeyIdor,lo as mailInboundDispatchWithoutVerify,po as mailRecipientFromRequestInput,fo as maskUncoveredPiiColumn,co as maskWeakHashStrategyOnPii,uo as maskedRelationLeakViaWith,ho as mutatorFullRowReplace,go as nondeterministicQueryMutation,bo as normalizeIdUsedAsAuthorization,yo as notifyMissingPushConfig,wo as notifySendOutsideAction,So as outputProjectionMissingOnPublicRead,vo as ownerFieldFromArgsNotAuth,ht as parseAdvisorMap,Ao as paymentCreateWithoutAuthorize,Uo as paymentWebhookWideTolerance,Ro as plaintextSecretInWranglerVariables,Wo as policyReferencesUnknownTable,To as privilegedDispatchUnvalidatedPayload,Io as privilegedFanoutFromPublicProcedure,No as procedureWithoutStructuredEvent,ko as publicArgumentUsesAny,xo as publicMutationWithoutRatelimit,Fo as publicTableRlsOptoutConfusion,Co as queueWithoutDlq,Oo as r2sqlOutsideAction,Mo as ratelimitDefaultMemoryStore,$o as ratelimitKeySpoofableOrGlobal,Do as ratelimitMiddlewareFailOpen,Eo as relationReferencesUnknownField,Po as relationReferencesUnknownTable,Ko as rlsUncoveredTable,ft as runAdvisor,vt as scoreAdvisor,qo as shapeTargetsGlobalTable,Lo as shapeUnknownTable,Go as signupMutationWithoutDisposableGating,_o as softDeleteIncludeDeletedFromArgs,zo as sqlInjectionRisk,Ho as storageGenerateUploadUrlNoContentTypePin,Bo as storageKeyFromUserArgs,jo as storagePresignedUrlForPrivateContent,Vo as storageUploadWithoutContentTypeAllowlist,Qo as storageUploadWithoutMaxSize,Xo as tableWithoutInsert,Zo as ttlFieldNotTimestamp,oe as unboundedStringArgument,ee as unindexedForeignKey,re as unindexedRelationTarget,te as unrestrictedWhereBranch,ie as userCreatingMutationWithoutCaptcha,ne as vectorsNamespaceFromUserInput,ae as workflowDuplicateStepName,me as workflowUnknownTarget,se as workflowUnused};
@@ -0,0 +1 @@
1
+ import{scoreProcedure as h,procedureWeight as S,coverageFromScore as b,scoreGlobal as k,projectWeight as N,gradeFromScore as j,weightFor as M,worstLevel as F}from"./gradeFromScore-KSt58rj1.mjs";import P from"./classifySensitivity-JnjaTGYi.mjs";const y=(e,r)=>e===r?0:e<r?-1:1,w=(e,r)=>y(e.name,r.name),x=(e,r)=>{const o=e[r];return typeof o=="string"?o:void 0},g=(e,r)=>`${e}#${r}`,A=(e,r,o,c)=>e===void 0?{level:r,name:o,occurrences:1,weight:c}:{level:F(e.level,r),name:o,occurrences:e.occurrences+1,weight:Math.max(e.weight,c)},I=(e,r)=>{const o=new Map;for(const t of e)o.set(g(t.file,t.exportName),new Map);const c=new Map;for(const t of r){const s=x(t.metadata,"file"),i=x(t.metadata,"exportName"),l=(s!==void 0&&i!==void 0?o.get(g(s,i)):void 0)??c;l.set(t.name,A(l.get(t.name),t.level,t.name,M(t.level)))}return{byProcedure:new Map([...o].map(([t,s])=>[t,[...s.values()].toSorted(w)])),project:[...c.values()].toSorted(w)}},C=(e,r)=>{const o={clean:0,exempt:0,failing:0,warned:0};let c=r.checks.length;for(const t of e)o[t.coverage]+=1,c+=t.checks.length;return{clean:o.clean,exempt:o.exempt,failing:o.failing,procedures:e.length,rulesFired:c,warned:o.warned}},R=1,O=(e,r,o={})=>{const c=new Set(o.exempt),t=I(e,r),s=e.map(n=>{const a=g(n.file,n.exportName),f=t.byProcedure.get(a)??[],u=h(f),d=c.has(a)||n.exempt===!0,v=P(n);return{checks:f,coverage:d?"exempt":b(u),...d?{exemptReason:n.exemptReason??""}:{},exportName:n.exportName,file:n.file,id:a,kind:n.kind,score:u,sensitivity:v,visibility:n.visibility,weight:d?0:S(n,v.level)}}).toSorted((n,a)=>y(n.id,a.id)),i={checks:t.project,score:h(t.project)},m=s.filter(n=>n.coverage!=="exempt"),l=m.reduce((n,a)=>n+a.weight,0),p=k([...m,{score:i.score,weight:N(l)}]);return{generatedAt:o.generatedAt??new Date().toISOString(),grade:j(p),procedures:s,project:i,score:p,summary:C(s,i),version:R}};export{R as MAP_VERSION,y as byCodepoint,O as scoreAdvisor};
@@ -0,0 +1 @@
1
+ import{byCodepoint as f}from"./MAP_VERSION-Bnr7lVVy.mjs";const m=new Set(["clean","exempt","failing","warned"]),u=(e,r)=>{const c=new Map(e.map(s=>[s.name,s.occurrences]));return r.some(s=>s.occurrences>(c.get(s.name)??0))},n=e=>typeof e=="number"&&Number.isFinite(e),g=e=>Array.isArray(e)&&e.every(r=>{if(typeof r!="object"||r===null)return!1;const c=r;return typeof c.name=="string"&&n(c.occurrences)}),h=e=>{if(typeof e!="object"||e===null)return!1;const r=e;return typeof r.id=="string"&&n(r.score)&&typeof r.coverage=="string"&&m.has(r.coverage)&&g(r.checks)},y=e=>{if(typeof e!="object"||e===null)return!1;const r=e;return g(r.checks)&&n(r.score)},v=(e,r)=>{if(r.version!==e.version)return{comparable:!1,reason:"version-mismatch"};const c=new Map(r.procedures.map(o=>[o.id,o])),s=[],i=[],p=[];for(const o of e.procedures){if(o.coverage==="exempt")continue;const t=c.get(o.id);t!==void 0&&t.coverage!=="exempt"&&o.score<t.score&&s.push({after:o.score,before:t.score,id:o.id}),o.coverage==="failing"&&t?.coverage!=="failing"&&i.push(o.id),u(t?.checks??[],o.checks)&&p.push(o.id)}s.sort((o,t)=>f(o.id,t.id)),i.sort(f),p.sort(f);const a=e.score-r.score,d=u(r.project.checks,e.project.checks);return{comparable:!0,dropped:s,newFailing:i,projectRegressed:d,regressed:a<0||s.length>0||i.length>0||p.length>0||d,scoreDelta:a,worsened:p}},b=e=>{if(typeof e!="object"||e===null)return;const r=e;if(!(!n(r.version)||!n(r.score))&&!(!Array.isArray(r.procedures)||!r.procedures.every(c=>h(c)))&&y(r.project))return e};export{v as compareToBaseline,b as parseAdvisorMap};
@@ -0,0 +1 @@
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const r=new Set(["disallow","disallowed","enforce","enforced","enforcement","enforcing","gate","gated","gating","lockdown","rls"]),o=new Set(["allow","allowed","bypass","bypassed","permit","permitted"]),i=new Set(["disable","disabled","no","off","skip","skipped","without"]),f=/(?<=[A-Z])(?=[A-Z][a-z])/gu,d=/(?<=[a-z0-9])(?=[A-Z])/gu,u=/[^a-z0-9]+/u,c=a=>a.replaceAll(f," ").replaceAll(d," ").toLowerCase().split(u).filter(e=>e.length>0),g=a=>{let e=0;for(;e<a.length&&i.has(a[e]);)e+=1;if(e===a.length)return e;let t=0;for(;t<a.length-e&&i.has(a[a.length-1-t]);)t+=1;return e+t},p=a=>{const e=c(a),t=e.some(s=>r.has(s)),l=e.some(s=>o.has(s));if(t!==l)return{protects:t,safeDefault:g(e)%2===1?!t:t}},h={categories:["SECURITY"],description:"A `ctx.flags.boolean(key, default)` read on a security-shaped key has a fail-open default that selects the permissive branch. OpenFeature returns the default when the provider errors, so an outage silently disables a protection (a `false` default on an `enforce`/`rls`/`gate`/`lockdown` key) or grants a permission (a `true` default on an `allow`/`permit`/`bypass` key). A negating token in the key (`disableRls`, `rlsDisabled`, `skipEnforcement`) inverts which default is the safe one.",facing:"EXTERNAL",level:"WARN",name:"flag_gates_security_with_unsafe_default",remediation:"Flip the default so a provider outage fails closed — the finding's own detail names the value to write. The safe default is always the RESTRICTIVE branch, which is not a fixed value: a protection flag (`enforce*`/`rls*`/`gate*`/`lockdown*`/`disallow*`) defaults `true` and a permission/bypass flag (`allow*`/`permit*`/`bypass*`) defaults `false`, but a negated key (`disableRls`, `rlsDisabled`, `skipEnforcement`) inverts both. Never let an unreachable flag backend open access.",run:a=>a.flagSecurityDefaults===void 0?[]:a.flagSecurityDefaults.flatMap(e=>{const t=p(e.key);if(t===void 0||t.safeDefault===e.defaultValue)return[];const l=t.protects?"disabling the guarded protection":"granting the guarded permission";return[n(h,{cacheKey:`flag_gates_security_with_unsafe_default:${e.file}:${e.line.toString()}`,detail:`\`ctx.flags.boolean("${e.key}", ${String(e.defaultValue)})\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) fails open to the permissive branch — a provider outage returns \`${String(e.defaultValue)}\`, ${l}. Fail closed: default it to \`${String(t.safeDefault)}\`.`,metadata:{defaultValue:e.defaultValue,exportName:e.exportName,file:e.file,key:e.key,line:e.line}})]}),source:"static",title:"Security flag fails open to the permissive branch"};export{h as default};
@@ -0,0 +1 @@
1
+ import{e as n}from"./finding-NrKO8idM.mjs";const o={categories:["SECURITY"],description:"A `ctx.kv` read/write uses a namespace key derived from the handler's `args` with no server-side scoping. Workers KV is a flat namespace, so an unscoped key lets any caller read, overwrite, or delete another user's entry — an insecure direct object reference (IDOR).",facing:"EXTERNAL",level:"ERROR",name:"kv_unscoped_user_key_idor",remediation:"Prefix every `ctx.kv` key with a server-trusted identity (e.g. `${ctx.auth.userId}:${args.id}`) so a caller can only address their own entries. Never pass request input straight to `ctx.kv.get`/`put`/`delete`.",run:r=>r.kvKeyAccesses===void 0?[]:r.kvKeyAccesses.map(e=>{const t=`\`ctx.kv.${e.method}\` in \`${e.exportName}\` (${e.file}:${e.line.toString()})`,i={exportName:e.exportName,file:e.file,line:e.line,method:e.method,visibility:e.visibility??"unknown"};return e.visibility==="internal"?n(o,{cacheKey:`kv_unscoped_user_key_idor:${e.file}:${e.line.toString()}`,detail:`${t} uses a KV key derived from \`args\` with no server-side scoping. This is expected for an \`internal\` procedure — no caller can reach it directly, so the key is only ever supplied by trusted server code. Audit the PUBLIC procedures that dispatch to it: if one forwards \`args\` straight through, the IDOR is there.`,facing:"INTERNAL",level:"INFO",metadata:i}):n(o,{cacheKey:`kv_unscoped_user_key_idor:${e.file}:${e.line.toString()}`,detail:`${t} uses a KV key derived from \`args\` with no server-side scoping — any caller can read/overwrite/delete another user's entry (IDOR). Prefix the key with a server-trusted identity (e.g. \`\${ctx.auth.userId}:…\`).`,metadata:i})}),source:"static",title:"Possible IDOR from arg-derived unscoped KV key"};export{o as default};
@@ -1 +1 @@
1
- import{e as a}from"./finding-NrKO8idM.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};
1
+ import{e as a}from"./finding-NrKO8idM.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||o.workflowCalls===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.107",
3
+ "version": "1.0.0-alpha.109",
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.30",
50
- "@lunora/server": "1.0.0-alpha.101"
49
+ "@lunora/errors": "1.0.0-alpha.31",
50
+ "@lunora/server": "1.0.0-alpha.102"
51
51
  },
52
52
  "engines": {
53
53
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
1
- import{scoreProcedure as h,procedureWeight as S,coverageFromScore as b,scoreGlobal as k,projectWeight as N,gradeFromScore as j,weightFor as M,worstLevel as F}from"./gradeFromScore-KSt58rj1.mjs";import P from"./classifySensitivity-JnjaTGYi.mjs";const y=(e,o)=>e===o?0:e<o?-1:1,w=(e,o)=>y(e.name,o.name),x=(e,o)=>{const r=e[o];return typeof r=="string"?r:void 0},g=(e,o)=>`${e}#${o}`,A=(e,o,r,c)=>e===void 0?{level:o,name:r,occurrences:1,weight:c}:{level:F(e.level,o),name:r,occurrences:e.occurrences+1,weight:Math.max(e.weight,c)},I=(e,o,r)=>{const c=new Map;for(const n of e)c.set(g(n.file,n.exportName),new Map);const s=new Map;for(const n of o){const a=x(n.metadata,"file"),l=x(n.metadata,"exportName"),m=(a!==void 0&&l!==void 0?c.get(g(a,l)):void 0)??s;m.set(n.name,A(m.get(n.name),n.level,n.name,r(n)))}return{byProcedure:new Map([...c].map(([n,a])=>[n,[...a.values()].toSorted(w)])),project:[...s.values()].toSorted(w)}},C=(e,o)=>{const r={clean:0,exempt:0,failing:0,warned:0};let c=o.checks.length;for(const s of e)r[s.coverage]+=1,c+=s.checks.length;return{clean:r.clean,exempt:r.exempt,failing:r.failing,procedures:e.length,rulesFired:c,warned:r.warned}},R=1,O=(e,o,r={})=>{const c=new Set(r.exempt),s=I(e,o,t=>M(t.level)),n=e.map(t=>{const i=g(t.file,t.exportName),f=s.byProcedure.get(i)??[],u=h(f),d=c.has(i)||t.exempt===!0,v=P(t);return{checks:f,coverage:d?"exempt":b(u),...d?{exemptReason:t.exemptReason??""}:{},exportName:t.exportName,file:t.file,id:i,kind:t.kind,score:u,sensitivity:v,visibility:t.visibility,weight:d?0:S(t,v.level)}}).toSorted((t,i)=>y(t.id,i.id)),a={checks:s.project,score:h(s.project)},l=n.filter(t=>t.coverage!=="exempt"),p=l.reduce((t,i)=>t+i.weight,0),m=k([...l,{score:a.score,weight:N(p)}]);return{generatedAt:r.generatedAt??new Date().toISOString(),grade:j(m),procedures:n,project:a,score:m,summary:C(n,a),version:R}};export{R as MAP_VERSION,y as byCodepoint,O as scoreAdvisor};
@@ -1 +0,0 @@
1
- import{byCodepoint as d}from"./MAP_VERSION-DEJGm0wI.mjs";const g=new Set(["clean","exempt","failing","warned"]),u=(e,r)=>{const t=new Map(e.map(s=>[s.name,s.occurrences]));return r.some(s=>s.occurrences>(t.get(s.name)??0))},p=e=>typeof e=="number"&&Number.isFinite(e),m=e=>{if(typeof e!="object"||e===null)return!1;const r=e;return typeof r.id=="string"&&p(r.score)&&typeof r.coverage=="string"&&g.has(r.coverage)},h=e=>{if(typeof e!="object"||e===null)return!1;const r=e;return Array.isArray(r.checks)&&p(r.score)},v=(e,r)=>{if(r.version!==e.version)return{comparable:!1,reason:"version-mismatch"};const t=new Map(r.procedures.map(o=>[o.id,o])),s=[],n=[],i=[];for(const o of e.procedures){if(o.coverage==="exempt")continue;const c=t.get(o.id);c!==void 0&&c.coverage!=="exempt"&&o.score<c.score&&s.push({after:o.score,before:c.score,id:o.id}),o.coverage==="failing"&&c?.coverage!=="failing"&&n.push(o.id),u(c?.checks??[],o.checks)&&i.push(o.id)}s.sort((o,c)=>d(o.id,c.id)),n.sort(d),i.sort(d);const a=e.score-r.score,f=u(r.project.checks,e.project.checks);return{comparable:!0,dropped:s,newFailing:n,projectRegressed:f,regressed:a<0||s.length>0||n.length>0||i.length>0||f,scoreDelta:a,worsened:i}},l=e=>{if(typeof e!="object"||e===null)return;const r=e;if(!(!p(r.version)||!p(r.score))&&!(!Array.isArray(r.procedures)||!r.procedures.every(t=>m(t)))&&h(r.project))return e};export{v as compareToBaseline,l as parseAdvisorMap};
@@ -1 +0,0 @@
1
- import{e as i}from"./finding-NrKO8idM.mjs";const n=new Set(["disallow","disallowed","enforce","enforced","enforcement","enforcing","gate","gated","gating","lockdown","rls"]),o=new Set(["allow","allowed","bypass","bypassed","permit","permitted"]),r=new Set(["disable","disabled","no","off","skip","skipped","without"]),f=/(?<=[A-Z])(?=[A-Z][a-z])/gu,d=/(?<=[a-z0-9])(?=[A-Z])/gu,u=/[^a-z0-9]+/u,c=t=>t.replaceAll(f," ").replaceAll(d," ").toLowerCase().split(u).filter(e=>e.length>0),g=t=>{const e=c(t),a=e.some(s=>n.has(s)),l=e.some(s=>o.has(s));return a===l?void 0:e.filter(s=>r.has(s)).length%2===1?!a:a},p={categories:["SECURITY"],description:"A `ctx.flags.boolean(key, default)` read on a security-shaped key has a fail-open default that selects the permissive branch. OpenFeature returns the default when the provider errors, so an outage silently disables a protection (a `false` default on an `enforce`/`rls`/`gate`/`lockdown` key) or grants a permission (a `true` default on an `allow`/`permit`/`bypass` key). A negating token in the key (`disableRls`, `rlsDisabled`, `skipEnforcement`) inverts which default is the safe one.",facing:"EXTERNAL",level:"WARN",name:"flag_gates_security_with_unsafe_default",remediation:"Flip the default so a provider outage fails closed — the finding's own detail names the value to write. The safe default is always the RESTRICTIVE branch, which is not a fixed value: a protection flag (`enforce*`/`rls*`/`gate*`/`lockdown*`/`disallow*`) defaults `true` and a permission/bypass flag (`allow*`/`permit*`/`bypass*`) defaults `false`, but a negated key (`disableRls`, `rlsDisabled`, `skipEnforcement`) inverts both. Never let an unreachable flag backend open access.",run:t=>t.flagSecurityDefaults===void 0?[]:t.flagSecurityDefaults.filter(e=>{const a=g(e.key);return a!==void 0&&a!==e.defaultValue}).map(e=>i(p,{cacheKey:`flag_gates_security_with_unsafe_default:${e.file}:${e.line.toString()}`,detail:`\`ctx.flags.boolean("${e.key}", ${String(e.defaultValue)})\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) fails open to the permissive branch — a provider outage returns \`${String(e.defaultValue)}\`, ${e.defaultValue?"granting the guarded permission":"disabling the guarded protection"}. Fail closed: default it to \`${String(!e.defaultValue)}\`.`,metadata:{defaultValue:e.defaultValue,exportName:e.exportName,file:e.file,key:e.key,line:e.line}})),source:"static",title:"Security flag fails open to the permissive branch"};export{p as default};
@@ -1 +0,0 @@
1
- import{m as r}from"./argument-derived-sink-DLXFC79t.mjs";const i=r({cacheKey:e=>`kv_unscoped_user_key_idor:${e.file}:${e.line.toString()}`,categories:["SECURITY"],description:"A `ctx.kv` read/write uses a namespace key derived from the handler's `args` with no server-side scoping. Workers KV is a flat namespace, so an unscoped key lets any caller read, overwrite, or delete another user's entry — an insecure direct object reference (IDOR).",detail:e=>`\`ctx.kv.${e.method}\` in \`${e.exportName}\` (${e.file}:${e.line.toString()}) uses a KV key derived from \`args\` with no server-side scoping — any caller can read/overwrite/delete another user's entry (IDOR). Prefix the key with a server-trusted identity (e.g. \`\${ctx.auth.userId}:…\`).`,facing:"EXTERNAL",getAccesses:e=>e.kvKeyAccesses,level:"ERROR",metadata:e=>({exportName:e.exportName,file:e.file,line:e.line,method:e.method}),name:"kv_unscoped_user_key_idor",remediation:"Prefix every `ctx.kv` key with a server-trusted identity (e.g. `${ctx.auth.userId}:${args.id}`) so a caller can only address their own entries. Never pass request input straight to `ctx.kv.get`/`put`/`delete`.",title:"Possible IDOR from arg-derived unscoped KV key"});export{i as default};