Skip to content

Commit

Permalink
Merge pull request #3 from wubinworks/issue-1-php7-support
Browse files Browse the repository at this point in the history
Support Magento 2.3 and PHP 7
  • Loading branch information
wubinworks authored Feb 6, 2025
2 parents 18d9de4 + be431cf commit 20eaa3c
Show file tree
Hide file tree
Showing 7 changed files with 363 additions and 86 deletions.
14 changes: 0 additions & 14 deletions Model/Exception/InvalidArgumentException.php

This file was deleted.

109 changes: 109 additions & 0 deletions Model/RequestInfo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* Copyright © Wubinworks. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Wubinworks\CosmicStingPatch\Model;

use Laminas\Http\PhpEnvironment\RemoteAddress as LaminasHttpRemoteAddress;
use Zend\Http\PhpEnvironment\RemoteAddress as ZendHttpRemoteAddress;
use Magento\Framework\HTTP\PhpEnvironment\Request;

/**
* Request information
*/
class RequestInfo
{
public const INFO_KEYS = [
'ip',
'request_line',
'body'
];

/**
* @var Request
*/
protected $request;

/**
* Constructor
*
* @param Request $request
*/
public function __construct(
Request $request
) {
$this->request = $request;
}

/**
* Get request information
*
* @param ?string $key
* @return string|string[]
*
* @throws \InvalidArgumentException
*/
public function getRequestInfo(?string $key = null)
{
$info = [
'ip' => (string)$this->getRemoteAddress(), // `false` converted to empty string
'request_line' => $this->getRequestLine(),
'body' => $this->getRequestBody()
];
if ($key === null) {
return $info;
}
if (!array_key_exists($key, $info)) {
throw new \InvalidArgumentException(sprintf(
'Unknown key %s was used to retrieve RequestInfo.',
$key
));
}
return $info[$key];
}

/**
* Get correct remote IP address
*
* @return string|bool `false` if failed
*/
protected function getRemoteAddress()
{
if (class_exists(LaminasHttpRemoteAddress::class)) {
$httpRemoteAddressClass = LaminasHttpRemoteAddress::class;
} else {
$httpRemoteAddressClass = ZendHttpRemoteAddress::class;
}

$ip = (new $httpRemoteAddressClass())->getIpAddress();
if ($ip) {
return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6);
}

return false;
}

/**
* Get request line
*
* @return string
*/
protected function getRequestLine(): string
{
return (string)$this->request->renderRequestLine();
}

/**
* Get request body with limited length
*
* @param int $maxLength
* @return string
*/
protected function getRequestBody(int $maxLength = 1000): string
{
return mb_substr((string)$this->request->getContent(), 0, $maxLength);
}
}
54 changes: 0 additions & 54 deletions Model/Simplexml/Element.php

This file was deleted.

164 changes: 164 additions & 0 deletions Plugin/Framework/Webapi/ServiceInputProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php
/**
* Copyright © Wubinworks. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Wubinworks\CosmicStingPatch\Plugin\Framework\Webapi;

use Magento\Framework\Phrase;
use Magento\Framework\Exception\SerializationException;
use Wubinworks\CosmicStingPatch\Model\RequestInfo;

/**
* Patch for CVE-2024-34102(aka Cosmic Sting)
*
* @link https://nvd.nist.gov/vuln/detail/CVE-2024-34102
* @link https://helpx.adobe.com/security/products/magento/apsb24-40.html
* @link https://experienceleague.adobe.com/en/docs/commerce-knowledge-base/kb/troubleshooting/known-issues-patches-attached/security-update-available-for-adobe-commerce-apsb24-40-revised-to-include-isolated-patch-for-cve-2024-34102
*/
class ServiceInputProcessor
{
/**
* Including inherited classes
*
* @var string[]
*/
protected $forbiddenClasses = [
\SimpleXMLElement::class,
\DOMElement::class
];

/**
* @var RequestInfo
*/
protected $requestInfo;

/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;

/**
* @var array
*/
protected $loggerConfig;

/**
* Constructor
*
* @param RequestInfo $requestInfo
* @param \Psr\Log\LoggerInterface $logger
* @param array $loggerConfig
*/
public function __construct(
RequestInfo $requestInfo,
\Psr\Log\LoggerInterface $logger,
array $loggerConfig = []
) {
$this->requestInfo = $requestInfo;
$this->logger = $logger;
$this->loggerConfig = array_merge($this->_initLoggerConfig(), $loggerConfig);
}

/**
* Before plugin to detect forbidden type
*
* @param \Magento\Framework\Webapi\ServiceInputProcessor $subject
* @param mixed $data
* @param string $type
* @return null
*
* @throws SerializationException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function beforeConvertValue(
\Magento\Framework\Webapi\ServiceInputProcessor $subject,
$data,
$type
) {
$type = (string)$type;
if ($this->isForbiddenType($type)) {
$message = $this->prepareLogMessage();
if ($message) {
$this->logger->info($message);
}
throw new SerializationException(
new Phrase('Invalid data type detected in deserialization process.')
);
}

return null;
}

/**
* Check forbidden type
*
* @param string $type
* @return bool
*/
protected function isForbiddenType(string $type): bool
{
foreach ($this->forbiddenClasses as $forbiddenClass) {
if (is_subclass_of($type, $forbiddenClass)) {
return true;
}
}
return false;
}

/**
* Prepare log message
*
* @return string
*/
protected function prepareLogMessage(): string
{
if (!$this->isLoggerEnabled()) {
return '';
}

$message = 'Detected possible Cosmic Sting attack.' . "\n";
if ($this->loggerConfig['ip']['enabled']) {
$message .= 'IP: ' . $this->requestInfo->getRequestInfo('ip') . "\n";
}
if ($this->loggerConfig['request_line']['enabled']) {
$message .= $this->requestInfo->getRequestInfo('request_line') . "\n";
}
if ($this->loggerConfig['body']['enabled']) {
$message .= $this->requestInfo->getRequestInfo('body') . "\n";
}

return $message;
}

/**
* Initialize logger config
*
* @return array
*/
protected function _initLoggerConfig(): array
{
$result = [];
foreach (RequestInfo::INFO_KEYS as $key) {
$result[$key]['enabled'] = false;
}
return $result;
}

/**
* Check logger enabled
*
* @return bool
*/
protected function isLoggerEnabled(): bool
{
foreach ($this->loggerConfig as $item) {
if ($item['enabled']) {
return true;
}
}
return false;
}
}
Loading

0 comments on commit 20eaa3c

Please sign in to comment.