mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-27 21:44:25 -07:00
fix(core): allow environment variable expansion and explicit overrides for MCP servers (#18837)
This commit is contained in:
@@ -163,7 +163,8 @@ Each server configuration supports the following properties:
|
|||||||
- **`args`** (string[]): Command-line arguments for Stdio transport
|
- **`args`** (string[]): Command-line arguments for Stdio transport
|
||||||
- **`headers`** (object): Custom HTTP headers when using `url` or `httpUrl`
|
- **`headers`** (object): Custom HTTP headers when using `url` or `httpUrl`
|
||||||
- **`env`** (object): Environment variables for the server process. Values can
|
- **`env`** (object): Environment variables for the server process. Values can
|
||||||
reference environment variables using `$VAR_NAME` or `${VAR_NAME}` syntax
|
reference environment variables using `$VAR_NAME` or `${VAR_NAME}` syntax (all
|
||||||
|
platforms), or `%VAR_NAME%` (Windows only).
|
||||||
- **`cwd`** (string): Working directory for Stdio transport
|
- **`cwd`** (string): Working directory for Stdio transport
|
||||||
- **`timeout`** (number): Request timeout in milliseconds (default: 600,000ms =
|
- **`timeout`** (number): Request timeout in milliseconds (default: 600,000ms =
|
||||||
10 minutes)
|
10 minutes)
|
||||||
@@ -184,6 +185,63 @@ Each server configuration supports the following properties:
|
|||||||
Service Account to impersonate. Used with
|
Service Account to impersonate. Used with
|
||||||
`authProviderType: 'service_account_impersonation'`.
|
`authProviderType: 'service_account_impersonation'`.
|
||||||
|
|
||||||
|
### Environment variable expansion
|
||||||
|
|
||||||
|
Gemini CLI automatically expands environment variables in the `env` block of
|
||||||
|
your MCP server configuration. This allows you to securely reference variables
|
||||||
|
defined in your shell or environment without hardcoding sensitive information
|
||||||
|
directly in your `settings.json` file.
|
||||||
|
|
||||||
|
The expansion utility supports:
|
||||||
|
|
||||||
|
- **POSIX/Bash syntax:** `$VARIABLE_NAME` or `${VARIABLE_NAME}` (supported on
|
||||||
|
all platforms)
|
||||||
|
- **Windows syntax:** `%VARIABLE_NAME%` (supported only when running on Windows)
|
||||||
|
|
||||||
|
If a variable is not defined in the current environment, it resolves to an empty
|
||||||
|
string.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
"env": {
|
||||||
|
"API_KEY": "$MY_EXTERNAL_TOKEN",
|
||||||
|
"LOG_LEVEL": "$LOG_LEVEL",
|
||||||
|
"TEMP_DIR": "%TEMP%"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security and environment sanitization
|
||||||
|
|
||||||
|
To protect your credentials, Gemini CLI performs environment sanitization when
|
||||||
|
spawning MCP server processes.
|
||||||
|
|
||||||
|
#### Automatic redaction
|
||||||
|
|
||||||
|
By default, the CLI redacts sensitive environment variables from the base
|
||||||
|
environment (inherited from the host process) to prevent unintended exposure to
|
||||||
|
third-party MCP servers. This includes:
|
||||||
|
|
||||||
|
- Core project keys: `GEMINI_API_KEY`, `GOOGLE_API_KEY`, etc.
|
||||||
|
- Variables matching sensitive patterns: `*TOKEN*`, `*SECRET*`, `*PASSWORD*`,
|
||||||
|
`*KEY*`, `*AUTH*`, `*CREDENTIAL*`.
|
||||||
|
- Certificates and private key patterns.
|
||||||
|
|
||||||
|
#### Explicit overrides
|
||||||
|
|
||||||
|
If an environment variable must be passed to an MCP server, you must explicitly
|
||||||
|
state it in the `env` property of the server configuration in `settings.json`.
|
||||||
|
Explicitly defined variables (including those from extensions) are trusted and
|
||||||
|
are **not** subjected to the automatic redaction process.
|
||||||
|
|
||||||
|
This follows the security principle that if a variable is explicitly configured
|
||||||
|
by the user for a specific server, it constitutes informed consent to share that
|
||||||
|
specific data with that server.
|
||||||
|
|
||||||
|
> **Note:** Even when explicitly defined, you should avoid hardcoding secrets.
|
||||||
|
> Instead, use environment variable expansion (e.g., `"MY_KEY": "$MY_KEY"`) to
|
||||||
|
> securely pull the value from your host environment at runtime.
|
||||||
|
|
||||||
### OAuth support for remote MCP servers
|
### OAuth support for remote MCP servers
|
||||||
|
|
||||||
The Gemini CLI supports OAuth 2.0 authentication for remote MCP servers using
|
The Gemini CLI supports OAuth 2.0 authentication for remote MCP servers using
|
||||||
@@ -738,7 +796,9 @@ The MCP integration tracks several states:
|
|||||||
- **Trust settings:** The `trust` option bypasses all confirmation dialogs. Use
|
- **Trust settings:** The `trust` option bypasses all confirmation dialogs. Use
|
||||||
cautiously and only for servers you completely control
|
cautiously and only for servers you completely control
|
||||||
- **Access tokens:** Be security-aware when configuring environment variables
|
- **Access tokens:** Be security-aware when configuring environment variables
|
||||||
containing API keys or tokens
|
containing API keys or tokens. See
|
||||||
|
[Security and environment sanitization](#security-and-environment-sanitization)
|
||||||
|
for details on how Gemini CLI protects your credentials.
|
||||||
- **Sandbox compatibility:** When using sandboxing, ensure MCP servers are
|
- **Sandbox compatibility:** When using sandboxing, ensure MCP servers are
|
||||||
available within the sandbox environment
|
available within the sandbox environment
|
||||||
- **Private data:** Using broadly scoped personal access tokens can lead to
|
- **Private data:** Using broadly scoped personal access tokens can lead to
|
||||||
|
|||||||
Generated
+32
-3
@@ -7432,9 +7432,36 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "17.1.0",
|
"version": "17.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz",
|
||||||
"integrity": "sha512-tG9VUTJTuju6GcXgbdsOuRhupE8cb4mRgY5JLRCh4MtGoVo3/gfGUtOMwmProM6d0ba2mCFvv+WrpYJV6qgJXQ==",
|
"integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv-expand": {
|
||||||
|
"version": "12.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz",
|
||||||
|
"integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^16.4.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv-expand/node_modules/dotenv": {
|
||||||
|
"version": "16.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||||
|
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -17400,6 +17427,8 @@
|
|||||||
"ajv-formats": "^3.0.0",
|
"ajv-formats": "^3.0.0",
|
||||||
"chardet": "^2.1.0",
|
"chardet": "^2.1.0",
|
||||||
"diff": "^8.0.3",
|
"diff": "^8.0.3",
|
||||||
|
"dotenv": "^17.2.4",
|
||||||
|
"dotenv-expand": "^12.0.3",
|
||||||
"fast-levenshtein": "^2.0.6",
|
"fast-levenshtein": "^2.0.6",
|
||||||
"fdir": "^6.4.6",
|
"fdir": "^6.4.6",
|
||||||
"fzf": "^0.5.2",
|
"fzf": "^0.5.2",
|
||||||
|
|||||||
@@ -53,6 +53,8 @@
|
|||||||
"ajv-formats": "^3.0.0",
|
"ajv-formats": "^3.0.0",
|
||||||
"chardet": "^2.1.0",
|
"chardet": "^2.1.0",
|
||||||
"diff": "^8.0.3",
|
"diff": "^8.0.3",
|
||||||
|
"dotenv": "^17.2.4",
|
||||||
|
"dotenv-expand": "^12.0.3",
|
||||||
"fast-levenshtein": "^2.0.6",
|
"fast-levenshtein": "^2.0.6",
|
||||||
"fdir": "^6.4.6",
|
"fdir": "^6.4.6",
|
||||||
"fzf": "^0.5.2",
|
"fzf": "^0.5.2",
|
||||||
|
|||||||
@@ -1704,6 +1704,40 @@ describe('mcp-client', () => {
|
|||||||
expect(callArgs.env!['GEMINI_CLI_EXT_VAR']).toBeUndefined();
|
expect(callArgs.env!['GEMINI_CLI_EXT_VAR']).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should expand environment variables in mcpServerConfig.env and not redact them', async () => {
|
||||||
|
const mockedTransport = vi
|
||||||
|
.spyOn(SdkClientStdioLib, 'StdioClientTransport')
|
||||||
|
.mockReturnValue({} as SdkClientStdioLib.StdioClientTransport);
|
||||||
|
|
||||||
|
const originalEnv = process.env;
|
||||||
|
process.env = {
|
||||||
|
...originalEnv,
|
||||||
|
GEMINI_TEST_VAR: 'expanded-value',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createTransport(
|
||||||
|
'test-server',
|
||||||
|
{
|
||||||
|
command: 'test-command',
|
||||||
|
env: {
|
||||||
|
TEST_EXPANDED: 'Value is $GEMINI_TEST_VAR',
|
||||||
|
SECRET_KEY: 'intentional-secret-123',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
EMPTY_CONFIG,
|
||||||
|
);
|
||||||
|
|
||||||
|
const callArgs = mockedTransport.mock.calls[0][0];
|
||||||
|
expect(callArgs.env).toBeDefined();
|
||||||
|
expect(callArgs.env!['TEST_EXPANDED']).toBe('Value is expanded-value');
|
||||||
|
expect(callArgs.env!['SECRET_KEY']).toBe('intentional-secret-123');
|
||||||
|
} finally {
|
||||||
|
process.env = originalEnv;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
describe('useGoogleCredentialProvider', () => {
|
describe('useGoogleCredentialProvider', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Mock GoogleAuth client
|
// Mock GoogleAuth client
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ import {
|
|||||||
sanitizeEnvironment,
|
sanitizeEnvironment,
|
||||||
type EnvironmentSanitizationConfig,
|
type EnvironmentSanitizationConfig,
|
||||||
} from '../services/environmentSanitization.js';
|
} from '../services/environmentSanitization.js';
|
||||||
|
import { expandEnvVars } from '../utils/envExpansion.js';
|
||||||
import {
|
import {
|
||||||
GEMINI_CLI_IDENTIFICATION_ENV_VAR,
|
GEMINI_CLI_IDENTIFICATION_ENV_VAR,
|
||||||
GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE,
|
GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE,
|
||||||
@@ -783,9 +784,16 @@ function createTransportRequestInit(
|
|||||||
mcpServerConfig: MCPServerConfig,
|
mcpServerConfig: MCPServerConfig,
|
||||||
headers: Record<string, string>,
|
headers: Record<string, string>,
|
||||||
): RequestInit {
|
): RequestInit {
|
||||||
|
const expandedHeaders: Record<string, string> = {};
|
||||||
|
if (mcpServerConfig.headers) {
|
||||||
|
for (const [key, value] of Object.entries(mcpServerConfig.headers)) {
|
||||||
|
expandedHeaders[key] = expandEnvVars(value, process.env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
...mcpServerConfig.headers,
|
...expandedHeaders,
|
||||||
...headers,
|
...headers,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1970,15 +1978,33 @@ export async function createTransport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mcpServerConfig.command) {
|
if (mcpServerConfig.command) {
|
||||||
|
// 1. Sanitize the base process environment to prevent unintended leaks of system-wide secrets.
|
||||||
|
const sanitizedEnv = sanitizeEnvironment(process.env, {
|
||||||
|
...sanitizationConfig,
|
||||||
|
enableEnvironmentVariableRedaction: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const finalEnv: Record<string, string> = {
|
||||||
|
[GEMINI_CLI_IDENTIFICATION_ENV_VAR]:
|
||||||
|
GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE,
|
||||||
|
};
|
||||||
|
for (const [key, value] of Object.entries(sanitizedEnv)) {
|
||||||
|
if (value !== undefined) {
|
||||||
|
finalEnv[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand and merge explicit environment variables from the MCP configuration.
|
||||||
|
if (mcpServerConfig.env) {
|
||||||
|
for (const [key, value] of Object.entries(mcpServerConfig.env)) {
|
||||||
|
finalEnv[key] = expandEnvVars(value, process.env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let transport: Transport = new StdioClientTransport({
|
let transport: Transport = new StdioClientTransport({
|
||||||
command: mcpServerConfig.command,
|
command: mcpServerConfig.command,
|
||||||
args: mcpServerConfig.args || [],
|
args: mcpServerConfig.args || [],
|
||||||
env: {
|
env: finalEnv,
|
||||||
...sanitizeEnvironment(process.env, sanitizationConfig),
|
|
||||||
...(mcpServerConfig.env || {}),
|
|
||||||
[GEMINI_CLI_IDENTIFICATION_ENV_VAR]:
|
|
||||||
GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE,
|
|
||||||
} as Record<string, string>,
|
|
||||||
cwd: mcpServerConfig.cwd,
|
cwd: mcpServerConfig.cwd,
|
||||||
stderr: 'pipe',
|
stderr: 'pipe',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2026 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { expandEnvVars } from './envExpansion.js';
|
||||||
|
|
||||||
|
describe('expandEnvVars', () => {
|
||||||
|
const defaultEnv = {
|
||||||
|
USER: 'morty',
|
||||||
|
HOME: '/home/morty',
|
||||||
|
TEMP: 'C:\\Temp',
|
||||||
|
EMPTY: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('POSIX behavior (non-Windows)', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.spyOn(process, 'platform', 'get').mockReturnValue('darwin');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['$VAR (POSIX)', 'Hello $USER', defaultEnv, 'Hello morty'],
|
||||||
|
[
|
||||||
|
'${VAR} (POSIX)',
|
||||||
|
'Welcome to ${HOME}',
|
||||||
|
defaultEnv,
|
||||||
|
'Welcome to /home/morty',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'should NOT expand %VAR% on non-Windows',
|
||||||
|
'Data in %TEMP%',
|
||||||
|
defaultEnv,
|
||||||
|
'Data in %TEMP%',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mixed formats (only POSIX expanded)',
|
||||||
|
'$USER lives in ${HOME} on %TEMP%',
|
||||||
|
defaultEnv,
|
||||||
|
'morty lives in /home/morty on %TEMP%',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'missing variables (POSIX only)',
|
||||||
|
'Missing $UNDEFINED and ${NONE} and %MISSING%',
|
||||||
|
defaultEnv,
|
||||||
|
'Missing and and %MISSING%',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'empty or undefined values',
|
||||||
|
'Value is "$EMPTY"',
|
||||||
|
defaultEnv,
|
||||||
|
'Value is ""',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'original string if no variables',
|
||||||
|
'No vars here',
|
||||||
|
defaultEnv,
|
||||||
|
'No vars here',
|
||||||
|
],
|
||||||
|
['literal values like "1234"', '1234', defaultEnv, '1234'],
|
||||||
|
['empty input string', '', defaultEnv, ''],
|
||||||
|
[
|
||||||
|
'complex paths',
|
||||||
|
'${HOME}/bin:$PATH',
|
||||||
|
{ ...defaultEnv, PATH: '/usr/bin' },
|
||||||
|
'/home/morty/bin:/usr/bin',
|
||||||
|
],
|
||||||
|
])('should handle %s', (_, input, env, expected) => {
|
||||||
|
expect(expandEnvVars(input, env)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Windows behavior', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.spyOn(process, 'platform', 'get').mockReturnValue('win32');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['$VAR (POSIX)', 'Hello $USER', defaultEnv, 'Hello morty'],
|
||||||
|
[
|
||||||
|
'${VAR} (POSIX)',
|
||||||
|
'Welcome to ${HOME}',
|
||||||
|
defaultEnv,
|
||||||
|
'Welcome to /home/morty',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'should expand %VAR% on Windows',
|
||||||
|
'Data in %TEMP%',
|
||||||
|
defaultEnv,
|
||||||
|
'Data in C:\\Temp',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mixed formats (both expanded)',
|
||||||
|
'$USER lives in ${HOME} on %TEMP%',
|
||||||
|
defaultEnv,
|
||||||
|
'morty lives in /home/morty on C:\\Temp',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'missing variables (all expanded to empty)',
|
||||||
|
'Missing $UNDEFINED and ${NONE} and %MISSING%',
|
||||||
|
defaultEnv,
|
||||||
|
'Missing and and ',
|
||||||
|
],
|
||||||
|
])('should handle %s', (_, input, env, expected) => {
|
||||||
|
expect(expandEnvVars(input, env)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2026 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { expand } from 'dotenv-expand';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands environment variables in a string using the provided environment record.
|
||||||
|
* Uses the standard `dotenv-expand` library to handle expansion consistently with
|
||||||
|
* other tools.
|
||||||
|
*
|
||||||
|
* Supports POSIX/Bash syntax ($VAR, ${VAR}).
|
||||||
|
* Note: Windows syntax (%VAR%) is not natively supported by dotenv-expand.
|
||||||
|
*
|
||||||
|
* @param str - The string containing environment variable placeholders.
|
||||||
|
* @param env - A record of environment variable names and their values.
|
||||||
|
* @returns The string with environment variables expanded. Missing variables resolve to an empty string.
|
||||||
|
*/
|
||||||
|
export function expandEnvVars(
|
||||||
|
str: string,
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
): string {
|
||||||
|
if (!str) return str;
|
||||||
|
|
||||||
|
// 1. Pre-process Windows-style variables (%VAR%) since dotenv-expand only handles POSIX ($VAR).
|
||||||
|
// We only do this on Windows to limit the blast radius and avoid conflicts with other
|
||||||
|
// systems where % might be a literal character (e.g. in URLs or shell commands).
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
const processedStr = isWindows
|
||||||
|
? str.replace(/%(\w+)%/g, (_, name) => env[name] ?? '')
|
||||||
|
: str;
|
||||||
|
|
||||||
|
// 2. Use dotenv-expand for POSIX/Bash syntax ($VAR, ${VAR}).
|
||||||
|
// dotenv-expand is designed to process an object of key-value pairs (like a .env file).
|
||||||
|
// To expand a single string, we wrap it in an object with a temporary key.
|
||||||
|
const dummyKey = '__GCLI_EXPAND_TARGET__';
|
||||||
|
|
||||||
|
// Filter out undefined values to satisfy the Record<string, string> requirement safely
|
||||||
|
const processEnv: Record<string, string> = {};
|
||||||
|
for (const [key, value] of Object.entries(env)) {
|
||||||
|
if (value !== undefined) {
|
||||||
|
processEnv[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = expand({
|
||||||
|
parsed: { [dummyKey]: processedStr },
|
||||||
|
processEnv,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.parsed?.[dummyKey] ?? '';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user