Skip to content

Commit

Permalink
Implement OpenSslEncryptParameterOutTypeExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan committed Dec 22, 2024
1 parent b5fc9ec commit e22aae5
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,11 @@ services:
tags:
- phpstan.dynamicFunctionThrowTypeExtension

-
class: PHPStan\Type\Php\OpenSslEncryptParameterOutTypeExtension
tags:
- phpstan.functionParameterOutTypeExtension

-
class: PHPStan\Type\Php\ParseStrParameterOutTypeExtension
tags:
Expand Down
57 changes: 57 additions & 0 deletions src/Type/Php/OpenSslEncryptParameterOutTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Type\FunctionParameterOutTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function current;
use function in_array;
use function openssl_get_cipher_methods;
use function strtolower;
use function substr;

final class OpenSslEncryptParameterOutTypeExtension implements FunctionParameterOutTypeExtension
{

public function isFunctionSupported(FunctionReflection $functionReflection, ParameterReflection $parameter): bool
{
return $functionReflection->getName() === 'openssl_encrypt' && $parameter->getName() === 'tag';
}

public function getParameterOutTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $funcCall, ParameterReflection $parameter, Scope $scope): ?Type
{
$args = $funcCall->getArgs();
$cipherArg = $args[1] ?? null;

if ($cipherArg === null) {
return null;
}

$cipherType = current($scope->getType($cipherArg->value)->getConstantStrings());

if ($cipherType === false) {
return TypeCombinator::addNull(new StringType());
}

$cipher = strtolower($cipherType->getValue());
$mode = substr($cipher, -3);

if (!in_array($mode, openssl_get_cipher_methods(), true)) {
return new NullType();
}

if (in_array($mode, ['gcm', 'ccm'], true)) {
return new StringType();
}

return new NullType();
}

}
46 changes: 46 additions & 0 deletions tests/PHPStan/Analyser/nsrt/openssl-encrypt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types = 1);

namespace OpenSslEncrypt;

use function PHPStan\Testing\assertType;

class Foo
{
public function testStringCipher(string $cipher): void
{
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('string|null', $tag);
}

public function testUnknownCipher(): void
{
openssl_encrypt('data', 'aes-256-cde', random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('null', $tag);

openssl_encrypt('data', 'abc-256-gcm', random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('null', $tag);

openssl_encrypt('data', 'abc-256-ccm', random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('null', $tag);
}

public function testAeadCipher(): void
{
$cipher = 'aes-256-gcm';
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('string', $tag);

$cipher = 'aes-256-ccm';
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('string', $tag);
}

public function testNonAeadCipher(): void
{
$cipher = 'aes-256-cbc';
openssl_encrypt('data', $cipher, random_bytes(32), OPENSSL_RAW_DATA, random_bytes(16), $tag);
assertType('null', $tag);
}
}

0 comments on commit e22aae5

Please sign in to comment.