From 2c2a67b8bba0c8917a9a373fd72826e7568c41e9 Mon Sep 17 00:00:00 2001 From: Patrick Hoogkamer Date: Fri, 5 Jan 2024 15:43:24 +0100 Subject: [PATCH] feat!: only encrypt secrets (#2) BREAKING CHANGE: with the only values flag now only variables ending with _PASSWORD, _KEY or _SECRET will be encrypted by default. Configurable with the --only flag. --- README.md | 22 ++++++++++++++++--- src/Console/EnvironmentDecryptCommand.php | 14 ++++++++++-- src/Console/EnvironmentEncryptCommand.php | 13 +++++++++-- .../EnvironmentDecryptCommandTest.php | 12 +++++----- .../EnvironmentEncryptCommandTest.php | 9 ++++++-- 5 files changed, 56 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cb45282..7c34227 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ eyJpdiI6ImplT2xTaGRzV... # Really long string But with this package you can make it look like this: ```text -APP_NAME=eyJpdiI6ImplT2xTaGRzV... -APP_ENV=eyJpdiI6ImplT2xTaGRzV... -APP_DEBUG=eyJpdiI6ImplT2xTaGRzV... +APP_NAME="My awesome app" +APP_ENV=local +APP_DEBUG=true SOME_API_KEY=eyJpdiI6ImplT2xTaGRzV... ``` @@ -44,3 +44,19 @@ Just use the `env:encrypt` and `env:decrypt` commands as usual, but add an `--on php artisan env:encrypt --only-values ... php artisan env:decrypt --only-values ... ``` + +### Only Encrypting Secrets +By default, if the `--only-values` flag is used only variables ending with `_PASSWORD`, `_KEY` and `_SECRET` will be encrypted. You can configure this behaviour with the `--only` flag. If you would only want to encrypt the variables ending with `_SECRET` and the `APP_KEY`, use it like this: + +```shell +php artisan env:encrypt --only-values --only="*_SECRET,APP_KEY" +``` + +For decrypting, there is no difference: the decrypt command will leave unencrypted values. + +### Encrypting Everything +If you still want to encrypt everything while keeping variable names readable, use the `--all` flag: + +```shell +php artisan env:encrypt --only-values --all +``` diff --git a/src/Console/EnvironmentDecryptCommand.php b/src/Console/EnvironmentDecryptCommand.php index 6b939cd..2035fac 100644 --- a/src/Console/EnvironmentDecryptCommand.php +++ b/src/Console/EnvironmentDecryptCommand.php @@ -5,6 +5,7 @@ namespace Intermax\Veil\Console; use Exception; +use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Encryption\Encrypter; use Illuminate\Foundation\Console\EnvironmentDecryptCommand as BaseDecryptCommand; use Illuminate\Support\Env; @@ -96,8 +97,17 @@ protected function decryptValues(string $contents, Encrypter $encrypter): string return $line->before('=') ->append('=') ->append( - $line->after('=') - ->pipe(fn (Stringable $value) => $encrypter->decrypt($value->toString())) + $line->after('=')->pipe(function (Stringable $value) use ($encrypter) { + try { + return $encrypter->decrypt($value->toString()); + } catch (DecryptException $e) { + if ($e->getMessage() == 'The payload is invalid.') { + return $value->toString(); + } + + throw $e; + } + }) ); })->toArray()); } diff --git a/src/Console/EnvironmentEncryptCommand.php b/src/Console/EnvironmentEncryptCommand.php index 27e1b67..b6fbe83 100644 --- a/src/Console/EnvironmentEncryptCommand.php +++ b/src/Console/EnvironmentEncryptCommand.php @@ -17,7 +17,9 @@ class EnvironmentEncryptCommand extends BaseEncryptCommand {--cipher= : The encryption cipher} {--env= : The environment to be encrypted} {--force : Overwrite the existing encrypted environment file} - {--only-values : Encrypt only the values to keep the file readable}'; + {--only-values : Encrypt only the values to keep the file readable} + {--only=**_KEY,*_SECRET,*_PASSWORD : Encrypt only variables that match provided comma-separated patterns, by default values with *_KEY, *_SECRET and *_PASSWORD will be encrypted} + {--all : Ignore the --only flag and default patterns to encrypt all variables}'; public function handle() { @@ -76,13 +78,20 @@ public function handle() protected function encryptValues(string $contents, Encrypter $encrypter): string { - return implode(PHP_EOL, collect(explode(PHP_EOL, $contents))->map(function (string $line) use ($encrypter) { + /** @var array $only */ + $only = $this->option('only'); + + return implode(PHP_EOL, collect(explode(PHP_EOL, $contents))->map(function (string $line) use ($encrypter, $only) { $line = Str::of($line); if (! $line->contains('=')) { return $line; } + if (! $this->option('all') && $only !== null && ! $line->before('=')->is($only)) { + return $line; + } + return $line->before('=') ->append('=') ->append( diff --git a/tests/Integration/EnvironmentDecryptCommandTest.php b/tests/Integration/EnvironmentDecryptCommandTest.php index a2d5442..0378555 100644 --- a/tests/Integration/EnvironmentDecryptCommandTest.php +++ b/tests/Integration/EnvironmentDecryptCommandTest.php @@ -14,21 +14,23 @@ File::swap($this->filesystem); }); -it('decrypts an encrypted environment where only values are encrypted', function () { +it('decrypts an encrypted environment where only secrets are encrypted', function () { $contents = <<<'Text' APP_NAME=Laravel APP_ENV=local APP_DEBUG=true APP_URL=http://localhost + APP_KEY=1234 Text; $encrypter = new Encrypter('abcdefghijklmnopabcdefghijklmnop', 'AES-256-CBC'); $encryptedContents = <<encrypt('Laravel')} - APP_ENV={$encrypter->encrypt('local')} - APP_DEBUG={$encrypter->encrypt('true')} - APP_URL={$encrypter->encrypt('http://localhost')} + APP_NAME=Laravel + APP_ENV=local + APP_DEBUG=true + APP_URL=http://localhost + APP_KEY={$encrypter->encrypt('1234')} TEXT; $this->filesystem->shouldReceive('exists') diff --git a/tests/Integration/EnvironmentEncryptCommandTest.php b/tests/Integration/EnvironmentEncryptCommandTest.php index df61811..32c69ec 100644 --- a/tests/Integration/EnvironmentEncryptCommandTest.php +++ b/tests/Integration/EnvironmentEncryptCommandTest.php @@ -5,6 +5,7 @@ use Illuminate\Encryption\Encrypter; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Facades\File; +use Illuminate\Support\Str; use Mockery as m; beforeEach(function () { @@ -16,8 +17,9 @@ File::swap($this->filesystem); }); -it('encrypts the values of an environment', function () { +it('encrypts the secrets of an environment', function () { $contents = <<<'Text' + APP_KEY=1234 APP_NAME=Laravel APP_ENV=local APP_DEBUG=true @@ -40,8 +42,11 @@ $this->assertStringContainsString('APP_ENV', $contents); $this->assertStringContainsString('APP_DEBUG', $contents); $this->assertStringContainsString('APP_URL', $contents); + $this->assertStringContainsString('APP_KEY', $contents); - $this->assertEquals('Laravel', $encrypter->decrypt(Str::betweenFirst($contents, '=', "\n"))); + $this->assertEquals('1234', $encrypter->decrypt(Str::betweenFirst($contents, '=', "\n"))); + + $this->assertEquals('http://localhost', Str::afterLast($contents, '=')); return true; })->andReturn(true);