Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

SDK-4093: Added Security Update Checker #38

Merged
merged 18 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ ignore:
- "src/Dto/*.php"
- "src/Kernel.php"
- "src/EvaluatorBundle.php"
- "**/*Dto.php"
- "**/*Collection.php"
- "**/*ConfigurationProvider.php"
- "src/ReleaseApp/Domain/Entities/*"
- "src/ReleaseApp/Infrastructure/Client/Request/*"
- "src/ReleaseApp/Infrastructure/Client/Response/*"
- "bin"
- "tests"
- "**/*.md"
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"symfony/uid": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0",
"symfony/stopwatch": "^5.4|^6.0",
"symfony/yaml": "^5.4|^6.0"
"symfony/yaml": "^5.4|^6.0",
"laminas/laminas-filter": "^2.11.0"
},
"require-dev": {
"phpstan/phpstan": "^1.10",
Expand Down
1 change: 1 addition & 0 deletions config/checkers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ parameters:

# Security checker
security_checker_doc_url: https://docs.spryker.com/docs/scos/dev/guidelines/keeping-a-project-upgradable/upgradability-guidelines/security.html
security_update_checker_doc_url: https://docs.spryker.com/docs/scos/dev/guidelines/keeping-a-project-upgradable/upgradability-guidelines/security_update.html
VladislavStrelchenko marked this conversation as resolved.
Show resolved Hide resolved

# Multidimensional array checker
multidimensional_array_checker_doc_url: https://docs.spryker.com/docs/scos/dev/guidelines/keeping-a-project-upgradable/upgradability-guidelines/multidimensional-array.html
Expand Down
15 changes: 15 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ services:
_defaults:
autowire: true
autoconfigure: true
public: true
VladislavStrelchenko marked this conversation as resolved.
Show resolved Hide resolved
bind:
$projectDirEnv: '%env(EVALUATOR_PROJECT_DIR)%'
$githubAuth: '%env(GITHUB_AUTH)%'
Expand All @@ -27,6 +28,14 @@ services:
resource: '../src/'
exclude:
- '../src/Kernel.php'
- '../src/Dto/*'
- '../src/Report/Dto/*'
- '../src/Checker/PhpVersionChecker/CheckerStrategyResponse.php'
- '../src/ReleaseApp/Domain/Entities/*'
- '../src/ReleaseApp/Domain/Client/Request/*'
- '../src/ReleaseApp/Domain/Client/Response/*'
- '../src/ReleaseApp/Infrastructure/Client/Request/*'
- '../src/ReleaseApp/Infrastructure/Shared/Dto/*'

symfony.console.application:
class: Symfony\Bundle\FrameworkBundle\Console\Application
Expand Down Expand Up @@ -82,6 +91,12 @@ services:
- '@SprykerSdk\Evaluator\Resolver\PathResolver'
- '%security_checker_doc_url%'

SprykerSdk\Evaluator\Checker\SecurityChecker\SecurityUpdateChecker:
arguments:
- '@SprykerSdk\Evaluator\Reader\ComposerReaderInterface'
- '@SprykerSdk\Evaluator\ReleaseApp\Infrastructure\Service\ReleaseAppService'
- '%security_update_checker_doc_url%'

SprykerSdk\Evaluator\Checker\DependencyProviderAdditionalLogicChecker\DependencyProviderAdditionalLogicChecker:
arguments:
$checkerDocUrl: '%dp_additional_logic_checker_doc_url%'
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ parameters:
level: 8
paths:
- src/
checkGenericClassInNonGenericObjectType: false
treatPhpDocTypesAsCertain: false
133 changes: 133 additions & 0 deletions src/Checker/SecurityChecker/SecurityUpdateChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Checker\SecurityChecker;

use Composer\Semver\Comparator;
use RuntimeException;
use SprykerSdk\Evaluator\Checker\CheckerInterface;
use SprykerSdk\Evaluator\Dto\CheckerInputDataDto;
use SprykerSdk\Evaluator\Dto\CheckerResponseDto;
use SprykerSdk\Evaluator\Dto\ViolationDto;
use SprykerSdk\Evaluator\Helper\SemanticVersionHelper;
use SprykerSdk\Evaluator\Reader\ComposerReaderInterface;
use SprykerSdk\Evaluator\ReleaseApp\Domain\Client\Request\UpgradeAnalysisRequest;
use SprykerSdk\Evaluator\ReleaseApp\Infrastructure\Service\ReleaseAppServiceInterface;

class SecurityUpdateChecker implements CheckerInterface
{
/**
* @var string
*/
public const NAME = 'SECURITY_UPDATE_CHECKER';

/**
* @var \SprykerSdk\Evaluator\Reader\ComposerReaderInterface
*/
protected ComposerReaderInterface $composerReader;

/**
* @var \SprykerSdk\Evaluator\ReleaseApp\Infrastructure\Service\ReleaseAppServiceInterface
*/
protected ReleaseAppServiceInterface $releaseAppService;

/**
* @var string
*/
protected string $checkerDocUrl;

/**
* @param \SprykerSdk\Evaluator\Reader\ComposerReaderInterface $composerReader
* @param \SprykerSdk\Evaluator\ReleaseApp\Infrastructure\Service\ReleaseAppServiceInterface $releaseAppService
* @param string $checkerDocUrl
*/
public function __construct(
ComposerReaderInterface $composerReader,
ReleaseAppServiceInterface $releaseAppService,
string $checkerDocUrl = ''
) {
$this->composerReader = $composerReader;
$this->releaseAppService = $releaseAppService;
$this->checkerDocUrl = $checkerDocUrl;
}

/**
* @return string
*/
public function getName(): string
{
return static::NAME;
}

/**
* @param \SprykerSdk\Evaluator\Dto\CheckerInputDataDto $inputData
*
* @return \SprykerSdk\Evaluator\Dto\CheckerResponseDto
*/
public function check(CheckerInputDataDto $inputData): CheckerResponseDto
{
$violationMessages = [];

try {
pavelmaksimov25 marked this conversation as resolved.
Show resolved Hide resolved
$releaseAppResponse = $this->releaseAppService->getNewSecurityReleaseGroups($this->createDataProviderRequest());
} catch (RuntimeException $exception) {
$violationMessages[] = new ViolationDto(
sprintf(
'Service is not available, please try latter. Error: %s %s',
$exception->getCode(),
$exception->getMessage(),
),
$this->getName(),
);

return new CheckerResponseDto($violationMessages, $this->checkerDocUrl);
}

foreach ($releaseAppResponse->getReleaseGroupCollection()->toArray() as $releaseGroupDto) {
foreach ($releaseGroupDto->getModuleCollection()->toArray() as $moduleDto) {
VladislavStrelchenko marked this conversation as resolved.
Show resolved Hide resolved
$installedVersion = $this->composerReader->getPackageVersion($moduleDto->getName());
if ($installedVersion === null) {
continue;
}

$installedMajorVersion = SemanticVersionHelper::getMajorVersion($installedVersion);
pavelmaksimov25 marked this conversation as resolved.
Show resolved Hide resolved
$securityUpdateMajorVersion = SemanticVersionHelper::getMajorVersion($moduleDto->getVersion());
if ($securityUpdateMajorVersion !== $installedMajorVersion) {
continue;
}
if (!Comparator::greaterThan($moduleDto->getVersion(), $installedVersion)) {
continue;
}

$violationMessages[] = new ViolationDto(
sprintf(
'Security update available for the module %s, actual version %s',
$moduleDto->getName(),
$installedVersion,
),
sprintf('%s:%s', $moduleDto->getName(), $moduleDto->getVersion()),
);
}
}

return new CheckerResponseDto($violationMessages, $this->checkerDocUrl);
}

/**
* @return \SprykerSdk\Evaluator\ReleaseApp\Domain\Client\Request\UpgradeAnalysisRequest
*/
protected function createDataProviderRequest(): UpgradeAnalysisRequest
{
$projectName = $this->composerReader->getProjectName();
$composerJson = $this->composerReader->getComposerData();
$composerLock = $this->composerReader->getComposerLockData();

return new UpgradeAnalysisRequest($projectName, $composerJson, $composerLock);
}
}
44 changes: 44 additions & 0 deletions src/Helper/SemanticVersionHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Helper;

use Composer\Semver\Comparator;

class SemanticVersionHelper
{
/**
* @param string $semanticVersion
*
* @return int|null
*/
public static function getMajorVersion(string $semanticVersion): ?int
pavelmaksimov25 marked this conversation as resolved.
Show resolved Hide resolved
{
if (!static::isSemanticVersion($semanticVersion)) {
return null;

Check warning on line 24 in src/Helper/SemanticVersionHelper.php

View check run for this annotation

Codecov / codecov/patch

src/Helper/SemanticVersionHelper.php#L24

Added line #L24 was not covered by tests
}

$versionParts = explode('.', $semanticVersion);
if (!$versionParts) {
return null;

Check warning on line 29 in src/Helper/SemanticVersionHelper.php

View check run for this annotation

Codecov / codecov/patch

src/Helper/SemanticVersionHelper.php#L29

Added line #L29 was not covered by tests
}

return (int)array_shift($versionParts);
}

/**
* @param string $version
*
* @return bool
*/
protected static function isSemanticVersion(string $version): bool
{
return Comparator::greaterThanOrEqualTo($version, '0.0.1') === true;
}
}
77 changes: 77 additions & 0 deletions src/Helper/TextCaseHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Helper;

use Laminas\Filter\FilterChain;
use Laminas\Filter\StringToLower;
use Laminas\Filter\Word\CamelCaseToDash;
use Laminas\Filter\Word\DashToCamelCase;
use SprykerSdk\Evaluator\Helper\UtilText\Filter\CamelCaseToDash as CamelCaseToDashWithoutAbbreviation;

class TextCaseHelper
{
/**
* @param string $value
* @param bool $separateAbbreviation
*
* @return string
*/
public static function camelCaseToDash(string $value, bool $separateAbbreviation = true): string
{
$filterChain = new FilterChain();

if ($separateAbbreviation) {
pavelmaksimov25 marked this conversation as resolved.
Show resolved Hide resolved
$filterChain->attach(new CamelCaseToDash());
} else {
$filterChain->attach(new CamelCaseToDashWithoutAbbreviation());
}

$filterChain->attach(new StringToLower());

return $filterChain->filter($value);
}

/**
* @param string $value
* @param bool $upperCaseFirst
*
* @return string
*/
public static function dashToCamelCase(string $value, bool $upperCaseFirst = true): string
{
$filterChain = new FilterChain();
$filterChain->attach(new DashToCamelCase());

if ($upperCaseFirst) {
return ucfirst($filterChain->filter($value));
}

// Set first character in original case

return mb_substr($value, 0, 1) . mb_substr($filterChain->filter($value), 1);
}

/**
* Spryker.SymfonyMailer => spryker/symfony-mailer
*
* @param string $originName
*
* @return string
*/
public static function packageCamelCaseToDash(string $originName): string
{
[$organization, $package] = explode('.', $originName);

return implode('/', [
static::camelCaseToDash($organization),
static::camelCaseToDash($package),
]);
}
}
32 changes: 32 additions & 0 deletions src/Helper/UtilText/Filter/CamelCaseToDash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/

declare(strict_types=1);

namespace SprykerSdk\Evaluator\Helper\UtilText\Filter;

use Laminas\Filter\Word\AbstractSeparator;

class CamelCaseToDash extends AbstractSeparator
{
public function __construct()
{
parent::__construct('-');
}

/**
* @param mixed $string
*
* @return string
*/
public function filter($string): string
{
$value = preg_replace('/([a-z])([A-Z])/', '$1' . addcslashes($this->getSeparator(), '$') . '$2', $string);

return is_array($value) ? (string)array_shift($value) : (string)$value;
}
}
Loading
Loading