diff --git a/README.md b/README.md index f77b096..28d88a9 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ Here's a comprehensive list of all available rules: 21. **ForbidWeakCryptoKeyRule**: Prevents weak cryptographic key sizes in `openssl_pkey_new()` calls. RSA keys must be at least 2048 bits. +22. **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/rules.neon b/rules.neon index ef04548..086b8f6 100644 --- a/rules.neon +++ b/rules.neon @@ -22,6 +22,7 @@ rules: - Shopware\PhpStan\Rule\ForwardSalesChannelContextToSystemConfigServiceRule - Shopware\PhpStan\Rule\ForbidPredictableSaltRule - Shopware\PhpStan\Rule\ForbidWeakCryptoKeyRule + - Shopware\PhpStan\Rule\ForbidHardcodedCredentialsRule services: - diff --git a/src/Rule/ForbidHardcodedCredentialsRule.php b/src/Rule/ForbidHardcodedCredentialsRule.php new file mode 100644 index 0000000..a4fcbaf --- /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 list + */ + 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', + ]; + } +}