Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PHPStan analysis, level 5 #195

Merged
merged 12 commits into from
Jan 25, 2025
Merged
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ jobs:
uses: "ramsey/composer-install@v3"

- name: PHPUnit tests
run: vendor/bin/phpunit
run: vendor/bin/phpunit

- name: PHPStan analysis
run: vendor/bin/phpstan
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
/composer.phar
/.idea/
/.phpunit.result.cache
/phpstan.neon
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@
}
},
"require-dev": {
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4"
},
"scripts": {
"test": "phpunit && psalm"
"test": "phpunit && phpstan && psalm"
},
"support": {
"docs": "https://github.com/paragonie/halite/tree/master/doc"
Expand Down
115 changes: 115 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
parameters:
ignoreErrors:
-
message: '#^Call to function is_string\(\) with non\-empty\-string will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 4
path: src/File.php

-
message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 2
path: src/File.php

-
message: '#^Comparison operation "\<" between 10 and 10 is always false\.$#'
identifier: smaller.alwaysFalse
count: 1
path: src/Halite.php

-
message: '#^Property ParagonIE\\Halite\\Key\:\:\$keyMaterial \(string\) does not accept null\.$#'
identifier: assign.propertyType
count: 1
path: src/Key.php

-
message: '#^Comparison operation "\<\=" between int\<1, max\> and 0 is always false\.$#'
identifier: smallerOrEqual.alwaysFalse
count: 1
path: src/Stream/MutableFile.php

-
message: '#^Comparison operation "\<\=" between int\<1, max\> and 0 is always false\.$#'
identifier: smallerOrEqual.alwaysFalse
count: 1
path: src/Stream/ReadOnlyFile.php

-
message: '#^Offset 1\|int\<3, max\> on array\<int, string\> on left side of \?\? always exists and is not nullable\.$#'
identifier: nullCoalesce.offset
count: 1
path: src/Structure/MerkleTree.php

-
message: '#^Parameter &\$var @param\-out type of method ParagonIE\\Halite\\Util\:\:memzero\(\) expects null, int given\.$#'
identifier: paramOut.type
count: 1
path: src/Util.php

-
message: '#^Comparison operation "\<" between 10 and 7 is always false\.$#'
identifier: smaller.alwaysFalse
count: 2
path: test/unit/AsymmetricTest.php

-
message: '#^Comparison operation "\<" between 3 and 5 is always true\.$#'
identifier: smaller.alwaysTrue
count: 2
path: test/unit/AsymmetricTest.php

-
message: '#^Loose comparison using \=\= between 10 and 7 will always evaluate to false\.$#'
identifier: equal.alwaysFalse
count: 2
path: test/unit/AsymmetricTest.php

-
message: '#^Result of && is always false\.$#'
identifier: booleanAnd.alwaysFalse
count: 2
path: test/unit/AsymmetricTest.php

-
message: '#^Result of \|\| is always false\.$#'
identifier: booleanOr.alwaysFalse
count: 2
path: test/unit/AsymmetricTest.php

-
message: '#^Access to an undefined property object\{abc\: int\}&ParagonIE\\Halite\\Config\:\:\$missing\.$#'
identifier: property.notFound
count: 1
path: test/unit/ConfigTest.php

-
message: '#^Dead catch \- ParagonIE\\Halite\\Alerts\\ConfigDirectiveNotFound is never thrown in the try block\.$#'
identifier: catch.neverThrown
count: 1
path: test/unit/ConfigTest.php

-
message: '#^Parameter \#1 \$key of static method ParagonIE\\Halite\\KeyFactory\:\:export\(\) expects ParagonIE\\Halite\\Key\|ParagonIE\\Halite\\KeyPair, stdClass given\.$#'
identifier: argument.type
count: 1
path: test/unit/KeyTest.php

-
message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsString\(\) with string will always evaluate to true\.$#'
identifier: method.alreadyNarrowedType
count: 3
path: test/unit/PasswordTest.php

-
message: '#^Parameter \#1 \$file of class ParagonIE\\Halite\\Stream\\MutableFile constructor expects resource\|string, int given\.$#'
identifier: argument.type
count: 1
path: test/unit/StreamTest.php

-
message: '#^Parameter \#1 \$file of class ParagonIE\\Halite\\Stream\\ReadOnlyFile constructor expects resource\|string, int given\.$#'
identifier: argument.type
count: 1
path: test/unit/StreamTest.php
9 changes: 9 additions & 0 deletions phpstan.dist.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
parameters:
paths:
- src
- test
level: 5
bootstrapFiles:
- src/HiddenString.php
includes:
- phpstan-baseline.neon
39 changes: 19 additions & 20 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,27 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* @property string|bool ENCODING
* @property string|bool $ENCODING
*
* AsymmetricCrypto:
* @property string HASH_DOMAIN_SEPARATION
* @property bool HASH_SCALARMULT
* @property string $HASH_DOMAIN_SEPARATION
* @property bool $HASH_SCALARMULT
*
* SymmetricCrypto:
* @property bool CHECKSUM_PUBKEY
* @property int BUFFER
* @property int HASH_LEN
* @property int SHORTEST_CIPHERTEXT_LENGTH
* @property int NONCE_BYTES
* @property int HKDF_SALT_LEN
* @property string ENC_ALGO
* @property string MAC_ALGO
* @property int MAC_SIZE
* @property int PUBLICKEY_BYTES
* @property bool HKDF_USE_INFO
* @property string HKDF_SBOX
* @property string HKDF_AUTH
* @property bool USE_PAE
* @property bool $CHECKSUM_PUBKEY
* @property int $BUFFER
* @property int $HASH_LEN
* @property int $SHORTEST_CIPHERTEXT_LENGTH
* @property int $NONCE_BYTES
* @property int $HKDF_SALT_LEN
* @property string $ENC_ALGO
* @property string $MAC_ALGO
* @property int $MAC_SIZE
* @property int $PUBLICKEY_BYTES
* @property bool $HKDF_USE_INFO
* @property string $HKDF_SBOX
* @property string $HKDF_AUTH
* @property bool $USE_PAE
*/
class Config
{
Expand Down Expand Up @@ -79,11 +79,10 @@ public function __get(string $key)
*
* @param string $key
* @param mixed $value
* @return bool
* @return void
* @codeCoverageIgnore
*/
public function __set(string $key, mixed $value = null)
public function __set(string $key, mixed $value = null): void
{
return false;
}
}
2 changes: 1 addition & 1 deletion src/Cookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function __construct(EncryptionKey $key)
*
* @return array
*/
public function __debugInfo()
public function __debugInfo(): array
{
return [
'key' => 'private'
Expand Down
2 changes: 1 addition & 1 deletion src/EncryptionKeyPair.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class EncryptionKeyPair extends KeyPair
/**
* Pass it a secret key, it will automatically generate a public key
*
* @param array<int, Key> $keys
* @param Key ...$keys
*
* @throws InvalidKey
* @throws \InvalidArgumentException
Expand Down
21 changes: 9 additions & 12 deletions src/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,19 +118,16 @@ public static function checksum(
return $checksum;
}

if (is_string($filePath)) {
$readOnly = new ReadOnlyFile($filePath);
try {
return self::checksumData(
$readOnly,
$key,
$encoding
);
} finally {
$readOnly->close();
}
$readOnly = new ReadOnlyFile($filePath);
try {
return self::checksumData(
$readOnly,
$key,
$encoding
);
} finally {
$readOnly->close();
}
throw new InvalidType('Argument 1: Expected a filename');
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/SignatureKeyPair.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ final class SignatureKeyPair extends KeyPair
/**
* Pass it a secret key, it will automatically generate a public key
*
* @param array<int, Key> $keys
* @param Key ...$keys
*
* @throws CannotPerformOperation
* @throws InvalidKey
* @throws InvalidArgumentException
* @throws SodiumException
* @throws TypeError
*/
Expand Down Expand Up @@ -153,7 +154,6 @@ public function getEncryptionKeyPair(): EncryptionKeyPair
* @param SignatureSecretKey $secret
* @return void
*
* @throws CannotPerformOperation
* @throws InvalidKey
* @throws SodiumException
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Stream/MutableFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public function readBytes(int $num, bool $skipTests = false): string
// @codeCoverageIgnoreEnd
}
$bufSize = min($remaining, self::CHUNK);
/** @var string|bool $read */
/** @var string|false $read */
$read = fread($this->fp, $bufSize);
if (!is_string($read)) {
// @codeCoverageIgnoreStart
Expand Down
2 changes: 1 addition & 1 deletion src/Stream/ReadOnlyFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function __construct($file, ?Key $key = null)
'Could not open file for reading'
);
}
/** @var resource|bool $fp */
/** @var resource|false $fp */
$fp = fopen($file, 'rb');
// @codeCoverageIgnoreStart
if (!is_resource($fp)) {
Expand Down
5 changes: 2 additions & 3 deletions src/Structure/MerkleTree.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class MerkleTree
/**
* Instantiate a Merkle tree
*
* @param array<int, Node> $nodes
* @param Node ...$nodes
*/
public function __construct(Node ...$nodes)
{
Expand Down Expand Up @@ -84,7 +84,7 @@ public function getRoot(bool $raw = false): string
/**
* Merkle Trees are immutable. Return a replacement with extra nodes.
*
* @param array<int, Node> $nodes
* @param Node ...$nodes
*
* @return MerkleTree
*
Expand Down Expand Up @@ -214,7 +214,6 @@ protected function calculateRoot(): string
$tmp = [];
$j = 0;
for ($i = 0; $i < $order; $i += 2) {
/** @var string $prev */
$curr = (string) ($hash[$i] ?? '');
if (empty($hash[$i + 1])) {
// @codeCoverageIgnoreStart
Expand Down
2 changes: 1 addition & 1 deletion src/Structure/TrimmedMerkleTree.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ protected function calculateRoot(): string
/**
* Merkle Trees are immutable. Return a replacement with extra nodes.
*
* @param array<int, Node> $nodes
* @param Node ...$nodes
*
* @return TrimmedMerkleTree
*
Expand Down
6 changes: 1 addition & 5 deletions src/Symmetric/Crypto.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,7 @@ public static function decryptWithAD(

This uses salted HKDF to split the keys, which is why we need the
salt in the first place. */
/**
* @var array<int, string> $split
* @var string $encKey
* @var string $authKey
*/
/** @var array<int, string> $split */
$split = Util::splitKeys($secretKey, $salt, $config);
$encKey = $split[0];
$authKey = $split[1];
Expand Down
8 changes: 4 additions & 4 deletions test/unit/AsymmetricTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public function testEncryptFail()
'This should have thrown an InvalidMessage exception!'
);
} catch (CryptoException\InvalidMessage $e) {
$this->assertTrue($e instanceof CryptoException\InvalidMessage);
$this->assertInstanceOf(CryptoException\InvalidMessage::class, $e);
}
}

Expand Down Expand Up @@ -285,9 +285,9 @@ public function testSealFail()
'This should have thrown an InvalidMessage exception!'
);
} catch (CryptoException\InvalidKey $e) {
$this->assertTrue($e instanceof CryptoException\InvalidKey);
$this->assertInstanceOf(CryptoException\InvalidKey::class, $e);
} catch (CryptoException\InvalidMessage $e) {
$this->assertTrue($e instanceof CryptoException\InvalidMessage);
$this->assertInstanceOf(CryptoException\InvalidMessage::class, $e);
}
}

Expand Down Expand Up @@ -394,7 +394,7 @@ public function testSignEncryptFail()
);
$this->fail('Invalid signature was accepted.');
} catch (CryptoException\InvalidSignature $ex) {
$this->assertTrue(true);
$this->assertInstanceOf(CryptoException\InvalidSignature::class, $ex);
}

// http://time.com/4261796/tim-cook-transcript/
Expand Down
1 change: 1 addition & 0 deletions test/unit/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class ConfigTest extends TestCase
{
public function testConfig()
{
/** @var object{abc:12345}&Config $config */
$config = new Config([
'abc' => 12345
]);
Expand Down
Loading
Loading