Skip to content

Commit

Permalink
Merge pull request #8763 from magento-cia/AC-10868-2
Browse files Browse the repository at this point in the history
AC-10686: [PCI] SRI enabled on payment pages.
  • Loading branch information
admanesachin authored Mar 6, 2024
2 parents 9fd6d58 + 10d0c30 commit ff4439c
Show file tree
Hide file tree
Showing 26 changed files with 1,500 additions and 8 deletions.
94 changes: 94 additions & 0 deletions app/code/Magento/Csp/Block/Sri/Hashes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Csp\Block\Sri;

use Magento\Framework\UrlInterface;
use Magento\Deploy\Package\Package;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\View\Element\Template;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\View\Element\Template\Context;
use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;

/**
* Block for Subresource Integrity hashes rendering.
*
* @api
*/
class Hashes extends Template
{
/**
* @var SerializerInterface
*/
private SerializerInterface $serializer;

/**
* @var SubresourceIntegrityRepositoryPool
*/
private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;

/**
* @param Context $context
* @param array $data
* @param SubresourceIntegrityRepositoryPool|null $integrityRepositoryPool
* @param SerializerInterface|null $serializer
*/
public function __construct(
Context $context,
array $data = [],
?SubresourceIntegrityRepositoryPool $integrityRepositoryPool = null,
?SerializerInterface $serializer = null
) {
parent::__construct($context, $data);

$this->integrityRepositoryPool = $integrityRepositoryPool ?: ObjectManager::getInstance()
->get(SubresourceIntegrityRepositoryPool::class);

$this->serializer = $serializer ?: ObjectManager::getInstance()
->get(SerializerInterface::class);
}

/**
* Retrieves integrity hashes in serialized format.
*
* @throws LocalizedException
*
* @return string
*/
public function getSerialized(): string
{
$result = [];

$baseUrl = $this->_urlBuilder->getBaseUrl(
["_type" => UrlInterface::URL_TYPE_STATIC]
);

$integrityRepository = $this->integrityRepositoryPool->get(
Package::BASE_AREA
);

foreach ($integrityRepository->getAll() as $integrity) {
$url = $baseUrl . $integrity->getPath();

$result[$url] = $integrity->getHash();
}

$integrityRepository = $this->integrityRepositoryPool->get(
$this->_appState->getAreaCode()
);

foreach ($integrityRepository->getAll() as $integrity) {
$url = $baseUrl . $integrity->getPath();

$result[$url] = $integrity->getHash();
}

return $this->serializer->serialize($result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Csp\Model\Deploy\Package\Processor\PostProcessor;

use Magento\Framework\Filesystem;
use Magento\Deploy\Package\Package;
use Magento\Csp\Model\SubresourceIntegrityFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Csp\Model\SubresourceIntegrityCollector;
use Magento\Deploy\Package\Processor\ProcessorInterface;
use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;

/**
* Post-processor that generates integrity hashes after static content package deployed.
*/
class Integrity implements ProcessorInterface
{
/**
* @var Filesystem
*/
private Filesystem $filesystem;

/**
* @var HashGenerator
*/
private HashGenerator $hashGenerator;

/**
* @var SubresourceIntegrityFactory
*/
private SubresourceIntegrityFactory $integrityFactory;

/**
* @var SubresourceIntegrityCollector
*/
private SubresourceIntegrityCollector $integrityCollector;

/**
* @param Filesystem $filesystem
* @param HashGenerator $hashGenerator
* @param SubresourceIntegrityFactory $integrityFactory
* @param SubresourceIntegrityCollector $integrityCollector
*/
public function __construct(
Filesystem $filesystem,
HashGenerator $hashGenerator,
SubresourceIntegrityFactory $integrityFactory,
SubresourceIntegrityCollector $integrityCollector
) {
$this->filesystem = $filesystem;
$this->hashGenerator = $hashGenerator;
$this->integrityFactory = $integrityFactory;
$this->integrityCollector = $integrityCollector;
}

/**
* @inheritdoc
*/
public function process(Package $package, array $options): bool
{
$staticDir = $this->filesystem->getDirectoryRead(
DirectoryList::ROOT
);

foreach ($package->getFiles() as $file) {
if ($file->getExtension() == "js") {
$integrity = $this->integrityFactory->create(
[
"data" => [
'hash' => $this->hashGenerator->generate(
$staticDir->readFile($file->getSourcePath())
),
'path' => $file->getDeployedFilePath()
]
]
);

$this->integrityCollector->collect($integrity);
}
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Csp\Model\Deploy\Package\Processor\PostProcessor;

use Magento\Deploy\Package\Package;
use Magento\Deploy\Package\PackageFileFactory;
use Magento\Deploy\Service\DeployStaticFile;
use Magento\Framework\App\DeploymentConfig\Writer\PhpFormatter;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Filesystem;
use Magento\Framework\View\Asset\Minification;
use Magento\Framework\View\Asset\RepositoryMap;
use Magento\Csp\Model\SubresourceIntegrityFactory;
use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
use Magento\Framework\Filesystem\DriverInterface;
use Magento\Csp\Model\SubresourceIntegrityCollector;

/**
* Class Adds Integrity attribute to requirejs-map.js asset
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Map extends \Magento\Deploy\Package\Processor\PostProcessor\Map
{

/**
* @var HashGenerator
*/
private HashGenerator $hashGenerator;

/**
* @var SubresourceIntegrityCollector
*/
private SubresourceIntegrityCollector $integrityCollector;

/**
* @var SubresourceIntegrityFactory
*/
private SubresourceIntegrityFactory $integrityFactory;

/**
* @var Minification
*/
private Minification $minification;

/**
* @var DriverInterface
*/
private DriverInterface $driver;

/**
* @var FileSystem
*/
private FileSystem $filesystem;

/**
* Constructor
*
* @param DeployStaticFile $deployStaticFile
* @param PhpFormatter $formatter
* @param PackageFileFactory $packageFileFactory
* @param Minification $minification
* @param SubresourceIntegrityFactory $integrityFactory
* @param HashGenerator $hashGenerator
* @param DriverInterface $driver
* @param SubresourceIntegrityCollector $integrityCollector
* @param FileSystem $filesystem
*/
public function __construct(
DeployStaticFile $deployStaticFile,
PhpFormatter $formatter,
PackageFileFactory $packageFileFactory,
Minification $minification,
SubresourceIntegrityFactory $integrityFactory,
HashGenerator $hashGenerator,
DriverInterface $driver,
SubresourceIntegrityCollector $integrityCollector,
Filesystem $filesystem
) {
$this->minification = $minification;
$this->integrityFactory = $integrityFactory;
$this->hashGenerator = $hashGenerator;
$this->driver = $driver;
$this->integrityCollector = $integrityCollector;
$this->filesystem = $filesystem;
parent::__construct($deployStaticFile, $formatter, $packageFileFactory, $minification);
}

/**
* @inheritdoc
*
* @throws FileSystemException
*/
public function process(Package $package, array $options): bool
{
parent::process($package, $options);
$fileName = $this->minification->addMinifiedSign(RepositoryMap::REQUIRE_JS_MAP_NAME);
$path = $package->getPath();
$relativePath = $path . DIRECTORY_SEPARATOR . $fileName;

if ($this->fileExists($relativePath)) {
$dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW);
$absolutePath = $dir->getAbsolutePath($relativePath);
$fileContent = $this->driver->fileGetContents($absolutePath);

if ($fileContent) {
$integrity = $this->integrityFactory->create(
[
"data" => [
'hash' => $this->hashGenerator->generate($fileContent),
'path' => $relativePath
]
]
);
$this->integrityCollector->collect($integrity);
}
}
return true;
}

/**
* Check if file exist
*
* @param string $path
* @return bool
* @throws FileSystemException
*/
private function fileExists(string $path): bool
{
$dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW);
return $dir->isExist($path);
}
}
34 changes: 34 additions & 0 deletions app/code/Magento/Csp/Model/SubresourceIntegrity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Csp\Model;

/**
* Subresource Integrity data model.
*/
class SubresourceIntegrity extends \Magento\Framework\DataObject
{
/**
* Gets an integrity Path.
*
* @return string|null
*/
public function getPath(): ?string
{
return $this->getData("path");
}

/**
* Gets an integrity hash.
*
* @return string|null
*/
public function getHash(): ?string
{
return $this->getData("hash");
}
}
37 changes: 37 additions & 0 deletions app/code/Magento/Csp/Model/SubresourceIntegrity/HashGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Csp\Model\SubresourceIntegrity;

/**
* Subresource Integrity hashes generator.
*/
class HashGenerator
{
/**
* CHashing algorithm.
*
* @var string
*/
private const ALGORITHM = 'sha256';

/**
* Computes integrity hash for a given content.
*
* @param string $content
*
* @return string
*/
public function generate(string $content): string
{
$base64Hash = base64_encode(
hash(self::ALGORITHM, $content, true)
);

return self::ALGORITHM . "-{$base64Hash}";
}
}
Loading

0 comments on commit ff4439c

Please sign in to comment.