@lunora/advisor 1.0.0-alpha.66 → 1.0.0-alpha.67
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 +53 -53
- package/dist/index.d.ts +53 -53
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Schema } from '@lunora/server';
|
|
2
2
|
/**
|
|
3
|
-
* One `httpRoute
|
|
3
|
+
* One `httpRoute.<verb>("/admin/…")` REST route on an admin/privileged-looking
|
|
4
4
|
* path, with whether its handler references an auth/admin guard — the input the
|
|
5
5
|
* `admin_route_without_guard` lint consumes. Produced by the codegen feeder;
|
|
6
6
|
* runtime callers don't supply it, so the lint finds nothing there.
|
|
@@ -147,7 +147,7 @@ interface AdvisorAuthConfig {
|
|
|
147
147
|
trustedOriginsWildcard: boolean;
|
|
148
148
|
}
|
|
149
149
|
/**
|
|
150
|
-
* One `ctx.authApi
|
|
150
|
+
* One `ctx.authApi.<method>(...)` call discovered in a function body — the input
|
|
151
151
|
* the `auth_api_call_without_headers` lint consumes. Produced by the codegen
|
|
152
152
|
* feeder; runtime callers don't supply it, so the lint finds nothing there.
|
|
153
153
|
*/
|
|
@@ -164,7 +164,7 @@ interface AdvisorAuthApiCall {
|
|
|
164
164
|
method: string;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
|
-
* One `ctx.browser
|
|
167
|
+
* One `ctx.browser.<method>(url, …)` call whose navigation URL (`arguments[0]`)
|
|
168
168
|
* is derived from the handler's `args` with no server-side scoping — the input
|
|
169
169
|
* the `browser_user_url_without_allowlist` lint consumes. `@lunora/browser`
|
|
170
170
|
* blocks private/internal targets by default, but a request-supplied *public*
|
|
@@ -216,7 +216,7 @@ interface AdvisorConfigCall {
|
|
|
216
216
|
trueKeys: ReadonlyArray<string>;
|
|
217
217
|
}
|
|
218
218
|
/**
|
|
219
|
-
* One `ctx.containers
|
|
219
|
+
* One `ctx.containers.<exportName>.get(name, …)` call whose instance key is
|
|
220
220
|
* derived from the handler's `args` with no server-side scoping — the input
|
|
221
221
|
* the `container_instance_key_from_user_input` lint consumes. A container
|
|
222
222
|
* definition's `.get(name)` accessor routes to one instance per `name`, so a
|
|
@@ -238,8 +238,8 @@ interface AdvisorContainerKeyAccess {
|
|
|
238
238
|
method: string;
|
|
239
239
|
}
|
|
240
240
|
/**
|
|
241
|
-
* One runtime container-override call: a
|
|
242
|
-
* launch override, or a
|
|
241
|
+
* One runtime container-override call: a `<handle>.start({ enableInternet: true, … })`
|
|
242
|
+
* launch override, or a `<handle>.egress.<method>(...)` runtime firewall mutation
|
|
243
243
|
* (`allow` / `deny` / `setAllowed`) — the `container_start_enable_internet_override`
|
|
244
244
|
* and `container_runtime_egress_relaxation` lint input. Both shapes re-open network
|
|
245
245
|
* access the static `defineContainer` declaration (and its `container_public_internet`
|
|
@@ -333,7 +333,7 @@ interface AdvisorExportSink {
|
|
|
333
333
|
interface AdvisorFailOpenGuard {
|
|
334
334
|
/** The middleware factory at the call site: `rateLimit` / `dbRateLimit` / `verifyTurnstileMiddleware`. */
|
|
335
335
|
callee: string;
|
|
336
|
-
/** The exported binding name of the procedure the guard is attached to, or `"
|
|
336
|
+
/** The exported binding name of the procedure the guard is attached to, or `"<module>"` at file scope. */
|
|
337
337
|
exportName: string;
|
|
338
338
|
/** `true` only when the options literal set `failOpen: true` as a boolean literal; a non-literal or absent option is treated as fail-closed. */
|
|
339
339
|
failOpen: boolean;
|
|
@@ -345,7 +345,7 @@ interface AdvisorFailOpenGuard {
|
|
|
345
345
|
line: number;
|
|
346
346
|
}
|
|
347
347
|
/**
|
|
348
|
-
* One `ctx.flags.boolean("key",
|
|
348
|
+
* One `ctx.flags.boolean("key", <boolean-literal>)` read — the
|
|
349
349
|
* `flag_gates_security_with_unsafe_default` lint input. OpenFeature returns the
|
|
350
350
|
* `defaultValue` when the provider errors, so a fail-open default on a
|
|
351
351
|
* security-shaped key (an `enforce`/`rls`/`gate`/`lockdown` protection
|
|
@@ -358,7 +358,7 @@ interface AdvisorFailOpenGuard {
|
|
|
358
358
|
interface AdvisorFlagSecurityDefault {
|
|
359
359
|
/** The boolean-literal default returned on a provider outage (fail-open value). */
|
|
360
360
|
defaultValue: boolean;
|
|
361
|
-
/** The exported binding name of the procedure performing the flag read, or `"
|
|
361
|
+
/** The exported binding name of the procedure performing the flag read, or `"<module>"` at file scope. */
|
|
362
362
|
exportName: string;
|
|
363
363
|
/** Source file relative to the lunora dir, no extension. */
|
|
364
364
|
file: string;
|
|
@@ -394,7 +394,7 @@ interface AdvisorFunctionMetrics {
|
|
|
394
394
|
errors: number;
|
|
395
395
|
/** Slowest single dispatch, ms, over the same window. */
|
|
396
396
|
maxDurationMs: number;
|
|
397
|
-
/** The function's registered path (
|
|
397
|
+
/** The function's registered path (`<file>:<exportName>`). */
|
|
398
398
|
path: string;
|
|
399
399
|
}
|
|
400
400
|
/**
|
|
@@ -402,7 +402,7 @@ interface AdvisorFunctionMetrics {
|
|
|
402
402
|
* input the `geo_index_unused` lint cross-references against the declared geo
|
|
403
403
|
* indexes in the lint context's schema. Produced by the codegen feeder, which
|
|
404
404
|
* walks the lunora source for `ctx.db.query("t").withGeoIndex(name, …)` /
|
|
405
|
-
* `ctx.db
|
|
405
|
+
* `ctx.db.<table>.withGeoIndex(name, …)` reads and records each referenced index
|
|
406
406
|
* name. Runtime callers don't supply it, so the lint finds nothing there.
|
|
407
407
|
*
|
|
408
408
|
* A `.geoIndex(name, { field })` maintains a geohash companion column on every
|
|
@@ -432,7 +432,7 @@ interface AdvisorGeoIndexUsage {
|
|
|
432
432
|
* there.
|
|
433
433
|
*/
|
|
434
434
|
interface AdvisorHttpActionGuard {
|
|
435
|
-
/** The exported binding name of the handler (or `"
|
|
435
|
+
/** The exported binding name of the handler (or `"<module>"` when mounted inline / not a named binding). */
|
|
436
436
|
exportName: string;
|
|
437
437
|
/** Source file relative to the lunora dir, no extension. */
|
|
438
438
|
file: string;
|
|
@@ -444,7 +444,7 @@ interface AdvisorHttpActionGuard {
|
|
|
444
444
|
method?: string;
|
|
445
445
|
/** `true` when the handler reads `ctx.auth` (a direct member access or a `const { auth } = ctx` destructure). */
|
|
446
446
|
readsAuth: boolean;
|
|
447
|
-
/** The first side effect found, as a stable label: `runMutation`, `runAction`, or `db
|
|
447
|
+
/** The first side effect found, as a stable label: `runMutation`, `runAction`, or `db.<method>`. */
|
|
448
448
|
sideEffect: string;
|
|
449
449
|
}
|
|
450
450
|
/**
|
|
@@ -461,7 +461,7 @@ interface AdvisorHttpActionGuard {
|
|
|
461
461
|
* to `HttpHeaderWriteIR`.
|
|
462
462
|
*/
|
|
463
463
|
interface AdvisorHttpHeaderWrite {
|
|
464
|
-
/** The exported binding name of the enclosing handler, or `"
|
|
464
|
+
/** The exported binding name of the enclosing handler, or `"<module>"` when mounted inline. */
|
|
465
465
|
exportName: string;
|
|
466
466
|
/** Source file relative to the lunora dir, no extension. */
|
|
467
467
|
file: string;
|
|
@@ -499,7 +499,7 @@ interface AdvisorHyperdriveCall {
|
|
|
499
499
|
line: number;
|
|
500
500
|
}
|
|
501
501
|
/**
|
|
502
|
-
* One
|
|
502
|
+
* One `<receiver>.identity.<key>` claim read (where `<receiver>` is an RLS/mask
|
|
503
503
|
* policy `auth`, or `ctx.auth`/`context.auth`) — the shared input for the
|
|
504
504
|
* `identity_undeclared_claim_trusted` lint. `defineIdentity` validates only its
|
|
505
505
|
* declared claims at the trust boundary and forwards undeclared claims
|
|
@@ -513,7 +513,7 @@ interface AdvisorHyperdriveCall {
|
|
|
513
513
|
interface AdvisorIdentityClaimRead {
|
|
514
514
|
/** `true` when `key` is a declared claim (in the `defineIdentity` contract, or the always-present `userId`). */
|
|
515
515
|
declared: boolean;
|
|
516
|
-
/** The exported binding name of the enclosing declaration (
|
|
516
|
+
/** The exported binding name of the enclosing declaration (`<module>` at file scope). */
|
|
517
517
|
exportName: string;
|
|
518
518
|
/** Source file relative to the lunora dir, no extension. */
|
|
519
519
|
file: string;
|
|
@@ -601,7 +601,7 @@ interface AdvisorInsertWrite {
|
|
|
601
601
|
table: string;
|
|
602
602
|
}
|
|
603
603
|
/**
|
|
604
|
-
* One `ctx.kv
|
|
604
|
+
* One `ctx.kv.<method>(key, …)` call whose namespace key is derived from the
|
|
605
605
|
* handler's `args` with no server-side scoping — the input the
|
|
606
606
|
* `kv_unscoped_user_key_idor` lint consumes. Workers KV is a single flat
|
|
607
607
|
* namespace, so a key taken straight from request input lets any caller read,
|
|
@@ -693,7 +693,7 @@ interface AdvisorMaskProcedure {
|
|
|
693
693
|
interface AdvisorMaskStrategy {
|
|
694
694
|
/** Masked column name. */
|
|
695
695
|
column: string;
|
|
696
|
-
/** The exported binding name of the procedure whose `.use(mask(...))` chain declared this column, or `"
|
|
696
|
+
/** The exported binding name of the procedure whose `.use(mask(...))` chain declared this column, or `"<module>"` when declared at file scope. */
|
|
697
697
|
exportName: string;
|
|
698
698
|
/** Source file relative to the lunora dir, no extension. */
|
|
699
699
|
file: string;
|
|
@@ -866,7 +866,7 @@ interface AdvisorOwnerFieldWrite {
|
|
|
866
866
|
interface AdvisorPaymentWebhook {
|
|
867
867
|
/** The adapter factory invoked. */
|
|
868
868
|
callee: "createAutumnAdapter" | "createDodoPaymentsAdapter" | "createPolarAdapter" | "createStripeAdapter";
|
|
869
|
-
/** The exported binding name of the enclosing declaration (
|
|
869
|
+
/** The exported binding name of the enclosing declaration (`<module>` at file scope). */
|
|
870
870
|
exportName: string;
|
|
871
871
|
/** Source file relative to the lunora dir, no extension. */
|
|
872
872
|
file: string;
|
|
@@ -1123,8 +1123,8 @@ interface AdvisorRatelimitKeySelector {
|
|
|
1123
1123
|
}
|
|
1124
1124
|
/**
|
|
1125
1125
|
* One `query` handler whose `return` hands back the raw rows of a table — the
|
|
1126
|
-
* result of a `ctx.db
|
|
1127
|
-
* `ctx.db.query("
|
|
1126
|
+
* result of a `ctx.db.<table>.findMany()` / `.findFirst()` / `.get()` read, or a
|
|
1127
|
+
* `ctx.db.query("<table>")…collect()` fluent chain — returned directly (or through
|
|
1128
1128
|
* one local `const` hop) with no hand-built projection. The shared input for the
|
|
1129
1129
|
* `output_projection_missing_on_public_read` lint, which keeps only `visibility
|
|
1130
1130
|
* === "public"` rows with no `.output(...)` / `.use(mask(...))` on the chain and
|
|
@@ -1149,7 +1149,7 @@ interface AdvisorRawRowReturn {
|
|
|
1149
1149
|
visibility: "internal" | "public";
|
|
1150
1150
|
}
|
|
1151
1151
|
/**
|
|
1152
|
-
* One `ctx.db
|
|
1152
|
+
* One `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list read
|
|
1153
1153
|
* — the shared input for the `masked_relation_leak_via_with` lint. Column
|
|
1154
1154
|
* masking is applied per-procedure to the top-level rows of the table named in
|
|
1155
1155
|
* the read; it does **not** descend into `with`-hydrated relations, so a masked
|
|
@@ -1434,7 +1434,7 @@ interface AdvisorShardTraffic {
|
|
|
1434
1434
|
shardKey: string;
|
|
1435
1435
|
}
|
|
1436
1436
|
/**
|
|
1437
|
-
* One `ctx.db
|
|
1437
|
+
* One `ctx.db.<table>.findMany({ includeDeleted })` list read whose
|
|
1438
1438
|
* `includeDeleted` is either a hardcoded `true` or derived from the handler's
|
|
1439
1439
|
* `args` — the shared input for the `soft_delete_include_deleted_from_args`
|
|
1440
1440
|
* lint. `includeDeleted` resurfaces rows a `.softDelete()` table would otherwise
|
|
@@ -1477,7 +1477,7 @@ interface AdvisorSqlInterpolation {
|
|
|
1477
1477
|
line: number;
|
|
1478
1478
|
}
|
|
1479
1479
|
/**
|
|
1480
|
-
* One `ctx.storage
|
|
1480
|
+
* One `ctx.storage.<bucket>.<method>(key, …)` call whose R2 object key is derived
|
|
1481
1481
|
* from the handler's `args` with no server-side scoping — the input the
|
|
1482
1482
|
* `storage_key_from_user_args` lint consumes. An object key taken straight from
|
|
1483
1483
|
* request input lets any caller read, overwrite, or delete another user's object
|
|
@@ -1505,7 +1505,7 @@ interface AdvisorStorageKeyAccess {
|
|
|
1505
1505
|
visibility?: "internal" | "public";
|
|
1506
1506
|
}
|
|
1507
1507
|
/**
|
|
1508
|
-
* One tracked `ctx.storage
|
|
1508
|
+
* One tracked `ctx.storage.<bucket>.<method>(...)` upload/signing call — the
|
|
1509
1509
|
* shared input for the storage config-hygiene security lints
|
|
1510
1510
|
* (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
|
|
1511
1511
|
* `storage_generate_upload_url_no_content_type_pin`, `storage_presigned_url_for_private_content`).
|
|
@@ -1589,7 +1589,7 @@ interface AdvisorUnrestrictedWhereBranch {
|
|
|
1589
1589
|
owner: string;
|
|
1590
1590
|
}
|
|
1591
1591
|
/**
|
|
1592
|
-
* One `ctx.vectors
|
|
1592
|
+
* One `ctx.vectors.<method>(indexName, input)` call whose `input.namespace` is
|
|
1593
1593
|
* derived from the handler's `args` with no server-side scoping — the input the
|
|
1594
1594
|
* `vectors_namespace_from_user_input` lint consumes. A Vectorize namespace
|
|
1595
1595
|
* partitions one index into isolated sub-collections, so a namespace taken
|
|
@@ -1737,7 +1737,7 @@ interface Finding {
|
|
|
1737
1737
|
*/
|
|
1738
1738
|
interface LintContext {
|
|
1739
1739
|
/**
|
|
1740
|
-
* `httpRoute
|
|
1740
|
+
* `httpRoute.<verb>("/admin/…")` routes on admin/privileged-looking paths and
|
|
1741
1741
|
* whether each references an auth/admin guard — the `admin_route_without_guard`
|
|
1742
1742
|
* input. Supplied by the codegen feeder; absent for runtime callers, where the
|
|
1743
1743
|
* lint finds nothing.
|
|
@@ -1778,7 +1778,7 @@ interface LintContext {
|
|
|
1778
1778
|
*/
|
|
1779
1779
|
argValidators?: ReadonlyArray<AdvisorArgumentValidator>;
|
|
1780
1780
|
/**
|
|
1781
|
-
* `ctx.authApi
|
|
1781
|
+
* `ctx.authApi.<method>(...)` calls discovered in function bodies (the
|
|
1782
1782
|
* `auth_api_call_without_headers` input). Supplied by the codegen feeder; absent
|
|
1783
1783
|
* for runtime callers, where the lint finds nothing.
|
|
1784
1784
|
*/
|
|
@@ -1797,7 +1797,7 @@ interface LintContext {
|
|
|
1797
1797
|
*/
|
|
1798
1798
|
authConfigs?: ReadonlyArray<AdvisorAuthConfig>;
|
|
1799
1799
|
/**
|
|
1800
|
-
* `ctx.browser
|
|
1800
|
+
* `ctx.browser.<method>(url, …)` calls whose navigation URL is derived from the
|
|
1801
1801
|
* handler's `args` with no server-side scoping — the
|
|
1802
1802
|
* `browser_user_url_without_allowlist` input. `@lunora/browser` blocks
|
|
1803
1803
|
* private/internal targets by default, but a request-supplied public URL can
|
|
@@ -1816,7 +1816,7 @@ interface LintContext {
|
|
|
1816
1816
|
*/
|
|
1817
1817
|
configCalls?: ReadonlyArray<AdvisorConfigCall>;
|
|
1818
1818
|
/**
|
|
1819
|
-
* `ctx.containers
|
|
1819
|
+
* `ctx.containers.<name>.get(key, …)` calls whose instance key is derived from
|
|
1820
1820
|
* the handler's `args` with no server-side scoping — the
|
|
1821
1821
|
* `container_instance_key_from_user_input` input. Each container definition's
|
|
1822
1822
|
* `.get(name)` accessor routes to one instance per key, so an arg-derived key lets
|
|
@@ -1827,7 +1827,7 @@ interface LintContext {
|
|
|
1827
1827
|
containerKeyAccesses?: ReadonlyArray<AdvisorContainerKeyAccess>;
|
|
1828
1828
|
/**
|
|
1829
1829
|
* Runtime container-override calls — a `.start({ enableInternet: true, … })`
|
|
1830
|
-
* launch override, or a `.egress
|
|
1830
|
+
* launch override, or a `.egress.<method>(...)` runtime firewall mutation — the
|
|
1831
1831
|
* `container_start_enable_internet_override` and `container_runtime_egress_relaxation`
|
|
1832
1832
|
* lint input. Supplied by the codegen feeder; absent for runtime callers, where
|
|
1833
1833
|
* those lints find nothing.
|
|
@@ -1912,7 +1912,7 @@ interface LintContext {
|
|
|
1912
1912
|
*/
|
|
1913
1913
|
hyperdriveCalls?: ReadonlyArray<AdvisorHyperdriveCall>;
|
|
1914
1914
|
/**
|
|
1915
|
-
*
|
|
1915
|
+
* `<receiver>.identity.<key>` claim reads (RLS/mask policy `auth`, or
|
|
1916
1916
|
* `ctx.auth`/`context.auth`) — the `identity_undeclared_claim_trusted` input.
|
|
1917
1917
|
* `defineIdentity` validates only declared claims and forwards undeclared ones
|
|
1918
1918
|
* verbatim, so each row's `declared` flag says whether `key` is in the contract
|
|
@@ -1951,7 +1951,7 @@ interface LintContext {
|
|
|
1951
1951
|
*/
|
|
1952
1952
|
inserts?: ReadonlyArray<AdvisorInsertWrite>;
|
|
1953
1953
|
/**
|
|
1954
|
-
* `ctx.kv
|
|
1954
|
+
* `ctx.kv.<method>(key, …)` calls whose namespace key is derived from the
|
|
1955
1955
|
* handler's `args` with no server-side scoping — the `kv_unscoped_user_key_idor`
|
|
1956
1956
|
* input. Workers KV is one flat namespace, so a key taken straight from request
|
|
1957
1957
|
* input lets any caller read/overwrite/delete another user's entry (IDOR). Only
|
|
@@ -2113,7 +2113,7 @@ interface LintContext {
|
|
|
2113
2113
|
*/
|
|
2114
2114
|
rawRowReturns?: ReadonlyArray<AdvisorRawRowReturn>;
|
|
2115
2115
|
/**
|
|
2116
|
-
* `ctx.db
|
|
2116
|
+
* `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list reads
|
|
2117
2117
|
* — the `masked_relation_leak_via_with` input. Column masking is applied to a
|
|
2118
2118
|
* read's top-level rows but does not descend into `with`-hydrated relations,
|
|
2119
2119
|
* so a masked table surfaced only through a `with` on an unprotected public
|
|
@@ -2154,7 +2154,7 @@ interface LintContext {
|
|
|
2154
2154
|
*/
|
|
2155
2155
|
shardTraffic?: ReadonlyArray<AdvisorShardTraffic>;
|
|
2156
2156
|
/**
|
|
2157
|
-
* `ctx.db
|
|
2157
|
+
* `ctx.db.<table>.findMany({ includeDeleted })` list reads whose
|
|
2158
2158
|
* `includeDeleted` is a hardcoded `true` or derived from the handler's
|
|
2159
2159
|
* `args` — the `soft_delete_include_deleted_from_args` input. On a public
|
|
2160
2160
|
* read of a `.softDelete()` table this resurfaces soft-deleted rows to any
|
|
@@ -2170,7 +2170,7 @@ interface LintContext {
|
|
|
2170
2170
|
*/
|
|
2171
2171
|
sqlInterpolations?: ReadonlyArray<AdvisorSqlInterpolation>;
|
|
2172
2172
|
/**
|
|
2173
|
-
* `ctx.storage
|
|
2173
|
+
* `ctx.storage.<bucket>.<method>(key, …)` calls whose R2 object key is derived
|
|
2174
2174
|
* from the handler's `args` with no server-side scoping — the
|
|
2175
2175
|
* `storage_key_from_user_args` input. The bucket read/write/URL/delete methods
|
|
2176
2176
|
* key by their first argument, so an arg-derived key is object-level IDOR
|
|
@@ -2181,7 +2181,7 @@ interface LintContext {
|
|
|
2181
2181
|
*/
|
|
2182
2182
|
storageKeyAccesses?: ReadonlyArray<AdvisorStorageKeyAccess>;
|
|
2183
2183
|
/**
|
|
2184
|
-
* Tracked `ctx.storage
|
|
2184
|
+
* Tracked `ctx.storage.<bucket>.<method>(...)` upload/signing calls — the
|
|
2185
2185
|
* shared input for the storage config-hygiene lints
|
|
2186
2186
|
* (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
|
|
2187
2187
|
* `storage_generate_upload_url_no_content_type_pin`,
|
|
@@ -2221,7 +2221,7 @@ interface LintContext {
|
|
|
2221
2221
|
*/
|
|
2222
2222
|
unrestrictedWhereBranches?: ReadonlyArray<AdvisorUnrestrictedWhereBranch>;
|
|
2223
2223
|
/**
|
|
2224
|
-
* `ctx.vectors
|
|
2224
|
+
* `ctx.vectors.<method>(index, { namespace, … })` calls whose `namespace` is
|
|
2225
2225
|
* derived from the handler's `args` with no server-side scoping — the
|
|
2226
2226
|
* `vectors_namespace_from_user_input` input. A Vectorize namespace partitions one
|
|
2227
2227
|
* index into isolated sub-collections, so an arg-derived namespace lets any caller
|
|
@@ -2329,7 +2329,7 @@ interface AnalyticsRuntimeMetrics {
|
|
|
2329
2329
|
* within-line discriminator the second finding is silently hidden.
|
|
2330
2330
|
*
|
|
2331
2331
|
* This suffixes the second-and-later occurrence of any repeated key with
|
|
2332
|
-
*
|
|
2332
|
+
* `:<n>` (`:2`, `:3`, …), leaving the first occurrence unsuffixed so existing
|
|
2333
2333
|
* single-occurrence keys stay stable across runs. Order is preserved. Keys are
|
|
2334
2334
|
* lint-name-prefixed, so this never merges across lints.
|
|
2335
2335
|
*/
|
|
@@ -2553,7 +2553,7 @@ declare const aiUnboundedGenerationPublic: Lint;
|
|
|
2553
2553
|
*/
|
|
2554
2554
|
declare const allowUnauthenticatedShardAccessEnabled: Lint;
|
|
2555
2555
|
/**
|
|
2556
|
-
* Flags a `ctx.authApi
|
|
2556
|
+
* Flags a `ctx.authApi.<method>(...)` call whose argument object omits `headers`.
|
|
2557
2557
|
*
|
|
2558
2558
|
* `@lunora/auth`'s `withAuthPlugins` middleware attaches the full privileged
|
|
2559
2559
|
* better-auth API to `ctx.authApi` — `banUser`, `setRole`, impersonation,
|
|
@@ -2701,7 +2701,7 @@ declare const authTrustedOriginsWildcard: Lint;
|
|
|
2701
2701
|
*/
|
|
2702
2702
|
declare const browserAllowPrivateTargets: Lint;
|
|
2703
2703
|
/**
|
|
2704
|
-
* Flags a `ctx.browser
|
|
2704
|
+
* Flags a `ctx.browser.<method>(url, …)` call whose navigation URL is derived
|
|
2705
2705
|
* from the handler's `args` with no server-side scoping — and no hardened
|
|
2706
2706
|
* `createBrowser` allowlist to contain it.
|
|
2707
2707
|
*
|
|
@@ -2757,7 +2757,7 @@ declare const browserUserUrlWithoutAllowlist: Lint;
|
|
|
2757
2757
|
*/
|
|
2758
2758
|
declare const circularFk: Lint;
|
|
2759
2759
|
/**
|
|
2760
|
-
* Flags a `ctx.containers
|
|
2760
|
+
* Flags a `ctx.containers.<exportName>.get(name, …)` call whose instance key
|
|
2761
2761
|
* is derived from the handler's `args` with no server-side scoping — a
|
|
2762
2762
|
* cross-tenant container IDOR.
|
|
2763
2763
|
*
|
|
@@ -2982,7 +2982,7 @@ declare const geoIndexFieldNotGeopoint: Lint;
|
|
|
2982
2982
|
* `withGeoIndex(name, q => q.near(…) | q.within(…))` the companion is pure dead
|
|
2983
2983
|
* overhead — the geo analogue of a dead regular index. Cross-references every geo
|
|
2984
2984
|
* index in the schema against the set of index names some handler references via
|
|
2985
|
-
* `withGeoIndex("
|
|
2985
|
+
* `withGeoIndex("<name>")`.
|
|
2986
2986
|
*
|
|
2987
2987
|
* Suppressed entirely when any usage passes a non-literal name
|
|
2988
2988
|
* (`withGeoIndex(someVariable, …)`), because a dynamic reference could target any
|
|
@@ -3081,8 +3081,8 @@ declare const hyperdriveOutsideAction: Lint;
|
|
|
3081
3081
|
* `defineIdentity` is the trust boundary for `ctx.auth.identity`: the worker
|
|
3082
3082
|
* validates a resolver's returned claims against the *declared* validators, but
|
|
3083
3083
|
* — by design — forwards any **undeclared** claims through verbatim, unchecked.
|
|
3084
|
-
* So a policy predicate or authorize hook that reads `auth.identity
|
|
3085
|
-
*
|
|
3084
|
+
* So a policy predicate or authorize hook that reads `auth.identity.<key>` for a
|
|
3085
|
+
* `<key>` the contract never declares is trusting a value the runtime never
|
|
3086
3086
|
* validated — a claim an attacker's token can carry with an arbitrary value.
|
|
3087
3087
|
* Reading `userId` (always declared) or any declared claim is fine.
|
|
3088
3088
|
*
|
|
@@ -3262,8 +3262,8 @@ declare const maskWeakHashStrategyOnPii: Lint;
|
|
|
3262
3262
|
* relation.
|
|
3263
3263
|
*
|
|
3264
3264
|
* INFO, near-zero false positives by construction: it fires only when all of
|
|
3265
|
-
* (1) the enclosing read is public, (2) the read declares `with: {
|
|
3266
|
-
* (3)
|
|
3265
|
+
* (1) the enclosing read is public, (2) the read declares `with: { <rel> }`,
|
|
3266
|
+
* (3) `<rel>` resolves through the schema to a real target table, and (4) that
|
|
3267
3267
|
* target table actually has masked columns (per the discovered mask evidence).
|
|
3268
3268
|
* Absent any mask usage the lint is a no-op. Runs only when the codegen feeder
|
|
3269
3269
|
* supplies `context.relationLoads`; a runtime caller flags nothing. One finding
|
|
@@ -3529,7 +3529,7 @@ declare const policyReferencesUnknownTable: Lint;
|
|
|
3529
3529
|
* id) becomes an act-as-any-user / cross-tenant write.
|
|
3530
3530
|
*
|
|
3531
3531
|
* The lint is deliberately narrow to stay false-positive-free: it fires **only**
|
|
3532
|
-
* when the resolved target (`api
|
|
3532
|
+
* when the resolved target (`api.<file>.<export>`) is found in the
|
|
3533
3533
|
* RLS-procedure evidence with `usesRls: true`. A dispatch into a function that
|
|
3534
3534
|
* does its own arg validation and carries no row policy (the common, correct
|
|
3535
3535
|
* case — e.g. a welcome-message mutation with no `rls`) is not flagged. Runs
|
|
@@ -3541,8 +3541,8 @@ declare const privilegedDispatchUnvalidatedPayload: Lint;
|
|
|
3541
3541
|
* Flags a public procedure whose handler fans work out to a privileged,
|
|
3542
3542
|
* cost-bearing dispatch surface without a rate-limit guard.
|
|
3543
3543
|
*
|
|
3544
|
-
* `ctx.scheduler.runAfter` / `runAt`, a `ctx.queues
|
|
3545
|
-
* `ctx.workflows
|
|
3544
|
+
* `ctx.scheduler.runAfter` / `runAt`, a `ctx.queues.<name>` producer send, and
|
|
3545
|
+
* `ctx.workflows.<name>.create` all enqueue work that runs later under the
|
|
3546
3546
|
* system identity (RLS disabled) and bills against the account. Triggered from a
|
|
3547
3547
|
* `.public()` procedure with no rate limit, an anonymous caller can drive that
|
|
3548
3548
|
* dispatch in a loop — a cost-amplification / denial-of-wallet vector, and a way
|
|
@@ -3906,7 +3906,7 @@ declare const sqlInjectionRisk: Lint;
|
|
|
3906
3906
|
*/
|
|
3907
3907
|
declare const storageGenerateUploadUrlNoContentTypePin: Lint;
|
|
3908
3908
|
/**
|
|
3909
|
-
* Flags a `ctx.storage
|
|
3909
|
+
* Flags a `ctx.storage.<bucket>.<method>(key, …)` whose R2 object key is derived
|
|
3910
3910
|
* from the handler's `args` with no server-side scoping — an object-level IDOR.
|
|
3911
3911
|
*
|
|
3912
3912
|
* The bucket read/write/URL/delete methods (`get`, `put`, `delete`, `download`,
|
|
@@ -3993,7 +3993,7 @@ declare const storageUploadWithoutMaxSize: Lint;
|
|
|
3993
3993
|
* Using `@lunora/codegen`'s write-side discovery (the analog of the read
|
|
3994
3994
|
* discovery that feeds `filter_without_index`), this lint cross-references every
|
|
3995
3995
|
* schema table against the set of tables some exported function writes via
|
|
3996
|
-
* `ctx.db.insert("
|
|
3996
|
+
* `ctx.db.insert("<table>", …)`. A table with no such write either is dead schema
|
|
3997
3997
|
* or is populated through a path the static analysis can't see — a migration/seed,
|
|
3998
3998
|
* cross-region replication, the `ctx.orm.insert(...)` builder, or a trusted
|
|
3999
3999
|
* snapshot import. Hence `INFO`/`INTERNAL`: a nudge to confirm intent, not an error.
|
|
@@ -4162,7 +4162,7 @@ declare const workflowUnknownTarget: Lint;
|
|
|
4162
4162
|
* Flags a declared workflow that nothing starts.
|
|
4163
4163
|
*
|
|
4164
4164
|
* Cross-references every `defineWorkflow` export against the set of workflow
|
|
4165
|
-
* names some function references via `ctx.workflows.get("
|
|
4165
|
+
* names some function references via `ctx.workflows.get("<name>")`. A workflow
|
|
4166
4166
|
* with no such call is either dead code (declared, deployed as a billable
|
|
4167
4167
|
* `WorkflowEntrypoint`, never triggered) or is started through a path the static
|
|
4168
4168
|
* analysis can't see — the Cloudflare REST API, a `wrangler` invocation, or a
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Schema } from '@lunora/server';
|
|
2
2
|
/**
|
|
3
|
-
* One `httpRoute
|
|
3
|
+
* One `httpRoute.<verb>("/admin/…")` REST route on an admin/privileged-looking
|
|
4
4
|
* path, with whether its handler references an auth/admin guard — the input the
|
|
5
5
|
* `admin_route_without_guard` lint consumes. Produced by the codegen feeder;
|
|
6
6
|
* runtime callers don't supply it, so the lint finds nothing there.
|
|
@@ -147,7 +147,7 @@ interface AdvisorAuthConfig {
|
|
|
147
147
|
trustedOriginsWildcard: boolean;
|
|
148
148
|
}
|
|
149
149
|
/**
|
|
150
|
-
* One `ctx.authApi
|
|
150
|
+
* One `ctx.authApi.<method>(...)` call discovered in a function body — the input
|
|
151
151
|
* the `auth_api_call_without_headers` lint consumes. Produced by the codegen
|
|
152
152
|
* feeder; runtime callers don't supply it, so the lint finds nothing there.
|
|
153
153
|
*/
|
|
@@ -164,7 +164,7 @@ interface AdvisorAuthApiCall {
|
|
|
164
164
|
method: string;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
|
-
* One `ctx.browser
|
|
167
|
+
* One `ctx.browser.<method>(url, …)` call whose navigation URL (`arguments[0]`)
|
|
168
168
|
* is derived from the handler's `args` with no server-side scoping — the input
|
|
169
169
|
* the `browser_user_url_without_allowlist` lint consumes. `@lunora/browser`
|
|
170
170
|
* blocks private/internal targets by default, but a request-supplied *public*
|
|
@@ -216,7 +216,7 @@ interface AdvisorConfigCall {
|
|
|
216
216
|
trueKeys: ReadonlyArray<string>;
|
|
217
217
|
}
|
|
218
218
|
/**
|
|
219
|
-
* One `ctx.containers
|
|
219
|
+
* One `ctx.containers.<exportName>.get(name, …)` call whose instance key is
|
|
220
220
|
* derived from the handler's `args` with no server-side scoping — the input
|
|
221
221
|
* the `container_instance_key_from_user_input` lint consumes. A container
|
|
222
222
|
* definition's `.get(name)` accessor routes to one instance per `name`, so a
|
|
@@ -238,8 +238,8 @@ interface AdvisorContainerKeyAccess {
|
|
|
238
238
|
method: string;
|
|
239
239
|
}
|
|
240
240
|
/**
|
|
241
|
-
* One runtime container-override call: a
|
|
242
|
-
* launch override, or a
|
|
241
|
+
* One runtime container-override call: a `<handle>.start({ enableInternet: true, … })`
|
|
242
|
+
* launch override, or a `<handle>.egress.<method>(...)` runtime firewall mutation
|
|
243
243
|
* (`allow` / `deny` / `setAllowed`) — the `container_start_enable_internet_override`
|
|
244
244
|
* and `container_runtime_egress_relaxation` lint input. Both shapes re-open network
|
|
245
245
|
* access the static `defineContainer` declaration (and its `container_public_internet`
|
|
@@ -333,7 +333,7 @@ interface AdvisorExportSink {
|
|
|
333
333
|
interface AdvisorFailOpenGuard {
|
|
334
334
|
/** The middleware factory at the call site: `rateLimit` / `dbRateLimit` / `verifyTurnstileMiddleware`. */
|
|
335
335
|
callee: string;
|
|
336
|
-
/** The exported binding name of the procedure the guard is attached to, or `"
|
|
336
|
+
/** The exported binding name of the procedure the guard is attached to, or `"<module>"` at file scope. */
|
|
337
337
|
exportName: string;
|
|
338
338
|
/** `true` only when the options literal set `failOpen: true` as a boolean literal; a non-literal or absent option is treated as fail-closed. */
|
|
339
339
|
failOpen: boolean;
|
|
@@ -345,7 +345,7 @@ interface AdvisorFailOpenGuard {
|
|
|
345
345
|
line: number;
|
|
346
346
|
}
|
|
347
347
|
/**
|
|
348
|
-
* One `ctx.flags.boolean("key",
|
|
348
|
+
* One `ctx.flags.boolean("key", <boolean-literal>)` read — the
|
|
349
349
|
* `flag_gates_security_with_unsafe_default` lint input. OpenFeature returns the
|
|
350
350
|
* `defaultValue` when the provider errors, so a fail-open default on a
|
|
351
351
|
* security-shaped key (an `enforce`/`rls`/`gate`/`lockdown` protection
|
|
@@ -358,7 +358,7 @@ interface AdvisorFailOpenGuard {
|
|
|
358
358
|
interface AdvisorFlagSecurityDefault {
|
|
359
359
|
/** The boolean-literal default returned on a provider outage (fail-open value). */
|
|
360
360
|
defaultValue: boolean;
|
|
361
|
-
/** The exported binding name of the procedure performing the flag read, or `"
|
|
361
|
+
/** The exported binding name of the procedure performing the flag read, or `"<module>"` at file scope. */
|
|
362
362
|
exportName: string;
|
|
363
363
|
/** Source file relative to the lunora dir, no extension. */
|
|
364
364
|
file: string;
|
|
@@ -394,7 +394,7 @@ interface AdvisorFunctionMetrics {
|
|
|
394
394
|
errors: number;
|
|
395
395
|
/** Slowest single dispatch, ms, over the same window. */
|
|
396
396
|
maxDurationMs: number;
|
|
397
|
-
/** The function's registered path (
|
|
397
|
+
/** The function's registered path (`<file>:<exportName>`). */
|
|
398
398
|
path: string;
|
|
399
399
|
}
|
|
400
400
|
/**
|
|
@@ -402,7 +402,7 @@ interface AdvisorFunctionMetrics {
|
|
|
402
402
|
* input the `geo_index_unused` lint cross-references against the declared geo
|
|
403
403
|
* indexes in the lint context's schema. Produced by the codegen feeder, which
|
|
404
404
|
* walks the lunora source for `ctx.db.query("t").withGeoIndex(name, …)` /
|
|
405
|
-
* `ctx.db
|
|
405
|
+
* `ctx.db.<table>.withGeoIndex(name, …)` reads and records each referenced index
|
|
406
406
|
* name. Runtime callers don't supply it, so the lint finds nothing there.
|
|
407
407
|
*
|
|
408
408
|
* A `.geoIndex(name, { field })` maintains a geohash companion column on every
|
|
@@ -432,7 +432,7 @@ interface AdvisorGeoIndexUsage {
|
|
|
432
432
|
* there.
|
|
433
433
|
*/
|
|
434
434
|
interface AdvisorHttpActionGuard {
|
|
435
|
-
/** The exported binding name of the handler (or `"
|
|
435
|
+
/** The exported binding name of the handler (or `"<module>"` when mounted inline / not a named binding). */
|
|
436
436
|
exportName: string;
|
|
437
437
|
/** Source file relative to the lunora dir, no extension. */
|
|
438
438
|
file: string;
|
|
@@ -444,7 +444,7 @@ interface AdvisorHttpActionGuard {
|
|
|
444
444
|
method?: string;
|
|
445
445
|
/** `true` when the handler reads `ctx.auth` (a direct member access or a `const { auth } = ctx` destructure). */
|
|
446
446
|
readsAuth: boolean;
|
|
447
|
-
/** The first side effect found, as a stable label: `runMutation`, `runAction`, or `db
|
|
447
|
+
/** The first side effect found, as a stable label: `runMutation`, `runAction`, or `db.<method>`. */
|
|
448
448
|
sideEffect: string;
|
|
449
449
|
}
|
|
450
450
|
/**
|
|
@@ -461,7 +461,7 @@ interface AdvisorHttpActionGuard {
|
|
|
461
461
|
* to `HttpHeaderWriteIR`.
|
|
462
462
|
*/
|
|
463
463
|
interface AdvisorHttpHeaderWrite {
|
|
464
|
-
/** The exported binding name of the enclosing handler, or `"
|
|
464
|
+
/** The exported binding name of the enclosing handler, or `"<module>"` when mounted inline. */
|
|
465
465
|
exportName: string;
|
|
466
466
|
/** Source file relative to the lunora dir, no extension. */
|
|
467
467
|
file: string;
|
|
@@ -499,7 +499,7 @@ interface AdvisorHyperdriveCall {
|
|
|
499
499
|
line: number;
|
|
500
500
|
}
|
|
501
501
|
/**
|
|
502
|
-
* One
|
|
502
|
+
* One `<receiver>.identity.<key>` claim read (where `<receiver>` is an RLS/mask
|
|
503
503
|
* policy `auth`, or `ctx.auth`/`context.auth`) — the shared input for the
|
|
504
504
|
* `identity_undeclared_claim_trusted` lint. `defineIdentity` validates only its
|
|
505
505
|
* declared claims at the trust boundary and forwards undeclared claims
|
|
@@ -513,7 +513,7 @@ interface AdvisorHyperdriveCall {
|
|
|
513
513
|
interface AdvisorIdentityClaimRead {
|
|
514
514
|
/** `true` when `key` is a declared claim (in the `defineIdentity` contract, or the always-present `userId`). */
|
|
515
515
|
declared: boolean;
|
|
516
|
-
/** The exported binding name of the enclosing declaration (
|
|
516
|
+
/** The exported binding name of the enclosing declaration (`<module>` at file scope). */
|
|
517
517
|
exportName: string;
|
|
518
518
|
/** Source file relative to the lunora dir, no extension. */
|
|
519
519
|
file: string;
|
|
@@ -601,7 +601,7 @@ interface AdvisorInsertWrite {
|
|
|
601
601
|
table: string;
|
|
602
602
|
}
|
|
603
603
|
/**
|
|
604
|
-
* One `ctx.kv
|
|
604
|
+
* One `ctx.kv.<method>(key, …)` call whose namespace key is derived from the
|
|
605
605
|
* handler's `args` with no server-side scoping — the input the
|
|
606
606
|
* `kv_unscoped_user_key_idor` lint consumes. Workers KV is a single flat
|
|
607
607
|
* namespace, so a key taken straight from request input lets any caller read,
|
|
@@ -693,7 +693,7 @@ interface AdvisorMaskProcedure {
|
|
|
693
693
|
interface AdvisorMaskStrategy {
|
|
694
694
|
/** Masked column name. */
|
|
695
695
|
column: string;
|
|
696
|
-
/** The exported binding name of the procedure whose `.use(mask(...))` chain declared this column, or `"
|
|
696
|
+
/** The exported binding name of the procedure whose `.use(mask(...))` chain declared this column, or `"<module>"` when declared at file scope. */
|
|
697
697
|
exportName: string;
|
|
698
698
|
/** Source file relative to the lunora dir, no extension. */
|
|
699
699
|
file: string;
|
|
@@ -866,7 +866,7 @@ interface AdvisorOwnerFieldWrite {
|
|
|
866
866
|
interface AdvisorPaymentWebhook {
|
|
867
867
|
/** The adapter factory invoked. */
|
|
868
868
|
callee: "createAutumnAdapter" | "createDodoPaymentsAdapter" | "createPolarAdapter" | "createStripeAdapter";
|
|
869
|
-
/** The exported binding name of the enclosing declaration (
|
|
869
|
+
/** The exported binding name of the enclosing declaration (`<module>` at file scope). */
|
|
870
870
|
exportName: string;
|
|
871
871
|
/** Source file relative to the lunora dir, no extension. */
|
|
872
872
|
file: string;
|
|
@@ -1123,8 +1123,8 @@ interface AdvisorRatelimitKeySelector {
|
|
|
1123
1123
|
}
|
|
1124
1124
|
/**
|
|
1125
1125
|
* One `query` handler whose `return` hands back the raw rows of a table — the
|
|
1126
|
-
* result of a `ctx.db
|
|
1127
|
-
* `ctx.db.query("
|
|
1126
|
+
* result of a `ctx.db.<table>.findMany()` / `.findFirst()` / `.get()` read, or a
|
|
1127
|
+
* `ctx.db.query("<table>")…collect()` fluent chain — returned directly (or through
|
|
1128
1128
|
* one local `const` hop) with no hand-built projection. The shared input for the
|
|
1129
1129
|
* `output_projection_missing_on_public_read` lint, which keeps only `visibility
|
|
1130
1130
|
* === "public"` rows with no `.output(...)` / `.use(mask(...))` on the chain and
|
|
@@ -1149,7 +1149,7 @@ interface AdvisorRawRowReturn {
|
|
|
1149
1149
|
visibility: "internal" | "public";
|
|
1150
1150
|
}
|
|
1151
1151
|
/**
|
|
1152
|
-
* One `ctx.db
|
|
1152
|
+
* One `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list read
|
|
1153
1153
|
* — the shared input for the `masked_relation_leak_via_with` lint. Column
|
|
1154
1154
|
* masking is applied per-procedure to the top-level rows of the table named in
|
|
1155
1155
|
* the read; it does **not** descend into `with`-hydrated relations, so a masked
|
|
@@ -1434,7 +1434,7 @@ interface AdvisorShardTraffic {
|
|
|
1434
1434
|
shardKey: string;
|
|
1435
1435
|
}
|
|
1436
1436
|
/**
|
|
1437
|
-
* One `ctx.db
|
|
1437
|
+
* One `ctx.db.<table>.findMany({ includeDeleted })` list read whose
|
|
1438
1438
|
* `includeDeleted` is either a hardcoded `true` or derived from the handler's
|
|
1439
1439
|
* `args` — the shared input for the `soft_delete_include_deleted_from_args`
|
|
1440
1440
|
* lint. `includeDeleted` resurfaces rows a `.softDelete()` table would otherwise
|
|
@@ -1477,7 +1477,7 @@ interface AdvisorSqlInterpolation {
|
|
|
1477
1477
|
line: number;
|
|
1478
1478
|
}
|
|
1479
1479
|
/**
|
|
1480
|
-
* One `ctx.storage
|
|
1480
|
+
* One `ctx.storage.<bucket>.<method>(key, …)` call whose R2 object key is derived
|
|
1481
1481
|
* from the handler's `args` with no server-side scoping — the input the
|
|
1482
1482
|
* `storage_key_from_user_args` lint consumes. An object key taken straight from
|
|
1483
1483
|
* request input lets any caller read, overwrite, or delete another user's object
|
|
@@ -1505,7 +1505,7 @@ interface AdvisorStorageKeyAccess {
|
|
|
1505
1505
|
visibility?: "internal" | "public";
|
|
1506
1506
|
}
|
|
1507
1507
|
/**
|
|
1508
|
-
* One tracked `ctx.storage
|
|
1508
|
+
* One tracked `ctx.storage.<bucket>.<method>(...)` upload/signing call — the
|
|
1509
1509
|
* shared input for the storage config-hygiene security lints
|
|
1510
1510
|
* (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
|
|
1511
1511
|
* `storage_generate_upload_url_no_content_type_pin`, `storage_presigned_url_for_private_content`).
|
|
@@ -1589,7 +1589,7 @@ interface AdvisorUnrestrictedWhereBranch {
|
|
|
1589
1589
|
owner: string;
|
|
1590
1590
|
}
|
|
1591
1591
|
/**
|
|
1592
|
-
* One `ctx.vectors
|
|
1592
|
+
* One `ctx.vectors.<method>(indexName, input)` call whose `input.namespace` is
|
|
1593
1593
|
* derived from the handler's `args` with no server-side scoping — the input the
|
|
1594
1594
|
* `vectors_namespace_from_user_input` lint consumes. A Vectorize namespace
|
|
1595
1595
|
* partitions one index into isolated sub-collections, so a namespace taken
|
|
@@ -1737,7 +1737,7 @@ interface Finding {
|
|
|
1737
1737
|
*/
|
|
1738
1738
|
interface LintContext {
|
|
1739
1739
|
/**
|
|
1740
|
-
* `httpRoute
|
|
1740
|
+
* `httpRoute.<verb>("/admin/…")` routes on admin/privileged-looking paths and
|
|
1741
1741
|
* whether each references an auth/admin guard — the `admin_route_without_guard`
|
|
1742
1742
|
* input. Supplied by the codegen feeder; absent for runtime callers, where the
|
|
1743
1743
|
* lint finds nothing.
|
|
@@ -1778,7 +1778,7 @@ interface LintContext {
|
|
|
1778
1778
|
*/
|
|
1779
1779
|
argValidators?: ReadonlyArray<AdvisorArgumentValidator>;
|
|
1780
1780
|
/**
|
|
1781
|
-
* `ctx.authApi
|
|
1781
|
+
* `ctx.authApi.<method>(...)` calls discovered in function bodies (the
|
|
1782
1782
|
* `auth_api_call_without_headers` input). Supplied by the codegen feeder; absent
|
|
1783
1783
|
* for runtime callers, where the lint finds nothing.
|
|
1784
1784
|
*/
|
|
@@ -1797,7 +1797,7 @@ interface LintContext {
|
|
|
1797
1797
|
*/
|
|
1798
1798
|
authConfigs?: ReadonlyArray<AdvisorAuthConfig>;
|
|
1799
1799
|
/**
|
|
1800
|
-
* `ctx.browser
|
|
1800
|
+
* `ctx.browser.<method>(url, …)` calls whose navigation URL is derived from the
|
|
1801
1801
|
* handler's `args` with no server-side scoping — the
|
|
1802
1802
|
* `browser_user_url_without_allowlist` input. `@lunora/browser` blocks
|
|
1803
1803
|
* private/internal targets by default, but a request-supplied public URL can
|
|
@@ -1816,7 +1816,7 @@ interface LintContext {
|
|
|
1816
1816
|
*/
|
|
1817
1817
|
configCalls?: ReadonlyArray<AdvisorConfigCall>;
|
|
1818
1818
|
/**
|
|
1819
|
-
* `ctx.containers
|
|
1819
|
+
* `ctx.containers.<name>.get(key, …)` calls whose instance key is derived from
|
|
1820
1820
|
* the handler's `args` with no server-side scoping — the
|
|
1821
1821
|
* `container_instance_key_from_user_input` input. Each container definition's
|
|
1822
1822
|
* `.get(name)` accessor routes to one instance per key, so an arg-derived key lets
|
|
@@ -1827,7 +1827,7 @@ interface LintContext {
|
|
|
1827
1827
|
containerKeyAccesses?: ReadonlyArray<AdvisorContainerKeyAccess>;
|
|
1828
1828
|
/**
|
|
1829
1829
|
* Runtime container-override calls — a `.start({ enableInternet: true, … })`
|
|
1830
|
-
* launch override, or a `.egress
|
|
1830
|
+
* launch override, or a `.egress.<method>(...)` runtime firewall mutation — the
|
|
1831
1831
|
* `container_start_enable_internet_override` and `container_runtime_egress_relaxation`
|
|
1832
1832
|
* lint input. Supplied by the codegen feeder; absent for runtime callers, where
|
|
1833
1833
|
* those lints find nothing.
|
|
@@ -1912,7 +1912,7 @@ interface LintContext {
|
|
|
1912
1912
|
*/
|
|
1913
1913
|
hyperdriveCalls?: ReadonlyArray<AdvisorHyperdriveCall>;
|
|
1914
1914
|
/**
|
|
1915
|
-
*
|
|
1915
|
+
* `<receiver>.identity.<key>` claim reads (RLS/mask policy `auth`, or
|
|
1916
1916
|
* `ctx.auth`/`context.auth`) — the `identity_undeclared_claim_trusted` input.
|
|
1917
1917
|
* `defineIdentity` validates only declared claims and forwards undeclared ones
|
|
1918
1918
|
* verbatim, so each row's `declared` flag says whether `key` is in the contract
|
|
@@ -1951,7 +1951,7 @@ interface LintContext {
|
|
|
1951
1951
|
*/
|
|
1952
1952
|
inserts?: ReadonlyArray<AdvisorInsertWrite>;
|
|
1953
1953
|
/**
|
|
1954
|
-
* `ctx.kv
|
|
1954
|
+
* `ctx.kv.<method>(key, …)` calls whose namespace key is derived from the
|
|
1955
1955
|
* handler's `args` with no server-side scoping — the `kv_unscoped_user_key_idor`
|
|
1956
1956
|
* input. Workers KV is one flat namespace, so a key taken straight from request
|
|
1957
1957
|
* input lets any caller read/overwrite/delete another user's entry (IDOR). Only
|
|
@@ -2113,7 +2113,7 @@ interface LintContext {
|
|
|
2113
2113
|
*/
|
|
2114
2114
|
rawRowReturns?: ReadonlyArray<AdvisorRawRowReturn>;
|
|
2115
2115
|
/**
|
|
2116
|
-
* `ctx.db
|
|
2116
|
+
* `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list reads
|
|
2117
2117
|
* — the `masked_relation_leak_via_with` input. Column masking is applied to a
|
|
2118
2118
|
* read's top-level rows but does not descend into `with`-hydrated relations,
|
|
2119
2119
|
* so a masked table surfaced only through a `with` on an unprotected public
|
|
@@ -2154,7 +2154,7 @@ interface LintContext {
|
|
|
2154
2154
|
*/
|
|
2155
2155
|
shardTraffic?: ReadonlyArray<AdvisorShardTraffic>;
|
|
2156
2156
|
/**
|
|
2157
|
-
* `ctx.db
|
|
2157
|
+
* `ctx.db.<table>.findMany({ includeDeleted })` list reads whose
|
|
2158
2158
|
* `includeDeleted` is a hardcoded `true` or derived from the handler's
|
|
2159
2159
|
* `args` — the `soft_delete_include_deleted_from_args` input. On a public
|
|
2160
2160
|
* read of a `.softDelete()` table this resurfaces soft-deleted rows to any
|
|
@@ -2170,7 +2170,7 @@ interface LintContext {
|
|
|
2170
2170
|
*/
|
|
2171
2171
|
sqlInterpolations?: ReadonlyArray<AdvisorSqlInterpolation>;
|
|
2172
2172
|
/**
|
|
2173
|
-
* `ctx.storage
|
|
2173
|
+
* `ctx.storage.<bucket>.<method>(key, …)` calls whose R2 object key is derived
|
|
2174
2174
|
* from the handler's `args` with no server-side scoping — the
|
|
2175
2175
|
* `storage_key_from_user_args` input. The bucket read/write/URL/delete methods
|
|
2176
2176
|
* key by their first argument, so an arg-derived key is object-level IDOR
|
|
@@ -2181,7 +2181,7 @@ interface LintContext {
|
|
|
2181
2181
|
*/
|
|
2182
2182
|
storageKeyAccesses?: ReadonlyArray<AdvisorStorageKeyAccess>;
|
|
2183
2183
|
/**
|
|
2184
|
-
* Tracked `ctx.storage
|
|
2184
|
+
* Tracked `ctx.storage.<bucket>.<method>(...)` upload/signing calls — the
|
|
2185
2185
|
* shared input for the storage config-hygiene lints
|
|
2186
2186
|
* (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
|
|
2187
2187
|
* `storage_generate_upload_url_no_content_type_pin`,
|
|
@@ -2221,7 +2221,7 @@ interface LintContext {
|
|
|
2221
2221
|
*/
|
|
2222
2222
|
unrestrictedWhereBranches?: ReadonlyArray<AdvisorUnrestrictedWhereBranch>;
|
|
2223
2223
|
/**
|
|
2224
|
-
* `ctx.vectors
|
|
2224
|
+
* `ctx.vectors.<method>(index, { namespace, … })` calls whose `namespace` is
|
|
2225
2225
|
* derived from the handler's `args` with no server-side scoping — the
|
|
2226
2226
|
* `vectors_namespace_from_user_input` input. A Vectorize namespace partitions one
|
|
2227
2227
|
* index into isolated sub-collections, so an arg-derived namespace lets any caller
|
|
@@ -2329,7 +2329,7 @@ interface AnalyticsRuntimeMetrics {
|
|
|
2329
2329
|
* within-line discriminator the second finding is silently hidden.
|
|
2330
2330
|
*
|
|
2331
2331
|
* This suffixes the second-and-later occurrence of any repeated key with
|
|
2332
|
-
*
|
|
2332
|
+
* `:<n>` (`:2`, `:3`, …), leaving the first occurrence unsuffixed so existing
|
|
2333
2333
|
* single-occurrence keys stay stable across runs. Order is preserved. Keys are
|
|
2334
2334
|
* lint-name-prefixed, so this never merges across lints.
|
|
2335
2335
|
*/
|
|
@@ -2553,7 +2553,7 @@ declare const aiUnboundedGenerationPublic: Lint;
|
|
|
2553
2553
|
*/
|
|
2554
2554
|
declare const allowUnauthenticatedShardAccessEnabled: Lint;
|
|
2555
2555
|
/**
|
|
2556
|
-
* Flags a `ctx.authApi
|
|
2556
|
+
* Flags a `ctx.authApi.<method>(...)` call whose argument object omits `headers`.
|
|
2557
2557
|
*
|
|
2558
2558
|
* `@lunora/auth`'s `withAuthPlugins` middleware attaches the full privileged
|
|
2559
2559
|
* better-auth API to `ctx.authApi` — `banUser`, `setRole`, impersonation,
|
|
@@ -2701,7 +2701,7 @@ declare const authTrustedOriginsWildcard: Lint;
|
|
|
2701
2701
|
*/
|
|
2702
2702
|
declare const browserAllowPrivateTargets: Lint;
|
|
2703
2703
|
/**
|
|
2704
|
-
* Flags a `ctx.browser
|
|
2704
|
+
* Flags a `ctx.browser.<method>(url, …)` call whose navigation URL is derived
|
|
2705
2705
|
* from the handler's `args` with no server-side scoping — and no hardened
|
|
2706
2706
|
* `createBrowser` allowlist to contain it.
|
|
2707
2707
|
*
|
|
@@ -2757,7 +2757,7 @@ declare const browserUserUrlWithoutAllowlist: Lint;
|
|
|
2757
2757
|
*/
|
|
2758
2758
|
declare const circularFk: Lint;
|
|
2759
2759
|
/**
|
|
2760
|
-
* Flags a `ctx.containers
|
|
2760
|
+
* Flags a `ctx.containers.<exportName>.get(name, …)` call whose instance key
|
|
2761
2761
|
* is derived from the handler's `args` with no server-side scoping — a
|
|
2762
2762
|
* cross-tenant container IDOR.
|
|
2763
2763
|
*
|
|
@@ -2982,7 +2982,7 @@ declare const geoIndexFieldNotGeopoint: Lint;
|
|
|
2982
2982
|
* `withGeoIndex(name, q => q.near(…) | q.within(…))` the companion is pure dead
|
|
2983
2983
|
* overhead — the geo analogue of a dead regular index. Cross-references every geo
|
|
2984
2984
|
* index in the schema against the set of index names some handler references via
|
|
2985
|
-
* `withGeoIndex("
|
|
2985
|
+
* `withGeoIndex("<name>")`.
|
|
2986
2986
|
*
|
|
2987
2987
|
* Suppressed entirely when any usage passes a non-literal name
|
|
2988
2988
|
* (`withGeoIndex(someVariable, …)`), because a dynamic reference could target any
|
|
@@ -3081,8 +3081,8 @@ declare const hyperdriveOutsideAction: Lint;
|
|
|
3081
3081
|
* `defineIdentity` is the trust boundary for `ctx.auth.identity`: the worker
|
|
3082
3082
|
* validates a resolver's returned claims against the *declared* validators, but
|
|
3083
3083
|
* — by design — forwards any **undeclared** claims through verbatim, unchecked.
|
|
3084
|
-
* So a policy predicate or authorize hook that reads `auth.identity
|
|
3085
|
-
*
|
|
3084
|
+
* So a policy predicate or authorize hook that reads `auth.identity.<key>` for a
|
|
3085
|
+
* `<key>` the contract never declares is trusting a value the runtime never
|
|
3086
3086
|
* validated — a claim an attacker's token can carry with an arbitrary value.
|
|
3087
3087
|
* Reading `userId` (always declared) or any declared claim is fine.
|
|
3088
3088
|
*
|
|
@@ -3262,8 +3262,8 @@ declare const maskWeakHashStrategyOnPii: Lint;
|
|
|
3262
3262
|
* relation.
|
|
3263
3263
|
*
|
|
3264
3264
|
* INFO, near-zero false positives by construction: it fires only when all of
|
|
3265
|
-
* (1) the enclosing read is public, (2) the read declares `with: {
|
|
3266
|
-
* (3)
|
|
3265
|
+
* (1) the enclosing read is public, (2) the read declares `with: { <rel> }`,
|
|
3266
|
+
* (3) `<rel>` resolves through the schema to a real target table, and (4) that
|
|
3267
3267
|
* target table actually has masked columns (per the discovered mask evidence).
|
|
3268
3268
|
* Absent any mask usage the lint is a no-op. Runs only when the codegen feeder
|
|
3269
3269
|
* supplies `context.relationLoads`; a runtime caller flags nothing. One finding
|
|
@@ -3529,7 +3529,7 @@ declare const policyReferencesUnknownTable: Lint;
|
|
|
3529
3529
|
* id) becomes an act-as-any-user / cross-tenant write.
|
|
3530
3530
|
*
|
|
3531
3531
|
* The lint is deliberately narrow to stay false-positive-free: it fires **only**
|
|
3532
|
-
* when the resolved target (`api
|
|
3532
|
+
* when the resolved target (`api.<file>.<export>`) is found in the
|
|
3533
3533
|
* RLS-procedure evidence with `usesRls: true`. A dispatch into a function that
|
|
3534
3534
|
* does its own arg validation and carries no row policy (the common, correct
|
|
3535
3535
|
* case — e.g. a welcome-message mutation with no `rls`) is not flagged. Runs
|
|
@@ -3541,8 +3541,8 @@ declare const privilegedDispatchUnvalidatedPayload: Lint;
|
|
|
3541
3541
|
* Flags a public procedure whose handler fans work out to a privileged,
|
|
3542
3542
|
* cost-bearing dispatch surface without a rate-limit guard.
|
|
3543
3543
|
*
|
|
3544
|
-
* `ctx.scheduler.runAfter` / `runAt`, a `ctx.queues
|
|
3545
|
-
* `ctx.workflows
|
|
3544
|
+
* `ctx.scheduler.runAfter` / `runAt`, a `ctx.queues.<name>` producer send, and
|
|
3545
|
+
* `ctx.workflows.<name>.create` all enqueue work that runs later under the
|
|
3546
3546
|
* system identity (RLS disabled) and bills against the account. Triggered from a
|
|
3547
3547
|
* `.public()` procedure with no rate limit, an anonymous caller can drive that
|
|
3548
3548
|
* dispatch in a loop — a cost-amplification / denial-of-wallet vector, and a way
|
|
@@ -3906,7 +3906,7 @@ declare const sqlInjectionRisk: Lint;
|
|
|
3906
3906
|
*/
|
|
3907
3907
|
declare const storageGenerateUploadUrlNoContentTypePin: Lint;
|
|
3908
3908
|
/**
|
|
3909
|
-
* Flags a `ctx.storage
|
|
3909
|
+
* Flags a `ctx.storage.<bucket>.<method>(key, …)` whose R2 object key is derived
|
|
3910
3910
|
* from the handler's `args` with no server-side scoping — an object-level IDOR.
|
|
3911
3911
|
*
|
|
3912
3912
|
* The bucket read/write/URL/delete methods (`get`, `put`, `delete`, `download`,
|
|
@@ -3993,7 +3993,7 @@ declare const storageUploadWithoutMaxSize: Lint;
|
|
|
3993
3993
|
* Using `@lunora/codegen`'s write-side discovery (the analog of the read
|
|
3994
3994
|
* discovery that feeds `filter_without_index`), this lint cross-references every
|
|
3995
3995
|
* schema table against the set of tables some exported function writes via
|
|
3996
|
-
* `ctx.db.insert("
|
|
3996
|
+
* `ctx.db.insert("<table>", …)`. A table with no such write either is dead schema
|
|
3997
3997
|
* or is populated through a path the static analysis can't see — a migration/seed,
|
|
3998
3998
|
* cross-region replication, the `ctx.orm.insert(...)` builder, or a trusted
|
|
3999
3999
|
* snapshot import. Hence `INFO`/`INTERNAL`: a nudge to confirm intent, not an error.
|
|
@@ -4162,7 +4162,7 @@ declare const workflowUnknownTarget: Lint;
|
|
|
4162
4162
|
* Flags a declared workflow that nothing starts.
|
|
4163
4163
|
*
|
|
4164
4164
|
* Cross-references every `defineWorkflow` export against the set of workflow
|
|
4165
|
-
* names some function references via `ctx.workflows.get("
|
|
4165
|
+
* names some function references via `ctx.workflows.get("<name>")`. A workflow
|
|
4166
4166
|
* with no such call is either dead code (declared, deployed as a billable
|
|
4167
4167
|
* `WorkflowEntrypoint`, never triggered) or is started through a path the static
|
|
4168
4168
|
* analysis can't see — the Cloudflare REST API, a `wrangler` invocation, or a
|
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.67",
|
|
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.14",
|
|
50
|
+
"@lunora/server": "1.0.0-alpha.62"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": "^22.15.0 || >=24.11.0"
|