From 5c9ee2c6d50c0e4ae8e35e17d29ed096caa561ca Mon Sep 17 00:00:00 2001 From: nexxo Date: Tue, 4 Aug 2026 15:20:19 +0200 Subject: [PATCH] Plan: apply() darf den Wert nicht als Regex-Ersatz behandeln MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit preg_replace deutet $1, \1 und \\ im ERSATZ als Rueckverweise, auch ohne Gruppen im Muster. Der Wert kommt von der Befehlszeile, das Ziel ist die Datei mit allen Zugangsdaten, und das Ergebnis waere still verstuemmelt statt abgelehnt — isValidLine() sieht weiterhin ein gueltiges KEY=value. Jetzt zeilenweise, ohne Regex auf dem Schreibweg. Dazu faengt der Befehl InvalidEnvContentException ab, statt dem Betreiber einen Stapelabzug zu zeigen. Co-Authored-By: Claude Opus 5 --- .../plans/2026-08-04-hostnamen-trennung.md | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/plans/2026-08-04-hostnamen-trennung.md b/docs/superpowers/plans/2026-08-04-hostnamen-trennung.md index f21871c..c1cc719 100644 --- a/docs/superpowers/plans/2026-08-04-hostnamen-trennung.md +++ b/docs/superpowers/plans/2026-08-04-hostnamen-trennung.md @@ -729,6 +729,7 @@ Datei `app/Console/Commands/BindHosts.php`: namespace App\Console\Commands; use App\Services\Env\EnvFileEditor; +use App\Services\Env\InvalidEnvContentException; use Illuminate\Console\Command; /** @@ -821,7 +822,16 @@ class BindHosts extends Command return self::FAILURE; } - $backup = $env->write($this->apply($content, $missing)); + try { + $backup = $env->write($this->apply($content, $missing)); + } catch (InvalidEnvContentException $e) { + // Abgelehnt heißt hier: NICHTS geschrieben, und die Sicherung ist + // erst gar nicht angelegt worden — der Editor prüft vor beidem. + // Der Betreiber soll das als Satz erfahren, nicht als Stapelabzug. + $this->error('Die Datei wurde nicht geschrieben: '.$e->getMessage()); + + return self::FAILURE; + } $this->info('Geschrieben. Die vorherige Fassung liegt unter '.$backup); $this->line('Danach: php artisan config:cache && php artisan route:cache'); @@ -850,6 +860,12 @@ class BindHosts extends Command /** * Vorhandene Zeile ersetzen, sonst anhängen. * + * Zeilenweise und ohne `preg_replace`: der Wert kommt von der Befehlszeile, + * und `preg_replace` deutet `$1`, `\1` und `\\` im ERSATZ als Rückverweise. + * Aus `--site 'www.example.test$1'` würde still `SITE_HOST=www.example.test` + * — und diese Datei hält jedes Geheimnis dieser Installation. Ein leise + * verstümmelter Wert darin ist schlimmer als eine Fehlermeldung. + * * Ersetzen und nicht nur anhängen, weil ein leerer Schlüssel als fehlend * gilt: `APP_HOST=` steht dann schon da, und ein zweites `APP_HOST=…` * darunter wäre eine Datei mit zwei Antworten auf dieselbe Frage. @@ -858,16 +874,25 @@ class BindHosts extends Command */ private function apply(string $content, array $values): string { - foreach ($values as $key => $value) { - $line = $key.'='.$value; - $pattern = '/^'.preg_quote($key, '/').'=.*$/m'; + $lines = preg_split('/\R/', rtrim($content, "\r\n")); - $content = preg_match($pattern, $content) === 1 - ? preg_replace($pattern, $line, $content, 1) - : rtrim($content, "\n")."\n".$line."\n"; + foreach ($values as $key => $value) { + $replaced = false; + + foreach ($lines as $index => $line) { + if (str_starts_with($line, $key.'=')) { + $lines[$index] = $key.'='.$value; + $replaced = true; + break; + } + } + + if (! $replaced) { + $lines[] = $key.'='.$value; + } } - return $content; + return implode("\n", $lines)."\n"; } } ```