From ad73b494441c0cdcaab588d19d7e09acfb20ea93 Mon Sep 17 00:00:00 2001 From: Martin Bens Date: Wed, 11 Feb 2026 10:44:12 +0100 Subject: [PATCH 1/3] feat: add ForbidHardcodedCredentialsRule to detect hardcoded credentials Implements a new PHPStan rule that detects hardcoded credentials (passwords, API keys, secrets, tokens) in array definitions and encourages using environment variables instead. --- README.md | 2 + src/Rule/ForbidHardcodedCredentialsRule.php | 65 +++++++++++++++++++ .../ForbidHardcodedCredentialsRuleTest.php | 31 +++++++++ .../correct-usage.php | 50 ++++++++++++++ .../wrong-usage.php | 15 +++++ 5 files changed, 163 insertions(+) create mode 100644 src/Rule/ForbidHardcodedCredentialsRule.php create mode 100644 tests/Rule/ForbidHardcodedCredentialsRuleTest.php create mode 100644 tests/Rule/fixtures/ForbidHardcodedCredentialsRule/correct-usage.php create mode 100644 tests/Rule/fixtures/ForbidHardcodedCredentialsRule/wrong-usage.php diff --git a/README.md b/README.md index 6aeed13..2bff8ba 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ Here's a comprehensive list of all available rules: 19. **ForwardSalesChannelContextToSystemConfigServiceRule**: Ensures that when a method has a SalesChannelContext parameter, it is forwarded to SystemConfigService methods as the salesChannelId argument. +20. **ForbidHardcodedCredentialsRule**: Detects hardcoded credentials (passwords, API keys, secrets, tokens) in array definitions. Use environment variables instead. + ## Configuration You can customize the behavior of these rules by adding configuration to your `phpstan.neon` file. See the [configuration section](#configuration) for more details. diff --git a/src/Rule/ForbidHardcodedCredentialsRule.php b/src/Rule/ForbidHardcodedCredentialsRule.php new file mode 100644 index 0000000..d7f0241 --- /dev/null +++ b/src/Rule/ForbidHardcodedCredentialsRule.php @@ -0,0 +1,65 @@ + + */ +class ForbidHardcodedCredentialsRule implements Rule +{ + private const SENSITIVE_KEYS = ['password', 'api_key', 'secret', 'token', 'apikey']; + + public function getNodeType(): string + { + return ArrayItem::class; + } + + /** + * @return array + */ + public function processNode(Node $node, Scope $scope): array + { + if (!$node->key instanceof String_) { + return []; + } + + $keyName = strtolower($node->key->value); + $isSensitive = false; + foreach (self::SENSITIVE_KEYS as $sensitive) { + if (str_contains($keyName, $sensitive)) { + $isSensitive = true; + break; + } + } + + if (!$isSensitive) { + return []; + } + + if (!$node->value instanceof String_) { + return []; + } + + $value = $node->value->value; + if ($value === '' || $value === '0' || str_starts_with($value, '%') || str_contains($value, 'env(')) { + return []; + } + + return [ + RuleErrorBuilder::message('Possible hardcoded credentials detected. Use environment variables.') + ->identifier('shopware.forbidHardcodedCredentials') + ->line($node->getLine()) + ->build(), + ]; + } +} diff --git a/tests/Rule/ForbidHardcodedCredentialsRuleTest.php b/tests/Rule/ForbidHardcodedCredentialsRuleTest.php new file mode 100644 index 0000000..ee6676f --- /dev/null +++ b/tests/Rule/ForbidHardcodedCredentialsRuleTest.php @@ -0,0 +1,31 @@ + + */ +class ForbidHardcodedCredentialsRuleTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new ForbidHardcodedCredentialsRule(); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/fixtures/ForbidHardcodedCredentialsRule/correct-usage.php'], []); + $this->analyse([__DIR__ . '/fixtures/ForbidHardcodedCredentialsRule/wrong-usage.php'], [ + [ + 'Possible hardcoded credentials detected. Use environment variables.', + 12, + ], + ]); + } +} diff --git a/tests/Rule/fixtures/ForbidHardcodedCredentialsRule/correct-usage.php b/tests/Rule/fixtures/ForbidHardcodedCredentialsRule/correct-usage.php new file mode 100644 index 0000000..4f5d73a --- /dev/null +++ b/tests/Rule/fixtures/ForbidHardcodedCredentialsRule/correct-usage.php @@ -0,0 +1,50 @@ + '%env(DATABASE_PASSWORD)%', + ]; + } + + public function envFunctionPattern(): array + { + return [ + 'secret' => 'env(APP_SECRET)', + ]; + } + + public function emptyValue(): array + { + return [ + 'api_key' => '', + ]; + } + + public function zeroValue(): array + { + return [ + 'apikey' => '0', + ]; + } + + public function nullValue(): array + { + return [ + 'token' => null, + ]; + } + + public function nonSensitiveKey(): array + { + return [ + 'username' => 'admin', + ]; + } +} diff --git a/tests/Rule/fixtures/ForbidHardcodedCredentialsRule/wrong-usage.php b/tests/Rule/fixtures/ForbidHardcodedCredentialsRule/wrong-usage.php new file mode 100644 index 0000000..5f35b40 --- /dev/null +++ b/tests/Rule/fixtures/ForbidHardcodedCredentialsRule/wrong-usage.php @@ -0,0 +1,15 @@ + 'secret123', + ]; + } +} From e2de4f50de1c02f5e0857e1807b6e590ed3acb28 Mon Sep 17 00:00:00 2001 From: Martin Bens Date: Thu, 12 Feb 2026 08:45:42 +0100 Subject: [PATCH 2/3] refactor: improvie PHPStan typing --- src/Rule/ForbidHardcodedCredentialsRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rule/ForbidHardcodedCredentialsRule.php b/src/Rule/ForbidHardcodedCredentialsRule.php index d7f0241..a4fcbaf 100644 --- a/src/Rule/ForbidHardcodedCredentialsRule.php +++ b/src/Rule/ForbidHardcodedCredentialsRule.php @@ -25,7 +25,7 @@ public function getNodeType(): string } /** - * @return array + * @return list */ public function processNode(Node $node, Scope $scope): array { From a62251ff684815da99798fa7239a7d77bdb4cb8b Mon Sep 17 00:00:00 2001 From: Martin Bens Date: Thu, 12 Feb 2026 08:45:58 +0100 Subject: [PATCH 3/3] config: register ForbidHardcodedCredentialsRule in phpstan config --- rules.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/rules.neon b/rules.neon index 3230f57..ca4f4bb 100644 --- a/rules.neon +++ b/rules.neon @@ -20,6 +20,7 @@ rules: - Shopware\PhpStan\Rule\BestPractise\PreferRouteEventRule - Shopware\PhpStan\Rule\BestPractise\DALDefinitionRule - Shopware\PhpStan\Rule\ForwardSalesChannelContextToSystemConfigServiceRule + - Shopware\PhpStan\Rule\ForbidHardcodedCredentialsRule services: -