diff --git a/HISTORY.md b/HISTORY.md index 083b8f9b..9dce192e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -15,7 +15,7 @@ All notable changes to this project will be documented in this file. * Enumeration-like classes were converted to native [PHP Enumerations](https://www.php.net/manual/en/language.types.enumerations.php) ([#140] via [#204]) * Added * Support for CycloneDX schema/spec v1.4 ([#57] via [#65], [#118], [#123]) - * Support for [properties](https://cyclonedx.org/use-cases/#properties--name-value-store) ([#228] via [#165], [#229]) + * Support for [properties](https://cyclonedx.org/use-cases/#properties--name-value-store) ([#228] via [#165], [#229], [#231]) * Misc * All class properties now enforce the correct types ([#6], [#114] via [#125]) This is considered a non-breaking change, because the types were already correctly annotated. @@ -210,6 +210,7 @@ All notable changes to this project will be documented in this file. [#204]: https://github.com/CycloneDX/cyclonedx-php-library/pull/204 [#228]: https://github.com/CycloneDX/cyclonedx-php-library/issues/228 [#229]: https://github.com/CycloneDX/cyclonedx-php-library/pull/229 +[#231]: https://github.com/CycloneDX/cyclonedx-php-library/pull/231 ## 1.6.3 - 2022-09-15 diff --git a/src/Core/Serialization/DOM/Normalizers/BomNormalizer.php b/src/Core/Serialization/DOM/Normalizers/BomNormalizer.php index b7eb7518..d39f7924 100644 --- a/src/Core/Serialization/DOM/Normalizers/BomNormalizer.php +++ b/src/Core/Serialization/DOM/Normalizers/BomNormalizer.php @@ -29,6 +29,7 @@ use CycloneDX\Core\Models\Bom; use CycloneDX\Core\Models\Metadata; use CycloneDX\Core\Serialization\DOM\_BaseNormalizer; +use CycloneDX\Core\Spec\Format; use DOMElement; /** @@ -145,7 +146,7 @@ private function normalizeDependencies(Bom $bom): ?DOMElement private function normalizeProperties(PropertyRepository $properties): ?DOMElement { - if (false === $this->getNormalizerFactory()->getSpec()->supportsBomProperties()) { + if (false === $this->getNormalizerFactory()->getSpec()->supportsBomProperties(Format::XML)) { return null; } diff --git a/src/Core/Serialization/JSON/Normalizers/BomNormalizer.php b/src/Core/Serialization/JSON/Normalizers/BomNormalizer.php index d610ee4d..5a852bcc 100644 --- a/src/Core/Serialization/JSON/Normalizers/BomNormalizer.php +++ b/src/Core/Serialization/JSON/Normalizers/BomNormalizer.php @@ -24,9 +24,11 @@ namespace CycloneDX\Core\Serialization\JSON\Normalizers; use CycloneDX\Core\_helpers\Assert; +use CycloneDX\Core\Collections\PropertyRepository; use CycloneDX\Core\Models\Bom; use CycloneDX\Core\Models\Metadata; use CycloneDX\Core\Serialization\JSON\_BaseNormalizer; +use CycloneDX\Core\Spec\Format; use CycloneDX\Core\Spec\Version; /** @@ -64,9 +66,13 @@ public function normalize(Bom $bom): array 'version' => $bom->getVersion(), 'metadata' => $this->normalizeMetadata($bom->getMetadata()), 'components' => $factory->makeForComponentRepository()->normalize($bom->getComponents()), + // services 'externalReferences' => $this->normalizeExternalReferences($bom), 'dependencies' => $this->normalizeDependencies($bom), - // 'properties' => not supported, yet - see https://github.com/CycloneDX/specification/issues/130 + // compositions + // vulnerabilities + 'properties' => $this->normalizeProperties($bom->getProperties()), + // signature ], Assert::isNotNull(...) ); @@ -127,4 +133,15 @@ private function normalizeDependencies(Bom $bom): ?array ? null : $data; } + + private function normalizeProperties(PropertyRepository $properties): ?array + { + if (false === $this->getNormalizerFactory()->getSpec()->supportsBomProperties(Format::JSON)) { + return null; + } + + return 0 === \count($properties) + ? null + : $this->getNormalizerFactory()->makeForPropertyRepository()->normalize($properties); + } } diff --git a/src/Core/Spec/Spec.php b/src/Core/Spec/Spec.php index 1260f92f..8c08e71c 100644 --- a/src/Core/Spec/Spec.php +++ b/src/Core/Spec/Spec.php @@ -66,5 +66,5 @@ public function supportsComponentAuthor(): bool; public function supportsComponentProperties(): bool; - public function supportsBomProperties(): bool; + public function supportsBomProperties(Format $format): bool; } diff --git a/src/Core/Spec/SpecFactory.php b/src/Core/Spec/SpecFactory.php index 0e607a04..8875500c 100644 --- a/src/Core/Spec/SpecFactory.php +++ b/src/Core/Spec/SpecFactory.php @@ -49,8 +49,6 @@ abstract class SpecFactory /** * Create the appropriate {@see \CycloneDX\Core\Spec\Spec Specification} based on {@see \CycloneDX\Core\Spec\Version}. * - * @psalm-assert Version::* $version - * * @throws DomainException when $version was unsupported */ public static function makeForVersion(Version $version): Spec @@ -120,7 +118,7 @@ public static function make1dot1(): Spec false, false, false, - false, + [], ); } @@ -187,7 +185,7 @@ public static function make1dot2(): Spec false, true, false, - false, + [], ); } @@ -254,7 +252,9 @@ public static function make1dot3(): Spec true, true, true, - true, + [ + Format::XML, + ], ); } @@ -322,7 +322,9 @@ public static function make1dot4(): Spec true, true, true, - true, + [ + Format::XML, + ], ); } } diff --git a/src/Core/Spec/_Spec.php b/src/Core/Spec/_Spec.php index e99b8597..7c9a3d78 100644 --- a/src/Core/Spec/_Spec.php +++ b/src/Core/Spec/_Spec.php @@ -46,6 +46,7 @@ class _Spec implements Spec * @psalm-param list $lComponentTypes * @psalm-param list $lHashAlgorithms * @psalm-param list $lExternalReferenceTypes + * @psalm-param list $lFormatsSupportingBomProperties * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -66,7 +67,7 @@ public function __construct( private readonly bool $bMetadataProperties, private readonly bool $bComponentAuthor, private readonly bool $bComponentProperties, - private readonly bool $bBomProperties, + private readonly array $lFormatsSupportingBomProperties, ) { } @@ -150,8 +151,8 @@ public function supportsComponentProperties(): bool return $this->bComponentProperties; } - public function supportsBomProperties(): bool + public function supportsBomProperties(Format $format): bool { - return $this->bBomProperties; + return \in_array($format, $this->lFormatsSupportingBomProperties, true); } } diff --git a/tests/Core/Spec/Spec1dot1Test.php b/tests/Core/Spec/Spec1dot1Test.php index 3ed037ab..521b108c 100644 --- a/tests/Core/Spec/Spec1dot1Test.php +++ b/tests/Core/Spec/Spec1dot1Test.php @@ -92,8 +92,8 @@ protected static function shouldSupportComponentProperties(): bool return false; } - protected static function shouldSupportBomProperties(): bool + protected static function shouldSupportBomProperties(): array { - return false; + return []; } } diff --git a/tests/Core/Spec/Spec1dot2Test.php b/tests/Core/Spec/Spec1dot2Test.php index f73522b7..0d3238a1 100644 --- a/tests/Core/Spec/Spec1dot2Test.php +++ b/tests/Core/Spec/Spec1dot2Test.php @@ -92,8 +92,8 @@ protected static function shouldSupportComponentProperties(): bool return false; } - protected static function shouldSupportBomProperties(): bool + protected static function shouldSupportBomProperties(): array { - return false; + return []; } } diff --git a/tests/Core/Spec/Spec1dot3Test.php b/tests/Core/Spec/Spec1dot3Test.php index 11b19cb1..cba6d9b5 100644 --- a/tests/Core/Spec/Spec1dot3Test.php +++ b/tests/Core/Spec/Spec1dot3Test.php @@ -92,8 +92,8 @@ protected static function shouldSupportComponentProperties(): bool return true; } - protected static function shouldSupportBomProperties(): bool + protected static function shouldSupportBomProperties(): array { - return true; + return [Format::XML]; } } diff --git a/tests/Core/Spec/Spec1dot4Test.php b/tests/Core/Spec/Spec1dot4Test.php index c2219209..78fac1d6 100644 --- a/tests/Core/Spec/Spec1dot4Test.php +++ b/tests/Core/Spec/Spec1dot4Test.php @@ -92,8 +92,8 @@ protected static function shouldSupportComponentProperties(): bool return true; } - protected static function shouldSupportBomProperties(): bool + protected static function shouldSupportBomProperties(): array { - return true; + return [Format::XML]; } } diff --git a/tests/Core/Spec/SpecBaseTestCase.php b/tests/Core/Spec/SpecBaseTestCase.php index 96d8aa94..ae6e3149 100644 --- a/tests/Core/Spec/SpecBaseTestCase.php +++ b/tests/Core/Spec/SpecBaseTestCase.php @@ -205,11 +205,21 @@ final public function testSupportsComponentProperties(): void abstract protected static function shouldSupportComponentProperties(): bool; - final public function testSupportsBomProperties(): void + #[\PHPUnit\Framework\Attributes\DataProvider('dpSupportsBomProperties')] + final public function testSupportsBomProperties(Format $format, bool $shouldSupportBomProperties): void { - $isSupported = static::getSpec()->supportsBomProperties(); - self::assertSame(static::shouldSupportBomProperties(), $isSupported); + $isSupported = static::getSpec()->supportsBomProperties($format); + self::assertSame($shouldSupportBomProperties, $isSupported); } - abstract protected static function shouldSupportBomProperties(): bool; + final public static function dpSupportsBomProperties(): Generator + { + $should = static::shouldSupportBomProperties(); + foreach (Format::cases() as $format) { + yield $format->name => [$format, \in_array($format, $should, true)]; + } + } + + /** @return Format[] */ + abstract protected static function shouldSupportBomProperties(): array; }