From 4f578adff8e1d2b39f7e6c0cddd4d2ea47a7b483 Mon Sep 17 00:00:00 2001 From: andreas-hilti <69210561+andreas-hilti@users.noreply.github.com> Date: Sun, 11 Aug 2024 23:21:53 +0200 Subject: [PATCH] Improve JSON validation (#317) Improve JSON validation, in particular if you don't specify the specification version. This helps to address https://github.com/CycloneDX/cyclonedx-cli/issues/221. On the given example, it will result in: ``` cyclonedx.exe validate --input-file badType_log4j_2.17.2_cyclonedx_1.3_sbom.json Validation failed: Value is "boolean" but should be "string" #/properties/dependencies/items/$ref/properties/ref/type On instance: #/dependencies/0/ref: True Unable to validate against any JSON schemas. BOM is not valid. ``` Signed-off-by: andreas hilti Co-authored-by: Michael Tsfoni <80639729+mtsfoni@users.noreply.github.com> --- src/cyclonedx/Commands/ValidateCommand.cs | 23 +-- .../Resources/bom-1.4-invalid.json | 178 ++++++++++++++++++ tests/cyclonedx.tests/ValidateTests.cs | 3 + 3 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 tests/cyclonedx.tests/Resources/bom-1.4-invalid.json diff --git a/src/cyclonedx/Commands/ValidateCommand.cs b/src/cyclonedx/Commands/ValidateCommand.cs index 75075b3..4aa1953 100644 --- a/src/cyclonedx/Commands/ValidateCommand.cs +++ b/src/cyclonedx/Commands/ValidateCommand.cs @@ -20,6 +20,7 @@ using System.CommandLine.Invocation; using System.Diagnostics.Contracts; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; using CycloneDX.Models; @@ -106,25 +107,17 @@ public static async Task Validate(ValidateCommandOptions options) } else if (options.InputFormat == ValidationBomFormat.json) { - validationResult = Json.Validator.Validate(inputBom, SpecificationVersion.v1_5); - if (!validationResult.Valid) - { - validationResult = Json.Validator.Validate(inputBom, SpecificationVersion.v1_4); - } - if (!validationResult.Valid) - { - validationResult = Json.Validator.Validate(inputBom, SpecificationVersion.v1_3); - } - if (!validationResult.Valid) - { - validationResult = Json.Validator.Validate(inputBom, SpecificationVersion.v1_2); - } + validationResult = Json.Validator.Validate(inputBom); + if (!validationResult.Valid) { - validationResult.Messages = new List + if (validationResult.Messages == null) { + validationResult.Messages = new List(); + } + validationResult.Messages = validationResult.Messages.Append( "Unable to validate against any JSON schemas." - }; + ); } } diff --git a/tests/cyclonedx.tests/Resources/bom-1.4-invalid.json b/tests/cyclonedx.tests/Resources/bom-1.4-invalid.json new file mode 100644 index 0000000..5299fcf --- /dev/null +++ b/tests/cyclonedx.tests/Resources/bom-1.4-invalid.json @@ -0,0 +1,178 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "invalidAttribute": 2, + "metadata": { + "timestamp": "2020-04-13T20:20:39+00:00", + "tools": [ + { + "vendor": "Awesome Vendor", + "name": "Awesome Tool", + "version": "9.1.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "25ed8e31b995bb927966616df2a42b979a2717f0" + }, + { + "alg": "SHA-256", + "content": "a74f733635a19aefb1f73e5947cef59cd7440c6952ef0f03d09d974274cbd6df" + } + ] + } + ], + "authors": [ + { + "name": "Samantha Wright", + "email": "samantha.wright@example.com", + "phone": "800-555-1212" + } + ], + "component": { + "type": "application", + "author": "Acme Super Heros", + "name": "Acme Application", + "version": "9.1.1", + "swid": { + "tagId": "swidgen-242eb18a-503e-ca37-393b-cf156ef09691_9.1.1", + "name": "Acme Application", + "version": "9.1.1", + "text": { + "contentType": "text/xml", + "encoding": "base64", + "content": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg==" + } + } + }, + "manufacture": { + "name": "Acme, Inc.", + "url": [ + "https://example.com" + ], + "contact": [ + { + "name": "Acme Professional Services", + "email": "professional.services@example.com" + } + ] + }, + "supplier": { + "name": "Acme, Inc.", + "url": [ + "https://example.com" + ], + "contact": [ + { + "name": "Acme Distribution", + "email": "distribution@example.com" + } + ] + } + }, + "components": [ + { + "bom-ref": "pkg:npm/acme/component@1.0.0", + "type": "library", + "publisher": "Acme Inc", + "group": "com.acme", + "name": "tomcat-catalina", + "version": "9.0.14", + "hashes": [ + { + "alg": "MD5", + "content": "3942447fac867ae5cdb3229b658f4d48" + }, + { + "alg": "SHA-1", + "content": "e6b1000b94e835ffd37f4c6dcbdad43f4b48a02a" + }, + { + "alg": "SHA-256", + "content": "f498a8ff2dd007e29c2074f5e4b01a9a01775c3ff3aeaf6906ea503bc5791b7b" + }, + { + "alg": "SHA-512", + "content": "e8f33e424f3f4ed6db76a482fde1a5298970e442c531729119e37991884bdffab4f9426b7ee11fccd074eeda0634d71697d6f88a460dce0ac8d627a29f7d1282" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0", + "text": { + "contentType": "text/plain", + "encoding": "base64", + "content": "License text here" + }, + "url": "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + ], + "purl": "pkg:npm/acme/component@1.0.0", + "pedigree": { + "ancestors": [ + { + "type": "library", + "publisher": "Acme Inc", + "group": "com.acme", + "name": "tomcat-catalina", + "version": "9.0.14" + }, + { + "type": "library", + "publisher": "Acme Inc", + "group": "com.acme", + "name": "tomcat-catalina", + "version": "9.0.14" + } + ], + "commits": [ + { + "uid": "123", + "url": "", + "author": { + "timestamp": "2018-11-13T20:20:39+00:00", + "name": "", + "email": "example@example.com" + } + } + ] + } + }, + { + "type": "library", + "supplier": { + "name": "Example, Inc.", + "url": [ + "https://example.com", + "https://example.net" + ], + "contact": [ + { + "name": "Example Support AMER Distribution", + "email": "support@example.com", + "phone": "800-555-1212" + }, + { + "name": "Example Support APAC", + "email": "support@apac.example.com" + } + ] + }, + "author": "Example Super Heros", + "group": "org.example", + "name": "mylibrary", + "version": "1.0.0" + } + ], + "dependencies": [ + { + "ref": "pkg:npm/acme/component@1.0.0", + "dependsOn": [ + "pkg:npm/acme/component@1.0.0" + ] + } + ] +} diff --git a/tests/cyclonedx.tests/ValidateTests.cs b/tests/cyclonedx.tests/ValidateTests.cs index d40ee2f..a7a728b 100644 --- a/tests/cyclonedx.tests/ValidateTests.cs +++ b/tests/cyclonedx.tests/ValidateTests.cs @@ -58,6 +58,9 @@ public class ValidateTests [InlineData("bom-1.4.json", ValidationBomFormat.autodetect, null, true)] [InlineData("bom-1.4.json", ValidationBomFormat.json, SpecificationVersion.v1_4, true)] + + [InlineData("bom-1.4-invalid.json", ValidationBomFormat.autodetect, null, false)] + [InlineData("bom-1.4-invalid.json", ValidationBomFormat.json, SpecificationVersion.v1_4, false)] [InlineData("bom-1.5.json", ValidationBomFormat.autodetect, null, true)] [InlineData("bom-1.5.json", ValidationBomFormat.json, SpecificationVersion.v1_5, true)]