From e3b71763ae71752e535f742129043d2f9f3fd54a Mon Sep 17 00:00:00 2001 From: Jack Worman Date: Fri, 28 Jan 2022 13:14:38 -0600 Subject: [PATCH 1/2] Fixed psalm level 6 errors in phpseclib/Net/ --- phpseclib/Common/Functions/Strings.php | 4 +- phpseclib/Crypt/Common/AsymmetricKey.php | 8 ++- phpseclib/Crypt/Common/SymmetricKey.php | 9 +++- phpseclib/Crypt/DH.php | 4 +- phpseclib/Crypt/DSA.php | 4 +- phpseclib/Crypt/Hash.php | 5 ++ phpseclib/Net/SFTP.php | 26 +++++++--- phpseclib/Net/SSH2.php | 65 ++++++++++++++---------- psalm.xml | 29 +++++++++++ 9 files changed, 112 insertions(+), 42 deletions(-) create mode 100644 psalm.xml diff --git a/phpseclib/Common/Functions/Strings.php b/phpseclib/Common/Functions/Strings.php index e6483e91..4b492712 100644 --- a/phpseclib/Common/Functions/Strings.php +++ b/phpseclib/Common/Functions/Strings.php @@ -158,9 +158,9 @@ abstract class Strings /** * Create SSH2-style string * - * @param string[] ...$elements + * @param string|int|float|array|bool ...$elements * @access public - * @return mixed + * @return string */ public static function packSSH2(...$elements) { diff --git a/phpseclib/Crypt/Common/AsymmetricKey.php b/phpseclib/Crypt/Common/AsymmetricKey.php index 4b70ac73..e94aa976 100644 --- a/phpseclib/Crypt/Common/AsymmetricKey.php +++ b/phpseclib/Crypt/Common/AsymmetricKey.php @@ -123,6 +123,12 @@ abstract class AsymmetricKey */ private $comment; + /** + * @param string $type + * @return string + */ + abstract public function toString($type, array $options = []); + /** * The constructor */ @@ -245,7 +251,7 @@ abstract class AsymmetricKey * @param string $type * @param string $key * @param string $password optional - * @return AsymmetricKey + * @return static */ public static function loadFormat($type, $key, $password = false) { diff --git a/phpseclib/Crypt/Common/SymmetricKey.php b/phpseclib/Crypt/Common/SymmetricKey.php index 08dec4bc..9d4725ea 100644 --- a/phpseclib/Crypt/Common/SymmetricKey.php +++ b/phpseclib/Crypt/Common/SymmetricKey.php @@ -213,6 +213,13 @@ abstract class SymmetricKey self::ENGINE_OPENSSL_GCM => 'OpenSSL (GCM)' ]; + /** @var string|null */ + public $fixed; + /** @var string|null */ + public $invocation_counter; + /** @var string|null */ + public $name; + /** * The Encryption Mode * @@ -3412,4 +3419,4 @@ abstract class SymmetricKey { return array_flip(self::MODE_MAP)[$this->mode]; } -} \ No newline at end of file +} diff --git a/phpseclib/Crypt/DH.php b/phpseclib/Crypt/DH.php index 9337200c..c7e6e57a 100644 --- a/phpseclib/Crypt/DH.php +++ b/phpseclib/Crypt/DH.php @@ -78,7 +78,7 @@ abstract class DH extends AsymmetricKey * - a string (eg. diffie-hellman-group14-sha1) * * @access public - * @return \phpseclib3\Crypt\DH|bool + * @return Parameters */ public static function createParameters(...$args) { @@ -397,4 +397,4 @@ abstract class DH extends AsymmetricKey $key = $type::saveParameters($this->prime, $this->base); return self::load($key, 'PKCS1'); } -} \ No newline at end of file +} diff --git a/phpseclib/Crypt/DSA.php b/phpseclib/Crypt/DSA.php index b368d83f..110e7767 100644 --- a/phpseclib/Crypt/DSA.php +++ b/phpseclib/Crypt/DSA.php @@ -129,7 +129,7 @@ abstract class DSA extends AsymmetricKey SSH DSA implementations only support keys with an N of 160. puttygen let's you set the size of L (but not the size of N) and uses 2048 as the default L value. that's not really compliant with any of the FIPS standards, however, - for the purposes of maintaining compatibility with puttygen, we'll support it + for the purposes of maintaining compatibility with puttygen, we'll support it */ //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160: // FIPS 186-3 changed this as follows: @@ -341,4 +341,4 @@ abstract class DSA extends AsymmetricKey { return $this->shortFormat; } -} \ No newline at end of file +} diff --git a/phpseclib/Crypt/Hash.php b/phpseclib/Crypt/Hash.php index 706de214..3c4601c8 100644 --- a/phpseclib/Crypt/Hash.php +++ b/phpseclib/Crypt/Hash.php @@ -69,6 +69,11 @@ class Hash */ const PADDING_SHAKE = 3; + /** @var int|false */ + public $etm; + /** @var string|false */ + public $name; + /** * Padding Type * diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 9e702826..dc624658 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -116,6 +116,18 @@ class SFTP extends SSH2 */ private $status_codes = []; + /** @var array */ + private $attributes; + + /** @var array */ + private $open_flags; + + /** @var array */ + private $open_flags5; + + /** @var array */ + private $file_types; + /** * The Request ID * @@ -188,7 +200,7 @@ class SFTP extends SSH2 /** * Current working directory * - * @var string + * @var string|bool * @see self::realpath() * @see self::chdir() * @access private @@ -242,7 +254,7 @@ class SFTP extends SSH2 * * @see self::__construct() * @see self::get() - * @var array + * @var int * @access private */ private $max_sftp_packet; @@ -781,7 +793,7 @@ class SFTP extends SSH2 /** * Returns the current directory name * - * @return string|false + * @return string|bool * @access public */ public function pwd() @@ -1226,7 +1238,7 @@ class SFTP extends SSH2 * $sftp->setListOrder(); * Don't do any sort of sorting * - * @param string[] ...$args + * @param string ...$args * @access public */ public function setListOrder(...$args) @@ -1809,7 +1821,7 @@ class SFTP extends SSH2 $packet = Strings::packSSH2('s', $path); $packet.= $this->version >= 4 ? pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) : - $atr; + $attr; $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet); $i++; @@ -3131,7 +3143,7 @@ class SFTP extends SSH2 // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4 // represents the number of bytes that the file consumes on the disk. will // usually be larger than the 'size' field - list($attr['allocation-size']) = Strings::unpack('Q', $response); + list($attr['allocation-size']) = Strings::unpackSSH2('Q', $response); break; case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800 // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10 @@ -3146,7 +3158,7 @@ class SFTP extends SSH2 break; case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000 // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12 - list($attr['link-count']) = Strings::unpackSS2('N', $response); + list($attr['link-count']) = Strings::unpackSSH2('N', $response); break; case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000 // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13 diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index d8e21257..f98a3865 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -204,7 +204,7 @@ class SSH2 /** * The Socket Object * - * @var object + * @var resource|closed-resource|null * @access private */ public $fsock; @@ -711,7 +711,7 @@ class SSH2 * Interactive Buffer * * @see self::read() - * @var array + * @var string * @access private */ private $interactiveBuffer = ''; @@ -756,7 +756,7 @@ class SSH2 * Real-time log file pointer * * @see self::_append_log() - * @var resource + * @var resource|closed-resource * @access private */ private $realtime_log_file; @@ -798,7 +798,7 @@ class SSH2 /** * Time of first network activity * - * @var int + * @var float * @access private */ private $last_packet; @@ -966,7 +966,7 @@ class SSH2 /** * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario * - * @var \phpseclib3\System\Ssh\Agent + * @var Agent * @access private */ private $agent; @@ -975,7 +975,7 @@ class SSH2 * Connection storage to replicates ssh2 extension functionality: * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} * - * @var SSH2[] + * @var array> */ private static $connections; @@ -1063,7 +1063,7 @@ class SSH2 /** * Decompression method * - * @var resource|object + * @var int * @access private */ private $decompress = self::NET_SSH2_COMPRESSION_NONE; @@ -1071,7 +1071,7 @@ class SSH2 /** * Compression context * - * @var int + * @var resource|false|null * @access private */ private $compress_context; @@ -1200,7 +1200,13 @@ class SSH2 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] ); - self::$connections[$this->getResourceId()] = class_exists('WeakReference') ? \WeakReference::create($this) : $this; + /** + * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508 + * @var \WeakReference|SSH2 + */ + self::$connections[$this->getResourceId()] = \class_exists('WeakReference') + ? \WeakReference::create($this) + : $this; if (is_resource($host)) { $this->fsock = $host; @@ -1411,7 +1417,7 @@ class SSH2 if (!$this->send_kex_first) { $response = $this->get_binary_packet(); - if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + if (\is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { $this->bitmap = 0; throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } @@ -1546,7 +1552,11 @@ class SSH2 $kexinit_payload_server = $this->get_binary_packet(); - if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) { + if ( + \is_bool($kexinit_payload_server) + || !strlen($kexinit_payload_server) + || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT + ) { $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } @@ -2159,7 +2169,7 @@ class SSH2 * Login Helper * * @param string $username - * @param string[] ...$args + * @param string ...$args * @return bool * @see self::_login_helper() * @access private @@ -2402,7 +2412,7 @@ class SSH2 * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. * * @param string $username - * @param string $password + * @param string|array $password * @return bool * @access private */ @@ -2425,7 +2435,7 @@ class SSH2 /** * Handle the keyboard-interactive requests / responses. * - * @param mixed[] ...$responses + * @param string|array ...$responses * @return bool * @throws \RuntimeException on connection error * @access private @@ -2719,12 +2729,12 @@ class SSH2 * In all likelihood, this is not a feature you want to be taking advantage of. * * @param string $command - * @param callback $callback - * @return string + * @return string|bool + * @psalm-return ($callback is callable ? bool : string|bool) * @throws \RuntimeException on connection error * @access public */ - public function exec($command, $callback = null) + public function exec($command, callable $callback = null) { $this->curTimeout = $this->timeout; $this->is_timeout = false; @@ -3290,12 +3300,15 @@ class SSH2 * * @see self::_send_binary_packet() * @param bool $skip_channel_filter - * @return string + * @return bool|string * @access private */ private function get_binary_packet($skip_channel_filter = false) { if ($skip_channel_filter) { + if (!\is_resource($this->fsock)) { + throw new \InvalidArgumentException('fsock is not a resource.'); + } $read = [$this->fsock]; $write = $except = null; @@ -3314,9 +3327,6 @@ class SSH2 return true; } - $read = [$this->fsock]; - $write = $except = null; - $start = microtime(true); if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { @@ -3590,7 +3600,7 @@ class SSH2 * @see self::_get_binary_packet() * @param string $payload * @param bool $skip_channel_filter - * @return string + * @return string|bool * @access private */ private function filter($payload, $skip_channel_filter) @@ -3624,7 +3634,7 @@ class SSH2 } // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in - if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !\is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { Strings::shift($payload, 1); list($this->banner_message) = Strings::unpackSSH2('s', $payload); $payload = $this->get_binary_packet(); @@ -3632,8 +3642,8 @@ class SSH2 // only called when we've already logged in if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { - if ($payload === true) { - return true; + if (\is_bool($payload)) { + return $payload; } switch (ord($payload[0])) { @@ -4364,7 +4374,7 @@ class SSH2 } $this->bitmap = 0; - if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') { + if (is_resource($this->fsock) && get_resource_type($this->fsock) === 'stream') { fclose($this->fsock); } @@ -5115,11 +5125,12 @@ class SSH2 /** * Return all excising connections * - * @return SSH2[] + * @return array */ public static function getConnections() { if (!class_exists('WeakReference')) { + /** @var array */ return self::$connections; } $temp = []; diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..4d54fb20 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + From 0a9fc99dc8b2eb3aa3ce5c37e7752b2e7683bbf9 Mon Sep 17 00:00:00 2001 From: Jack Worman Date: Sat, 29 Jan 2022 19:56:43 -0600 Subject: [PATCH 2/2] Un-qualifying global functions Un-qualifying global functions --- phpseclib/Crypt/Random.php | 2 +- phpseclib/Net/SSH2.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/phpseclib/Crypt/Random.php b/phpseclib/Crypt/Random.php index b05f040e..1c943933 100644 --- a/phpseclib/Crypt/Random.php +++ b/phpseclib/Crypt/Random.php @@ -51,7 +51,7 @@ abstract class Random } try { - return \random_bytes($length); + return random_bytes($length); } catch (\Exception $e) { // random_compat will throw an Exception, which in PHP 5 does not implement Throwable } catch (\Throwable $e) { diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index f98a3865..899f647f 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -1204,7 +1204,7 @@ class SSH2 * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508 * @var \WeakReference|SSH2 */ - self::$connections[$this->getResourceId()] = \class_exists('WeakReference') + self::$connections[$this->getResourceId()] = class_exists('WeakReference') ? \WeakReference::create($this) : $this; @@ -1417,7 +1417,7 @@ class SSH2 if (!$this->send_kex_first) { $response = $this->get_binary_packet(); - if (\is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + if (is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { $this->bitmap = 0; throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } @@ -1553,7 +1553,7 @@ class SSH2 $kexinit_payload_server = $this->get_binary_packet(); if ( - \is_bool($kexinit_payload_server) + is_bool($kexinit_payload_server) || !strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT ) { @@ -3306,7 +3306,7 @@ class SSH2 private function get_binary_packet($skip_channel_filter = false) { if ($skip_channel_filter) { - if (!\is_resource($this->fsock)) { + if (!is_resource($this->fsock)) { throw new \InvalidArgumentException('fsock is not a resource.'); } $read = [$this->fsock]; @@ -3634,7 +3634,7 @@ class SSH2 } // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in - if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !\is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { Strings::shift($payload, 1); list($this->banner_message) = Strings::unpackSSH2('s', $payload); $payload = $this->get_binary_packet(); @@ -3642,7 +3642,7 @@ class SSH2 // only called when we've already logged in if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { - if (\is_bool($payload)) { + if (is_bool($payload)) { return $payload; }