@sarj/eslint-plugin 19.14.0 → 19.15.0
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/catalog.js +271 -8
- package/dist/{chunk-YWEV6GQM.js → chunk-SIHWYU24.js} +330 -38
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/catalog.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
publicDocumentation,
|
|
3
3
|
ruleSpec,
|
|
4
4
|
src_default
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SIHWYU24.js";
|
|
6
6
|
|
|
7
7
|
// src/docs/directive-disable-enable-pair.ts
|
|
8
8
|
var DIRECTIVE_DISABLE_ENABLE_PAIR_DOCUMENTATION = {
|
|
@@ -2523,8 +2523,13 @@ var NO_RAW_ENV_DOCUMENTATION = {
|
|
|
2523
2523
|
remediation: "Define a Zod settings schema, call SettingsSchema.parse(process.env) once at startup, and export or inject that single parsed settings object. Use Pydantic/Pydantic Settings for the equivalent Python boundary. Required values must fail before remote effects; preserve independent security policy and existing optional defaults.",
|
|
2524
2524
|
category: "correctness",
|
|
2525
2525
|
limitations: [
|
|
2526
|
-
"Host markers, assignment targets, tests, ordinary scripts and build config retain their policy exemptions. Boundary-named modules, including env.mjs and settings.ts, are checked.
|
|
2527
|
-
"
|
|
2526
|
+
"Host markers, assignment targets, tests, ordinary scripts and build config retain their policy exemptions. Boundary-named modules, including env.mjs and settings.ts, are checked. Native named process env imports and simple const { env } = process aliases follow the same read policy; foreign, type-only, shadowed and mutable sources are not inferred.",
|
|
2527
|
+
"Each raw value must reach a parse/safeParse argument or supported environment loader, directly or through stable nonexported const aliases whose every use reaches validation. Directly awaited parseAsync/safeParseAsync calls also count; unawaited promises or later await-through-alias flows do not. Conditional tests and logical-and conditions are control inputs, not the parsed result; value arms and existing nullish/logical-or defaults remain supported.",
|
|
2528
|
+
"Simple nonexported const object patterns may select static keys into plain identifier bindings when every ordinary read of each selected field reaches validation. Type-only references are ignored. Native whole-environment host marker keys retain their exemptions, including through stable aliases; an arbitrary raw field with a marker-shaped property does not. Unused, unparsed, escaping or later-written ordinary fields fail the proof. Opaque computed keys, defaults, rest and nested patterns remain unsupported.",
|
|
2529
|
+
"Renamed value imports of createEnv from @t3-oss/env-core or @t3-oss/env-nextjs retain loader ownership. Existing unresolved project parse/safeParse and legacy createEnv protocols remain accepted; this is not a proof of their implementation. A narrowly proven private identity adapter only forwards its input: that input must still reach an actual parser.",
|
|
2530
|
+
"Identity-adapter detection is limited to an explicit module, no namespace or runtime eval reference, one stable nonexported binding and a synchronous function whose sole plain identifier parameter is returned unchanged. An object may contain only one or two unique own parse/safeParse identity methods, with every ordinary receiver reference a direct method call. Exported/mutable/escaped receivers, spreads, getters, duplicate/opaque keys, extra methods, imported loaders, real Zod wrappers and optional/awaited adapter-result ambiguity retain the existing protocol. The proof stops after 128 binding references, 512 module references or 32 enclosing AST parents; it adds no whole-program library-provenance requirement.",
|
|
2531
|
+
"Only unshadowed Number/String/Boolean/BigInt calls and one-argument native JSON decoding are traversed as preprocessing. JSON.parse alone does not validate settings: its decoded result must itself reach the parser. JSON revivers and arbitrary calls cannot consume raw settings under this preprocessing exemption.",
|
|
2532
|
+
"This is bounded same-file syntax analysis (24 flow steps, 128 selected fields per pattern, 128 reads per binding and 512 total expression/field visits per raw read; whole-environment host-marker provenance follows at most 16 aliases); flows exceeding these budgets are unreported. It does not prove a parser's implementation, cross-file schemas, schema completeness, safeParse failure handling or a project-wide singleton. Handle safeParse failure before exposing result.data; prefer throwing parse at startup. Nested component settings and separate server/client runtime boundaries remain valid.",
|
|
2528
2533
|
"Assignment-target exemptions also include compound writes that read the previous value. Whole-environment spreads outside boundary modules retain the existing forwarding exemption; a spread inside a boundary must also reach its parser. No automatic rewrite."
|
|
2529
2534
|
],
|
|
2530
2535
|
examples: [
|
|
@@ -2559,6 +2564,190 @@ var NO_RAW_ENV_DOCUMENTATION = {
|
|
|
2559
2564
|
focusPath: "src/env.ts",
|
|
2560
2565
|
expectedCount: 1,
|
|
2561
2566
|
public: true
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
id: "native-env-import-read",
|
|
2570
|
+
scenarioId: "native-env-import",
|
|
2571
|
+
title: "Native env import aliases still need validation",
|
|
2572
|
+
outcome: "match",
|
|
2573
|
+
files: [
|
|
2574
|
+
{
|
|
2575
|
+
path: "src/database.ts",
|
|
2576
|
+
source: 'import { env as raw } from "node:process";\nexport const databaseUrl = raw.DATABASE_URL;'
|
|
2577
|
+
}
|
|
2578
|
+
],
|
|
2579
|
+
focusPath: "src/database.ts",
|
|
2580
|
+
expectedCount: 1,
|
|
2581
|
+
public: true
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
id: "native-env-import-validated",
|
|
2585
|
+
scenarioId: "native-env-import",
|
|
2586
|
+
title: "Parse the imported environment once and share settings",
|
|
2587
|
+
outcome: "no-match",
|
|
2588
|
+
files: [
|
|
2589
|
+
{
|
|
2590
|
+
path: "src/database.ts",
|
|
2591
|
+
source: 'import { settings } from "./env.js";\nexport const databaseUrl = settings.DATABASE_URL;'
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
path: "src/env.ts",
|
|
2595
|
+
source: 'import { env as raw } from "node:process";\nimport { z } from "zod";\nconst SettingsSchema = z.object({ DATABASE_URL: z.url() });\nexport const settings = SettingsSchema.parse(raw);'
|
|
2596
|
+
}
|
|
2597
|
+
],
|
|
2598
|
+
focusPath: "src/env.ts",
|
|
2599
|
+
expectedCount: 0,
|
|
2600
|
+
public: true
|
|
2601
|
+
},
|
|
2602
|
+
{
|
|
2603
|
+
id: "json-decoding-without-validation",
|
|
2604
|
+
scenarioId: "json-settings",
|
|
2605
|
+
title: "JSON decoding does not validate the settings contract",
|
|
2606
|
+
outcome: "match",
|
|
2607
|
+
files: [
|
|
2608
|
+
{
|
|
2609
|
+
path: "src/env.ts",
|
|
2610
|
+
source: "export const settings = JSON.parse(process.env.SETTINGS_JSON);"
|
|
2611
|
+
}
|
|
2612
|
+
],
|
|
2613
|
+
focusPath: "src/env.ts",
|
|
2614
|
+
expectedCount: 1,
|
|
2615
|
+
public: true
|
|
2616
|
+
},
|
|
2617
|
+
{
|
|
2618
|
+
id: "json-decoding-then-validation",
|
|
2619
|
+
scenarioId: "json-settings",
|
|
2620
|
+
title: "Validate decoded environment JSON with the actual schema",
|
|
2621
|
+
outcome: "no-match",
|
|
2622
|
+
files: [
|
|
2623
|
+
{
|
|
2624
|
+
path: "src/env.ts",
|
|
2625
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_ORIGIN: z.url() });\nexport const settings = SettingsSchema.parse(JSON.parse(process.env.SETTINGS_JSON));'
|
|
2626
|
+
}
|
|
2627
|
+
],
|
|
2628
|
+
focusPath: "src/env.ts",
|
|
2629
|
+
expectedCount: 0,
|
|
2630
|
+
public: true
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
id: "raw-input-controls-effects",
|
|
2634
|
+
scenarioId: "conditional-settings",
|
|
2635
|
+
title: "A raw conditional input is not the value reaching the parser",
|
|
2636
|
+
outcome: "match",
|
|
2637
|
+
files: [
|
|
2638
|
+
{
|
|
2639
|
+
path: "src/env.ts",
|
|
2640
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_ORIGIN: z.url() });\nexport const settings = SettingsSchema.parse(process.env.LOAD_REMOTE ? fetch("https://example.test/config") : {});'
|
|
2641
|
+
}
|
|
2642
|
+
],
|
|
2643
|
+
focusPath: "src/env.ts",
|
|
2644
|
+
expectedCount: 1,
|
|
2645
|
+
public: true
|
|
2646
|
+
},
|
|
2647
|
+
{
|
|
2648
|
+
id: "validated-input-controls-effects",
|
|
2649
|
+
scenarioId: "conditional-settings",
|
|
2650
|
+
title: "Parse the control setting before remote effects",
|
|
2651
|
+
outcome: "no-match",
|
|
2652
|
+
files: [
|
|
2653
|
+
{
|
|
2654
|
+
path: "src/env.ts",
|
|
2655
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ LOAD_REMOTE: z.enum(["0", "1"]).transform(value => value === "1") });\nexport const settings = SettingsSchema.parse(process.env);\nif (settings.LOAD_REMOTE) await fetch("https://example.test/config");'
|
|
2656
|
+
}
|
|
2657
|
+
],
|
|
2658
|
+
focusPath: "src/env.ts",
|
|
2659
|
+
expectedCount: 0,
|
|
2660
|
+
public: true
|
|
2661
|
+
},
|
|
2662
|
+
{
|
|
2663
|
+
id: "async-settings-promise",
|
|
2664
|
+
scenarioId: "async-settings",
|
|
2665
|
+
title: "An unfinished parser promise is not startup settings",
|
|
2666
|
+
outcome: "match",
|
|
2667
|
+
files: [
|
|
2668
|
+
{
|
|
2669
|
+
path: "src/env.ts",
|
|
2670
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nexport const settings = SettingsSchema.parseAsync(process.env);'
|
|
2671
|
+
}
|
|
2672
|
+
],
|
|
2673
|
+
focusPath: "src/env.ts",
|
|
2674
|
+
expectedCount: 1,
|
|
2675
|
+
public: true
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
id: "async-settings-awaited",
|
|
2679
|
+
scenarioId: "async-settings",
|
|
2680
|
+
title: "Await parsing before exporting the shared settings object",
|
|
2681
|
+
outcome: "no-match",
|
|
2682
|
+
files: [
|
|
2683
|
+
{
|
|
2684
|
+
path: "src/env.ts",
|
|
2685
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nexport const settings = await SettingsSchema.parseAsync(process.env);'
|
|
2686
|
+
}
|
|
2687
|
+
],
|
|
2688
|
+
focusPath: "src/env.ts",
|
|
2689
|
+
expectedCount: 0,
|
|
2690
|
+
public: true
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
id: "selected-fields-raw-escape",
|
|
2694
|
+
scenarioId: "selected-settings-fields",
|
|
2695
|
+
title: "Parsing one selected field does not validate another raw export",
|
|
2696
|
+
outcome: "match",
|
|
2697
|
+
files: [
|
|
2698
|
+
{
|
|
2699
|
+
path: "src/env.ts",
|
|
2700
|
+
source: 'import { env as raw } from "node:process";\nimport { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nconst { API_KEY, API_ORIGIN } = raw;\nexport const settings = SettingsSchema.parse({ API_KEY });\nexport const unparsedOrigin = API_ORIGIN;'
|
|
2701
|
+
}
|
|
2702
|
+
],
|
|
2703
|
+
focusPath: "src/env.ts",
|
|
2704
|
+
expectedCount: 1,
|
|
2705
|
+
public: true
|
|
2706
|
+
},
|
|
2707
|
+
{
|
|
2708
|
+
id: "selected-fields-validated",
|
|
2709
|
+
scenarioId: "selected-settings-fields",
|
|
2710
|
+
title: "Validate every selected ordinary field and reuse the parsed result",
|
|
2711
|
+
outcome: "no-match",
|
|
2712
|
+
files: [
|
|
2713
|
+
{
|
|
2714
|
+
path: "src/env.ts",
|
|
2715
|
+
source: 'import { env as raw } from "node:process";\nimport { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1), API_ORIGIN: z.url() });\nconst { API_KEY, API_ORIGIN } = raw;\nexport const settings = SettingsSchema.parse({ API_KEY, API_ORIGIN });'
|
|
2716
|
+
}
|
|
2717
|
+
],
|
|
2718
|
+
focusPath: "src/env.ts",
|
|
2719
|
+
expectedCount: 0,
|
|
2720
|
+
public: true
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
id: "identity-loader-without-parser",
|
|
2724
|
+
scenarioId: "closed-identity-adapter",
|
|
2725
|
+
title: "A local identity adapter does not validate environment input",
|
|
2726
|
+
outcome: "match",
|
|
2727
|
+
files: [
|
|
2728
|
+
{
|
|
2729
|
+
path: "src/env.ts",
|
|
2730
|
+
source: "const createEnv = input => input;\nexport const settings = createEnv(process.env);"
|
|
2731
|
+
}
|
|
2732
|
+
],
|
|
2733
|
+
focusPath: "src/env.ts",
|
|
2734
|
+
expectedCount: 1,
|
|
2735
|
+
public: true
|
|
2736
|
+
},
|
|
2737
|
+
{
|
|
2738
|
+
id: "identity-loader-before-real-parser",
|
|
2739
|
+
scenarioId: "closed-identity-adapter",
|
|
2740
|
+
title: "An identity adapter can forward into the actual Zod parser",
|
|
2741
|
+
outcome: "no-match",
|
|
2742
|
+
files: [
|
|
2743
|
+
{
|
|
2744
|
+
path: "src/env.ts",
|
|
2745
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nconst createEnv = input => input;\nexport const settings = SettingsSchema.parse(createEnv(process.env));'
|
|
2746
|
+
}
|
|
2747
|
+
],
|
|
2748
|
+
focusPath: "src/env.ts",
|
|
2749
|
+
expectedCount: 0,
|
|
2750
|
+
public: true
|
|
2562
2751
|
}
|
|
2563
2752
|
]
|
|
2564
2753
|
};
|
|
@@ -3205,12 +3394,13 @@ var NO_SENTINEL_RETURN_ON_CATCH_DOCUMENTATION = {
|
|
|
3205
3394
|
|
|
3206
3395
|
// src/docs/no-server-env-in-client-component.ts
|
|
3207
3396
|
var NO_SERVER_ENV_IN_CLIENT_COMPONENT_DOCUMENTATION = {
|
|
3208
|
-
summary: "server-only environment settings imported by a client
|
|
3209
|
-
rationale: "Next.js client modules run in the browser, where server-only environment values are unavailable; importing a server settings module can produce undefined configuration or bundle a secret-bearing module into the client graph.",
|
|
3210
|
-
remediation: "Pass an explicitly public value from a Server Component, or import it from a separately validated client-settings module backed only by NEXT_PUBLIC_* values.",
|
|
3397
|
+
summary: "server-only environment settings imported or re-exported by a client module",
|
|
3398
|
+
rationale: "Next.js client modules run in the browser, where server-only environment values are unavailable; importing or re-exporting a server settings module can produce undefined configuration or bundle a secret-bearing module into the client graph.",
|
|
3399
|
+
remediation: "Pass an explicitly public value from a Server Component, or import/re-export it from a separately validated client-settings module backed only by NEXT_PUBLIC_* values.",
|
|
3211
3400
|
category: "correctness",
|
|
3212
3401
|
limitations: [
|
|
3213
|
-
"Only static
|
|
3402
|
+
"Only direct static runtime imports and re-exports in files with a top-level 'use client' directive targeting the existing server-env/server-settings module protocol are checked. Type-only declarations/specifiers, local exports without a source and ordinary server modules are excluded.",
|
|
3403
|
+
"Dynamic imports, require calls and transitive barrel graphs are not followed. Public client-settings modules and unrelated module paths are not classified as server-only."
|
|
3214
3404
|
],
|
|
3215
3405
|
examples: [
|
|
3216
3406
|
{
|
|
@@ -3240,6 +3430,44 @@ var NO_SERVER_ENV_IN_CLIENT_COMPONENT_DOCUMENTATION = {
|
|
|
3240
3430
|
focusPath: "status-card.tsx",
|
|
3241
3431
|
expectedCount: 1,
|
|
3242
3432
|
public: true
|
|
3433
|
+
},
|
|
3434
|
+
{
|
|
3435
|
+
id: "public-client-settings-re-export",
|
|
3436
|
+
scenarioId: "client-re-export",
|
|
3437
|
+
title: "Forward public settings from the client boundary",
|
|
3438
|
+
outcome: "no-match",
|
|
3439
|
+
files: [
|
|
3440
|
+
{
|
|
3441
|
+
path: "client-barrel.tsx",
|
|
3442
|
+
source: "'use client';\nexport { CLIENT_SETTINGS } from './client-settings.js';\n"
|
|
3443
|
+
},
|
|
3444
|
+
{
|
|
3445
|
+
path: "client-settings.ts",
|
|
3446
|
+
source: "import { z } from 'zod';\nconst SettingsSchema = z.object({ apiOrigin: z.url() });\nexport const CLIENT_SETTINGS = SettingsSchema.parse({ apiOrigin: process.env.NEXT_PUBLIC_API_ORIGIN });\n"
|
|
3447
|
+
}
|
|
3448
|
+
],
|
|
3449
|
+
focusPath: "client-barrel.tsx",
|
|
3450
|
+
expectedCount: 0,
|
|
3451
|
+
public: true
|
|
3452
|
+
},
|
|
3453
|
+
{
|
|
3454
|
+
id: "server-settings-client-re-export",
|
|
3455
|
+
scenarioId: "client-re-export",
|
|
3456
|
+
title: "A client barrel cannot forward server settings",
|
|
3457
|
+
outcome: "match",
|
|
3458
|
+
files: [
|
|
3459
|
+
{
|
|
3460
|
+
path: "client-barrel.tsx",
|
|
3461
|
+
source: "'use client';\nexport { SERVER_SETTINGS } from './server-settings.js';\n"
|
|
3462
|
+
},
|
|
3463
|
+
{
|
|
3464
|
+
path: "server-settings.ts",
|
|
3465
|
+
source: "import { z } from 'zod';\nconst SettingsSchema = z.object({ apiOrigin: z.url() });\nexport const SERVER_SETTINGS = SettingsSchema.parse({ apiOrigin: process.env.API_ORIGIN });\n"
|
|
3466
|
+
}
|
|
3467
|
+
],
|
|
3468
|
+
focusPath: "client-barrel.tsx",
|
|
3469
|
+
expectedCount: 1,
|
|
3470
|
+
public: true
|
|
3243
3471
|
}
|
|
3244
3472
|
]
|
|
3245
3473
|
};
|
|
@@ -4545,7 +4773,8 @@ var PREFER_CONFIGURED_CLOUD_SCOPE_DOCUMENTATION = {
|
|
|
4545
4773
|
requiresTypeInformation: false,
|
|
4546
4774
|
limitations: [
|
|
4547
4775
|
"Initial coverage is canonical HTTPS Cloudflare v4 account-management URLs with a 32-character hexadecimal account segment sent to an unshadowed fetch, stable const alias, or fetch-default parameter. Other providers, SDKs, arbitrary endpoints, Worker names and resource IDs require separate evidence.",
|
|
4548
|
-
"Follows bounded same-file const aliases, concatenations and template strings
|
|
4776
|
+
"Follows bounded same-file const aliases, concatenations and template strings. One-argument native URL wrappers are supported only for fully known absolute account URLs whose native normalization leaves the text unchanged. Local/imported constructors, dynamic constructor arguments, relative-base URLs, Request objects, unknown accounts, fallback expressions, function returns and cross-file values remain unreported.",
|
|
4777
|
+
"Native URL constructor/prototype mutation or capability capture outside new/typeof vetoes wrapper folding. Escaping/dynamic global receiver access and ambient eval/Function also leave URL semantics unknown. A stored URL must be nonexported, have at most 128 references, and every runtime read must be a direct native fetch argument; mutable instances, aliases, injected transports and other escapes remain unknown. These URL limits do not hide independent direct string fetch findings. Direct writes to ambient fetch or its global receivers retain their existing veto; indirect runtime monkeypatches are not modeled.",
|
|
4549
4778
|
"Analysis stops at 16 nested expressions or fetch aliases, 256 URL-expression visits per request, 128 string fragments or 8,192 UTF-16 code units of assembled URL text. These limits deliberately leave more complex expressions unreported.",
|
|
4550
4779
|
"Scripts remain covered. Test/fixture and generated files are excluded. Comparisons, schemas, allowlists and unused URL strings do not produce findings. Each originating account literal is reported at most once per file.",
|
|
4551
4780
|
"This is a warning without an automatic fix: the deployment configuration boundary and independent authorization policy require review. Application code should import validated configuration as required by no-raw-env; browser-exposed variables must never contain secrets."
|
|
@@ -4586,6 +4815,40 @@ var PREFER_CONFIGURED_CLOUD_SCOPE_DOCUMENTATION = {
|
|
|
4586
4815
|
focusPath: ".github/scripts/publish.mjs",
|
|
4587
4816
|
expectedCount: 0,
|
|
4588
4817
|
public: true
|
|
4818
|
+
},
|
|
4819
|
+
{
|
|
4820
|
+
id: "native-url-literal-account",
|
|
4821
|
+
scenarioId: "native-url-account",
|
|
4822
|
+
title: "A native URL wrapper still fixes the outgoing account in source",
|
|
4823
|
+
outcome: "match",
|
|
4824
|
+
files: [
|
|
4825
|
+
{
|
|
4826
|
+
path: "src/cloud-client.ts",
|
|
4827
|
+
source: 'export function list() { return fetch(new URL("https://api.cloudflare.com/client/v4/accounts/0123456789abcdef0123456789abcdef/workers/scripts")); }'
|
|
4828
|
+
}
|
|
4829
|
+
],
|
|
4830
|
+
focusPath: "src/cloud-client.ts",
|
|
4831
|
+
expectedCount: 1,
|
|
4832
|
+
public: true
|
|
4833
|
+
},
|
|
4834
|
+
{
|
|
4835
|
+
id: "native-url-configured-account",
|
|
4836
|
+
scenarioId: "native-url-account",
|
|
4837
|
+
title: "Supply the account from shared Zod-validated settings",
|
|
4838
|
+
outcome: "no-match",
|
|
4839
|
+
files: [
|
|
4840
|
+
{
|
|
4841
|
+
path: "src/cloud-client.ts",
|
|
4842
|
+
source: 'import { settings } from "./env.js";\nexport function list() { return fetch(new URL(`https://api.cloudflare.com/client/v4/accounts/${settings.CLOUDFLARE_ACCOUNT_ID}/workers/scripts`)); }'
|
|
4843
|
+
},
|
|
4844
|
+
{
|
|
4845
|
+
path: "src/env.ts",
|
|
4846
|
+
source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ CLOUDFLARE_ACCOUNT_ID: z.string().length(32).regex(/^[a-fA-F0-9]+$/u) });\nexport const settings = SettingsSchema.parse(process.env);'
|
|
4847
|
+
}
|
|
4848
|
+
],
|
|
4849
|
+
focusPath: "src/cloud-client.ts",
|
|
4850
|
+
expectedCount: 0,
|
|
4851
|
+
public: true
|
|
4589
4852
|
}
|
|
4590
4853
|
]
|
|
4591
4854
|
};
|
|
@@ -15576,9 +15576,9 @@ var PLATFORM_MARKERS = /* @__PURE__ */ new Set([
|
|
|
15576
15576
|
"VERCEL_ENV",
|
|
15577
15577
|
"CI"
|
|
15578
15578
|
]);
|
|
15579
|
-
function
|
|
15580
|
-
const receiver = unwrapExpression(node
|
|
15581
|
-
if (receiver.type !== "Identifier"
|
|
15579
|
+
function isNativeProcess(node, sourceCode2) {
|
|
15580
|
+
const receiver = unwrapExpression(node);
|
|
15581
|
+
if (receiver.type !== "Identifier") return false;
|
|
15582
15582
|
const binding3 = findVariable(sourceCode2.getScope(receiver), receiver.name);
|
|
15583
15583
|
if (binding3 === null || binding3.defs.length === 0) return receiver.name === "process";
|
|
15584
15584
|
return binding3.defs.every((definition) => {
|
|
@@ -15586,6 +15586,9 @@ function isProcessEnv(node, sourceCode2) {
|
|
|
15586
15586
|
return definition.type === "ImportBinding" && parent?.type === "ImportDeclaration" && parent.importKind !== "type" && PROCESS_MODULES.includes(parent.source.value) && PROCESS_IMPORT_SPECIFIERS.includes(definition.node.type);
|
|
15587
15587
|
});
|
|
15588
15588
|
}
|
|
15589
|
+
function isProcessEnv(node, sourceCode2) {
|
|
15590
|
+
return getPropertyName(node) === "env" && isNativeProcess(node.object, sourceCode2);
|
|
15591
|
+
}
|
|
15589
15592
|
function isImportMetaEnv(node) {
|
|
15590
15593
|
return getPropertyName(node) === "env" && node.object.type === "MetaProperty" && node.object.meta.name === "import" && node.object.property.name === "meta";
|
|
15591
15594
|
}
|
|
@@ -15611,37 +15614,170 @@ function isWholeEnvSpread(node) {
|
|
|
15611
15614
|
const parent = parentOf(node);
|
|
15612
15615
|
return parent?.type === "SpreadElement" && parent.argument === node;
|
|
15613
15616
|
}
|
|
15614
|
-
function
|
|
15615
|
-
|
|
15617
|
+
function createValidationBudget() {
|
|
15618
|
+
let remaining = 512;
|
|
15619
|
+
return () => remaining--;
|
|
15620
|
+
}
|
|
15621
|
+
function reachesValidation(node, sourceCode2, seen = /* @__PURE__ */ new Set(), depth = 0, budget = createValidationBudget()) {
|
|
15622
|
+
if (depth >= 24 || budget() <= 0) return true;
|
|
15616
15623
|
const parent = parentOf(node);
|
|
15617
15624
|
if (parent === null) return false;
|
|
15618
15625
|
switch (parent.type) {
|
|
15619
|
-
case "CallExpression":
|
|
15620
|
-
|
|
15626
|
+
case "CallExpression": {
|
|
15627
|
+
const identity2 = isClosedIdentityAdapter(parent, sourceCode2);
|
|
15628
|
+
return parent.arguments.some((argument) => argument === node) && (!identity2 && isValidationCall(parent, sourceCode2) || (identity2 || isScalarCoercion(parent, sourceCode2) || isNativeJsonParse(parent, sourceCode2) && parent.arguments.length === 1) && reachesValidation(parent, sourceCode2, seen, depth + 1, budget));
|
|
15629
|
+
}
|
|
15621
15630
|
case "VariableDeclarator":
|
|
15622
15631
|
return parent.init === node && validatedAlias(parent, sourceCode2, seen, depth, budget);
|
|
15623
15632
|
case "MemberExpression":
|
|
15624
15633
|
return parent.object === node && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
|
|
15625
15634
|
case "Property":
|
|
15626
15635
|
return parent.value === node && parent.kind === "init" && !parent.method && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
|
|
15636
|
+
case "ConditionalExpression":
|
|
15637
|
+
return parent.test !== node && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
|
|
15638
|
+
case "LogicalExpression":
|
|
15639
|
+
return !(parent.operator === "&&" && parent.left === node) && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
|
|
15627
15640
|
case "ObjectExpression":
|
|
15628
15641
|
case "ArrayExpression":
|
|
15629
15642
|
case "SpreadElement":
|
|
15630
|
-
case "LogicalExpression":
|
|
15631
15643
|
case "BinaryExpression":
|
|
15632
|
-
case "ConditionalExpression":
|
|
15633
15644
|
case "TemplateLiteral":
|
|
15634
15645
|
case "TSAsExpression":
|
|
15635
15646
|
case "TSNonNullExpression":
|
|
15636
15647
|
case "TSSatisfiesExpression":
|
|
15648
|
+
case "TSTypeAssertion":
|
|
15637
15649
|
return reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
|
|
15638
15650
|
default:
|
|
15639
15651
|
return false;
|
|
15640
15652
|
}
|
|
15641
15653
|
}
|
|
15642
|
-
function
|
|
15654
|
+
function identityFunction(node) {
|
|
15655
|
+
const value = unwrapExpression(node);
|
|
15656
|
+
if (value.type !== "ArrowFunctionExpression" && value.type !== "FunctionExpression" && value.type !== "FunctionDeclaration")
|
|
15657
|
+
return false;
|
|
15658
|
+
if (value.async || value.type !== "ArrowFunctionExpression" && value.generator || value.params.length !== 1)
|
|
15659
|
+
return false;
|
|
15660
|
+
const [parameter] = value.params;
|
|
15661
|
+
if (parameter?.type !== "Identifier" || value.body === null) return false;
|
|
15662
|
+
let returned2 = value.body;
|
|
15663
|
+
if (returned2.type === "BlockStatement") {
|
|
15664
|
+
const [statement] = returned2.body;
|
|
15665
|
+
if (returned2.body.length !== 1 || statement?.type !== "ReturnStatement" || statement.argument === null)
|
|
15666
|
+
return false;
|
|
15667
|
+
returned2 = statement.argument;
|
|
15668
|
+
}
|
|
15669
|
+
const result = unwrapExpression(returned2);
|
|
15670
|
+
return result.type === "Identifier" && result.name === parameter.name;
|
|
15671
|
+
}
|
|
15672
|
+
function isClosedIdentityAdapter(node, sourceCode2) {
|
|
15673
|
+
const callee = unwrapExpression(node.callee);
|
|
15674
|
+
const member3 = callee.type === "MemberExpression" ? callee : null;
|
|
15675
|
+
const receiver = member3 === null ? callee : unwrapExpression(member3.object);
|
|
15676
|
+
const method = member3 === null ? null : getPropertyName(member3);
|
|
15677
|
+
const output = parentOf(outerExpression(node));
|
|
15678
|
+
if (node.arguments.length !== 1 || node.arguments[0]?.type === "SpreadElement" || node.optional || member3?.optional || output?.type === "AwaitExpression" || output?.type === "ChainExpression")
|
|
15679
|
+
return false;
|
|
15680
|
+
if (receiver.type !== "Identifier" || (member3 === null ? receiver.name !== "createEnv" : method !== "parse" && method !== "safeParse"))
|
|
15681
|
+
return false;
|
|
15682
|
+
const variable = findVariable(sourceCode2.getScope(receiver), receiver.name);
|
|
15683
|
+
if (variable?.defs.length !== 1 || variable.references.length > 128 || !stableValueBinding(variable))
|
|
15684
|
+
return false;
|
|
15685
|
+
const value = privateIdentityValue(variable);
|
|
15686
|
+
if (value === null) return false;
|
|
15687
|
+
const methods = member3 === null ? null : identityMethods(value);
|
|
15688
|
+
if (member3 !== null && (methods === null || !methods.has(method ?? ""))) return false;
|
|
15689
|
+
if (member3 === null && !identityFunction(value)) return false;
|
|
15690
|
+
return identityReferences(variable, methods) && closedIdentityModule(sourceCode2);
|
|
15691
|
+
}
|
|
15692
|
+
function privateIdentityValue(variable) {
|
|
15693
|
+
const definition = variable.defs[0]?.node;
|
|
15694
|
+
if (definition === void 0) return null;
|
|
15695
|
+
let value = definition;
|
|
15696
|
+
let declaration = parentOf(definition);
|
|
15697
|
+
if (definition.type === "VariableDeclarator") {
|
|
15698
|
+
if (definition.id.type !== "Identifier" || definition.init === null || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const")
|
|
15699
|
+
return null;
|
|
15700
|
+
value = unwrapExpression(definition.init);
|
|
15701
|
+
declaration = parentOf(declaration);
|
|
15702
|
+
} else if (definition.type !== "FunctionDeclaration") return null;
|
|
15703
|
+
if (declaration?.type === "ExportNamedDeclaration" || declaration?.type === "ExportDefaultDeclaration")
|
|
15704
|
+
return null;
|
|
15705
|
+
let ancestor = declaration;
|
|
15706
|
+
let ancestors2 = 32;
|
|
15707
|
+
while (ancestor !== null && ancestor.type !== "Program") {
|
|
15708
|
+
if (ancestors2-- <= 0 || ancestor.type === "TSModuleBlock" || ancestor.type === "TSModuleDeclaration")
|
|
15709
|
+
return null;
|
|
15710
|
+
ancestor = parentOf(ancestor);
|
|
15711
|
+
}
|
|
15712
|
+
return value;
|
|
15713
|
+
}
|
|
15714
|
+
function closedIdentityModule(sourceCode2) {
|
|
15715
|
+
if (!sourceCode2.ast.body.some(
|
|
15716
|
+
(statement) => statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration"
|
|
15717
|
+
))
|
|
15718
|
+
return false;
|
|
15719
|
+
let remaining = 512;
|
|
15720
|
+
for (const scope of sourceCode2.scopeManager.scopes) {
|
|
15721
|
+
for (const reference of scope.references) {
|
|
15722
|
+
if (remaining-- <= 0) return false;
|
|
15723
|
+
if (reference.identifier.name === "eval" && !typeOnlyReference(tsReference(reference)))
|
|
15724
|
+
return false;
|
|
15725
|
+
}
|
|
15726
|
+
}
|
|
15727
|
+
return true;
|
|
15728
|
+
}
|
|
15729
|
+
function identityMethods(value) {
|
|
15730
|
+
if (value.type !== "ObjectExpression" || value.properties.length === 0 || value.properties.length > 2)
|
|
15731
|
+
return null;
|
|
15732
|
+
const methods = /* @__PURE__ */ new Set();
|
|
15733
|
+
for (const property of value.properties) {
|
|
15734
|
+
if (property.type !== "Property" || property.kind !== "init" || !identityFunction(property.value))
|
|
15735
|
+
return null;
|
|
15736
|
+
const name = getPropertyName(property);
|
|
15737
|
+
if (name !== "parse" && name !== "safeParse" || methods.has(name)) return null;
|
|
15738
|
+
methods.add(name);
|
|
15739
|
+
}
|
|
15740
|
+
return methods;
|
|
15741
|
+
}
|
|
15742
|
+
function identityReferences(variable, methods) {
|
|
15743
|
+
return variable.references.every((reference) => {
|
|
15744
|
+
if (reference.init === true || typeOnlyReference(reference)) return true;
|
|
15745
|
+
let target = outerExpression(reference.identifier);
|
|
15746
|
+
if (methods !== null) {
|
|
15747
|
+
const property = parentOf(target);
|
|
15748
|
+
if (property?.type !== "MemberExpression" || property.object !== target || property.optional || !methods.has(getPropertyName(property) ?? ""))
|
|
15749
|
+
return false;
|
|
15750
|
+
target = outerExpression(property);
|
|
15751
|
+
}
|
|
15752
|
+
const use = parentOf(target);
|
|
15753
|
+
return use?.type === "CallExpression" && use.callee === target && !use.optional && parentOf(use)?.type !== "ChainExpression";
|
|
15754
|
+
});
|
|
15755
|
+
}
|
|
15756
|
+
function isValidationCall(node, sourceCode2) {
|
|
15757
|
+
const callee = unwrapExpression(node.callee);
|
|
15758
|
+
if (callee.type === "Identifier") {
|
|
15759
|
+
if (callee.name === "createEnv") return true;
|
|
15760
|
+
const binding3 = findVariable(sourceCode2.getScope(callee), callee.name);
|
|
15761
|
+
const definition = binding3?.defs.length === 1 ? binding3.defs[0] : void 0;
|
|
15762
|
+
const declaration = definition?.parent;
|
|
15763
|
+
const specifier = definition?.node;
|
|
15764
|
+
return definition?.type === "ImportBinding" && declaration?.type === "ImportDeclaration" && declaration.importKind !== "type" && ["@t3-oss/env-core", "@t3-oss/env-nextjs"].includes(declaration.source.value) && specifier?.type === "ImportSpecifier" && specifier.importKind !== "type" && importSpecifierName(specifier) === "createEnv" && binding3 !== null && stableValueBinding(binding3);
|
|
15765
|
+
}
|
|
15766
|
+
if (callee.type !== "MemberExpression" || isNativeJsonParse(node, sourceCode2)) return false;
|
|
15767
|
+
const method = getPropertyName(callee);
|
|
15768
|
+
if (method === "parse" || method === "safeParse") return true;
|
|
15769
|
+
if (method !== "parseAsync" && method !== "safeParseAsync") return false;
|
|
15770
|
+
const value = outerExpression(node);
|
|
15771
|
+
const parent = parentOf(value);
|
|
15772
|
+
return parent?.type === "AwaitExpression" && parent.argument === value;
|
|
15773
|
+
}
|
|
15774
|
+
function isNativeJsonParse(node, sourceCode2) {
|
|
15643
15775
|
const callee = unwrapExpression(node.callee);
|
|
15644
|
-
|
|
15776
|
+
if (callee.type !== "MemberExpression" || getPropertyName(callee) !== "parse") return false;
|
|
15777
|
+
const receiver = unwrapExpression(callee.object);
|
|
15778
|
+
if (receiver.type !== "Identifier" || receiver.name !== "JSON") return false;
|
|
15779
|
+
const binding3 = findVariable(sourceCode2.getScope(receiver), receiver.name);
|
|
15780
|
+
return binding3 === null || binding3.defs.length === 0;
|
|
15645
15781
|
}
|
|
15646
15782
|
function isScalarCoercion(node, sourceCode2) {
|
|
15647
15783
|
const callee = unwrapExpression(node.callee);
|
|
@@ -15650,19 +15786,73 @@ function isScalarCoercion(node, sourceCode2) {
|
|
|
15650
15786
|
const binding3 = findVariable(sourceCode2.getScope(callee), callee.name);
|
|
15651
15787
|
return binding3 === null || binding3.defs.length === 0;
|
|
15652
15788
|
}
|
|
15789
|
+
function isWholeEnvironment(node, sourceCode2, seen = /* @__PURE__ */ new Set()) {
|
|
15790
|
+
const value = unwrapExpression(node);
|
|
15791
|
+
if (value.type === "MemberExpression")
|
|
15792
|
+
return isProcessEnv(value, sourceCode2) || isImportMetaEnv(value);
|
|
15793
|
+
if (value.type !== "Identifier" || seen.size >= 16) return false;
|
|
15794
|
+
const variable = stableAliasBinding(value, sourceCode2, seen);
|
|
15795
|
+
const definition = variable?.defs[0];
|
|
15796
|
+
if (variable === null || definition === void 0) return false;
|
|
15797
|
+
const declaration = definition.parent;
|
|
15798
|
+
const binding3 = definition.node;
|
|
15799
|
+
if (definition.type === "ImportBinding") {
|
|
15800
|
+
return declaration?.type === "ImportDeclaration" && declaration.importKind !== "type" && PROCESS_MODULES.includes(declaration.source.value) && binding3.type === "ImportSpecifier" && binding3.importKind !== "type" && importSpecifierName(binding3) === "env";
|
|
15801
|
+
}
|
|
15802
|
+
if (binding3.type !== "VariableDeclarator" || binding3.init === null || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const")
|
|
15803
|
+
return false;
|
|
15804
|
+
if (binding3.id.type === "Identifier")
|
|
15805
|
+
return isWholeEnvironment(binding3.init, sourceCode2, /* @__PURE__ */ new Set([...seen, variable]));
|
|
15806
|
+
return binding3.id.type === "ObjectPattern" && isNativeProcess(binding3.init, sourceCode2) && binding3.id.properties.some(
|
|
15807
|
+
(property) => property.type === "Property" && getPropertyName(property) === "env" && property.value.type === "Identifier" && property.value.name === value.name
|
|
15808
|
+
);
|
|
15809
|
+
}
|
|
15810
|
+
function stableAliasBinding(node, sourceCode2, seen) {
|
|
15811
|
+
const variable = findVariable(sourceCode2.getScope(node), node.name);
|
|
15812
|
+
return variable !== null && variable.defs.length === 1 && !seen.has(variable) && stableValueBinding(variable) ? variable : null;
|
|
15813
|
+
}
|
|
15653
15814
|
function validatedAlias(node, sourceCode2, seen, depth, budget) {
|
|
15654
15815
|
const declaration = parentOf(node);
|
|
15655
|
-
if (
|
|
15816
|
+
if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const" || parentOf(declaration)?.type === "ExportNamedDeclaration")
|
|
15656
15817
|
return false;
|
|
15657
|
-
if (
|
|
15658
|
-
|
|
15659
|
-
|
|
15818
|
+
if (node.id.type === "Identifier") {
|
|
15819
|
+
const variable = stableAliasBinding(node.id, sourceCode2, seen);
|
|
15820
|
+
return variable !== null && validatedBinding(variable, sourceCode2, seen, depth, budget);
|
|
15821
|
+
}
|
|
15822
|
+
if (node.id.type !== "ObjectPattern" || node.id.properties.length === 0 || node.init === null)
|
|
15660
15823
|
return false;
|
|
15661
|
-
|
|
15824
|
+
if (node.id.properties.length > 128) return true;
|
|
15825
|
+
return validatedFields(node.id, node.init, sourceCode2, seen, depth, budget);
|
|
15826
|
+
}
|
|
15827
|
+
function validatedFields(pattern, input, sourceCode2, seen, depth, budget) {
|
|
15828
|
+
const wholeEnvironment = isWholeEnvironment(input, sourceCode2);
|
|
15829
|
+
for (const property of pattern.properties) {
|
|
15830
|
+
if (budget() <= 0) return true;
|
|
15831
|
+
if (property.type !== "Property" || property.value.type !== "Identifier") return false;
|
|
15832
|
+
const name = getPropertyName(property);
|
|
15833
|
+
if (name === null) return false;
|
|
15834
|
+
const variable = stableAliasBinding(property.value, sourceCode2, seen);
|
|
15835
|
+
if (variable === null) return false;
|
|
15836
|
+
if (wholeEnvironment && (BUILD_TIME_CONSTANTS.has(name) || PLATFORM_MARKERS.has(name)))
|
|
15837
|
+
continue;
|
|
15838
|
+
if (!validatedBinding(variable, sourceCode2, seen, depth, budget)) return false;
|
|
15839
|
+
}
|
|
15840
|
+
return true;
|
|
15841
|
+
}
|
|
15842
|
+
function validatedBinding(variable, sourceCode2, seen, depth, budget) {
|
|
15843
|
+
const reads = variable.references.filter((ref) => ref.isRead() && !typeOnlyReference(ref));
|
|
15662
15844
|
const next = /* @__PURE__ */ new Set([...seen, variable]);
|
|
15663
15845
|
if (reads.length > 128) return true;
|
|
15664
15846
|
return reads.length > 0 && reads.every((ref) => reachesValidation(ref.identifier, sourceCode2, next, depth + 1, budget));
|
|
15665
15847
|
}
|
|
15848
|
+
function isPlatformExtraction(node, sourceCode2) {
|
|
15849
|
+
const declaration = parentOf(node);
|
|
15850
|
+
return declaration?.type === "VariableDeclarator" && declaration.init === node && declaration.id.type === "ObjectPattern" && declaration.id.properties.length > 0 && declaration.id.properties.length <= 128 && declaration.id.properties.every((property) => {
|
|
15851
|
+
if (property.type !== "Property" || property.value.type !== "Identifier") return false;
|
|
15852
|
+
const name = getPropertyName(property);
|
|
15853
|
+
return name !== null && (BUILD_TIME_CONSTANTS.has(name) || PLATFORM_MARKERS.has(name));
|
|
15854
|
+
}) && isWholeEnvironment(node, sourceCode2) && validatedAlias(declaration, sourceCode2, /* @__PURE__ */ new Set(), 0, createValidationBudget());
|
|
15855
|
+
}
|
|
15666
15856
|
var no_raw_env_default = defineRule({
|
|
15667
15857
|
meta: {
|
|
15668
15858
|
type: "problem",
|
|
@@ -15679,6 +15869,17 @@ var no_raw_env_default = defineRule({
|
|
|
15679
15869
|
let sourceCode2;
|
|
15680
15870
|
let reads = [];
|
|
15681
15871
|
let boundaryFile = false;
|
|
15872
|
+
function recordRead(node) {
|
|
15873
|
+
if (!isExemptVariableAccess(node) && !isWriteTarget(node) && !isPlatformExtraction(node, sourceCode2) && !(isWholeEnvSpread(node) && !boundaryFile))
|
|
15874
|
+
reads.push(node);
|
|
15875
|
+
}
|
|
15876
|
+
function recordAlias(node) {
|
|
15877
|
+
const variable = findVariable(sourceCode2.getScope(node), node.name);
|
|
15878
|
+
if (variable?.defs.length !== 1 || !stableValueBinding(variable)) return;
|
|
15879
|
+
for (const reference of variable.references) {
|
|
15880
|
+
if (reference.isRead() && !typeOnlyReference(reference)) recordRead(reference.identifier);
|
|
15881
|
+
}
|
|
15882
|
+
}
|
|
15682
15883
|
return {
|
|
15683
15884
|
before() {
|
|
15684
15885
|
const filename = context.filename;
|
|
@@ -15691,11 +15892,25 @@ var no_raw_env_default = defineRule({
|
|
|
15691
15892
|
reads = [];
|
|
15692
15893
|
return void 0;
|
|
15693
15894
|
},
|
|
15694
|
-
|
|
15695
|
-
if (
|
|
15696
|
-
|
|
15895
|
+
ImportDeclaration(node) {
|
|
15896
|
+
if (node.importKind === "type" || !PROCESS_MODULES.includes(node.source.value)) return;
|
|
15897
|
+
for (const specifier of node.specifiers) {
|
|
15898
|
+
if (specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && importSpecifierName(specifier) === "env")
|
|
15899
|
+
recordAlias(specifier.local);
|
|
15900
|
+
}
|
|
15901
|
+
},
|
|
15902
|
+
VariableDeclarator(node) {
|
|
15903
|
+
const declaration = parentOf(node);
|
|
15904
|
+
if (node.id.type !== "ObjectPattern" || node.init === null || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const" || !isNativeProcess(node.init, sourceCode2))
|
|
15905
|
+
return;
|
|
15906
|
+
for (const property of node.id.properties) {
|
|
15907
|
+
if (property.type === "Property" && getPropertyName(property) === "env" && property.value.type === "Identifier")
|
|
15908
|
+
recordAlias(property.value);
|
|
15697
15909
|
}
|
|
15698
15910
|
},
|
|
15911
|
+
MemberExpression(node) {
|
|
15912
|
+
if (isProcessEnv(node, sourceCode2) || isImportMetaEnv(node)) recordRead(node);
|
|
15913
|
+
},
|
|
15699
15914
|
"Program:exit"() {
|
|
15700
15915
|
for (const node of reads) {
|
|
15701
15916
|
if (!boundaryFile || !reachesValidation(node, sourceCode2))
|
|
@@ -18559,29 +18774,37 @@ function isTypeOnlyImport(node) {
|
|
|
18559
18774
|
(specifier) => specifier.type === "ImportSpecifier" && specifier.importKind === "type"
|
|
18560
18775
|
);
|
|
18561
18776
|
}
|
|
18777
|
+
function isTypeOnlyExport(node) {
|
|
18778
|
+
return node.exportKind === "type" || node.type === "ExportNamedDeclaration" && node.specifiers.length > 0 && node.specifiers.every((specifier) => specifier.exportKind === "type");
|
|
18779
|
+
}
|
|
18562
18780
|
var no_server_env_in_client_component_default = defineRule({
|
|
18563
18781
|
meta: {
|
|
18564
18782
|
type: "problem",
|
|
18565
18783
|
docs: {
|
|
18566
|
-
description: "server-only environment settings imported by a client
|
|
18784
|
+
description: "server-only environment settings imported or re-exported by a client module",
|
|
18567
18785
|
url: docsUrl("no-server-env-in-client-component")
|
|
18568
18786
|
},
|
|
18569
18787
|
schema: [],
|
|
18570
18788
|
messages: {
|
|
18571
|
-
noServerEnvInClientComponent: "
|
|
18789
|
+
noServerEnvInClientComponent: "Do not import or re-export server-only settings from a 'use client' module; use validated client settings."
|
|
18572
18790
|
}
|
|
18573
18791
|
},
|
|
18574
18792
|
createOnce(context) {
|
|
18575
18793
|
let clientModule = false;
|
|
18794
|
+
function checkDependency(node) {
|
|
18795
|
+
if (!clientModule || node.source === null) return;
|
|
18796
|
+
const typeOnly = node.type === "ImportDeclaration" ? isTypeOnlyImport(node) : isTypeOnlyExport(node);
|
|
18797
|
+
if (!typeOnly && SERVER_ENV_MODULE_RE.test(node.source.value)) {
|
|
18798
|
+
context.report({ node, messageId: "noServerEnvInClientComponent" });
|
|
18799
|
+
}
|
|
18800
|
+
}
|
|
18576
18801
|
return {
|
|
18577
18802
|
Program(node) {
|
|
18578
18803
|
clientModule = isClientModule(node);
|
|
18579
18804
|
},
|
|
18580
|
-
ImportDeclaration
|
|
18581
|
-
|
|
18582
|
-
|
|
18583
|
-
}
|
|
18584
|
-
}
|
|
18805
|
+
ImportDeclaration: checkDependency,
|
|
18806
|
+
ExportNamedDeclaration: checkDependency,
|
|
18807
|
+
ExportAllDeclaration: checkDependency
|
|
18585
18808
|
};
|
|
18586
18809
|
}
|
|
18587
18810
|
});
|
|
@@ -22094,6 +22317,7 @@ var prefer_coded_error_result_default = defineRule({
|
|
|
22094
22317
|
});
|
|
22095
22318
|
|
|
22096
22319
|
// src/rules/prefer-configured-cloud-scope.ts
|
|
22320
|
+
import { URL as NativeURL } from "url";
|
|
22097
22321
|
var NAME92 = "prefer-configured-cloud-scope";
|
|
22098
22322
|
var ACCOUNT_PREFIX = "https://api.cloudflare.com/client/v4/accounts/";
|
|
22099
22323
|
var ACCOUNT_URL = /^https:\/\/api\.cloudflare\.com\/client\/v4\/accounts\/[a-fA-F\d]{32}(?=\/|[?#]|$)/u;
|
|
@@ -22129,6 +22353,7 @@ var prefer_configured_cloud_scope_default = defineRule({
|
|
|
22129
22353
|
const requests = [];
|
|
22130
22354
|
let fetchOverridden = false;
|
|
22131
22355
|
let nodesRemaining = 0;
|
|
22356
|
+
let nativeUrlSafe;
|
|
22132
22357
|
function bindingOf(node) {
|
|
22133
22358
|
return findVariable(sourceCode2.getScope(node), node.name);
|
|
22134
22359
|
}
|
|
@@ -22137,7 +22362,8 @@ var prefer_configured_cloud_scope_default = defineRule({
|
|
|
22137
22362
|
return binding3 === null || binding3.defs.length === 0;
|
|
22138
22363
|
}
|
|
22139
22364
|
function globalReceiver2(node) {
|
|
22140
|
-
|
|
22365
|
+
const value = unwrapExpression(node);
|
|
22366
|
+
return value.type === "Identifier" && GLOBAL_RECEIVERS5.has(value.name) && unshadowed(value);
|
|
22141
22367
|
}
|
|
22142
22368
|
function outgoingFetch(node, seen = /* @__PURE__ */ new Set()) {
|
|
22143
22369
|
const callee = unwrapExpression(node);
|
|
@@ -22160,6 +22386,11 @@ var prefer_configured_cloud_scope_default = defineRule({
|
|
|
22160
22386
|
const declarator = definition.node;
|
|
22161
22387
|
return declarator.id.type === "Identifier" && declarator.init !== null && outgoingFetch(declarator.init, next);
|
|
22162
22388
|
}
|
|
22389
|
+
function nativeUrlReference(node) {
|
|
22390
|
+
const value = unwrapExpression(node);
|
|
22391
|
+
if (value.type === "Identifier") return value.name === "URL" && unshadowed(value);
|
|
22392
|
+
return value.type === "MemberExpression" && globalReceiver2(value.object) && staticPropertyName(value, sourceCode2) === "URL";
|
|
22393
|
+
}
|
|
22163
22394
|
function checkWrite(target, depth = 0) {
|
|
22164
22395
|
if (depth >= 16) {
|
|
22165
22396
|
fetchOverridden = true;
|
|
@@ -22201,17 +22432,7 @@ var prefer_configured_cloud_scope_default = defineRule({
|
|
|
22201
22432
|
if (value.type === "Literal" && typeof value.value === "string") {
|
|
22202
22433
|
return boundedParts([{ text: value.value, source: value }]);
|
|
22203
22434
|
}
|
|
22204
|
-
if (value.type === "Identifier")
|
|
22205
|
-
const binding3 = bindingOf(value);
|
|
22206
|
-
if (binding3 === null || seen.has(binding3) || binding3.defs.length !== 1 || binding3.references.some((ref) => ref.isWrite() && ref.init !== true))
|
|
22207
|
-
return UNKNOWN;
|
|
22208
|
-
const [definition] = binding3.defs;
|
|
22209
|
-
if (definition?.type !== "Variable" || definition.parent?.type !== "VariableDeclaration" || definition.parent.kind !== "const")
|
|
22210
|
-
return UNKNOWN;
|
|
22211
|
-
const declarator = definition.node;
|
|
22212
|
-
if (declarator.id.type !== "Identifier" || declarator.init === null) return UNKNOWN;
|
|
22213
|
-
return urlParts(declarator.init, /* @__PURE__ */ new Set([...seen, binding3]), depth + 1);
|
|
22214
|
-
}
|
|
22435
|
+
if (value.type === "Identifier") return identifierUrlParts(value, seen, depth);
|
|
22215
22436
|
if (value.type === "BinaryExpression" && value.operator === "+") {
|
|
22216
22437
|
return boundedParts([
|
|
22217
22438
|
...urlParts(value.left, seen, depth + 1),
|
|
@@ -22219,8 +22440,78 @@ var prefer_configured_cloud_scope_default = defineRule({
|
|
|
22219
22440
|
]);
|
|
22220
22441
|
}
|
|
22221
22442
|
if (value.type === "TemplateLiteral") return templateParts(value, seen, depth);
|
|
22443
|
+
if (value.type === "NewExpression") return nativeUrlParts(value, seen, depth);
|
|
22222
22444
|
return UNKNOWN;
|
|
22223
22445
|
}
|
|
22446
|
+
function identifierUrlParts(identifier, seen, depth) {
|
|
22447
|
+
const binding3 = bindingOf(identifier);
|
|
22448
|
+
if (binding3 === null || seen.has(binding3) || binding3.defs.length !== 1 || binding3.references.some((ref) => ref.isWrite() && ref.init !== true))
|
|
22449
|
+
return UNKNOWN;
|
|
22450
|
+
const [definition] = binding3.defs;
|
|
22451
|
+
if (definition?.type !== "Variable" || definition.parent?.type !== "VariableDeclaration" || definition.parent.kind !== "const")
|
|
22452
|
+
return UNKNOWN;
|
|
22453
|
+
const declarator = definition.node;
|
|
22454
|
+
if (declarator.id.type !== "Identifier" || declarator.init === null) return UNKNOWN;
|
|
22455
|
+
const initializer = unwrapExpression(declarator.init);
|
|
22456
|
+
if (initializer.type === "NewExpression" && nativeUrlReference(initializer.callee) && !onlyUrlRequests(binding3, declarator))
|
|
22457
|
+
return UNKNOWN;
|
|
22458
|
+
return urlParts(declarator.init, /* @__PURE__ */ new Set([...seen, binding3]), depth + 1);
|
|
22459
|
+
}
|
|
22460
|
+
function onlyUrlRequests(variable, declarator) {
|
|
22461
|
+
if (variable.references.length > 128) return false;
|
|
22462
|
+
const declaration = parentOf(declarator);
|
|
22463
|
+
if (declaration !== null && parentOf(declaration)?.type === "ExportNamedDeclaration")
|
|
22464
|
+
return false;
|
|
22465
|
+
return variable.references.every((reference) => {
|
|
22466
|
+
if (reference.init === true || typeOnlyReference(reference)) return true;
|
|
22467
|
+
const value = outerExpression(reference.identifier);
|
|
22468
|
+
const use = parentOf(value);
|
|
22469
|
+
if (use?.type !== "CallExpression" || use.arguments[0] !== value) return false;
|
|
22470
|
+
const callee = unwrapExpression(use.callee);
|
|
22471
|
+
return callee.type === "Identifier" && callee.name === "fetch" && unshadowed(callee) || callee.type === "MemberExpression" && globalReceiver2(callee.object) && staticPropertyName(callee, sourceCode2) === "fetch";
|
|
22472
|
+
});
|
|
22473
|
+
}
|
|
22474
|
+
function nativeUrlParts(value, seen, depth) {
|
|
22475
|
+
if (value.arguments.length !== 1 || !nativeUrlReference(value.callee) || !nativeUrlUnchanged())
|
|
22476
|
+
return UNKNOWN;
|
|
22477
|
+
const [argument] = value.arguments;
|
|
22478
|
+
if (argument === void 0 || argument.type === "SpreadElement") return UNKNOWN;
|
|
22479
|
+
const parts = urlParts(argument, seen, depth + 1);
|
|
22480
|
+
if (parts.some((part) => part.source === null)) return UNKNOWN;
|
|
22481
|
+
const text = parts.map((part) => part.text).join("");
|
|
22482
|
+
if (!ACCOUNT_URL.test(text)) return UNKNOWN;
|
|
22483
|
+
try {
|
|
22484
|
+
return new NativeURL(text).href === text ? parts : UNKNOWN;
|
|
22485
|
+
} catch {
|
|
22486
|
+
return UNKNOWN;
|
|
22487
|
+
}
|
|
22488
|
+
}
|
|
22489
|
+
function nativeUrlUnchanged() {
|
|
22490
|
+
if (nativeUrlSafe !== void 0) return nativeUrlSafe;
|
|
22491
|
+
nativeUrlSafe = sourceCode2.scopeManager.scopes.every(
|
|
22492
|
+
(scope) => scope.references.every((reference) => {
|
|
22493
|
+
if (typeOnlyReference(tsReference(reference))) return true;
|
|
22494
|
+
const identifier = reference.identifier;
|
|
22495
|
+
if (["eval", "Function"].includes(identifier.name) && unshadowed(identifier))
|
|
22496
|
+
return false;
|
|
22497
|
+
let value = outerExpression(identifier);
|
|
22498
|
+
if (!nativeUrlReference(value)) {
|
|
22499
|
+
const member3 = parentOf(value);
|
|
22500
|
+
if (globalReceiver2(value)) {
|
|
22501
|
+
if (member3?.type !== "MemberExpression" || member3.object !== value) return false;
|
|
22502
|
+
const key = staticPropertyName(member3, sourceCode2);
|
|
22503
|
+
if (key === null || GLOBAL_RECEIVERS5.has(key)) return false;
|
|
22504
|
+
}
|
|
22505
|
+
if (member3?.type !== "MemberExpression" || member3.object !== value || !nativeUrlReference(member3))
|
|
22506
|
+
return true;
|
|
22507
|
+
value = outerExpression(member3);
|
|
22508
|
+
}
|
|
22509
|
+
const use = parentOf(value);
|
|
22510
|
+
return use?.type === "NewExpression" && use.callee === value || use?.type === "UnaryExpression" && use.operator === "typeof";
|
|
22511
|
+
})
|
|
22512
|
+
);
|
|
22513
|
+
return nativeUrlSafe;
|
|
22514
|
+
}
|
|
22224
22515
|
function templateParts(value, seen, depth) {
|
|
22225
22516
|
const parts = [];
|
|
22226
22517
|
for (let index = 0; index < value.quasis.length; index++) {
|
|
@@ -22239,6 +22530,7 @@ var prefer_configured_cloud_scope_default = defineRule({
|
|
|
22239
22530
|
reported.clear();
|
|
22240
22531
|
requests.length = 0;
|
|
22241
22532
|
fetchOverridden = false;
|
|
22533
|
+
nativeUrlSafe = void 0;
|
|
22242
22534
|
const file = { filename: context.filename, cwd: context.cwd };
|
|
22243
22535
|
return !isTestFile(file) && !isGeneratedFile(file, sourceCode2.text);
|
|
22244
22536
|
},
|
package/dist/index.js
CHANGED