From 28b85108280439fe2a5cf9814cf22ccf4d0de237 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Sun, 19 Dec 2021 14:27:05 +0100 Subject: [PATCH] JSON result: add `$schema` fixes #43 Signed-off-by: Jan Kowalleck --- HISTORY.md | 6 ++ res/bom-1.2-strict.SNAPSHOT.schema.json | 6 ++ res/bom-1.3-strict.SNAPSHOT.schema.json | 6 ++ src/Core/Serialize/JsonSerializer.php | 25 +++++++- tests/Core/Serialize/JsonSerializerTest.php | 39 +++++++++++- tests/Core/Serialize/XmlSerializerTest.php | 68 ++++++++++++++++++++- tests/Core/SerializeToJsonTest.php | 6 +- 7 files changed, 151 insertions(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 4e38a2f9..7523d932 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## unreleased +* Added + * Resulting JSON files hold the correct `$schema`. ([#43] via [#42]) + +[#43]: https://github.com/CycloneDX/cyclonedx-php-library/issues/43 +[#42]: https://github.com/CycloneDX/cyclonedx-php-library/pull/42 + ## 1.3.1 - 2021-12-03 * Fixed diff --git a/res/bom-1.2-strict.SNAPSHOT.schema.json b/res/bom-1.2-strict.SNAPSHOT.schema.json index ed0e27a5..378b190b 100644 --- a/res/bom-1.2-strict.SNAPSHOT.schema.json +++ b/res/bom-1.2-strict.SNAPSHOT.schema.json @@ -11,6 +11,12 @@ ], "additionalProperties": false, "properties": { + "$schema": { + "type": "string", + "enum": [ + "http://cyclonedx.org/schema/bom-1.2a.schema.json" + ] + }, "bomFormat": { "$id": "#/properties/bomFormat", "type": "string", diff --git a/res/bom-1.3-strict.SNAPSHOT.schema.json b/res/bom-1.3-strict.SNAPSHOT.schema.json index 99a8fa92..11185233 100644 --- a/res/bom-1.3-strict.SNAPSHOT.schema.json +++ b/res/bom-1.3-strict.SNAPSHOT.schema.json @@ -11,6 +11,12 @@ ], "additionalProperties": false, "properties": { + "$schema": { + "type": "string", + "enum": [ + "http://cyclonedx.org/schema/bom-1.3.schema.json" + ] + }, "bomFormat": { "$id": "#/properties/bomFormat", "type": "string", diff --git a/src/Core/Serialize/JsonSerializer.php b/src/Core/Serialize/JsonSerializer.php index 666d68be..aaea3eb1 100644 --- a/src/Core/Serialize/JsonSerializer.php +++ b/src/Core/Serialize/JsonSerializer.php @@ -24,6 +24,7 @@ namespace CycloneDX\Core\Serialize; use CycloneDX\Core\Models\Bom; +use CycloneDX\Core\Spec\Version; use DomainException; /** @@ -39,16 +40,38 @@ class JsonSerializer extends BaseSerializer | \JSON_UNESCAPED_SLASHES // urls become shorter | \JSON_PRETTY_PRINT; + /** + * JSON schema `$id` that is applied. + * + * @var string[]|null[] + * @psalm-var array + */ + private const SCHEMA = [ + Version::V_1_1 => null, // unsupported version + Version::V_1_2 => 'http://cyclonedx.org/schema/bom-1.2a.schema.json', + Version::V_1_3 => 'http://cyclonedx.org/schema/bom-1.3.schema.json', + ]; + + private function getSchemaBase(): array + { + $schema = self::SCHEMA[$this->getSpec()->getVersion()] ?? null; + + return null === $schema + ? [] // @codeCoverageIgnore + : ['$schema' => $schema]; + } + /** * @throws DomainException if something was not supported */ protected function normalize(Bom $bom): string { + $schemaBase = $this->getSchemaBase(); $data = (new JSON\NormalizerFactory($this->getSpec())) ->makeForBom() ->normalize($bom); - $json = json_encode($data, self::NORMALIZE_OPTIONS); + $json = json_encode(array_merge($schemaBase, $data), self::NORMALIZE_OPTIONS); \assert(false !== $json); // as option JSON_THROW_ON_ERROR is expected to be set return $json; diff --git a/tests/Core/Serialize/JsonSerializerTest.php b/tests/Core/Serialize/JsonSerializerTest.php index ded7aafa..bc34d7f2 100644 --- a/tests/Core/Serialize/JsonSerializerTest.php +++ b/tests/Core/Serialize/JsonSerializerTest.php @@ -43,7 +43,7 @@ class JsonSerializerTest extends TestCase * @uses \CycloneDX\Core\Serialize\JSON\Normalizers\ComponentNormalizer * @uses \CycloneDX\Core\Serialize\BomRefDiscriminator */ - public function testSerialize(): void + public function testSerialize12(): void { $spec = $this->createConfiguredMock( SpecInterface::class, @@ -60,6 +60,7 @@ public function testSerialize(): void self::assertJsonStringEqualsJsonString( <<<'JSON' { + "$schema": "http://cyclonedx.org/schema/bom-1.2a.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.2", "version": 0, @@ -69,4 +70,40 @@ public function testSerialize(): void $actual ); } + + /** + * @uses \CycloneDX\Core\Serialize\JSON\AbstractNormalizer + * @uses \CycloneDX\Core\Serialize\JSON\NormalizerFactory + * @uses \CycloneDX\Core\Serialize\JSON\Normalizers\BomNormalizer + * @uses \CycloneDX\Core\Serialize\JSON\Normalizers\ComponentRepositoryNormalizer + * @uses \CycloneDX\Core\Serialize\JSON\Normalizers\ComponentNormalizer + * @uses \CycloneDX\Core\Serialize\BomRefDiscriminator + */ + public function testSerialize13(): void + { + $spec = $this->createConfiguredMock( + SpecInterface::class, + [ + 'getVersion' => '1.3', + 'isSupportedFormat' => true, + ] + ); + $serializer = new JsonSerializer($spec); + $bom = $this->createStub(Bom::class); + + $actual = $serializer->serialize($bom); + + self::assertJsonStringEqualsJsonString( + <<<'JSON' + { + "$schema": "http://cyclonedx.org/schema/bom-1.3.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3", + "version": 0, + "components": [] + } + JSON, + $actual + ); + } } diff --git a/tests/Core/Serialize/XmlSerializerTest.php b/tests/Core/Serialize/XmlSerializerTest.php index d6f22271..f0a544b3 100644 --- a/tests/Core/Serialize/XmlSerializerTest.php +++ b/tests/Core/Serialize/XmlSerializerTest.php @@ -43,7 +43,40 @@ class XmlSerializerTest extends TestCase * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\ComponentNormalizer * @uses \CycloneDX\Core\Serialize\BomRefDiscriminator */ - public function testSerialize(): void + public function testSerialize11(): void + { + $spec = $this->createConfiguredMock( + SpecInterface::class, + [ + 'getVersion' => '1.1', + 'isSupportedFormat' => true, + ] + ); + $serializer = new XmlSerializer($spec); + $bom = $this->createStub(Bom::class); + + $actual = $serializer->serialize($bom); + + self::assertXmlStringEqualsXmlString( + <<<'XML' + + + + + XML, + $actual + ); + } + + /** + * @uses \CycloneDX\Core\Serialize\DOM\AbstractNormalizer + * @uses \CycloneDX\Core\Serialize\DOM\NormalizerFactory + * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\BomNormalizer + * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\ComponentRepositoryNormalizer + * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\ComponentNormalizer + * @uses \CycloneDX\Core\Serialize\BomRefDiscriminator + */ + public function testSerialize12(): void { $spec = $this->createConfiguredMock( SpecInterface::class, @@ -67,4 +100,37 @@ public function testSerialize(): void $actual ); } + + /** + * @uses \CycloneDX\Core\Serialize\DOM\AbstractNormalizer + * @uses \CycloneDX\Core\Serialize\DOM\NormalizerFactory + * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\BomNormalizer + * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\ComponentRepositoryNormalizer + * @uses \CycloneDX\Core\Serialize\DOM\Normalizers\ComponentNormalizer + * @uses \CycloneDX\Core\Serialize\BomRefDiscriminator + */ + public function testSerialize13(): void + { + $spec = $this->createConfiguredMock( + SpecInterface::class, + [ + 'getVersion' => '1.3', + 'isSupportedFormat' => true, + ] + ); + $serializer = new XmlSerializer($spec); + $bom = $this->createStub(Bom::class); + + $actual = $serializer->serialize($bom); + + self::assertXmlStringEqualsXmlString( + <<<'XML' + + + + + XML, + $actual + ); + } } diff --git a/tests/Core/SerializeToJsonTest.php b/tests/Core/SerializeToJsonTest.php index 7b396a04..ec6cd49a 100644 --- a/tests/Core/SerializeToJsonTest.php +++ b/tests/Core/SerializeToJsonTest.php @@ -75,8 +75,9 @@ public function testSchema12(Bom $bom): void $validator = new JsonStrictValidator($spec); $json = $serializer->serialize($bom); - $validationErrors = $validator->validateString($json); + self::assertJson($json); + $validationErrors = $validator->validateString($json); self::assertNull($validationErrors); } @@ -99,8 +100,9 @@ public function testSchema13(Bom $bom): void $validator = new JsonStrictValidator($spec); $json = $serializer->serialize($bom); - $validationErrors = $validator->validateString($json); + self::assertJson($json); + $validationErrors = $validator->validateString($json); self::assertNull($validationErrors); }