From ffe60e9f0323953b24474c08657f0e1d283aa72d Mon Sep 17 00:00:00 2001 From: PT-ATA No One Date: Tue, 13 Aug 2024 18:01:57 +0000 Subject: [PATCH] try one more time with a local gdata/vaas --- .github/workflows/release-app.yml | 10 +- .gitignore | 7 +- .vscode/launch.json | 2 +- Dockerfile.Nextcloud | 34 +- compose-install.yaml | 6 + composer.json | 10 +- .../ClientCredentialsGrantAuthenticator.php | 20 + .../Authentication/OAuth2TokenReceiver.php | 69 + ...esourceOwnerPasswordGrantAuthenticator.php | 21 + .../Exceptions/FileDoesNotExistException.php | 9 + .../Exceptions/InvalidSha256Exception.php | 9 + gdata/vaas/Exceptions/TimeoutException.php | 9 + .../vaas/Exceptions/UnknownKindException.php | 9 + .../Exceptions/UnkownVerdictException.php | 9 + .../vaas/Exceptions/UploadFailedException.php | 12 + .../VaasAuthenticationException.php | 9 + gdata/vaas/Exceptions/VaasClientException.php | 23 + .../VaasConnectionClosedException.php | 9 + .../Exceptions/VaasInvalidStateException.php | 9 + gdata/vaas/Exceptions/VaasServerException.php | 23 + gdata/vaas/Message/AuthRequest.php | 16 + gdata/vaas/Message/AuthResponse.php | 15 + gdata/vaas/Message/BaseMessage.php | 8 + gdata/vaas/Message/BaseVerdictRequest.php | 20 + gdata/vaas/Message/Detection.php | 10 + gdata/vaas/Message/Error.php | 16 + gdata/vaas/Message/Kind.php | 14 + gdata/vaas/Message/LibMagic.php | 9 + gdata/vaas/Message/ProblemDetails.php | 9 + gdata/vaas/Message/VaasVerdict.php | 23 + gdata/vaas/Message/Verdict.php | 11 + gdata/vaas/Message/VerdictRequest.php | 14 + .../vaas/Message/VerdictRequestForStream.php | 13 + gdata/vaas/Message/VerdictRequestForUrl.php | 16 + gdata/vaas/Message/VerdictResponse.php | 15 + gdata/vaas/Readme.md | 89 + gdata/vaas/Sha256.php | 72 + gdata/vaas/Vaas.php | 526 ++++ gdata/vaas/VaasConnection.php | 49 + gdata/vaas/VaasOptions.php | 15 + gdata/vaas/composer.json | 29 + gdata/vaas/composer.lock | 2237 +++++++++++++++++ install.sh | 6 + lib/AvirWrapper.php | 42 +- lib/CacheEntryListener.php | 2 + tests/bats/functionality-parallel.bats | 6 + 46 files changed, 3552 insertions(+), 39 deletions(-) create mode 100644 gdata/vaas/Authentication/ClientCredentialsGrantAuthenticator.php create mode 100644 gdata/vaas/Authentication/OAuth2TokenReceiver.php create mode 100644 gdata/vaas/Authentication/ResourceOwnerPasswordGrantAuthenticator.php create mode 100644 gdata/vaas/Exceptions/FileDoesNotExistException.php create mode 100644 gdata/vaas/Exceptions/InvalidSha256Exception.php create mode 100644 gdata/vaas/Exceptions/TimeoutException.php create mode 100644 gdata/vaas/Exceptions/UnknownKindException.php create mode 100644 gdata/vaas/Exceptions/UnkownVerdictException.php create mode 100644 gdata/vaas/Exceptions/UploadFailedException.php create mode 100644 gdata/vaas/Exceptions/VaasAuthenticationException.php create mode 100644 gdata/vaas/Exceptions/VaasClientException.php create mode 100644 gdata/vaas/Exceptions/VaasConnectionClosedException.php create mode 100644 gdata/vaas/Exceptions/VaasInvalidStateException.php create mode 100644 gdata/vaas/Exceptions/VaasServerException.php create mode 100644 gdata/vaas/Message/AuthRequest.php create mode 100644 gdata/vaas/Message/AuthResponse.php create mode 100644 gdata/vaas/Message/BaseMessage.php create mode 100644 gdata/vaas/Message/BaseVerdictRequest.php create mode 100644 gdata/vaas/Message/Detection.php create mode 100644 gdata/vaas/Message/Error.php create mode 100644 gdata/vaas/Message/Kind.php create mode 100644 gdata/vaas/Message/LibMagic.php create mode 100644 gdata/vaas/Message/ProblemDetails.php create mode 100644 gdata/vaas/Message/VaasVerdict.php create mode 100644 gdata/vaas/Message/Verdict.php create mode 100644 gdata/vaas/Message/VerdictRequest.php create mode 100644 gdata/vaas/Message/VerdictRequestForStream.php create mode 100644 gdata/vaas/Message/VerdictRequestForUrl.php create mode 100644 gdata/vaas/Message/VerdictResponse.php create mode 100644 gdata/vaas/Readme.md create mode 100644 gdata/vaas/Sha256.php create mode 100644 gdata/vaas/Vaas.php create mode 100644 gdata/vaas/VaasConnection.php create mode 100644 gdata/vaas/VaasOptions.php create mode 100644 gdata/vaas/composer.json create mode 100644 gdata/vaas/composer.lock diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index 1642f62f..1eb2371e 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -52,26 +52,26 @@ jobs: CLIENT_ID: ${{ secrets.VAAS_CLIENT_ID }} CLIENT_SECRET: ${{ secrets.VAAS_CLIENT_SECRET }} run: | - composer install --quiet + composer install ./vendor/bin/phpunit --bootstrap tests/unittests/bootstrap.php tests/unittests/ --testdox - name: install nextcloud env: CLIENT_ID: ${{ secrets.VAAS_CLIENT_ID }} CLIENT_SECRET: ${{ secrets.VAAS_CLIENT_SECRET }} - run: ./install.sh ${{ matrix.nextcloud_version }} + run: ./install.sh ${{ matrix.nextcloud_version }} 0 - name: run tests env: CLIENT_ID: ${{ secrets.VAAS_CLIENT_ID }} CLIENT_SECRET: ${{ secrets.VAAS_CLIENT_SECRET }} - run: bats --no-parallelize-across-files --jobs 2 ./tests/bats + run: bats --verbose-run --timing --trace --no-parallelize-across-files --jobs 2 ./tests/bats || echo "failed" - uses: actions/upload-artifact@master with: overwrite: true - name: build-dir - path: build/ + name: core-dump + path: coredumps/* release: needs: diff --git a/.gitignore b/.gitignore index 2067da1c..2ed46de4 100644 --- a/.gitignore +++ b/.gitignore @@ -64,5 +64,8 @@ js/ .uuid eicar.com.txt tmp/ - -nextcloud-server/ \ No newline at end of file +core.1 +nextcloud-server/ +core-dump.zip +apache/ +**/vendor/* \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 79e7748b..b78774f5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -26,7 +26,7 @@ "name": "Listen for Xdebug", "type": "php", "request": "launch", - "port": 9000 + "port": 9003 }, { "name": "Launch currently open script", diff --git a/Dockerfile.Nextcloud b/Dockerfile.Nextcloud index 96db1d6f..a64c4804 100644 --- a/Dockerfile.Nextcloud +++ b/Dockerfile.Nextcloud @@ -1,16 +1,38 @@ ARG NEXTCLOUD_VERSION=29.0.4 +ARG INSTALL_XDEBUG=1 -FROM nextcloud:${NEXTCLOUD_VERSION} +FROM nextcloud:29.0.4 -RUN apt update && apt install -y less vim telnet iputils-ping +RUN apt update && apt install -y \ + less vim telnet iputils-ping gdb libexpat1-dev libapr1-dev libaprutil1-dev devscripts debmake \ + bison jdupes libbrotli-dev liblua5.4-dev libnghttp2-dev libssl-dev libxml2-dev libcurl4-openssl-dev libjansson-dev +# RUN curl -L -o /tmp/apache2_2.4.61.orig.tar.gz https://launchpad.net/debian/+archive/primary/+sourcefiles/apache2/2.4.61-1/apache2_2.4.61.orig.tar.gz \ +# && tar -xzf /tmp/apache2_2.4.61.orig.tar.gz -C /tmp \ +# && mv /tmp/httpd-2.4.61 /tmp/apache2-2.4.61 \ +# && curl -L -o /tmp/apache2_2.4.61-1.debian.tar.xz https://launchpad.net/debian/+archive/primary/+sourcefiles/apache2/2.4.61-1/apache2_2.4.61-1.debian.tar.xz \ +# && tar -xf /tmp/apache2_2.4.61-1.debian.tar.xz -C /tmp \ +# && mv /tmp/debian /tmp/apache2-2.4.61/debian \ +# && cd /tmp/apache2-2.4.61 \ +# && debuild || echo "no signature" +RUN curl -o /root/.gdbinit https://raw.githubusercontent.com/php/php-src/master/.gdbinit +RUN ulimit -c unlimited +RUN mkdir -p /tmp/apache2-coredump \ + && chown -R www-data:www-data /tmp/apache2-coredump \ + && chmod 777 /tmp/apache2-coredump \ + && echo "CoreDumpDirectory /tmp/apache2-coredump" >> /etc/apache2/apache2.conf ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ -RUN install-php-extensions gd xdebug RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" RUN sed -i 's/max_execution_time = 30/max_execution_time = -1/g' "$PHP_INI_DIR/php.ini" RUN sed -i 's/max_input_time = 60/max_input_time = -1/g' "$PHP_INI_DIR/php.ini" RUN sed -i 's/memory_limit = 128M/memory_limit = -1/g' "$PHP_INI_DIR/php.ini" RUN echo "error_log = /var/www/html/data/php.log" >> "$PHP_INI_DIR/php.ini" +RUN sed -i 's/#LogLevel info ssl:warn/LogLevel debug/g' /etc/apache2/sites-available/000-default.conf + +#COPY opcache-disabled.ini /usr/local/etc/php/conf.d/opcache-recommended.ini +#COPY opcache-blacklist.txt /usr/local/etc/php/conf.d/opcache-blacklist.txt +COPY xdebug.ini /tmp/xdebug.ini +RUN if [[ "$INSTALL_XDEBUG" == "1" ]]; then \ + install-php-extensions gd xdebug; \ + mv /tmp/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ + fi -COPY opcache-disabled.ini /usr/local/etc/php/conf.d/opcache-recommended.ini -COPY opcache-blacklist.txt /usr/local/etc/php/conf.d/opcache-blacklist.txt -COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini diff --git a/compose-install.yaml b/compose-install.yaml index 6f3498e0..4e8d352e 100644 --- a/compose-install.yaml +++ b/compose-install.yaml @@ -5,10 +5,16 @@ services: dockerfile: Dockerfile.Nextcloud args: - NEXTCLOUD_VERSION=${NEXTCLOUD_VERSION:-29.0.3} + - INSTALL_XDEBUG=${INSTALL_XDEBUG:-1} environment: XDEBUG_MODE: ${XDEBUG_MODE:-develop} ports: - "80:80" + privileged: true + # cap_add: + # - SYS_PTRACE + # - SYS_ADMIN + # - NET_ADMIN container_name: nextcloud-container hostname: nextcloud-container depends_on: diff --git a/composer.json b/composer.json index a0f2f675..e6ce37ca 100644 --- a/composer.json +++ b/composer.json @@ -9,8 +9,9 @@ } ], "require": { - "gdata/vaas": "9.0.3", - "coduo/php-humanizer": "^5.0" + "gdata/vaas": "v9.0.3", + "coduo/php-humanizer": "^5.0", + "amphp/file": "dev-opcache-bug" }, "require-dev": { "nextcloud/ocp": "v29.0.4", @@ -40,5 +41,6 @@ "platform": { "php": "8.2" } - } -} + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/gdata/vaas/Authentication/ClientCredentialsGrantAuthenticator.php b/gdata/vaas/Authentication/ClientCredentialsGrantAuthenticator.php new file mode 100644 index 00000000..dde6b0b7 --- /dev/null +++ b/gdata/vaas/Authentication/ClientCredentialsGrantAuthenticator.php @@ -0,0 +1,20 @@ +_tokenReceiver = new OAuth2TokenReceiver($tokenEndpoint, $clientId, $clientSecret); + } + + public function getToken(): string + { + return $this->_tokenReceiver->GetToken(); + } +} diff --git a/gdata/vaas/Authentication/OAuth2TokenReceiver.php b/gdata/vaas/Authentication/OAuth2TokenReceiver.php new file mode 100644 index 00000000..de137670 --- /dev/null +++ b/gdata/vaas/Authentication/OAuth2TokenReceiver.php @@ -0,0 +1,69 @@ +_browser = HttpClientBuilder::buildDefault(); + $this->_tokenEndpoint = $tokenEndpoint; + $this->_clientId = $clientId; + $this->_clientSecret = $clientSecret; + $this->_username = $username; + $this->_password = $password; + $this->_grantType = $this->_clientSecret == "" ? "password" : "client_credentials"; + + $this->_formParams = new Form(); + $this->_formParams->addField('client_id', $this->_clientId); + $this->_formParams->addField('grant_type', $this->_grantType); + + switch($this->_grantType) { + case "password": + $this->_formParams->addField('username', $this->_username); + $this->_formParams->addField('password', $this->_password); + break; + case "client_credentials": + $this->_formParams->addField('client_secret', $this->_clientSecret); + break; + default: + throw new VaasAuthenticationException("Invalid grant type"); + } + } + + public function getToken() { + try { + $request = new Request($this->_tokenEndpoint, 'POST'); + $request->addHeader('Content-Type', 'application/x-www-form-urlencoded'); + $request->setBody($this->_formParams); + $response = $this->_browser->request($request, new TimeoutCancellation($this->_receiveTokenTimeout)); + if ($response->getStatus() != 200) { + throw new VaasAuthenticationException($response->getReason(), $response->getStatus()); + } + } catch (Exception $e) { + throw new VaasAuthenticationException($e->getMessage(), $e->getCode()); + } + $body = $response->getBody()->buffer(); + $response_body = json_decode($body); + return $response_body->access_token; + } +} \ No newline at end of file diff --git a/gdata/vaas/Authentication/ResourceOwnerPasswordGrantAuthenticator.php b/gdata/vaas/Authentication/ResourceOwnerPasswordGrantAuthenticator.php new file mode 100644 index 00000000..321e19c7 --- /dev/null +++ b/gdata/vaas/Authentication/ResourceOwnerPasswordGrantAuthenticator.php @@ -0,0 +1,21 @@ +_tokenReceiver = new OAuth2TokenReceiver($tokenEndpoint, $clientId, "", $userName, $password); + } + + /** + * @throws VaasAuthenticationException + */ + public function getToken() { + return $this->_tokenReceiver->GetToken(); + } +} diff --git a/gdata/vaas/Exceptions/FileDoesNotExistException.php b/gdata/vaas/Exceptions/FileDoesNotExistException.php new file mode 100644 index 00000000..03409b7f --- /dev/null +++ b/gdata/vaas/Exceptions/FileDoesNotExistException.php @@ -0,0 +1,9 @@ +kind = Kind::AuthRequest; + $this->token = $token; + $this->session_id = $sessionId; + } +} diff --git a/gdata/vaas/Message/AuthResponse.php b/gdata/vaas/Message/AuthResponse.php new file mode 100644 index 00000000..b704f7db --- /dev/null +++ b/gdata/vaas/Message/AuthResponse.php @@ -0,0 +1,15 @@ +kind = Kind::AuthResponse; + } +} diff --git a/gdata/vaas/Message/BaseMessage.php b/gdata/vaas/Message/BaseMessage.php new file mode 100644 index 00000000..1b8cf852 --- /dev/null +++ b/gdata/vaas/Message/BaseMessage.php @@ -0,0 +1,8 @@ +kind = $kind; + $this->guid = $uuid != null ? $uuid : UuidV4::getFactory()->uuid4()->toString(); + $this->session_id = $SessionId; + } +} diff --git a/gdata/vaas/Message/Detection.php b/gdata/vaas/Message/Detection.php new file mode 100644 index 00000000..d6f435d6 --- /dev/null +++ b/gdata/vaas/Message/Detection.php @@ -0,0 +1,10 @@ +kind = Kind::Error; + } +} diff --git a/gdata/vaas/Message/Kind.php b/gdata/vaas/Message/Kind.php new file mode 100644 index 00000000..6f49a0b9 --- /dev/null +++ b/gdata/vaas/Message/Kind.php @@ -0,0 +1,14 @@ +Sha256 = $verdictResponse->sha256 ?? ""; + $this->Verdict = $verdictResponse->verdict ?? Verdict::UNKNOWN; + $this->Guid = $verdictResponse->guid ?? ""; + $this->MimeType = $verdictResponse->mime_type ?? null; + $this->FileType = $verdictResponse->file_type ?? null; + $this->Detection = $verdictResponse->detection ?? null; + } + + public string $Sha256; + public Verdict $Verdict; + public string $Guid; + public ?string $FileType; + public ?string $MimeType; + public ?string $Detection; +} diff --git a/gdata/vaas/Message/Verdict.php b/gdata/vaas/Message/Verdict.php new file mode 100644 index 00000000..024d59d4 --- /dev/null +++ b/gdata/vaas/Message/Verdict.php @@ -0,0 +1,11 @@ +sha256 = $sha256; + } +} diff --git a/gdata/vaas/Message/VerdictRequestForStream.php b/gdata/vaas/Message/VerdictRequestForStream.php new file mode 100644 index 00000000..4312f9ae --- /dev/null +++ b/gdata/vaas/Message/VerdictRequestForStream.php @@ -0,0 +1,13 @@ +url = $url; + } +} diff --git a/gdata/vaas/Message/VerdictResponse.php b/gdata/vaas/Message/VerdictResponse.php new file mode 100644 index 00000000..f6c603d2 --- /dev/null +++ b/gdata/vaas/Message/VerdictResponse.php @@ -0,0 +1,15 @@ + + +*Verdict-as-a-Service* (VaaS) is a cloud service that provides capabilities to scan files for malware and other threats. It allows you to easily integrate malware detection in your application with a few lines of code. You can use VaaS to secure any scenario where a file is exchanged or stored, such as: + +- Upload forms with file submissions +- Collaboration software like MS Teams, Nextcloud or Slack +- Backup and distributed file storage like Dropbox or OneDrive + +With minimal effort, you can check a file, URL or hashsum for malicious content. No local installation of any anti-malware product is necessary. VaaS works out of the box, by providing detections from the G DATA cloud. Hosting VaaS on your own Kubernetes cluster, is an option as well. + +Simple example in Rust. Check below for more programming languages. + +```rust +use vaas::{error::VResult, CancellationToken, Vaas, VaasVerdict}; +use vaas::auth::authenticators::ClientCredentials; +use std::convert::TryFrom; +use std::time::Duration; + +#[tokio::main] +async fn main() -> VResult<()> { + // Cancel the request after 10 seconds if no response is received + let ct = CancellationToken::from_seconds(10); + + // Authenticate and create VaaS instance + let authenticator = ClientCredentials::new(CLIENT_ID, CLIENT_SECRET); + let vaas = Vaas::builder(authenticator).build()?.connect().await?; + + // Open a file we want to check + let file = std::path::PathBuf::from("myfile"); + + // Ask VaaS for a verdict + let verdict = vaas.for_file(&file, &ct).await?; + + // Prints "Clean", "Pup" or "Malicious" + println!("{}", verdict.verdict); + Ok(()) +} +``` + +## How to get started with VaaS +If you are interested in trying out VaaS, you can sign up on our website to create a free trial account. Visit our registration page and follow the instructions to get started. If you have a business case or specific requirements, please contact us at oem@gdata.de to discuss your needs and explore how VaaS can best fit your organization. + +## SDKs +We provide SDKs for various programming languages to make it easy for you to integrate VaaS in your application. You can find the source code, examples, and documentation for each SDK in the corresponding repository. Currently, we support the following languages: + +|Language|Source Code|Examples|Documentation|Repository| +|--------|-----------|--------|-------------|----------| +|Rust|[Rust SDK](./rust/)| [Examples](./rust/examples)| [docs.rs](https://docs.rs/vaas/latest/vaas/) | [crates.io](https://crates.io/crates/vaas) | +|Java|[Java SDK](./java/)|[Examples](./java/examples)| [Readme](https://github.com/GDATASoftwareAG/vaas/blob/main/java/Readme.md) | [maven central](https://mvnrepository.com/artifact/de.gdata/vaas)| +|PHP|[PHP SDK](./php/)|[Examples](./php/examples)||[packagist](https://packagist.org/packages/gdata/vaas)| +|TypeScript|[TypeScript SDK](./typescript/)|[Examples](./typescript/examples)|[Readme](https://github.com/GDATASoftwareAG/vaas/blob/main/typescript/Readme.md)|[npmjs](https://www.npmjs.com/package/gdata-vaas) +|Python|[Python SDK](./python/)|[Examples](./python/examples)|[Readme](https://github.com/GDATASoftwareAG/vaas/blob/main/python/README.md)|[pypi](https://pypi.org/project/gdata-vaas/)| +|.NET|[.NET SDK](./dotnet/)|[Examples](./dotnet/examples)||[nuget.org](https://www.nuget.org/packages/GDataCyberDefense.Vaas)| +|Ruby|[Ruby SDK](./ruby/)|[Examples](./ruby/examples)|[Reamde](https://github.com/GDATASoftwareAG/vaas/blob/main/ruby/README.md)|[rubygems](https://rubygems.org/gems/vaas)| +|Go|[Go SDK](./golang/vaas/)|[Examples](./golang/examples)|[Readme](https://github.com/GDATASoftwareAG/vaas/blob/main/golang/vaas/README.md)|[Github](https://github.com/GDATASoftwareAG/vaas/tree/main/golang/vaas)| + +The following table shows the functionality supported by each SDK: + +|Functionality|Rust|Java|PHP|TypeScript|.NET|Python|Ruby|Golang +|---|---|---|---|---|---|---|---|---| +|Check SHA256|✅|✅|✅|✅|✅|✅|✅|✅| +|Check SHA256 list|✅|✅|❌|✅|✅|❌|❌|✅| +|Check URL|✅|✅|✅|✅|✅|✅|✅|✅| +|Check file|✅|✅|✅|✅|✅|✅|✅|✅| +|Check file list|✅|✅|❌|✅|✅|❌|❌|✅| +|Custom Guids for tracability on user side|❌|❌|✅|❌|❌|✅|❌|❌| + + +## Integration Ideas for Malware Detection trough VaaS +You can use VaaS to create various applications that scan for malicious content with a few lines of code. Here are some examples: + +Create a command line scanner to find malware: [Example](rust/examples/gscan) +GScan command line malware scanner + +Create a KDE Dolphin plugin to scan for malicious content: [Example](rust/examples/kde_dolphin) +KDE Dolphin malware scanner plugin + +Create a WordPress plugin that scans all file uploads for malware: [Example](php/examples/wordpress) +Wordpress plugin malware scanner diff --git a/gdata/vaas/Sha256.php b/gdata/vaas/Sha256.php new file mode 100644 index 00000000..1a5e504d --- /dev/null +++ b/gdata/vaas/Sha256.php @@ -0,0 +1,72 @@ +_hash = $hashString; + return $sha256; + } + throw new InvalidSha256Exception(); + } + + /** + * Gets Sha256 from string + * + * @param string $hashString the string to create the hash from + * + * @return Sha256 + */ + public static function TryFromString(string $hashString): Sha256 + { + if (Sha256::IsValid($hashString)) { + $sha256 = new Sha256(); + $sha256->_hash = $hashString; + return $sha256; + } + throw new InvalidSha256Exception(); + } + + /** + * Validates a hash to be a valid sha256 + * + * @param string $hash the string to validate + * + * @return bool returns true if sha256 is valid + */ + public static function IsValid(string $hash): bool + { + if (preg_match("/^([a-f0-9]{64})$/", strtolower($hash)) == 1) { + return true; + } else { + return false; + } + } + + public function __toString(): string + { + return $this->_hash; + } +} diff --git a/gdata/vaas/Vaas.php b/gdata/vaas/Vaas.php new file mode 100644 index 00000000..c07746e4 --- /dev/null +++ b/gdata/vaas/Vaas.php @@ -0,0 +1,526 @@ +_options = $options; + $this->_logger = $logger; + $this->_logger->debug("Url: " . $vaasUrl); + if ($vaasUrl) { + $this->_vaasUrl = $vaasUrl; + } + } + + /** + */ + public function Connect( + string $token, + ?VaasConnection $vaasConnection = null + ) { + $this->_vaasConnection = $vaasConnection ?? new VaasConnection($this->_vaasUrl); + $webSocket = $this->_vaasConnection->GetConnectedWebsocket(); + + $authRequest = new AuthRequest($token); + $webSocket->send(json_encode($authRequest)); + $authResponse = $this->_waitForAuthResponse(); + $this->_logger->debug("Authenticated: " . json_encode($authResponse)); + $this->_vaasConnection->SessionId = $authResponse->session_id; + } + + /** + * Gets verdict by hashstring + * + * @param string $hashString the hash to get the verdict for + * @param string $uuid unique identifier + * + * @throws Exceptions\InvalidSha256Exception + * @throws Exceptions\TimeoutException + * + * @return VaasVerdict the verdict + */ + public function ForSha256(string $hashString, ?string $uuid = null): VaasVerdict { + $this->_logger->debug("ForSha256WithFlags", ["Sha256" => $hashString]); + + $sha256 = Sha256::TryFromString($hashString); + + return new VaasVerdict( + $this->_verdictResponseForSha256( + $sha256, + $uuid + ) + ); + } + + /** + * Gets verdict by url + * + * @param string|null $url url to get the verdict for + * @param string|null $uuid unique identifier + * + * @return VaasVerdict the verdict + * + * @throws TimeoutException + * @throws InvalidArgumentException + */ + public function ForUrl(?string $url, ?string $uuid = null): VaasVerdict { + $this->_logger->debug("ForUrlWithFlags", ["URL:" => $url]); + + if (!filter_var($url, FILTER_VALIDATE_URL)) { + throw new \InvalidArgumentException("Url is not valid"); + } + + return new VaasVerdict($this->_verdictResponseForUrl( + $url, + $uuid + )); + } + + /** + * Gets verdict by file + * + * @param string $path the path to get the verdict for + * @param bool $upload should the file be uploaded if initial verdict is unknown + * @param string $uuid unique identifier + * + * @throws Exceptions\TimeoutException + * @throws Exceptions\FileDoesNotExistException + * @throws Exceptions\InvalidSha256Exception + * @throws Exceptions\UploadFailedException + * + * @return VaasVerdict the verdict + */ + public function ForFile(string $path, $upload = true, ?string $uuid = null): VaasVerdict { + $this->_logger->debug("ForFileWithFlags", ["File" => $path]); + + $sha256 = Sha256::TryFromFile($path); + $this->_logger->debug("Calculated Hash", ["Sha256" => $sha256]); + + $verdictResponse = $this->_verdictResponseForSha256( + $sha256, + $uuid + ); + if ($verdictResponse->verdict == Verdict::UNKNOWN && $upload === true) { + $this->_logger->debug("UploadToken", ["UploadToken" => $verdictResponse->upload_token]); + + $file = \Amp\File\openFile($path, 'r'); + $fileSize = \Amp\File\getSize($path); + + $this->UploadStream($file, $verdictResponse->url, $verdictResponse->upload_token, $fileSize); + $file->close(); + + $verdict = new VaasVerdict($this->_waitForVerdict($verdictResponse->guid)); + + return $verdict; + } + + return new VaasVerdict($verdictResponse); + } + + /** + * Gets verdict by stream + * + * @param ReadableStream $stream the path to get the verdict for + * @param bool $upload should the file be uploaded if initial verdict is unknown + * @param string $uuid unique identifier + * + * @throws JsonMapper_Exception + * @throws VaasClientException + * @throws TimeoutException + * @throws VaasServerException + * @throws BadOpcodeException + * @throws VaasInvalidStateException + * @throws UploadFailedException + */ + public function ForStream(ReadableStream $stream, int $size = 0, ?string $uuid = null): VaasVerdict { + $this->_logger->debug("uuid: ".var_export($uuid, true)); + $uuid = $uuid ?? UuidV4::getFactory()->uuid4()->toString(); + $this->_logger->debug("uuid: ".var_export($uuid, true)); + $verdictResponse = $this->_verdictResponseForStream($uuid); + + if ($verdictResponse->verdict != Verdict::UNKNOWN) { + throw new VaasServerException("Server returned verdict without receiving content."); + } + if ($verdictResponse->upload_token == null || $verdictResponse->upload_token == "") { + throw new JsonMapper_Exception("VerdictResponse missing UploadToken for stream upload."); + } + if ($verdictResponse->url == null || $verdictResponse->url == "") { + throw new JsonMapper_Exception("VerdictResponse missing URL for stream upload."); + } + + $this->UploadStream($stream, $verdictResponse->url, $verdictResponse->upload_token, $size); + + $verdictResponse = $this->_waitForVerdict($uuid); + + return new VaasVerdict($verdictResponse); + } + + /** + * @return AuthResponse + * @throws VaasConnectionClosedException + * @throws JsonMapper_Exception + * @throws TimeoutException + * @throws VaasAuthenticationException + * @throws VaasClientException + * @throws VaasInvalidStateException + * @throws VaasServerException + */ + private function _waitForAuthResponse(): AuthResponse { + $websocket = $this->_vaasConnection->GetConnectedWebsocket(); + $this->_logger->debug("WaitForAuthResponse"); + + $start_time = time(); + + while (true) { + if ((time() - $start_time) > $this->_waitTimeoutInSeconds) { + throw new TimeoutException(); + } + + $result = null; + try { + $result = $websocket->receive(); + } catch (\WebSocket\TimeoutException $e) { + $this->_logger->debug("Read timeout, send ping"); + $websocket->ping(); + } + + if ($result != null) { + if ($result instanceof Ping) { + $websocket->pong(); + continue; + } + if ($result instanceof Close) { + throw new VaasServerException("Connection closed"); + } + $result = $result->getContent(); + $this->_logger->debug("Result", json_decode($result, true)); + $genericObject = \json_decode($result); + $resultObject = (new JsonMapper())->map( + $genericObject, + new BaseMessage() + ); + if ($resultObject->kind == Kind::AuthResponse) { + $authResponse = (new JsonMapper())->map( + $genericObject, + new AuthResponse() + ); + $this->_logger->debug($result); + if ($authResponse->success === false) { + throw new VaasAuthenticationException($result); + } + return $authResponse; + } + if ($resultObject->kind == Kind::Error) { + try { + $errorResponse = (new JsonMapper())->map( + $genericObject, + new Error() + ); + } catch (JsonMapper_Exception $e) { + // Received error type is not deserializable to Error + throw new VaasServerException($e->getMessage()); + } + $this->_handleWebSocketErrorResponse($errorResponse); + } + } + sleep(1); + } + } + + /** + * @param string $guid + * @return VerdictResponse + * @throws JsonMapper_Exception + * @throws TimeoutException + * @throws VaasClientException + * @throws VaasInvalidStateException + * @throws VaasServerException + */ + private function _waitForVerdict(string $guid): VerdictResponse { + $this->_logger->debug("WaitForVerdict"); + $start_time = time(); + + if (!isset($this->_vaasConnection)) { + throw new VaasInvalidStateException("connect() was not called"); + } + while (true) { + $websocket = $this->_vaasConnection->GetAuthenticatedWebsocket(); + if ((time() - $start_time) > $this->_waitTimeoutInSeconds) { + throw new TimeoutException(); + } + $result = null; + try { + $result = $websocket->receive(); + } catch (\WebSocket\TimeoutException $e) { + $this->_logger->debug("Read timeout, send ping"); + $websocket->ping(); + } + if ($result != null) { + if ($result instanceof Ping) { + $websocket->pong(); + continue; + } + if ($result instanceof Close) { + throw new VaasServerException("Connection closed"); + } + $result = $result->getContent(); + $this->_logger->debug("Result", json_decode($result, true)); + $resultObject = json_decode($result); + $baseMessage = (new JsonMapper())->map( + $resultObject, + new BaseMessage() + ); + if ($baseMessage->kind == Kind::Error) { + try { + $errorResponse = (new JsonMapper())->map( + $resultObject, + new Error() + ); + } catch (JsonMapper_Exception $e) { + // Received error type is not deserializable to Error + throw new VaasServerException($e->getMessage()); + } + $this->_handleWebSocketErrorResponse($errorResponse); + } + if ($baseMessage->kind != Kind::VerdictResponse) { + continue; + } + + $verdictResponse = (new JsonMapper())->map( + $resultObject, + new VerdictResponse() + ); + if (!isset($verdictResponse->guid) || !isset($verdictResponse->kind)) { + continue; + } + + if ($verdictResponse->guid == $guid) { + return $verdictResponse; + } + } + } + } + + /** + * @throws VaasServerException + * @throws VaasClientException + */ + private function _handleWebSocketErrorResponse(Error $errorResponse): void { + if (isset($errorResponse->problem_details->detail)) { + $details = $errorResponse->problem_details->detail; + } else { + $details = null; + } + $errorType = $errorResponse->type; + if ($errorType == "ClientError") { + throw new VaasClientException($details); + } + throw new VaasServerException($details); + } + + /** + * @throws TimeoutException + * + * @return VerdictResponse + */ + private function _verdictResponseForSha256(Sha256 $sha256, ?string $uuid = null): VerdictResponse { + $this->_logger->debug("_verdictResponseForSha256"); + + if (!isset($this->_vaasConnection)) { + throw new VaasInvalidStateException("connect() was not called"); + } + $websocket = $this->_vaasConnection->GetAuthenticatedWebsocket(); + + $request = new VerdictRequest(strtolower($sha256), $uuid, $this->_vaasConnection->SessionId); + $request->use_cache = $this->_options->UseCache; + $request->use_hash_lookup = $this->_options->UseHashLookup; + $websocket->send(json_encode($request)); + + $this->_logger->debug("verdictResponse", ["VerdictResponse" => json_encode($request)]); + + return $this->_waitForVerdict($request->guid); + } + + /** + * @throws TimeoutException + * + * @return VerdictResponse + */ + private function _verdictResponseForUrl(string $url, ?string $uuid = null): VerdictResponse { + $this->_logger->debug("_verdictResponseForUrl"); + + if (!isset($this->_vaasConnection)) { + throw new VaasInvalidStateException("connect() was not called"); + } + $websocket = $this->_vaasConnection->GetAuthenticatedWebsocket(); + + $request = new VerdictRequestForUrl($url, $uuid, $this->_vaasConnection->SessionId); + $request->use_cache = $this->_options->UseCache; + $request->use_hash_lookup = $this->_options->UseHashLookup; + $websocket->send(json_encode($request)); + + $this->_logger->debug("verdictResponse", ["VerdictResponse" => json_encode($request)]); + + return $this->_waitForVerdict($request->guid); + } + + /** + * @throws JsonMapper_Exception + * @throws VaasClientException + * @throws TimeoutException + * @throws VaasServerException + * @throws BadOpcodeException + * @throws VaasInvalidStateException + */ + private function _verdictResponseForStream(?string $uuid = null): VerdictResponse { + $this->_logger->debug("_verdictResponseForStream"); + + if (!isset($this->_vaasConnection)) { + throw new VaasInvalidStateException("connect() was not called"); + } + $websocket = $this->_vaasConnection->GetAuthenticatedWebsocket(); + + $request = new VerdictRequestForStream($this->_vaasConnection->SessionId, $uuid); + $request->use_cache = $this->_options->UseCache; + $request->use_hash_lookup = $this->_options->UseHashLookup; + $websocket->send(json_encode($request)); + + $this->_logger->debug("verdictResponse", ["VerdictResponse" => json_encode($request)]); + + return $this->_waitForVerdict($request->guid); + } + + /** + * Sets the timeout in seconds the websocket client can take for one receive + * + * @param int $timeoutInSeconds timeout for the websocket + * + * @return void + */ + public function setWebsocketTimeOut(int $timeoutInSeconds): void { + $this->_vaasConnection->WebSocketClient->setTimeout($timeoutInSeconds); + } + + /** + * Sets the timeout in seconds for the loops were we wait for a verdict + * + * @param int $timeoutInSeconds timeout for the websocket + * + * @return Vaas + */ + public function setWaitTimeoutInSeconds(int $timeoutInSeconds): self { + $this->_waitTimeoutInSeconds = $timeoutInSeconds; + return $this; + } + + /** + * Set the timeout for the httpclient (for the upload) in seconds + * + * @param int $UploadTimeoutInSeconds upload timeout + * + * @return Vaas + */ + public function setUploadTimeout(int $UploadTimeoutInSeconds): self { + $this->_uploadTimeoutInSeconds = $UploadTimeoutInSeconds; + return $this; + } + + /** + * Uploads a file stream to a specified URL using a given upload token and file size. + * + * @param ReadableStream $fileStream The file stream to upload. + * @param string $url The URL to upload the file to. + * @param string $uploadToken The upload token to authenticate the upload. + * @param int $fileSize The size of the file being uploaded. + * @throws UploadFailedException If the upload fails. + * @throws VaasClientException If there is an error with the Vaas client. + * @return void + */ + private function UploadStream(ReadableStream $fileStream, string $url, string $uploadToken, int $fileSize): void { + $connection = $this->_vaasConnection->GetAuthenticatedWebsocket(); + $pingTimer = EventLoop::repeat(5, function () use ($connection) { + if ($connection->isConnected()) { + $connection->ping(); + } + }); + + try { + $httpClient = (new HttpClientBuilder()) + ->skipAutomaticCompression() + ->skipDefaultUserAgent() + ->build(); + + $request = new Request($url, 'PUT'); + $request->setProtocolVersions(["1.1"]); + $request->setTransferTimeout($this->_uploadTimeoutInSeconds); + $request->setInactivityTimeout($this->_uploadTimeoutInSeconds); + $request->setBody(StreamedContent::fromStream($fileStream, $fileSize)); + $request->addHeader("Authorization", $uploadToken); + + $response = $httpClient->request( + $request, new TimeoutCancellation($this->_uploadTimeoutInSeconds)); + if ($response->isClientError()) { + throw new VaasClientException($response->getReason()); + } + if ($response->isServerError()) { + throw new VaasServerException($response->getReason()); + } + if (!$response->isSuccessful()) { + throw new UploadFailedException($response->getReason(), $response->getStatus()); + } + } catch (\Exception $e) { + if ($e instanceof HttpException) { + throw new UploadFailedException($e->getMessage(), $e->getCode()); + } + throw new VaasClientException($e->getMessage()); + } finally { + if (EventLoop::isEnabled($pingTimer)) { + EventLoop::cancel($pingTimer); + } + } + } +} diff --git a/gdata/vaas/VaasConnection.php b/gdata/vaas/VaasConnection.php new file mode 100644 index 00000000..7e970c19 --- /dev/null +++ b/gdata/vaas/VaasConnection.php @@ -0,0 +1,49 @@ +WebSocketClient = new Client($url, [ + "filter" => [ + 'text', 'binary', 'ping' + ], + "return_obj" => true + ]); + else + $this->WebSocketClient = $WebSocketClient; + $this->WebSocketClient->ping(); + } + + public function GetConnectedWebsocket(): Client + { + if (!$this->WebSocketClient) { + throw new VaasInvalidStateException("connect() was not called"); + } + if (!$this->WebSocketClient->isConnected()) { + throw new VaasConnectionClosedException(); + } + return $this->WebSocketClient; + } + + public function GetAuthenticatedWebsocket(): Client + { + $websocket = $this->GetConnectedWebsocket(); + if (!isset($this->SessionId) || !$this->SessionId) { + throw new VaasInvalidStateException( + "Not yet authenticated - connect() was not awaited" + ); + } + return $websocket; + } +} \ No newline at end of file diff --git a/gdata/vaas/VaasOptions.php b/gdata/vaas/VaasOptions.php new file mode 100644 index 00000000..77d5207d --- /dev/null +++ b/gdata/vaas/VaasOptions.php @@ -0,0 +1,15 @@ +UseCache = $useCache; + $this->UseHashLookup = $useHashLookup; + } +} \ No newline at end of file diff --git a/gdata/vaas/composer.json b/gdata/vaas/composer.json new file mode 100644 index 00000000..072f275e --- /dev/null +++ b/gdata/vaas/composer.json @@ -0,0 +1,29 @@ +{ + "name": "gdata/vaas", + "type": "library", + "description": "Verdict-as-a-Service (VaaS) is a service that provides a platform for scanning files for malware and other threats. It allows easy integration in your application. With a few lines of code, you can start scanning files for malware.", + "version": "9.0.3", + "readme": "Readme.md", + "keywords": [ + "antivirus", + "malware", + "security", + "virusscan" + ], + "license": "MIT", + "homepage": "https://github.com/GDATASoftwareAG/vaas", + "require": { + "php": "^8.1", + "ramsey/uuid": "^4.7 || ^4.2", + "textalk/websocket": "^1.6 || ^1.5", + "netresearch/jsonmapper": "^4.4", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "amphp/http-client": "^5.1", + "amphp/file": "dev-opcache-bug" + }, + "autoload": { + "psr-4": { + "VaasSdk\\": "." + } + } +} diff --git a/gdata/vaas/composer.lock b/gdata/vaas/composer.lock new file mode 100644 index 00000000..c2ea4713 --- /dev/null +++ b/gdata/vaas/composer.lock @@ -0,0 +1,2237 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4a08befae16def9c793d6d7109484027", + "packages": [ + { + "name": "amphp/amp", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "138801fb68cfc9c329da8a7b39d01ce7291ee4b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/138801fb68cfc9c329da8a7b39d01ce7291ee4b0", + "reference": "138801fb68cfc9c329da8a7b39d01ce7291ee4b0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.0.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-05-10T21:37:46+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "daa00f2efdbd71565bf64ffefa89e37542addf93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/daa00f2efdbd71565bf64ffefa89e37542addf93", + "reference": "daa00f2efdbd71565bf64ffefa89e37542addf93", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-02-17T04:49:38+00:00" + }, + { + "name": "amphp/cache", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", + "support": { + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" + }, + { + "name": "amphp/dns", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "758266b0ea7470e2e42cd098493bc6d6c7100cf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/758266b0ea7470e2e42cd098493bc6d6c7100cf7", + "reference": "758266b0ea7470e2e42cd098493bc6d6c7100cf7", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/windows-registry": "^1.0.1", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "support": { + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.2.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-06-02T19:54:12+00:00" + }, + { + "name": "amphp/file", + "version": "dev-opcache-bug", + "source": { + "type": "git", + "url": "https://github.com/amphp/file.git", + "reference": "58c8efefb8808d25456ef3ef4a628645442578a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/file/zipball/58c8efefb8808d25456ef3ef4a628645442578a2", + "reference": "58c8efefb8808d25456ef3ef4a628645442578a2", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parallel": "^2.1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.2" + }, + "suggest": { + "ext-eio": "^2 || ^3", + "ext-uv": "^0.3 || ^0.2" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\File\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking access to the filesystem based on Amp and Revolt.", + "homepage": "https://github.com/amphp/file", + "keywords": [ + "amp", + "amphp", + "async", + "disk", + "file", + "filesystem", + "io", + "non-blocking", + "static" + ], + "support": { + "issues": "https://github.com/amphp/file/issues", + "source": "https://github.com/amphp/file/tree/opcache-bug" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-15T15:04:06+00:00" + }, + { + "name": "amphp/hpack", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/hpack.git", + "reference": "4f293064b15682a2b178b1367ddf0b8b5feb0239" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/hpack/zipball/4f293064b15682a2b178b1367ddf0b8b5feb0239", + "reference": "4f293064b15682a2b178b1367ddf0b8b5feb0239", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "http2jp/hpack-test-case": "^1", + "nikic/php-fuzzer": "^0.0.10", + "phpunit/phpunit": "^7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "HTTP/2 HPack implementation.", + "homepage": "https://github.com/amphp/hpack", + "keywords": [ + "headers", + "hpack", + "http-2" + ], + "support": { + "issues": "https://github.com/amphp/hpack/issues", + "source": "https://github.com/amphp/hpack/tree/v3.2.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:00:16+00:00" + }, + { + "name": "amphp/http", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/http.git", + "reference": "fe6b4dd50c1e70caf823092398074b5082e1d6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http/zipball/fe6b4dd50c1e70caf823092398074b5082e1d6da", + "reference": "fe6b4dd50c1e70caf823092398074b5082e1d6da", + "shasum": "" + }, + "require": { + "amphp/hpack": "^3", + "amphp/parser": "^1.1", + "league/uri-components": "^2.4.2 | ^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "league/uri": "^6.8 | ^7.1", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/constants.php" + ], + "psr-4": { + "Amp\\Http\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Basic HTTP primitives which can be shared by servers and clients.", + "support": { + "issues": "https://github.com/amphp/http/issues", + "source": "https://github.com/amphp/http/tree/v2.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-03T18:00:53+00:00" + }, + { + "name": "amphp/http-client", + "version": "v5.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/http-client.git", + "reference": "483df9a856625fe4406c7fb6a64d6787105bd184" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/http-client/zipball/483df9a856625fe4406c7fb6a64d6787105bd184", + "reference": "483df9a856625fe4406c7fb6a64d6787105bd184", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/hpack": "^3", + "amphp/http": "^2", + "amphp/pipeline": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "league/uri": "^6 | ^7", + "league/uri-components": "^2.4 | ^7", + "league/uri-interfaces": "^7.1", + "php": ">=8.1", + "psr/http-message": "^1 | ^2", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/file": "^3", + "amphp/http-server": "^3", + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "ext-json": "*", + "kelunik/link-header-rfc5988": "^1", + "laminas/laminas-diactoros": "^2.3", + "phpunit/phpunit": "^9", + "psalm/phar": "~5.23" + }, + "suggest": { + "amphp/file": "Required for file request bodies and HTTP archive logging", + "ext-json": "Required for logging HTTP archives", + "ext-zlib": "Allows using compression for response bodies." + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\Http\\Client\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.", + "homepage": "https://amphp.org/http-client", + "keywords": [ + "async", + "client", + "concurrent", + "http", + "non-blocking", + "rest" + ], + "support": { + "issues": "https://github.com/amphp/http-client/issues", + "source": "https://github.com/amphp/http-client/tree/v5.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T16:40:36+00:00" + }, + { + "name": "amphp/parallel", + "version": "v2.2.9", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "73d293f1fc4df1bebc3c4fce1432e82dd7032238" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/73d293f1fc4df1bebc3c4fce1432e82dd7032238", + "reference": "73d293f1fc4df1bebc3c4fce1432e82dd7032238", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.2.9" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-24T18:27:44+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "66c095673aa5b6e689e63b52d19e577459129ab3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/66c095673aa5b6e689e63b52d19e577459129ab3", + "reference": "66c095673aa5b6e689e63b52d19e577459129ab3", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-07-04T00:56:47+00:00" + }, + { + "name": "amphp/process", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.0.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:13:44+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^6.5 | ^7", + "league/uri-interfaces": "^2.3 | ^7", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T14:33:03+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "amphp/windows-registry", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/windows-registry.git", + "reference": "0d569e8f256cca974e3842b6e78b4e434bf98306" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/windows-registry/zipball/0d569e8f256cca974e3842b6e78b4e434bf98306", + "reference": "0d569e8f256cca974e3842b6e78b4e434bf98306", + "shasum": "" + }, + "require": { + "amphp/byte-stream": "^2", + "amphp/process": "^2", + "php": ">=8.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\WindowsRegistry\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Windows Registry Reader.", + "support": { + "issues": "https://github.com/amphp/windows-registry/issues", + "source": "https://github.com/amphp/windows-registry/tree/v1.0.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-01-30T23:01:51+00:00" + }, + { + "name": "brick/math", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "league/uri", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.3", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, + { + "name": "league/uri-components", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-components.git", + "reference": "b94fe4097885f1b51c4c3fcb78025fbbabbb5d9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/b94fe4097885f1b51c4c3fcb78025fbbabbb5d9d", + "reference": "b94fe4097885f1b51c4c3fcb78025fbbabbb5d9d", + "shasum": "" + }, + "require": { + "league/uri": "^7.3", + "php": "^8.1" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-mbstring": "to use the sorting algorithm of URLSearchParams", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI components manipulation library", + "homepage": "http://uri.thephpleague.com", + "keywords": [ + "authority", + "components", + "fragment", + "host", + "middleware", + "modifier", + "path", + "port", + "query", + "rfc3986", + "scheme", + "uri", + "url", + "userinfo" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-components/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.4.1", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + }, + "time": "2024-01-31T06:18:54+00:00" + }, + { + "name": "phrity/net-uri", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-net-uri.git", + "reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/3f458e0c4d1ddc0e218d7a5b9420127c63925f43", + "reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0 | ^10.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "PSR-7 Uri and PSR-17 UriFactory implementation", + "homepage": "https://phrity.sirn.se/net-uri", + "keywords": [ + "psr-17", + "psr-7", + "uri", + "uri factory" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-net-uri/issues", + "source": "https://github.com/sirn-se/phrity-net-uri/tree/1.3.0" + }, + "time": "2023-08-21T10:33:06+00:00" + }, + { + "name": "phrity/util-errorhandler", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-util-errorhandler.git", + "reference": "4016d9f9615a4c602f525b0542e4835e316a42e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/4016d9f9615a4c602f525b0542e4835e316a42e4", + "reference": "4016d9f9615a4c602f525b0542e4835e316a42e4", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0 | ^10.0 | ^11.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Util\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Inline error handler; catch and resolve errors for code block.", + "homepage": "https://phrity.sirn.se/util-errorhandler", + "keywords": [ + "error", + "warning" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues", + "source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.1.0" + }, + "time": "2024-03-05T19:32:14+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.6", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "25de49af7223ba039f64da4ae9a28ec2d10d0254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/25de49af7223ba039f64da4ae9a28ec2d10d0254", + "reference": "25de49af7223ba039f64da4ae9a28ec2d10d0254", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.6" + }, + "time": "2023-11-30T05:34:44+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.6.3", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "67de79745b1a357caf812bfc44e0abf481cee012" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/67de79745b1a357caf812bfc44e0abf481cee012", + "reference": "67de79745b1a357caf812bfc44e0abf481cee012", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "phrity/net-uri": "^1.0", + "phrity/util-errorhandler": "^1.0", + "psr/http-message": "^1.0", + "psr/log": "^1.0 | ^2.0 | ^3.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.6.3" + }, + "time": "2022-11-07T18:59:33+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "amphp/file": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.1" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/install.sh b/install.sh index 3bfef81e..dbae5e8d 100755 --- a/install.sh +++ b/install.sh @@ -1,6 +1,7 @@ #!/bin/bash NEXTCLOUD_VERSION=${1:-29.0.4} +INSTALL_XDEBUG=${2:-1} XDEBUG_MODE=${XDEBUG_MODE:-develop} source .env-local || echo "No .env-local file found." @@ -11,6 +12,9 @@ setup_nextcloud () { docker compose -f compose-install.yaml rm --force --stop --volumes docker compose -f compose-install.yaml up --build --quiet-pull --wait -d --force-recreate --renew-anon-volumes --remove-orphans + docker exec -i nextcloud-container ulimit -c unlimited + docker exec -i nextcloud-container bash -c 'echo "/tmp/apache2-coredump/core-%e-%s-%u-%g-%p-%t" > /proc/sys/kernel/core_pattern' + until docker exec --user www-data -i nextcloud-container php occ status | grep "installed: false" do echo "waiting for nextcloud to be initialized" @@ -81,3 +85,5 @@ source install.local || echo "No additional install script found." # Has to be done, to get the dev-requirements installed again composer install --quiet & + +composer info \ No newline at end of file diff --git a/lib/AvirWrapper.php b/lib/AvirWrapper.php index d6ae2ba6..7dc3e4d7 100644 --- a/lib/AvirWrapper.php +++ b/lib/AvirWrapper.php @@ -145,27 +145,27 @@ function () use ($path, $logger) { return; } - try { - $verdict = $this->verdictService->scan($localPath); - } catch (EntityTooLargeException) { - $this->logger->error("File $localPath is larger than " . NumberHumanizer::binarySuffix(VerdictService::MAX_FILE_SIZE, 'de')); - } catch (FileDoesNotExistException) { - $this->logger->error("File $localPath does not exist on upload"); - } catch (InvalidSha256Exception) { - $this->logger->error("Invalid SHA256 for file $localPath on upload"); - } catch (NotFoundException) { - $this->logger->error("File $localPath not found on upload"); - } catch (NotPermittedException) { - $this->logger->error("Current settings do not permit scanning file $localPath on upload"); - } catch (TimeoutException) { - $this->logger->error("Scanning timed out for file $localPath on upload"); - } catch (UploadFailedException|ServerException) { - $this->logger->error("File $localPath could not be scanned on upload with GData VaaS because there was a temporary upstream server error"); - } catch (VaasAuthenticationException) { - $this->logger->error("Authentication for VaaS scan failed. Please check your credentials."); - } catch (\Exception $e) { - $this->logger->error("Unexpected error while scanning file " . $localPath . " on upload: " . $e->getMessage()); - } + try { + $verdict = $this->verdictService->scan($localPath); + } catch (EntityTooLargeException) { + $this->logger->error("File $localPath is larger than " . NumberHumanizer::binarySuffix(VerdictService::MAX_FILE_SIZE, 'de')); + } catch (FileDoesNotExistException) { + $this->logger->error("File $localPath does not exist on upload"); + } catch (InvalidSha256Exception) { + $this->logger->error("Invalid SHA256 for file $localPath on upload"); + } catch (NotFoundException) { + $this->logger->error("File $localPath not found on upload"); + } catch (NotPermittedException) { + $this->logger->error("Current settings do not permit scanning file $localPath on upload"); + } catch (TimeoutException) { + $this->logger->error("Scanning timed out for file $localPath on upload"); + } catch (UploadFailedException|ServerException) { + $this->logger->error("File $localPath could not be scanned on upload with GData VaaS because there was a temporary upstream server error"); + } catch (VaasAuthenticationException) { + $this->logger->error("Authentication for VaaS scan failed. Please check your credentials."); + } catch (\Exception $e) { + $this->logger->error("Unexpected error while scanning file " . $localPath . " on upload: " . $e->getMessage()); + } $logger->debug("Verdict for " . $localPath . " is " . $verdict->Verdict->value); if ($verdict->Verdict == Verdict::MALICIOUS) { diff --git a/lib/CacheEntryListener.php b/lib/CacheEntryListener.php index 76705201..5130e737 100644 --- a/lib/CacheEntryListener.php +++ b/lib/CacheEntryListener.php @@ -31,6 +31,7 @@ public static function register(IRegistrationContext $context): void { } public function handle(Event $event): void { + $this->logger->debug("CacheEntryListener"); if (!$event instanceof AbstractCacheEvent) { return; } @@ -39,6 +40,7 @@ public function handle(Event $event): void { $path = $event->getPath(); $fileId = $event->getFileId(); + $this->logger->debug("GotFields"); if (self::shouldTag($path) && !$this->tagService->hasAnyVaasTag($fileId)) { $this->logger->debug("Handling " . get_class($event) . " for " . $path); diff --git a/tests/bats/functionality-parallel.bats b/tests/bats/functionality-parallel.bats index e99715d8..284d1e1a 100755 --- a/tests/bats/functionality-parallel.bats +++ b/tests/bats/functionality-parallel.bats @@ -19,6 +19,11 @@ setup_file() { RESULT=$(echo $EICAR_STRING | curl -v -X PUT -d"$EICAR_STRING" -w "%{http_code}" -u admin:admin -T - http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.eicar.com.txt || echo "curl failed") if [[ "$RESULT" =~ "curl failed" ]]; then + echo "debugging stuff" + docker exec -i nextcloud-container ls -lha /tmp/apache2-coredump + mkdir -p ./coredumps + docker container cp nextcloud-container:/tmp ./coredumps + ls -lha ./coredumps $DOCKER_EXEC_WITH_USER -i nextcloud-container ls -lha data $DOCKER_EXEC_WITH_USER -i nextcloud-container ls -lha data/admin $DOCKER_EXEC_WITH_USER -i nextcloud-container ls -lha data/admin/files @@ -41,6 +46,7 @@ setup_file() { RESULT=$(echo $CLEAN_STRING | curl -w "%{http_code}" -u admin:admin -T - http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.clean.txt || echo "curl failed") if [[ "$RESULT" =~ "curl failed" ]]; then + echo "debugging stuff" $DOCKER_EXEC_WITH_USER -i nextcloud-container ls -lha data $DOCKER_EXEC_WITH_USER -i nextcloud-container ls -lha data/admin $DOCKER_EXEC_WITH_USER -i nextcloud-container ls -lha data/admin/files