@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 +37 -21
- package/dist/index.d.ts +37 -21
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/MAP_VERSION-Bnr7lVVy.mjs +1 -0
- package/dist/packem_shared/compareToBaseline-Dfc50M_I.mjs +1 -0
- package/dist/packem_shared/flagGatesSecurityWithUnsafeDefault-CcOU6_tc.mjs +1 -0
- package/dist/packem_shared/kvUnscopedUserKeyIdor-BbKx1xPL.mjs +1 -0
- package/dist/packem_shared/{workflowUnused-BUSOPdHq.mjs → workflowUnused-Cog-2sf5.mjs} +1 -1
- package/package.json +3 -3
- package/dist/packem_shared/MAP_VERSION-DEJGm0wI.mjs +0 -1
- package/dist/packem_shared/compareToBaseline-DBgN5YqX.mjs +0 -1
- package/dist/packem_shared/flagGatesSecurityWithUnsafeDefault-20ccteHn.mjs +0 -1
- package/dist/packem_shared/kvUnscopedUserKeyIdor-CIgMPzFj.mjs +0 -1
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
|
|
1167
|
-
* the read
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1172
|
-
*
|
|
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
|
|
2124
|
-
*
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
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
|
|
2737
|
-
*
|
|
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
|
|
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
|
|
4273
|
-
*
|
|
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
|
|
4458
|
-
* would otherwise crash the gate, and a row of `{}` would
|
|
4459
|
-
* no-op. Non-finite scores are rejected for the same
|
|
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
|
|
1167
|
-
* the read
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1172
|
-
*
|
|
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
|
|
2124
|
-
*
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
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
|
|
2737
|
-
*
|
|
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
|
|
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
|
|
4273
|
-
*
|
|
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
|
|
4458
|
-
* would otherwise crash the gate, and a row of `{}` would
|
|
4459
|
-
* no-op. Non-finite scores are rejected for the same
|
|
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
|
|
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.
|
|
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.
|
|
50
|
-
"@lunora/server": "1.0.0-alpha.
|
|
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};
|