Skip to content
/ dotenv Public

Commit e25b004

Browse files
committed
[Dotenv] Don't truncate OS env vars containing $ when $_ENV is unpopulated
PR #63955 introduced `loadedRawVars` tracking in `doLoad()`. Its check `!isset($_ENV[$name])` was meant to protect host-provided environment variables from being processed by `resolveLoadedVars()`. However, with PHP's default `variables_order = "GPCS"` (no `E`), OS-provided env vars are placed in $_SERVER but not in $_ENV when PHP starts. As a result, an externally provided value like `abc$def` was added to `loadedRawVars`, and then `resolveLoadedVars()` interpreted the literal `$` as a variable reference and truncated the value to `abc`. Mirror the existing behavior of `populate()` which already considers $_SERVER as a source of pre-existing env vars, so externally provided values are correctly recognized regardless of `variables_order`.
1 parent 381ab08 commit e25b004

2 files changed

Lines changed: 34 additions & 3 deletions

File tree

Dotenv.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -673,10 +673,11 @@ private function doLoad(bool $overrideExistingVars, array $paths): void
673673
unset($loadedVars['']);
674674

675675
foreach ($values as $name => $_) {
676-
if (!isset($this->overriddenValues[$name]) && isset($_ENV[$name])) {
677-
$this->overriddenValues[$name] = $_ENV[$name];
676+
$alreadyExternal = isset($_ENV[$name]) || isset($_SERVER[$name]) && !str_starts_with($name, 'HTTP_');
677+
if (!isset($this->overriddenValues[$name]) && $alreadyExternal) {
678+
$this->overriddenValues[$name] = $_ENV[$name] ?? $_SERVER[$name];
678679
}
679-
if (isset($loadedVars[$name]) || $overrideExistingVars || !isset($_ENV[$name])) {
680+
if (isset($loadedVars[$name]) || $overrideExistingVars || !$alreadyExternal) {
680681
$this->loadedRawVars[$name] = true;
681682
}
682683
}

Tests/DotenvTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,36 @@ public function testLoadDoesNotReResolveAlreadyLoadedVars()
273273
}
274274
}
275275

276+
public function testLoadDoesNotResolveExternalEnvVarsOnlyPresentInServer()
277+
{
278+
// Mimics PHP's default `variables_order = "GPCS"` (no `E`) where
279+
// OS-provided environment variables (e.g. from Kubernetes envFrom or
280+
// Docker) are placed in $_SERVER but not in $_ENV when PHP starts.
281+
// Such values must be left untouched by Dotenv even when the same key
282+
// has a default value in the loaded .env file.
283+
unset($_ENV['FOO'], $_SERVER['FOO'], $_ENV['SYMFONY_DOTENV_VARS'], $_SERVER['SYMFONY_DOTENV_VARS']);
284+
putenv('FOO');
285+
putenv('SYMFONY_DOTENV_VARS');
286+
287+
$_SERVER['FOO'] = 'abc$def';
288+
289+
@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
290+
$path = tempnam($tmpdir, 'sf-');
291+
file_put_contents($path, "FOO=default\n");
292+
293+
try {
294+
(new Dotenv())->loadEnv($path, defaultEnv: 'prod');
295+
$this->assertSame('abc$def', $_ENV['FOO']);
296+
$this->assertSame('abc$def', $_SERVER['FOO']);
297+
} finally {
298+
unset($_ENV['FOO'], $_SERVER['FOO'], $_ENV['SYMFONY_DOTENV_VARS'], $_SERVER['SYMFONY_DOTENV_VARS']);
299+
putenv('FOO');
300+
putenv('SYMFONY_DOTENV_VARS');
301+
unlink($path);
302+
@rmdir($tmpdir);
303+
}
304+
}
305+
276306
public function testLoadEnv()
277307
{
278308
$resetContext = static function (): void {

0 commit comments

Comments
 (0)