@lunora/codegen 1.0.0-alpha.29 → 1.0.0-alpha.30
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 +694 -4
- package/dist/index.d.ts +694 -4
- package/dist/index.mjs +17 -16
- package/dist/packem_shared/{FLAGS_FILENAME-CQz5jIns.mjs → FLAGS_FILENAME-BjpS5w08.mjs} +1 -1
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-Cpfn80SH.mjs +3481 -0
- package/dist/packem_shared/{discoverAuthApiCalls-CGjWoLJ3.mjs → discoverAuthApiCalls-URDoTADN.mjs} +1 -1
- package/dist/packem_shared/{discoverCrons-De98wIzl.mjs → discoverCrons-DdZOeroL.mjs} +1 -1
- package/dist/packem_shared/{discoverFunctions-CCVrvWhX.mjs → discoverFunctions-BJdF7lRs.mjs} +56 -7
- package/dist/packem_shared/{discoverHttpRoutes-3tvf_95b.mjs → discoverHttpRoutes-CcwP-Rkr.mjs} +1 -1
- package/dist/packem_shared/{discoverInserts-B0k4i6Yu.mjs → discoverInserts-BpFhM32L.mjs} +1 -1
- package/dist/packem_shared/{discoverMaskProcedures-C0sofRvW.mjs → discoverMaskProcedures-DT-v8wvj.mjs} +58 -2
- package/dist/packem_shared/{discoverMigrations-DfBWvCoA.mjs → discoverMigrations-cG1idBVM.mjs} +1 -1
- package/dist/packem_shared/{discoverNondeterministicCalls-DXiWO-nI.mjs → discoverNondeterministicCalls-CEOjmfk4.mjs} +1 -1
- package/dist/packem_shared/{discoverQueries-B9KbrYF9.mjs → discoverQueries-DeVBQVAG.mjs} +1 -1
- package/dist/packem_shared/{discoverR2sqlCalls-CkBDIqPt.mjs → discoverR2sqlCalls-CWCAU-xJ.mjs} +1 -1
- package/dist/packem_shared/{discoverRlsMetadata-BXrKzID4.mjs → discoverRlsMetadata-BL0oukaK.mjs} +1 -1
- package/dist/packem_shared/{discoverSchema-B9Xfdg5V.mjs → discoverSchema-CeXJWVKV.mjs} +16 -7
- package/dist/packem_shared/{discoverStorageRulesMetadata-C1Q-Ct2p.mjs → discoverStorageRulesMetadata-CFHByjIl.mjs} +1 -1
- package/dist/packem_shared/{formatAdvisories-DdjK7sgh.mjs → formatAdvisories-C9wNBNvL.mjs} +35 -3
- package/dist/packem_shared/redact-oTmsol5A.mjs +33 -0
- package/package.json +2 -2
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-DtCmOeaL.mjs +0 -1154
package/dist/index.d.mts
CHANGED
|
@@ -163,6 +163,13 @@ interface TableIR {
|
|
|
163
163
|
*/
|
|
164
164
|
globalBackend?: "d1" | "hyperdrive";
|
|
165
165
|
indexes: ReadonlyArray<IndexIR>;
|
|
166
|
+
/**
|
|
167
|
+
* `true` when the table chain carried `.public()` — an explicit opt-OUT of
|
|
168
|
+
* the schema's `.rls("required")` enforcement for this one table. Optional:
|
|
169
|
+
* hand-built IR and tables that never called `.public()` default it to
|
|
170
|
+
* `false`.
|
|
171
|
+
*/
|
|
172
|
+
isPublic?: boolean;
|
|
166
173
|
name: string;
|
|
167
174
|
/** Rank indexes declared inline via `.rankIndex(name, …)`. */
|
|
168
175
|
rankIndexes: ReadonlyArray<RankIndexIR>;
|
|
@@ -191,6 +198,13 @@ interface SchemaIR {
|
|
|
191
198
|
* Absent ⇒ un-pinned.
|
|
192
199
|
*/
|
|
193
200
|
jurisdiction?: JurisdictionIR;
|
|
201
|
+
/**
|
|
202
|
+
* Set when `defineSchema(...).rls("required")` was chained onto the schema —
|
|
203
|
+
* every table's `ctx.db` write path is denied without an RLS-covering
|
|
204
|
+
* procedure unless the table itself is `.public()` (see {@link TableIR.isPublic}).
|
|
205
|
+
* Absent when the schema never called `.rls("required")`.
|
|
206
|
+
*/
|
|
207
|
+
rlsMode?: "required";
|
|
194
208
|
tables: ReadonlyArray<TableIR>;
|
|
195
209
|
/** All vector indexes (inline Shape A hoisted + standalone Shape B), flattened. */
|
|
196
210
|
vectorIndexes: ReadonlyArray<VectorIndexIR>;
|
|
@@ -688,6 +702,30 @@ interface MaskMetadataIR {
|
|
|
688
702
|
columns: MaskColumnMetadataIR[];
|
|
689
703
|
}
|
|
690
704
|
/**
|
|
705
|
+
* One masked column whose `mask(policies)` strategy is a statically-known
|
|
706
|
+
* literal (`"hash"` or `"redact"`) — the `mask_weak_hash_strategy_on_pii` lint
|
|
707
|
+
* input. Unlike {@link MaskColumnMetadataIR} (app-wide, deduped by `(table,
|
|
708
|
+
* column)`, studio-preview evidence), this is per declaration site (file + line
|
|
709
|
+
* + enclosing export), undeduped, so the lint can point at the exact
|
|
710
|
+
* `mask(...)` call that applies a weak strategy. A `MaskFn` (custom, non-literal)
|
|
711
|
+
* strategy carries no lint-relevant signal and is never recorded here.
|
|
712
|
+
* Structurally identical to `AdvisorMaskStrategy`.
|
|
713
|
+
*/
|
|
714
|
+
interface MaskStrategyIR {
|
|
715
|
+
/** Masked column name. */
|
|
716
|
+
column: string;
|
|
717
|
+
/** Export binding name of the procedure whose `.use(mask(...))` chain declared this column, or `"<module>"` when declared at file scope. */
|
|
718
|
+
exportName: string;
|
|
719
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
720
|
+
file: string;
|
|
721
|
+
/** 1-based line of the masked column's strategy property. */
|
|
722
|
+
line: number;
|
|
723
|
+
/** The statically-known strategy literal: `"hash"` or `"redact"`. */
|
|
724
|
+
strategy: string;
|
|
725
|
+
/** Logical table the masked column belongs to. */
|
|
726
|
+
table: string;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
691
729
|
* One statically-readable policy entry from an `rls([...])` array literal,
|
|
692
730
|
* surfaced to the studio's read-only RLS inspector via the generated
|
|
693
731
|
* `rlsPolicies()` hook. Captures the policy's `table` + `on` operation and the
|
|
@@ -796,12 +834,18 @@ interface ProcedureMiddlewareIR {
|
|
|
796
834
|
callsMail: boolean;
|
|
797
835
|
/** Export binding name of the procedure (e.g. `signUp`). */
|
|
798
836
|
exportName: string;
|
|
837
|
+
/** `true` when the handler fans work out to a privileged, cost-bearing dispatch surface (scheduler `runAfter`/`runAt`, a queue producer send, or a workflow create). Feeds the privileged-fanout lint. */
|
|
838
|
+
fanOut: boolean;
|
|
799
839
|
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
800
840
|
file: string;
|
|
801
841
|
/** Registration kind — only `mutation`/`action` are write-shaped; `query` is read-only. */
|
|
802
842
|
kind: "action" | "mutation" | "query";
|
|
843
|
+
/** `true` when the handler runs an AI generation (`generateText`/`streamText`/`generateObject`/`streamObject`) with no `maxOutputTokens` bound in its config literal. Feeds the `ai_unbounded_generation_public` lint. */
|
|
844
|
+
unboundedAiGeneration: boolean;
|
|
803
845
|
/** `true` when the chain carries `.use(verifyTurnstile(...))` or a `protectPublic({ captcha })` bundle. */
|
|
804
846
|
usesCaptcha: boolean;
|
|
847
|
+
/** `true` when the handler calls `ctx.db.insertManyUnsafe(...)`, bypassing validators and triggers. Feeds the `insert_many_unsafe_user_data` lint. */
|
|
848
|
+
usesInsertManyUnsafe: boolean;
|
|
805
849
|
/** `true` when the chain carries `.use(mask(...))`. */
|
|
806
850
|
usesMask: boolean;
|
|
807
851
|
/** `true` when the chain carries `.use(rateLimit(...))` or a `protectPublic({ rateLimit })` bundle. */
|
|
@@ -834,6 +878,27 @@ interface ArgumentValidatorIR {
|
|
|
834
878
|
unboundedStringArgs: string[];
|
|
835
879
|
}
|
|
836
880
|
/**
|
|
881
|
+
* One factory/constructor call in `lunora/` whose config object literal a
|
|
882
|
+
* security lint inspects for a present-or-absent key — the shared input for the
|
|
883
|
+
* config-call security lints (payment authorize, inbound-mail verify, rate-limit
|
|
884
|
+
* store, browser private-targets). Structurally identical to `AdvisorConfigCall`
|
|
885
|
+
* so it passes straight through to the advisor without conversion.
|
|
886
|
+
*/
|
|
887
|
+
interface ConfigCallIR {
|
|
888
|
+
/** `true` when the config argument was a static object literal the feeder could read. */
|
|
889
|
+
analyzable: boolean;
|
|
890
|
+
/** The factory function or constructor name at the call site, e.g. `createPayment` / `RateLimiter`. */
|
|
891
|
+
callee: string;
|
|
892
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
893
|
+
file: string;
|
|
894
|
+
/** 1-based line of the call site, or `0` when unknown. */
|
|
895
|
+
line: number;
|
|
896
|
+
/** Keys present in the config object literal (empty when not `analyzable`). */
|
|
897
|
+
presentKeys: string[];
|
|
898
|
+
/** Keys in the config object literal explicitly assigned the literal `true`. */
|
|
899
|
+
trueKeys: string[];
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
837
902
|
* One secret-shaped string literal discovered in `lunora/` source — the
|
|
838
903
|
* `hardcoded_secret` lint input. Complements the pre-commit `vis secrets` scan by
|
|
839
904
|
* surfacing the same class of finding in-IDE via the studio Advisors table.
|
|
@@ -867,6 +932,308 @@ interface SqlInterpolationIR {
|
|
|
867
932
|
line: number;
|
|
868
933
|
}
|
|
869
934
|
/**
|
|
935
|
+
* One `ctx.fetch(url, …)` call inside an action whose URL argument is derived
|
|
936
|
+
* from the handler's `args` — the `action_fetch_ssrf` lint input. `ctx.fetch` is
|
|
937
|
+
* the action-only outbound-request escape hatch with no host allowlist, so a URL
|
|
938
|
+
* assembled from request input is a server-side request forgery vector (cloud
|
|
939
|
+
* metadata endpoints, internal services). Only arg-derived URLs reach here; a
|
|
940
|
+
* fixed literal or a URL built from config/`ctx.*` is not recorded. Structurally
|
|
941
|
+
* identical to `AdvisorArgumentDerivedFetch`.
|
|
942
|
+
*/
|
|
943
|
+
interface ArgumentDerivedFetchIR {
|
|
944
|
+
/** Export binding name of the action performing the `ctx.fetch` call. */
|
|
945
|
+
exportName: string;
|
|
946
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
947
|
+
file: string;
|
|
948
|
+
/** 1-based line of the `ctx.fetch` call, or `0` when unknown. */
|
|
949
|
+
line: number;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* One `ctx.kv.<method>(key, …)` call whose namespace key is derived from the
|
|
953
|
+
* handler's `args` with no server-side scoping — the `kv_unscoped_user_key_idor`
|
|
954
|
+
* lint input. Workers KV is a single flat namespace, so a key taken straight from
|
|
955
|
+
* request input lets any caller read, overwrite, or delete another user's entry
|
|
956
|
+
* (IDOR). Only arg-derived, unscoped keys reach here; a fixed literal, or a key
|
|
957
|
+
* prefixed with a server-trusted identity (`${ctx.auth.userId}:…` — references
|
|
958
|
+
* `ctx`, so treated as scoped), is not recorded. `list` is excluded (it takes a
|
|
959
|
+
* prefix, not a per-entry key). Structurally identical to `AdvisorKvKeyAccess`.
|
|
960
|
+
*/
|
|
961
|
+
interface KvKeyAccessIR {
|
|
962
|
+
/** Export binding name of the procedure performing the `ctx.kv` access. */
|
|
963
|
+
exportName: string;
|
|
964
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
965
|
+
file: string;
|
|
966
|
+
/** 1-based line of the `ctx.kv` call, or `0` when unknown. */
|
|
967
|
+
line: number;
|
|
968
|
+
/** The `ctx.kv` method invoked: `get` / `getRaw` / `getWithMetadata` / `put` / `delete`. */
|
|
969
|
+
method: string;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* One `ctx.db` write (`insert` / `replace` / `patch` / `insertManyUnsafe`) that sets
|
|
973
|
+
* an ownership / identity column — `userId`, `ownerId`, `tenantId`, and the like —
|
|
974
|
+
* from the handler's `args` instead of the server-trusted identity. The
|
|
975
|
+
* `owner_field_from_args_not_auth` lint input: the ownership column decides who a
|
|
976
|
+
* row belongs to, so a value taken from request input lets any caller write rows
|
|
977
|
+
* owned by another user or tenant (the act-as-any-user / cross-tenant IDOR vector).
|
|
978
|
+
* A column stamped from `ctx.*`, or set to a fixed literal, is not recorded; only an
|
|
979
|
+
* arg-derived identity write reaches here. Structurally identical to
|
|
980
|
+
* `AdvisorOwnerFieldWrite`.
|
|
981
|
+
*/
|
|
982
|
+
interface OwnerFieldWriteIR {
|
|
983
|
+
/** Export binding name of the procedure performing the write. */
|
|
984
|
+
exportName: string;
|
|
985
|
+
/** The identity column being written from `args` (e.g. `userId`). */
|
|
986
|
+
field: string;
|
|
987
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
988
|
+
file: string;
|
|
989
|
+
/** 1-based line of the `ctx.db` write call, or `0` when unknown. */
|
|
990
|
+
line: number;
|
|
991
|
+
/** The `ctx.db` write method (`insert` / `replace` / `patch` / `insertManyUnsafe`). */
|
|
992
|
+
method: string;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* One `ctx.storage.<bucket>.<method>(key, …)` call whose R2 object key is derived
|
|
996
|
+
* from the handler's `args` with no server-side scoping — the
|
|
997
|
+
* `storage_key_from_user_args` lint input. The bucket read/write/URL/delete methods
|
|
998
|
+
* key by their first argument, so an object key taken straight from request input is
|
|
999
|
+
* object-level IDOR (read/overwrite/delete anyone's object). A key referencing a
|
|
1000
|
+
* server-trusted `ctx.*` value (e.g. `${ctx.auth.userId}/…`) is treated as scoped
|
|
1001
|
+
* and is not recorded; only an arg-derived, `ctx`-free key reaches here.
|
|
1002
|
+
* Structurally identical to `AdvisorStorageKeyAccess`.
|
|
1003
|
+
*/
|
|
1004
|
+
interface StorageKeyAccessIR {
|
|
1005
|
+
/** Export binding name of the procedure performing the storage call. */
|
|
1006
|
+
exportName: string;
|
|
1007
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1008
|
+
file: string;
|
|
1009
|
+
/** 1-based line of the storage call, or `0` when unknown. */
|
|
1010
|
+
line: number;
|
|
1011
|
+
/** The bucket method invoked with the arg-derived key, e.g. `get` / `put` / `delete` / `download`. */
|
|
1012
|
+
method: string;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* One `ctx.containers.<exportName>.get(name, …)` call whose instance key is derived
|
|
1016
|
+
* from the handler's `args` with no server-side scoping — the
|
|
1017
|
+
* `container_instance_key_from_user_input` lint input. Each container definition's
|
|
1018
|
+
* `.get(name)` accessor routes to one instance per `name`, so a key taken straight from
|
|
1019
|
+
* request input lets any caller reach another tenant's container (a cross-tenant IDOR). A
|
|
1020
|
+
* fixed literal key, or one derived from a server-trusted identity (`${ctx.auth.userId}` —
|
|
1021
|
+
* references `ctx`, so treated as scoped), is not recorded; only an arg-derived, unscoped
|
|
1022
|
+
* key reaches here. `.any()`/`.pool()` take no key and are not sinks. Structurally
|
|
1023
|
+
* identical to `AdvisorContainerKeyAccess`.
|
|
1024
|
+
*/
|
|
1025
|
+
interface ContainerKeyAccessIR {
|
|
1026
|
+
/** Export binding name of the procedure performing the `ctx.containers` access. */
|
|
1027
|
+
exportName: string;
|
|
1028
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1029
|
+
file: string;
|
|
1030
|
+
/** 1-based line of the `ctx.containers.*.get` call, or `0` when unknown. */
|
|
1031
|
+
line: number;
|
|
1032
|
+
/** The container accessor method invoked — always `get`. */
|
|
1033
|
+
method: string;
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* One `ctx.ai.run(model, …)` call whose model-id argument is derived from the handler's
|
|
1037
|
+
* `args` with no server-side scoping — the `ai_raw_run_escape_hatch` lint input.
|
|
1038
|
+
* `ctx.ai.run` is the raw Workers AI binding passthrough, bypassing the typed
|
|
1039
|
+
* `ctx.ai.model(...)` + AI-SDK layer (`generateText`/`streamText`/…) that caps output and
|
|
1040
|
+
* enforces a schema, so an arg-derived model id lets any caller select an arbitrary model.
|
|
1041
|
+
* A fixed literal model, or one scoped by a server-trusted `ctx.*` value, is not recorded;
|
|
1042
|
+
* only an arg-derived, unscoped model id reaches here (an arg-derived `inputs` argument is
|
|
1043
|
+
* normal usage and is never inspected). Structurally identical to `AdvisorAiRawRun`.
|
|
1044
|
+
*/
|
|
1045
|
+
interface AiRawRunIR {
|
|
1046
|
+
/** Export binding name of the procedure performing the `ctx.ai.run` call. */
|
|
1047
|
+
exportName: string;
|
|
1048
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1049
|
+
file: string;
|
|
1050
|
+
/** 1-based line of the `ctx.ai.run` call, or `0` when unknown. */
|
|
1051
|
+
line: number;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* One `ctx.vectors.<method>(indexName, input)` call whose `input.namespace` is derived
|
|
1055
|
+
* from the handler's `args` with no server-side scoping — the
|
|
1056
|
+
* `vectors_namespace_from_user_input` lint input. A Vectorize namespace partitions one
|
|
1057
|
+
* index into isolated sub-collections, so a namespace taken straight from request input
|
|
1058
|
+
* lets any caller read or poison another tenant's vectors. A fixed literal namespace, or
|
|
1059
|
+
* one prefixed with a server-trusted identity (`${ctx.auth.orgId}` — references `ctx`, so
|
|
1060
|
+
* treated as scoped), is not recorded; only an arg-derived, unscoped namespace reaches
|
|
1061
|
+
* here. Structurally identical to `AdvisorVectorNamespaceAccess`.
|
|
1062
|
+
*/
|
|
1063
|
+
interface VectorNamespaceAccessIR {
|
|
1064
|
+
/** Export binding name of the procedure performing the `ctx.vectors` access. */
|
|
1065
|
+
exportName: string;
|
|
1066
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1067
|
+
file: string;
|
|
1068
|
+
/** 1-based line of the `ctx.vectors` call, or `0` when unknown. */
|
|
1069
|
+
line: number;
|
|
1070
|
+
/** The `ctx.vectors` method invoked: `query` / `upsert` / `upsertMany`. */
|
|
1071
|
+
method: string;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* One `ctx.mail`/`ctx.email` `send`/`queue` call whose recipient field (`to`/`cc`/`bcc`)
|
|
1075
|
+
* is derived from the handler's `args` with no server-side scoping — the
|
|
1076
|
+
* `mail_recipient_from_request_input` lint input. A recipient taken straight from request
|
|
1077
|
+
* input turns the deployment into an open relay / spam amplifier (any caller can direct
|
|
1078
|
+
* mail to an arbitrary address). A fixed literal recipient, or one scoped by a
|
|
1079
|
+
* server-trusted `ctx.*` value (e.g. `ctx.auth.user.email`), is not recorded; only an
|
|
1080
|
+
* arg-derived, unscoped recipient reaches here. Structurally identical to
|
|
1081
|
+
* `AdvisorMailRecipientAccess`.
|
|
1082
|
+
*/
|
|
1083
|
+
interface MailRecipientAccessIR {
|
|
1084
|
+
/** Export binding name of the procedure performing the `ctx.mail`/`ctx.email` call. */
|
|
1085
|
+
exportName: string;
|
|
1086
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1087
|
+
file: string;
|
|
1088
|
+
/** 1-based line of the `ctx.mail`/`ctx.email` call, or `0` when unknown. */
|
|
1089
|
+
line: number;
|
|
1090
|
+
/** The mailer method invoked: `send` / `queue`. */
|
|
1091
|
+
method: string;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* One `ctx.browser.<method>(url, …)` call whose navigation URL (`arguments[0]`)
|
|
1095
|
+
* is derived from the handler's `args` with no server-side scoping — the
|
|
1096
|
+
* `browser_user_url_without_allowlist` lint input. The lint additionally
|
|
1097
|
+
* cross-references `createBrowser` config-call evidence to suppress findings
|
|
1098
|
+
* when the browser is hardened with an `allowedHosts` allowlist or
|
|
1099
|
+
* `resolveDns`. Structurally identical to `AdvisorBrowserUrlAccess`.
|
|
1100
|
+
*/
|
|
1101
|
+
interface BrowserUrlAccessIR {
|
|
1102
|
+
/** Export binding name of the procedure performing the `ctx.browser` call. */
|
|
1103
|
+
exportName: string;
|
|
1104
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1105
|
+
file: string;
|
|
1106
|
+
/** 1-based line of the `ctx.browser` call, or `0` when unknown. */
|
|
1107
|
+
line: number;
|
|
1108
|
+
/** The browser method invoked: `content` / `pdf` / `scrape` / `screenshot`. */
|
|
1109
|
+
method: string;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* One runtime container-override call: a `<handle>.start({ enableInternet: true, … })`
|
|
1113
|
+
* launch override, or a `<handle>.egress.<method>(...)` runtime firewall mutation
|
|
1114
|
+
* (`allow` / `deny` / `setAllowed`) — the `container_start_enable_internet_override`
|
|
1115
|
+
* and `container_runtime_egress_relaxation` lint input. Both shapes re-open network
|
|
1116
|
+
* access the static `defineContainer` declaration (and its `container_public_internet`
|
|
1117
|
+
* lint) assumes is locked down. Matched structurally by call shape, independent of the
|
|
1118
|
+
* receiver's resolved type. Structurally identical to `AdvisorContainerOverride`.
|
|
1119
|
+
*/
|
|
1120
|
+
interface ContainerOverrideIR {
|
|
1121
|
+
/** e.g. the egress method name, or `"enableInternet: true"`. */
|
|
1122
|
+
detail: string;
|
|
1123
|
+
/** Export binding name of the procedure performing the call. */
|
|
1124
|
+
exportName: string;
|
|
1125
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1126
|
+
file: string;
|
|
1127
|
+
/** Which override shape matched. */
|
|
1128
|
+
kind: "egress_relaxation" | "enable_internet";
|
|
1129
|
+
/** 1-based line of the call, or `0` when unknown. */
|
|
1130
|
+
line: number;
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* One `buildImageDeliveryUrl({ key, … })` call (`@lunora/bindings/images`) whose
|
|
1134
|
+
* `key` — the CDN transform's source image, an absolute URL or an
|
|
1135
|
+
* origin-relative key — is derived from the handler's `args` with no
|
|
1136
|
+
* server-side scoping — the `images_url_source_from_user_input` lint input.
|
|
1137
|
+
* `ctx.images.transform`/`info` take image *bytes*, never a URL, so they are not
|
|
1138
|
+
* sinks; only the `key` of `buildImageDeliveryUrl` accepts a URL-or-key source
|
|
1139
|
+
* and is inspected. An arg-derived `key` lets any caller point the CDN's
|
|
1140
|
+
* `/cdn-cgi/image/` transform at an attacker-chosen origin (SSRF / open proxy)
|
|
1141
|
+
* or at an arbitrary key under the account's own store. A fixed literal, or a
|
|
1142
|
+
* key scoped by a server-trusted `ctx.*` value, is not recorded. Structurally
|
|
1143
|
+
* identical to `AdvisorImageDeliveryUrlAccess`.
|
|
1144
|
+
*/
|
|
1145
|
+
interface ImageDeliveryUrlAccessIR {
|
|
1146
|
+
/** Export binding name of the procedure performing the `buildImageDeliveryUrl` call. */
|
|
1147
|
+
exportName: string;
|
|
1148
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1149
|
+
file: string;
|
|
1150
|
+
/** 1-based line of the `buildImageDeliveryUrl` call, or `0` when unknown. */
|
|
1151
|
+
line: number;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* One `createAuth({...})` call's configuration snapshot — the shared input for
|
|
1155
|
+
* the five `auth_*` security lints (trusted-origins wildcard, CSRF check
|
|
1156
|
+
* disabled, secure cookies disabled, email verification disabled, session
|
|
1157
|
+
* freshAge zero). Matched by callee NAME (an `import`-agnostic, fail-closed
|
|
1158
|
+
* convention the other feeders share), so a re-export or alias still resolves.
|
|
1159
|
+
* When the config argument isn't a statically-analyzable object literal (a
|
|
1160
|
+
* top-level spread, or not an object literal at all), `analyzable` is `false`
|
|
1161
|
+
* and every boolean fact defaults to its SAFE (not-flagged) value — an opaque
|
|
1162
|
+
* config can't be relied on either way. Structurally identical to
|
|
1163
|
+
* `AdvisorAuthConfig`.
|
|
1164
|
+
*/
|
|
1165
|
+
interface AuthConfigIR {
|
|
1166
|
+
/** `true` when the call's config argument was a static object literal the feeder could read. */
|
|
1167
|
+
analyzable: boolean;
|
|
1168
|
+
/** `advanced.disableCSRFCheck === true`. */
|
|
1169
|
+
disableCsrfCheck: boolean;
|
|
1170
|
+
/** `emailAndPassword.enabled === true`. */
|
|
1171
|
+
emailPasswordEnabled: boolean;
|
|
1172
|
+
/** Export binding name enclosing the `createAuth(...)` call. */
|
|
1173
|
+
exportName: string;
|
|
1174
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1175
|
+
file: string;
|
|
1176
|
+
/** 1-based line of the `createAuth(...)` call, or `0` when unknown. */
|
|
1177
|
+
line: number;
|
|
1178
|
+
/** `emailAndPassword.requireEmailVerification === true` present. */
|
|
1179
|
+
requireEmailVerification: boolean;
|
|
1180
|
+
/** `advanced.useSecureCookies === false`. */
|
|
1181
|
+
secureCookiesDisabled: boolean;
|
|
1182
|
+
/** `session.freshAge === 0` (explicit literal). */
|
|
1183
|
+
sessionFreshAgeZero: boolean;
|
|
1184
|
+
/** `trustedOrigins` array literal contains a `"*"` element. */
|
|
1185
|
+
trustedOriginsWildcard: boolean;
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* One `rateLimit`/`dbRateLimit` middleware call (`@lunora/ratelimit`) whose
|
|
1189
|
+
* `key` selector — the per-caller rate-limit sub-key, `(ctx) => string |
|
|
1190
|
+
* undefined` — is derived from the handler's `args` with no server-side
|
|
1191
|
+
* scoping (no reference to the trusted `ctx` binding anywhere in the selector)
|
|
1192
|
+
* — the `ratelimit_key_spoofable_or_global` lint input. A key an attacker
|
|
1193
|
+
* controls lets them rotate it per request and bypass the limit entirely,
|
|
1194
|
+
* defeating its purpose. A selector scoped by `ctx` (e.g. `ctx.auth.userId`,
|
|
1195
|
+
* `ctx.ip`), or one with no `args` reference at all (a fixed/global bucket —
|
|
1196
|
+
* the "no key" case this lint deliberately does not flag, to keep it low-FP),
|
|
1197
|
+
* is not recorded. Structurally identical to `AdvisorRatelimitKeySelector`.
|
|
1198
|
+
*/
|
|
1199
|
+
interface RatelimitKeySelectorIR {
|
|
1200
|
+
/** The `rateLimit`/`dbRateLimit` callee invoked. */
|
|
1201
|
+
callee: string;
|
|
1202
|
+
/** Export binding name of the procedure whose `.use(...)` chain carries the call. */
|
|
1203
|
+
exportName: string;
|
|
1204
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1205
|
+
file: string;
|
|
1206
|
+
/** The rate limit's `name` argument (the second positional argument), or `""` when not a string literal. */
|
|
1207
|
+
limitName: string;
|
|
1208
|
+
/** 1-based line of the `rateLimit`/`dbRateLimit` call, or `0` when unknown. */
|
|
1209
|
+
line: number;
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* One payload-derived privileged dispatch — a `ctx.run`/`context.run` back into a
|
|
1213
|
+
* Lunora function from inside a `defineQueue` push handler or a `defineWorkflow`
|
|
1214
|
+
* handler, whose args reference the handler's untrusted payload (`context.params`
|
|
1215
|
+
* for a workflow, a `for (… of batch.messages)` body for a queue) — the
|
|
1216
|
+
* `privileged_dispatch_unvalidated_payload` lint input. Both handler kinds run
|
|
1217
|
+
* under the **system identity** (RLS disabled), so forwarding attacker-influenced
|
|
1218
|
+
* payload into the dispatch bypasses the target's row policy. The resolved
|
|
1219
|
+
* `targetFile`/`targetExport` let the lint join RLS-procedure evidence and fire
|
|
1220
|
+
* only for RLS-gated targets. Structurally identical to `AdvisorPrivilegedDispatch`.
|
|
1221
|
+
*/
|
|
1222
|
+
interface PrivilegedDispatchIR {
|
|
1223
|
+
/** `"queue"` for a `defineQueue` handler, `"workflow"` for a `defineWorkflow` handler. */
|
|
1224
|
+
dispatchKind: "queue" | "workflow";
|
|
1225
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1226
|
+
file: string;
|
|
1227
|
+
/** Export binding name of the handler performing the dispatch. */
|
|
1228
|
+
handlerExport: string;
|
|
1229
|
+
/** 1-based line of the dispatch call, or `0` when unknown. */
|
|
1230
|
+
line: number;
|
|
1231
|
+
/** Export name of the dispatched target (`send` in `api.messages.send`). */
|
|
1232
|
+
targetExport: string;
|
|
1233
|
+
/** File path of the dispatched target relative to `lunora/` (`messages` in `api.messages.send`). */
|
|
1234
|
+
targetFile: string;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
870
1237
|
* One discovered `httpRoute.<verb>("/admin/…")` route on an admin/privileged-looking
|
|
871
1238
|
* path, with whether its builder chain references an auth/admin guard — the
|
|
872
1239
|
* `admin_route_without_guard` lint input. Structurally identical to
|
|
@@ -884,6 +1251,316 @@ interface AdminRouteIR {
|
|
|
884
1251
|
/** `true` when the handler body references an auth/session/admin guard (`ctx.auth`, `getSession`, `requireAdmin`, …). */
|
|
885
1252
|
usesGuard: boolean;
|
|
886
1253
|
}
|
|
1254
|
+
/**
|
|
1255
|
+
* One tracked `ctx.storage.<bucket>.<method>(...)` upload/signing call — the
|
|
1256
|
+
* shared input for the storage config-hygiene security lints
|
|
1257
|
+
* (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
|
|
1258
|
+
* `storage_generate_upload_url_no_content_type_pin`, `storage_presigned_url_for_private_content`).
|
|
1259
|
+
* `upload`/`store` carry the `UploadOptions` guards (`allowedContentTypes` /
|
|
1260
|
+
* `maxSize`); `generateUploadUrl` carries the signed-PUT `contentType` pin;
|
|
1261
|
+
* `getPresignedUrl`/`getSignedUrl` carry a statically-known `expiresInSeconds`
|
|
1262
|
+
* literal. `presentKeys` is empty (and `expiresInSeconds` unset) when the
|
|
1263
|
+
* options argument was absent, a non-literal, or a spread — see `analyzable`.
|
|
1264
|
+
* Structurally identical to `AdvisorStorageUpload`.
|
|
1265
|
+
*/
|
|
1266
|
+
interface StorageUploadIR {
|
|
1267
|
+
/** `true` when the call's options-object argument (or its deliberate absence) was statically resolvable. */
|
|
1268
|
+
analyzable: boolean;
|
|
1269
|
+
/** Numeric literal value of an `expiresInSeconds` option, when statically known (`getSignedUrl` / `getPresignedUrl` only). */
|
|
1270
|
+
expiresInSeconds?: number;
|
|
1271
|
+
/** Export binding name of the procedure performing the call. */
|
|
1272
|
+
exportName: string;
|
|
1273
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1274
|
+
file: string;
|
|
1275
|
+
/** 1-based line of the call, or `0` when unknown. */
|
|
1276
|
+
line: number;
|
|
1277
|
+
/** The `ctx.storage` method invoked. */
|
|
1278
|
+
method: "generateUploadUrl" | "getPresignedUrl" | "getSignedUrl" | "store" | "upload";
|
|
1279
|
+
/** Options-object keys present at the call site (empty when not `analyzable`, or when no options argument was passed). */
|
|
1280
|
+
presentKeys: string[];
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* One discovered `httpAction`/`httpRoute` handler in `lunora/` that performs a
|
|
1284
|
+
* side effect (`ctx.runMutation` / `ctx.runAction` / a `ctx.db.{insert,patch,
|
|
1285
|
+
* replace,delete,insertManyUnsafe}` write) from the HTTP edge, with whether it
|
|
1286
|
+
* reads `ctx.auth` — the `http_action_missing_auth_guard` lint input. A handler
|
|
1287
|
+
* that mutates state or dispatches an action without ever consulting the request
|
|
1288
|
+
* identity is an unauthenticated write bypassing identity/RLS. Only handlers with
|
|
1289
|
+
* a statically-resolvable inline body and `ctx` binding are recorded (fail-safe
|
|
1290
|
+
* under-report); read-only handlers are never recorded. Structurally identical to
|
|
1291
|
+
* `AdvisorHttpActionGuard`.
|
|
1292
|
+
*/
|
|
1293
|
+
interface HttpActionGuardIR {
|
|
1294
|
+
/** Export binding name of the handler (or `"<module>"` when mounted inline / not a named binding). */
|
|
1295
|
+
exportName: string;
|
|
1296
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1297
|
+
file: string;
|
|
1298
|
+
/** Which HTTP surface the handler is: a raw `httpAction` or a typed `httpRoute` route. */
|
|
1299
|
+
kind: "httpAction" | "httpRoute";
|
|
1300
|
+
/** 1-based line of the handler call, or `0` when unknown. */
|
|
1301
|
+
line: number;
|
|
1302
|
+
/** For an `httpRoute`, the uppercased verb (`"POST"`); absent for a raw `httpAction`. */
|
|
1303
|
+
method?: string;
|
|
1304
|
+
/** `true` when the handler reads `ctx.auth` (a direct member access or a `const { auth } = ctx` destructure). */
|
|
1305
|
+
readsAuth: boolean;
|
|
1306
|
+
/** The first side effect found, as a stable label: `runMutation`, `runAction`, or `db.<method>`. */
|
|
1307
|
+
sideEffect: string;
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* One response-header write, inside an `httpAction` handler, whose value is derived
|
|
1311
|
+
* from raw request input (`request.headers`, `request.url`/query, `await
|
|
1312
|
+
* request.json()`) with no CR/LF sanitizer — the
|
|
1313
|
+
* `http_action_response_header_injection` lint input. A `Request`-derived string
|
|
1314
|
+
* placed verbatim into a response header lets a caller smuggle `\r\n` and inject
|
|
1315
|
+
* extra headers or split the response (header injection / response splitting). Only
|
|
1316
|
+
* sites whose value is request-tainted AND unguarded are recorded: a value routed
|
|
1317
|
+
* through a CR/LF guard (`isSafeHeaderValue`), a URL/URI encoder
|
|
1318
|
+
* (`encodeURIComponent`/`encodeURI`), a numeric coercion (`Number`/`parseInt`/
|
|
1319
|
+
* `parseFloat`), or `btoa` is treated as safe and never recorded (`String(...)` /
|
|
1320
|
+
* `.toString()` are NOT sanitizers — they don't strip CR/LF). Structurally
|
|
1321
|
+
* identical to `AdvisorHttpHeaderWrite`.
|
|
1322
|
+
*/
|
|
1323
|
+
interface HttpHeaderWriteIR {
|
|
1324
|
+
/** Export binding name of the enclosing handler, or `"<module>"` when mounted inline. */
|
|
1325
|
+
exportName: string;
|
|
1326
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1327
|
+
file: string;
|
|
1328
|
+
/** The header name being written (`"location"`), or `""` when the key is not a string literal. */
|
|
1329
|
+
headerName: string;
|
|
1330
|
+
/** 1-based line of the request-tainted header value. */
|
|
1331
|
+
line: number;
|
|
1332
|
+
/** How the header was written. */
|
|
1333
|
+
via: "headers-append" | "headers-ctor" | "headers-set" | "response-init";
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* One rate-limit / Turnstile middleware call in `lunora/` — the
|
|
1337
|
+
* `ratelimit_middleware_fail_open` lint input. `rateLimit`/`dbRateLimit`
|
|
1338
|
+
* (`@lunora/ratelimit`) and `verifyTurnstileMiddleware` (`@lunora/auth`) each
|
|
1339
|
+
* accept a `failOpen` escape hatch that admits every request when the
|
|
1340
|
+
* limiter/siteverify is unavailable; `failOpen` is `true` only when the options
|
|
1341
|
+
* literal set it to the boolean literal `true` (anything else is fail-closed).
|
|
1342
|
+
* The lint escalates a fail-open guard to a finding when the guarded procedure
|
|
1343
|
+
* (`exportName`/`limitName`) looks auth/payment-sensitive. Structurally
|
|
1344
|
+
* identical to `AdvisorFailOpenGuard`.
|
|
1345
|
+
*/
|
|
1346
|
+
interface FailOpenGuardIR {
|
|
1347
|
+
/** The middleware factory at the call site: `rateLimit` / `dbRateLimit` / `verifyTurnstileMiddleware`. */
|
|
1348
|
+
callee: string;
|
|
1349
|
+
/** Export binding name of the procedure the guard is attached to, or `"<module>"` at file scope. */
|
|
1350
|
+
exportName: string;
|
|
1351
|
+
/** `true` only when the options literal set `failOpen: true` as a boolean literal; a non-literal or absent option is treated as fail-closed. */
|
|
1352
|
+
failOpen: boolean;
|
|
1353
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1354
|
+
file: string;
|
|
1355
|
+
/** The rate-limit `name` (second string argument) for `rateLimit`/`dbRateLimit`; `""` for `verifyTurnstileMiddleware`. */
|
|
1356
|
+
limitName: string;
|
|
1357
|
+
/** 1-based line of the middleware call, or `0` when unknown. */
|
|
1358
|
+
line: number;
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* One `ctx.flags.boolean("key", <boolean-literal>)` read in `lunora/` — the
|
|
1362
|
+
* `flag_gates_security_with_unsafe_default` lint input. OpenFeature returns the
|
|
1363
|
+
* `defaultValue` when the provider errors, so a fail-open default on a
|
|
1364
|
+
* security-shaped key silently opens access during an outage. Only reads with a
|
|
1365
|
+
* statically-known string key and boolean-literal default are recorded; the lint
|
|
1366
|
+
* owns the security-shape + polarity judgment. Structurally identical to
|
|
1367
|
+
* `AdvisorFlagSecurityDefault`.
|
|
1368
|
+
*/
|
|
1369
|
+
interface FlagSecurityDefaultIR {
|
|
1370
|
+
/** The boolean-literal default returned on a provider outage (fail-open value). */
|
|
1371
|
+
defaultValue: boolean;
|
|
1372
|
+
/** Export binding name of the procedure performing the flag read, or `"<module>"` at file scope. */
|
|
1373
|
+
exportName: string;
|
|
1374
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1375
|
+
file: string;
|
|
1376
|
+
/** The flag key — the first string-literal argument of `ctx.flags.boolean`. */
|
|
1377
|
+
key: string;
|
|
1378
|
+
/** 1-based line of the `ctx.flags.boolean` call, or `0` when unknown. */
|
|
1379
|
+
line: number;
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* One `generateText` / `streamText` call in `lunora/` whose `tools` reach a
|
|
1383
|
+
* privileged side effect (a DB write, function dispatch, or outbound
|
|
1384
|
+
* fetch/mail/queue send). `userInputDerived` records whether the model input
|
|
1385
|
+
* (`prompt`/`messages`/`system`) flows from the handler's `args`; the
|
|
1386
|
+
* `ai_tool_side_effect_prompt_injection` lint fires only when it does.
|
|
1387
|
+
* Structurally identical to `AdvisorAiToolSideEffect`.
|
|
1388
|
+
*/
|
|
1389
|
+
interface AiToolSideEffectIR {
|
|
1390
|
+
/** Export binding name of the procedure performing the call. */
|
|
1391
|
+
exportName: string;
|
|
1392
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1393
|
+
file: string;
|
|
1394
|
+
/** 1-based line of the generation call, or `0` when unknown. */
|
|
1395
|
+
line: number;
|
|
1396
|
+
/** The generation entrypoint invoked. */
|
|
1397
|
+
method: "generateText" | "streamText";
|
|
1398
|
+
/** The privileged side-effect sink a model-callable tool reaches (`ctx.db.insert`, `ctx.run`, `ctx.fetch`, …). */
|
|
1399
|
+
sideEffect: string;
|
|
1400
|
+
/** `true` when a model-input option is derived from the handler's `args` (a bare `args.x`, or a name destructured from `args`). */
|
|
1401
|
+
userInputDerived: boolean;
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* One `<receiver>.identity.<key>` claim read in `lunora/`, where `<receiver>` is
|
|
1405
|
+
* an RLS/mask policy `auth` (or `ctx.auth`/`context.auth`). `declared` records
|
|
1406
|
+
* whether `<key>` is in the app's `defineIdentity({ ... })` contract (or the
|
|
1407
|
+
* always-present `userId`); the `identity_undeclared_claim_trusted` lint fires on
|
|
1408
|
+
* the undeclared reads. Emitted only when a resolvable identity contract exists.
|
|
1409
|
+
* Structurally identical to `AdvisorIdentityClaimRead`.
|
|
1410
|
+
*/
|
|
1411
|
+
interface IdentityClaimReadIR {
|
|
1412
|
+
/** `true` when `key` is a declared claim (in the `defineIdentity` contract, or the always-present `userId`). */
|
|
1413
|
+
declared: boolean;
|
|
1414
|
+
/** Export binding name of the enclosing declaration (`<module>` at file scope). */
|
|
1415
|
+
exportName: string;
|
|
1416
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1417
|
+
file: string;
|
|
1418
|
+
/** The claim key read off the identity bag. */
|
|
1419
|
+
key: string;
|
|
1420
|
+
/** 1-based line of the read, or `0` when unknown. */
|
|
1421
|
+
line: number;
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* One payment webhook-adapter construction in `lunora/` (`createStripeAdapter` /
|
|
1425
|
+
* `createPolarAdapter`). `toleranceSeconds` carries the statically-known
|
|
1426
|
+
* `webhookToleranceSeconds` replay window when it is a plain numeric literal; the
|
|
1427
|
+
* payment-webhook wide-tolerance lint fires when it exceeds a conservative
|
|
1428
|
+
* ceiling. Structurally identical to `AdvisorPaymentWebhook`.
|
|
1429
|
+
*/
|
|
1430
|
+
interface PaymentWebhookIR {
|
|
1431
|
+
/** The adapter factory invoked. */
|
|
1432
|
+
callee: "createPolarAdapter" | "createStripeAdapter";
|
|
1433
|
+
/** Export binding name of the enclosing declaration (`<module>` at file scope). */
|
|
1434
|
+
exportName: string;
|
|
1435
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1436
|
+
file: string;
|
|
1437
|
+
/** 1-based line of the construction, or `0` when unknown. */
|
|
1438
|
+
line: number;
|
|
1439
|
+
/** Statically-known `webhookToleranceSeconds` literal, when present and a plain numeric literal. */
|
|
1440
|
+
toleranceSeconds?: number;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* One `ctx.db.<table>.findMany({ includeDeleted })` list read whose
|
|
1444
|
+
* `includeDeleted` is either a hardcoded `true` or derived from the handler's
|
|
1445
|
+
* `args` — the `soft_delete_include_deleted_from_args` lint input. The lint joins
|
|
1446
|
+
* `table` against the schema's soft-delete tables and `visibility` against
|
|
1447
|
+
* `.public()` before flagging. Structurally identical to `AdvisorSoftDeleteRead`
|
|
1448
|
+
* so values pass straight through without conversion.
|
|
1449
|
+
*/
|
|
1450
|
+
interface SoftDeleteReadIR {
|
|
1451
|
+
/** Export binding name of the procedure performing the read. */
|
|
1452
|
+
exportName: string;
|
|
1453
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1454
|
+
file: string;
|
|
1455
|
+
/** `true` when `includeDeleted` was derived from the handler's `args` (any caller can flip it). */
|
|
1456
|
+
fromArgs: boolean;
|
|
1457
|
+
/** `true` when `includeDeleted` was a hardcoded `true` literal (always resurfaces soft-deleted rows). */
|
|
1458
|
+
hardcodedTrue: boolean;
|
|
1459
|
+
/** 1-based line of the read call. */
|
|
1460
|
+
line: number;
|
|
1461
|
+
/** Table read, or `""` when the table-arg form's first argument wasn't a string literal. */
|
|
1462
|
+
table: string;
|
|
1463
|
+
/** `"internal"` for `internalQuery` / `internalMutation` / `internalAction`. */
|
|
1464
|
+
visibility: "internal" | "public";
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* One `ctx.db.<table>.findMany({ with: { <rel> } })` relation-hydrating list read
|
|
1468
|
+
* — the `masked_relation_leak_via_with` lint input. Column masking does not
|
|
1469
|
+
* descend into `with`-hydrated relations, so a masked table surfaced only through
|
|
1470
|
+
* a `with` on an unprotected parent read is returned in the clear. The lint
|
|
1471
|
+
* resolves each relation accessor to its target table and joins it against the
|
|
1472
|
+
* discovered mask evidence before flagging. Structurally identical to
|
|
1473
|
+
* `AdvisorRelationLoad` so values pass straight through without conversion.
|
|
1474
|
+
*/
|
|
1475
|
+
interface RelationLoadIR {
|
|
1476
|
+
/** Export binding name of the procedure performing the read. */
|
|
1477
|
+
exportName: string;
|
|
1478
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1479
|
+
file: string;
|
|
1480
|
+
/** 1-based line of the read call. */
|
|
1481
|
+
line: number;
|
|
1482
|
+
/** Parent table the read targets, or `""` when the table-arg form's first argument wasn't a string literal. */
|
|
1483
|
+
parentTable: string;
|
|
1484
|
+
/** Relation accessor names named in the read's `with: { … }` map — matched against the parent table's declared relations. */
|
|
1485
|
+
relations: string[];
|
|
1486
|
+
/** `"internal"` for `internalQuery` / `internalMutation` / `internalAction`. */
|
|
1487
|
+
visibility: "internal" | "public";
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* One `query` handler whose `return` hands back the raw rows of a table — the
|
|
1491
|
+
* result of a `ctx.db.<table>.findMany()` / `.findFirst()` / `.get()` read, or a
|
|
1492
|
+
* `ctx.db.query("<table>")…collect()` fluent chain — returned directly (or through
|
|
1493
|
+
* one local `const` hop) with no hand-built projection. The
|
|
1494
|
+
* `output_projection_missing_on_public_read` lint keeps only `visibility ===
|
|
1495
|
+
* "public"` rows with no `.output(...)` / `.use(mask(...))` on the chain, then
|
|
1496
|
+
* joins `table` against the schema and flags one whose columns are PII-named.
|
|
1497
|
+
* Structurally identical to `AdvisorRawRowReturn` so values pass straight through
|
|
1498
|
+
* without conversion.
|
|
1499
|
+
*/
|
|
1500
|
+
interface RawRowReturnIR {
|
|
1501
|
+
/** Export binding name of the query returning the raw rows. */
|
|
1502
|
+
exportName: string;
|
|
1503
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1504
|
+
file: string;
|
|
1505
|
+
/** 1-based line of the `return` (or concise-body) expression. */
|
|
1506
|
+
line: number;
|
|
1507
|
+
/** Table whose raw rows are returned, or `""` when the read's table wasn't a string literal. */
|
|
1508
|
+
table: string;
|
|
1509
|
+
/** `true` when the procedure's builder chain carries a `.use(mask(...))` step. */
|
|
1510
|
+
usesMask: boolean;
|
|
1511
|
+
/** `true` when the procedure's builder chain carries an `.output(...)` return-shape projection. */
|
|
1512
|
+
usesOutput: boolean;
|
|
1513
|
+
/** `"internal"` for `internalQuery`; `"public"` for `query`. */
|
|
1514
|
+
visibility: "internal" | "public";
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* One `query`/`mutation` handler that gates a `ctx.db.get`/`patch`/`delete` on a
|
|
1518
|
+
* null-checked `ctx.db.normalizeId(table, id)` result — the
|
|
1519
|
+
* `normalize_id_used_as_authorization` lint input. `normalizeId` validates an id's
|
|
1520
|
+
* structural shape only (it never reads the database), so a non-null result proves
|
|
1521
|
+
* the id is well-formed, never that the caller owns the row; gating access on it is
|
|
1522
|
+
* an IDOR. The lint owns the negative proof — it keeps only `visibility === "public"`
|
|
1523
|
+
* rows with no `.use(rls(...))` and no ownership/identity mention (`mentionsOwnership`),
|
|
1524
|
+
* then joins `table` against the schema's RLS mode before flagging. Structurally
|
|
1525
|
+
* identical to `AdvisorNormalizeIdAuthorization` so values pass straight through.
|
|
1526
|
+
*/
|
|
1527
|
+
interface NormalizeIdAuthorizationIR {
|
|
1528
|
+
/** Export binding name of the procedure performing the normalize-then-access. */
|
|
1529
|
+
exportName: string;
|
|
1530
|
+
/** Source file relative to `<projectRoot>/lunora/`, without extension. */
|
|
1531
|
+
file: string;
|
|
1532
|
+
/** 1-based line of the `ctx.db.normalizeId(...)` call the access is gated on. */
|
|
1533
|
+
line: number;
|
|
1534
|
+
/** `true` when the handler anywhere reads an ownership-named identifier or `ctx.auth`/`ctx.identity`/… — an intervening ownership signal. */
|
|
1535
|
+
mentionsOwnership: boolean;
|
|
1536
|
+
/** The id-first `ctx.db` sink the normalized id reaches. */
|
|
1537
|
+
sinkMethod: "delete" | "get" | "patch";
|
|
1538
|
+
/** Table named in the `normalizeId` call, or `""` when its table argument wasn't a string literal. */
|
|
1539
|
+
table: string;
|
|
1540
|
+
/** `true` when the procedure's builder chain carries a `.use(rls(...))` step. */
|
|
1541
|
+
usesRls: boolean;
|
|
1542
|
+
/** `"internal"` for `internalQuery`/`internalMutation`; `"public"` for `query`/`mutation`. */
|
|
1543
|
+
visibility: "internal" | "public";
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* One committed `wrangler.jsonc` `vars` entry whose value is a plaintext secret —
|
|
1547
|
+
* the `plaintext_secret_in_wrangler_vars` lint input. `vars` are baked into the
|
|
1548
|
+
* deployed Worker in cleartext and checked into source control, so a real API key
|
|
1549
|
+
* / token / private key there ships the secret to every reader of the repo and the
|
|
1550
|
+
* bundle; it belongs in a Secrets Store binding or `wrangler secret put`. Produced
|
|
1551
|
+
* by `@lunora/config` (which reads `wrangler.jsonc`), not a ts-morph feeder —
|
|
1552
|
+
* codegen only passes it through. Structurally identical to `AdvisorWranglerVariable`.
|
|
1553
|
+
*/
|
|
1554
|
+
interface WranglerVariableIR {
|
|
1555
|
+
/** The `wrangler.jsonc` file the var was read from, relative to the project root. */
|
|
1556
|
+
file: string;
|
|
1557
|
+
/** The offending `vars` key (e.g. `STRIPE_SECRET_KEY`). */
|
|
1558
|
+
key: string;
|
|
1559
|
+
/** Heuristic that matched, e.g. `stripe_live_key` / `private_key` / `secret_named_var`. */
|
|
1560
|
+
kind: string;
|
|
1561
|
+
/** Redacted preview of the value (first few chars + length) for the finding detail — never the full secret. */
|
|
1562
|
+
preview: string;
|
|
1563
|
+
}
|
|
887
1564
|
interface ProjectIR {
|
|
888
1565
|
crons: ReadonlyArray<CronJobIR>;
|
|
889
1566
|
functions: ReadonlyArray<FunctionIR>;
|
|
@@ -896,8 +1573,9 @@ interface ProjectIR {
|
|
|
896
1573
|
* Run the static lints against a discovered {@link SchemaIR} and the reads/writes/calls
|
|
897
1574
|
* found in function bodies: query reads feed `filter_without_index`, insert writes
|
|
898
1575
|
* feed `table_without_insert`, authApi calls feed `auth_api_call_without_headers`,
|
|
899
|
-
* rls procedure snapshots feed `rls_uncovered_table`,
|
|
900
|
-
* snapshots feed `mask_uncovered_pii_column
|
|
1576
|
+
* rls procedure snapshots feed `rls_uncovered_table`, mask procedure
|
|
1577
|
+
* snapshots feed `mask_uncovered_pii_column`, and per-column mask strategies
|
|
1578
|
+
* feed `mask_weak_hash_strategy_on_pii`; declared containers
|
|
901
1579
|
* feed the `container_*` lints; declared workflows (with their durable step labels)
|
|
902
1580
|
* + `ctx.workflows.get(...)` call sites feed the `workflow_unused` /
|
|
903
1581
|
* `workflow_unknown_target` / duplicate-step-name lints; non-deterministic
|
|
@@ -907,7 +1585,7 @@ interface ProjectIR {
|
|
|
907
1585
|
* pass straight through without conversion. Returns the findings; surfacing them
|
|
908
1586
|
* (console, error overlay, studio Advisors table) is the caller's choice.
|
|
909
1587
|
*/
|
|
910
|
-
declare const lintSchema: (schema: SchemaIR, queries?: ReadonlyArray<QueryReadIR>, inserts?: ReadonlyArray<InsertWriteIR>, authApiCalls?: ReadonlyArray<AuthApiCallIR>, rlsProcedures?: ReadonlyArray<RlsProcedureIR>, containers?: ReadonlyArray<ContainerIR>, workflows?: ReadonlyArray<WorkflowIR>, workflowCalls?: ReadonlyArray<WorkflowCallIR>, maskProcedures?: ReadonlyArray<MaskProcedureIR>, nondeterministicCalls?: ReadonlyArray<NondeterministicCallIR>, procedureProtections?: ReadonlyArray<ProcedureMiddlewareIR>, argumentValidators?: ReadonlyArray<ArgumentValidatorIR>, secretLiterals?: ReadonlyArray<SecretLiteralIR>, sqlInterpolations?: ReadonlyArray<SqlInterpolationIR>, adminRoutes?: ReadonlyArray<AdminRouteIR>, r2sqlCalls?: ReadonlyArray<R2sqlCallIR>, shapes?: ReadonlyArray<ShapeIR>, mutatorWrites?: ReadonlyArray<MutatorWriteIR>) => Finding[];
|
|
1588
|
+
declare const lintSchema: (schema: SchemaIR, queries?: ReadonlyArray<QueryReadIR>, inserts?: ReadonlyArray<InsertWriteIR>, authApiCalls?: ReadonlyArray<AuthApiCallIR>, rlsProcedures?: ReadonlyArray<RlsProcedureIR>, containers?: ReadonlyArray<ContainerIR>, workflows?: ReadonlyArray<WorkflowIR>, workflowCalls?: ReadonlyArray<WorkflowCallIR>, maskProcedures?: ReadonlyArray<MaskProcedureIR>, nondeterministicCalls?: ReadonlyArray<NondeterministicCallIR>, procedureProtections?: ReadonlyArray<ProcedureMiddlewareIR>, argumentValidators?: ReadonlyArray<ArgumentValidatorIR>, secretLiterals?: ReadonlyArray<SecretLiteralIR>, sqlInterpolations?: ReadonlyArray<SqlInterpolationIR>, adminRoutes?: ReadonlyArray<AdminRouteIR>, r2sqlCalls?: ReadonlyArray<R2sqlCallIR>, shapes?: ReadonlyArray<ShapeIR>, mutatorWrites?: ReadonlyArray<MutatorWriteIR>, configCalls?: ReadonlyArray<ConfigCallIR>, argumentDerivedFetches?: ReadonlyArray<ArgumentDerivedFetchIR>, kvKeyAccesses?: ReadonlyArray<KvKeyAccessIR>, ownerFieldWrites?: ReadonlyArray<OwnerFieldWriteIR>, storageKeyAccesses?: ReadonlyArray<StorageKeyAccessIR>, aiRawRuns?: ReadonlyArray<AiRawRunIR>, containerKeyAccesses?: ReadonlyArray<ContainerKeyAccessIR>, mailRecipientAccesses?: ReadonlyArray<MailRecipientAccessIR>, vectorNamespaceAccesses?: ReadonlyArray<VectorNamespaceAccessIR>, browserUrlAccesses?: ReadonlyArray<BrowserUrlAccessIR>, privilegedDispatches?: ReadonlyArray<PrivilegedDispatchIR>, containerOverrides?: ReadonlyArray<ContainerOverrideIR>, authConfigs?: ReadonlyArray<AuthConfigIR>, maskStrategies?: ReadonlyArray<MaskStrategyIR>, imageDeliveryUrlAccesses?: ReadonlyArray<ImageDeliveryUrlAccessIR>, ratelimitKeySelectors?: ReadonlyArray<RatelimitKeySelectorIR>, storageUploads?: ReadonlyArray<StorageUploadIR>, httpActionGuards?: ReadonlyArray<HttpActionGuardIR>, httpHeaderWrites?: ReadonlyArray<HttpHeaderWriteIR>, failOpenGuards?: ReadonlyArray<FailOpenGuardIR>, flagSecurityDefaults?: ReadonlyArray<FlagSecurityDefaultIR>, aiToolSideEffects?: ReadonlyArray<AiToolSideEffectIR>, identityClaimReads?: ReadonlyArray<IdentityClaimReadIR>, paymentWebhooks?: ReadonlyArray<PaymentWebhookIR>, softDeleteReads?: ReadonlyArray<SoftDeleteReadIR>, relationLoads?: ReadonlyArray<RelationLoadIR>, rawRowReturns?: ReadonlyArray<RawRowReturnIR>, normalizeIdAuthorizations?: ReadonlyArray<NormalizeIdAuthorizationIR>, wranglerVariables?: ReadonlyArray<WranglerVariableIR>) => Finding[];
|
|
911
1589
|
/**
|
|
912
1590
|
* Render advisor findings as a single multi-line string for console surfacing:
|
|
913
1591
|
* a one-line summary header followed by one `[LEVEL] name: detail` line per
|
|
@@ -1765,6 +2443,14 @@ interface CodegenOptions {
|
|
|
1765
2443
|
* a breaking change. Ignored when `dryRun` is true.
|
|
1766
2444
|
*/
|
|
1767
2445
|
updateSchemaBaseline?: boolean;
|
|
2446
|
+
/**
|
|
2447
|
+
* Committed `wrangler.jsonc` `vars` entries that hold plaintext secrets — the
|
|
2448
|
+
* `plaintext_secret_in_wrangler_vars` lint input. Produced by `@lunora/config`
|
|
2449
|
+
* (which reads `wrangler.jsonc`) and threaded through by the CLI / Vite plugin;
|
|
2450
|
+
* codegen only forwards it to the advisor. Absent when no wrangler config is
|
|
2451
|
+
* present or the caller doesn't scan it.
|
|
2452
|
+
*/
|
|
2453
|
+
wranglerVariables?: ReadonlyArray<WranglerVariableIR>;
|
|
1768
2454
|
}
|
|
1769
2455
|
interface CodegenResult {
|
|
1770
2456
|
/**
|
|
@@ -1879,5 +2565,9 @@ declare const validatorIrToJsonSchema: (validator: ValidatorIR) => JsonSchema;
|
|
|
1879
2565
|
* the contract; clients switch on `error.code`. Kept sorted for stable output.
|
|
1880
2566
|
*/
|
|
1881
2567
|
declare const LUNORA_ERROR_CODES: ReadonlyArray<string>;
|
|
2568
|
+
/** The matching secret rule's `kind` for a string value, or `undefined` when none matches. */
|
|
2569
|
+
declare const secretKindOf: (value: string) => string | undefined;
|
|
2570
|
+
/** A redacted preview of a secret value — first 4 chars plus its length, never the full value. */
|
|
2571
|
+
declare const redact: (value: string) => string;
|
|
1882
2572
|
declare const VERSION = "0.0.0";
|
|
1883
|
-
export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, FLAGS_FILENAME, type FieldSnapshot, type FlagsIR, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, MUTATORS_FILENAME, type MaskProcedureIR, type MigrationIR, type MutatorIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, SHAPES_FILENAME, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type ShapeIR, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverMutators, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverShapes, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, formatAdvisories, lintSchema, parseSchemaSnapshot, refreshCodegenProject, runCodegen, schemaFromIr, serializeSchemaSnapshot, validatorIrToJsonSchema };
|
|
2573
|
+
export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, FLAGS_FILENAME, type FieldSnapshot, type FlagsIR, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, MUTATORS_FILENAME, type MaskProcedureIR, type MigrationIR, type MutatorIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, SHAPES_FILENAME, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type ShapeIR, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, type WranglerVariableIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverMutators, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverShapes, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, formatAdvisories, lintSchema, parseSchemaSnapshot, redact, refreshCodegenProject, runCodegen, schemaFromIr, secretKindOf, serializeSchemaSnapshot, validatorIrToJsonSchema };
|