Skip to content

Don't expose non-prefixed env vars #386

Merged
yamcodes merged 5 commits intomainfrom
385-server-only-env-vars-are-exposed-to-client-code
Nov 20, 2025
Merged

Don't expose non-prefixed env vars #386
yamcodes merged 5 commits intomainfrom
385-server-only-env-vars-are-exposed-to-client-code

Conversation

@yamcodes
Copy link
Owner

@yamcodes yamcodes commented Nov 20, 2025

  • Updated the Vite plugin guide to include detailed examples of reusing ArkType schemas across environments.
  • Improved navigation and links in the documentation to reflect the new structure under /docs/arkenv.
  • Ensured consistency in documentation by removing outdated references and clarifying usage instructions.

This update aims to provide clearer guidance for users on leveraging schema reuse effectively.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented leakage of server-only environment variables to client code by exposing only variables matching Vite's configured prefix (default VITE_).
  • Documentation

    • Clarified plugin behavior and added design/spec docs describing prefix-based filtering and expected scenarios.
  • Tests

    • Added unit tests verifying default, custom, and array-based prefix handling and exclusion of server-only vars.

✏️ Tip: You can customize this high-level summary in your review settings.

- Updated the Vite plugin guide to include detailed examples of reusing ArkType schemas across environments.
- Improved navigation and links in the documentation to reflect the new structure under /docs/arkenv.
- Ensured consistency in documentation by removing outdated references and clarifying usage instructions.

This update aims to provide clearer guidance for users on leveraging schema reuse effectively.
@yamcodes yamcodes linked an issue Nov 20, 2025 that may be closed by this pull request
@changeset-bot
Copy link

changeset-bot bot commented Nov 20, 2025

🦋 Changeset detected

Latest commit: a7b36ab

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@arkenv/vite-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Warning

Rate limit exceeded

@yamcodes has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 15 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 28e4bed and a7b36ab.

📒 Files selected for processing (1)
  • openspec/changes/fix-vite-plugin-env-filter/tasks.md (1 hunks)

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds filtering to the Vite plugin so only environment variables matching Vite's configured prefix (default "VITE_") are exposed to client code; server-only/unprefixed variables are excluded. Tests, docs, spec, design, tasks, and a release note were added or updated to reflect this behavior.

Changes

Cohort / File(s) Summary
Release note
/.changeset/fast-cloths-decide.md
Patch release note documenting the security fix: Vite plugin now filters exposed env vars by prefix.
Plugin implementation
packages/vite-plugin/src/index.ts
Read config.envPrefix (default "VITE_"), normalize to array, filter env keys to those starting with any configured prefix, and expose only filtered vars via Vite define.
Plugin tests
packages/vite-plugin/src/index.test.ts
New/updated tests for default prefix, custom prefix, array of prefixes, and server-only variable exclusion.
Playground config comment
apps/playgrounds/vite/vite.config.ts
Comments updated to describe that only prefixed variables are exposed and server-only vars (e.g., PORT) are excluded.
Docs
apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx, apps/www/content/docs/vite-plugin/index.mdx
Explanations updated to state that the plugin filters environment variables to only expose those matching the configured Vite prefix.
Design / Spec / Tasks
openspec/changes/fix-vite-plugin-env-filter/...
Added proposal, design, spec scenarios, and task list describing envPrefix behavior, validation-before-filtering, and tests to implement/verify filtering.

Sequence Diagram(s)

sequenceDiagram
    participant Vite
    participant Plugin as ArkEnv Vite Plugin
    participant Config as config.envPrefix
    participant Schema as Validator

    Vite->>Plugin: config(config, env)
    Plugin->>Config: read envPrefix (default "VITE_")
    Config-->>Plugin: prefix or prefixes
    Plugin->>Plugin: normalize -> [prefixes]
    Plugin->>Plugin: filter env keys by prefixes
    Plugin->>Schema: validate filtered vars
    Schema-->>Plugin: validated filteredEnv
    Plugin->>Vite: return define with only filteredEnv
    Note over Plugin: unprefixed/server-only vars (e.g., PORT) not exposed
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to focus:
    • packages/vite-plugin/src/index.ts: correct reading/normalization of envPrefix (string vs array) and filtering logic.
    • packages/vite-plugin/src/index.test.ts: ensure tests cover array prefixes and default behavior without mutating global env.
    • Docs/spec coherence with implemented behavior.

Possibly related issues

Possibly related PRs

Poem

🐰
I hopped through envs to keep secrets tight,
I sniffed for prefixes in the moonlight,
Only VITE_* (and friends) may show,
PORT stays hidden — safe below,
Hooray for bundles sleeping right! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely describes the main security fix: preventing exposure of non-prefixed environment variables to the client bundle.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link

vercel bot commented Nov 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
arkenv Ready Ready Preview Comment Nov 20, 2025 9:53pm

@github-actions github-actions bot added the docs Improvements or additions to documentation label Nov 20, 2025
@yamcodes yamcodes changed the title Enhance Vite plugin documentation and examples for schema reuse Don't expose non-prefixed env vars Nov 20, 2025
- Updated the Vite plugin to automatically filter environment variables, exposing only those that match the configured prefix (defaults to `VITE_`).
- Improved documentation to clarify the filtering behavior and ensure server-only variables are not exposed to client code.
- Added tests to verify that only prefixed variables are included in the client bundle, preventing accidental exposure of sensitive information.

This update enhances security and usability for developers using the Vite plugin.
@github-actions github-actions bot added @arkenv/vite-plugin Issues or Pull Requests involving the Vite plugin for ArkEnv www Improvements or additions to arkenv.js.org tests This issue or PR is about adding, removing or changing tests labels Nov 20, 2025
@yamcodes yamcodes marked this pull request as ready for review November 20, 2025 20:57
@arkenv-bot
Copy link
Contributor

arkenv-bot bot commented Nov 20, 2025

📦 Bundle Size Report

No results found

All size limits passed!

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 20, 2025

Open in StackBlitz

npm i https://pkg.pr.new/arkenv@386
npm i https://pkg.pr.new/@arkenv/vite-plugin@386

commit: a7b36ab

- Enhanced the Vite plugin to better manage environment variables, ensuring only those with the specified prefix are included in the client bundle.
- Updated documentation to reflect these changes and clarify usage.
- Added additional tests to confirm the correct filtering of environment variables, enhancing security and preventing exposure of sensitive data.

This update aims to streamline the development process and improve security for users of the Vite plugin.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@yamcodes
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/vite-plugin/src/index.ts (1)

27-37: Critical: Filter schema before validation, not after.

The current implementation validates the entire schema first (line 29), then filters the results (lines 33-37). This causes validation to fail for server-only variables that aren't present in the environment, even though they shouldn't be exposed to the client.

The design document (openspec/changes/fix-vite-plugin-env-filter/design.md lines 52-63) specifies that the schema should be filtered before validation. A previous review comment also flagged this same issue.

Example failure scenario:

  • Schema includes PORT: "number.port" and VITE_API_URL: "string"
  • Environment only defines VITE_API_URL
  • Current code: createEnv validates both and fails because PORT is missing
  • Correct behavior: Filter to only VITE_API_URL, validate only that

Apply this diff to filter the schema before validation:

 		config(config, { mode }) {
 			// Get the Vite prefix for client-exposed environment variables
 			// Defaults to "VITE_" if not specified
 			// Vite allows envPrefix to be a string or array of strings
 			const envPrefix = config.envPrefix ?? "VITE_";
 			const prefixes = Array.isArray(envPrefix) ? envPrefix : [envPrefix];
 
-			// createEnv accepts both EnvSchema and type.Any at runtime
-			// We use overloads above to provide external type precision
-			const env = createEnv(options, loadEnv(mode, process.cwd(), ""));
-
-			// Filter to only include environment variables matching the prefix
-			// This prevents server-only variables from being exposed to client code
-			const filteredEnv = Object.fromEntries(
-				Object.entries(<Record<string, unknown>>env).filter(([key]) =>
-					prefixes.some((prefix) => key.startsWith(prefix)),
-				),
-			);
+			// Load all environment variables
+			const allEnv = loadEnv(mode, process.cwd(), "");
+
+			// Filter schema to only include keys matching the prefix
+			// This prevents validation errors for server-only variables
+			const filteredSchema = Object.fromEntries(
+				Object.entries(options as Record<string, unknown>).filter(([key]) =>
+					prefixes.some((prefix) => key.startsWith(prefix)),
+				),
+			);
+
+			// Validate only the filtered schema
+			// createEnv accepts both EnvSchema and type.Any at runtime
+			const env = createEnv(filteredSchema, allEnv);
 
 			// Expose transformed environment variables through Vite's define option
 			// Only prefixed variables are exposed to client code
 			const define = Object.fromEntries(
-				Object.entries(filteredEnv).map(([key, value]) => [
+				Object.entries(env).map(([key, value]) => [
 					`import.meta.env.${key}`,
 					JSON.stringify(value),
 				]),
 			);
 
 			return { define };
 		},
🧹 Nitpick comments (2)
packages/vite-plugin/src/index.test.ts (1)

481-528: LGTM! Consider adding edge case tests.

The test properly validates that multiple prefixes can be specified as an array and all matching variables are exposed.

Consider adding tests for edge cases:

  • Empty array: envPrefix: []
  • Empty string: envPrefix: ""
  • Array with empty string: envPrefix: ["VITE_", ""]

These edge cases would help ensure robust handling of unusual configurations.

openspec/changes/fix-vite-plugin-env-filter/specs/vite-plugin/spec.md (1)

1-46: Clear and comprehensive specification. Consider adding array prefix scenario.

The specification effectively outlines the environment variable filtering requirements using clear WHEN/THEN scenarios. It aligns well with the test cases in index.test.ts.

Consider adding a scenario for array of prefixes to match the test coverage at lines 481-528 in index.test.ts:

#### Scenario: Multiple prefixes via array configuration
- **WHEN** a user configures `envPrefix` as an array (e.g., `envPrefix: ["VITE_", "PUBLIC_"]`)
- **AND** they pass a schema with variables using different prefixes
- **THEN** variables matching any prefix in the array are exposed to client code
- **AND** variables not matching any prefix are filtered out

This would ensure the specification fully captures all tested behaviors.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4df6a54 and 4b8af57.

📒 Files selected for processing (10)
  • .changeset/fast-cloths-decide.md (1 hunks)
  • apps/playgrounds/vite/vite.config.ts (2 hunks)
  • apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx (1 hunks)
  • apps/www/content/docs/vite-plugin/index.mdx (1 hunks)
  • openspec/changes/fix-vite-plugin-env-filter/design.md (1 hunks)
  • openspec/changes/fix-vite-plugin-env-filter/proposal.md (1 hunks)
  • openspec/changes/fix-vite-plugin-env-filter/specs/vite-plugin/spec.md (1 hunks)
  • openspec/changes/fix-vite-plugin-env-filter/tasks.md (1 hunks)
  • packages/vite-plugin/src/index.test.ts (3 hunks)
  • packages/vite-plugin/src/index.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-09T17:37:19.650Z
Learnt from: yamcodes
Repo: yamcodes/arkenv PR: 132
File: packages/arkenv/README.md:13-14
Timestamp: 2025-09-09T17:37:19.650Z
Learning: For yamcodes/arkenv project: Runtime support documentation should link to specific examples: Node.js (examples/basic), Bun (examples/with-bun), Vite (examples/with-vite-react-ts).

Applied to files:

  • apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx
🔇 Additional comments (11)
.changeset/fast-cloths-decide.md (1)

1-7: LGTM!

The changeset clearly describes the security fix and its impact. The patch version bump is appropriate for this security improvement.

openspec/changes/fix-vite-plugin-env-filter/proposal.md (1)

1-42: LGTM!

The proposal clearly articulates the security issue, the impact, and the proposed solution. The documentation of current vs. desired behavior is thorough and helpful.

apps/www/content/docs/vite-plugin/index.mdx (1)

7-9: LGTM!

The prominent callout clearly explains the filtering behavior and security implications. This helps users understand what environment variables will be exposed to their client bundle.

apps/playgrounds/vite/vite.config.ts (2)

10-10: LGTM!

The comment accurately describes the plugin's automatic filtering behavior, helping developers understand how the schema is used.


29-32: LGTM!

These comments clearly explain the plugin's filtering behavior and how it protects against exposing server-only variables like PORT.

apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx (1)

40-41: LGTM!

The updated bullet points clearly explain the dual use of the schema and the automatic filtering behavior that protects server-only variables from exposure.

openspec/changes/fix-vite-plugin-env-filter/design.md (1)

1-79: LGTM!

The design document is comprehensive and well-reasoned. The decision to filter schema before validation (lines 52-63) is particularly important for preventing validation errors on server-only variables that shouldn't be in the client schema.

packages/vite-plugin/src/index.test.ts (4)

261-306: LGTM! Test correctly validates prefix filtering.

The updated test properly verifies that only variables starting with VITE_ (case-sensitive) are exposed to the client bundle. The negative assertion at line 305 confirms that vite_lowercase is correctly filtered out.


342-388: LGTM! Critical security test for server-only variable filtering.

This test properly validates that server-only variables (PORT, DATABASE_URL) are not exposed to the client bundle while client-safe variables (VITE_API_URL, VITE_DEBUG) are correctly exposed. This is essential for preventing sensitive configuration from leaking.


390-437: LGTM! Proper validation of custom prefix configuration.

The test correctly verifies that when a custom envPrefix is configured (e.g., PUBLIC_), only variables matching that prefix are exposed, and the default VITE_ prefix is no longer used.


439-479: LGTM! Validates default prefix behavior.

The test correctly confirms that when envPrefix is not configured, the plugin defaults to VITE_ and only exposes variables matching that prefix.

- Updated the implementation to validate all environment variables before filtering them based on the specified prefix, enhancing security and consistency.
- Improved documentation to clarify the new validation approach and its benefits, including better error messages and compatibility with various schema types.
- Addressed potential validation errors for server-only variables, ensuring a more robust handling of environment variables.

This change aims to streamline the validation process while maintaining security and usability for developers using the Vite plugin.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
openspec/changes/fix-vite-plugin-env-filter/design.md (1)

50-51: Minor stylistic improvement: reduce adverb repetition.

Lines 50 and 51 both use "only," making the narrative slightly redundant. Consider consolidating for cleaner prose.

Apply this diff to improve readability:

- Filter the validated results to only include those matching the prefix
- Expose only the filtered subset to client code
+ Filter the validated results to include only those matching the prefix
+ Expose the filtered subset to client code
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b8af57 and 28e4bed.

📒 Files selected for processing (1)
  • openspec/changes/fix-vite-plugin-env-filter/design.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
openspec/changes/fix-vite-plugin-env-filter/design.md

[style] ~51-~51: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...lude those matching the prefix - Expose only the filtered subset to client code ###...

(ADVERB_REPETITION_PREMIUM)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test-e2e (e2e)
🔇 Additional comments (1)
openspec/changes/fix-vite-plugin-env-filter/design.md (1)

1-83: Solid design document with clear rationale and risk mitigation.

The design is well-structured and addresses the security concern comprehensively. The decision to filter in the plugin (rather than require users to split schemas) is pragmatic and makes the plugin safer by default. The validation-first-then-filter approach is well-justified and handles both raw schemas and type() definitions seamlessly. Risk/mitigation pairs are identified, and the design respects Vite's envPrefix convention while maintaining backward compatibility. The open questions being resolved upfront is a strength.

…nd filtering

- Modified the implementation to validate all environment variables against the schema before filtering them based on the specified prefix.
- Updated documentation to reflect the new validation process, ensuring clarity on the handling of environment variables.

This change aims to improve the security and consistency of environment variable management in the Vite plugin.
@yamcodes yamcodes merged commit efc75c9 into main Nov 20, 2025
19 checks passed
@yamcodes yamcodes deleted the 385-server-only-env-vars-are-exposed-to-client-code branch November 20, 2025 21:56
@arkenv-bot arkenv-bot bot mentioned this pull request Nov 20, 2025
yamcodes pushed a commit that referenced this pull request Nov 20, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## arkenv@0.7.6

### Patch Changes

- #### Support type definitions for schema reuse
_[`2424391`](2424391)
[@yamcodes](https://github.com/yamcodes)_

`arkenv()` and `@arkenv/vite-plugin` now accept both raw schema objects
and type definitions created with ArkType's `type()` function. This
allows you to define your schema once and reuse it across your
application, which is especially useful for multi-runtime setups like
Vite where you need the same schema in both `vite.config.ts` and client
code.

    ```ts
    import arkenv, { type } from "arkenv";

    // Define schema once
    const Env = type({
      PORT: "number.port",
      HOST: "string.host",
    });

    // Reuse it in multiple places
    const configEnv = arkenv(Env, process.env);
    const testEnv = arkenv(Env, { PORT: "3000", HOST: "localhost" });
    ```

## @arkenv/vite-plugin@0.0.17

### Patch Changes

- #### Support type definitions for schema reuse
_[`2424391`](2424391)
[@yamcodes](https://github.com/yamcodes)_

`arkenv()` and `@arkenv/vite-plugin` now accept both raw schema objects
and type definitions created with ArkType's `type()` function. This
allows you to define your schema once and reuse it across your
application, which is especially useful for multi-runtime setups like
Vite where you need the same schema in both `vite.config.ts` and client
code.

    ```ts
    import arkenv, { type } from "arkenv";

    // Define schema once
    const Env = type({
      PORT: "number.port",
      HOST: "string.host",
    });

    // Reuse it in multiple places
    const configEnv = arkenv(Env, process.env);
    const testEnv = arkenv(Env, { PORT: "3000", HOST: "localhost" });
    ```

- #### Fix security issue where server-only environment variables were
exposed to client code
_[`#386`](#386)
[`efc75c9`](efc75c9)
[@yamcodes](https://github.com/yamcodes)_

The plugin now automatically filters to only expose variables matching
Vite's configured prefix (defaults to `VITE_`), preventing sensitive
server-side configuration from leaking into the client bundle.

<details><summary>Updated 1 dependency</summary>

<small>


[`2424391`](2424391)

</small>

-   `arkenv@0.7.6`

</details>

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

@arkenv/vite-plugin Issues or Pull Requests involving the Vite plugin for ArkEnv docs Improvements or additions to documentation tests This issue or PR is about adding, removing or changing tests www Improvements or additions to arkenv.js.org

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server-only env vars are exposed to client code

1 participant