Repository: CycloneDX/cyclonedx-core-java (java)

Issue:
Deserializing `OrganizationEntity` fails when `name` is not set
Trying to deserialize the following JSON BOM:

```json
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "components": [
    {
      "type": "library",
      "name": "acme-library",
      "licenses": [
        {
          "license": {
            "name": "foo",
            "licensing": {
              "purchaser": {
                "organization": {
                  "contact": [
                    {
                      "name": ""
                    }
                  ]
                }
              }
            }
          }
        }
      ]
    }
  ]
}
```

Fails with:

```
Caused by: java.lang.NullPointerException: Cannot invoke "com.fasterxml.jackson.databind.JsonNode.asText()" because the return value of "com.fasterxml.jackson.databind.JsonNode.get(String)" is null
	at org.cyclonedx.util.deserializer.OrganizationalChoiceDeserializer.deserializeOrganization(OrganizationalChoiceDeserializer.java:54)
	at org.cyclonedx.util.deserializer.OrganizationalChoiceDeserializer.deserialize(OrganizationalChoiceDeserializer.java:45)
	at org.cyclonedx.util.deserializer.OrganizationalChoiceDeserializer.deserialize(OrganizationalChoiceDeserializer.java:32)
```

Because `OrganizationalChoiceDeserializer` assumes `name` to always be present, despite it not being a mandatory field according to the spec:

https://github.com/CycloneDX/cyclonedx-core-java/blob/225e7bfb3e386ace86949a34a87f887597ef88bd/src/main/java/org/cyclonedx/util/deserializer/OrganizationalChoiceDeserializer.java#L52-L54

Pull request:
Fix NPE when deserializing `OrganizationalEntity` without name
Fixes #507

Patch:
diff --git a/src/main/java/org/cyclonedx/util/deserializer/OrganizationalChoiceDeserializer.java b/src/main/java/org/cyclonedx/util/deserializer/OrganizationalChoiceDeserializer.java
index 3a43307e5..451409a50 100644
--- a/src/main/java/org/cyclonedx/util/deserializer/OrganizationalChoiceDeserializer.java
+++ b/src/main/java/org/cyclonedx/util/deserializer/OrganizationalChoiceDeserializer.java
@@ -51,7 +51,9 @@ public class OrganizationalChoiceDeserializer
 
   private OrganizationalEntity deserializeOrganization(JsonParser jp, JsonNode organizationNode) throws JsonProcessingException {
     OrganizationalEntity organization = new OrganizationalEntity();
-    organization.setName(organizationNode.get("name").asText());
+    if (organizationNode.has("name")) {
+      organization.setName(organizationNode.get("name").asText());
+    }
 
     if (organizationNode.has("contact")) {
       JsonNode contactsNode = organizationNode.get("contact");


Test patch:
diff --git a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
index b10d06f87..a4c50f04c 100644
--- a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
+++ b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java
@@ -68,6 +68,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -256,6 +257,18 @@ public class JsonParserTest
         assertEquals(0, bom.getComponents().get(0).getHashes().size());
     }
 
+    @Test
+    public void testIssue507Regression() throws Exception {
+        final Bom bom = getJsonBom("regression/issue507.json");
+        assertThat(bom.getComponents()).hasSize(1);
+        assertThat(bom.getComponents().get(0).getLicenses()).isNotNull();
+        assertThat(bom.getComponents().get(0).getLicenses().getLicenses()).hasSize(1);
+        assertThat(bom.getComponents().get(0).getLicenses().getLicenses().get(0).getLicensing()).isNotNull();
+        assertThat(bom.getComponents().get(0).getLicenses().getLicenses().get(0).getLicensing().getPurchaser()).isNotNull();
+        assertThat(bom.getComponents().get(0).getLicenses().getLicenses().get(0).getLicensing().getPurchaser().getOrganization()).isNotNull();
+        assertThat(bom.getComponents().get(0).getLicenses().getLicenses().get(0).getLicensing().getPurchaser().getOrganization().getContacts()).hasSize(1);
+    }
+
     @Test
     public void schema16_license_id_acknowledgement() throws Exception {
         final Bom bom = getJsonBom("1.6/valid-license-id-1.6.json");
diff --git a/src/test/resources/regression/issue507.json b/src/test/resources/regression/issue507.json
new file mode 100644
index 000000000..6d5b063b0
--- /dev/null
+++ b/src/test/resources/regression/issue507.json
@@ -0,0 +1,28 @@
+{
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.6",
+  "components": [
+    {
+      "type": "library",
+      "name": "acme-library",
+      "licenses": [
+        {
+          "license": {
+            "name": "foo",
+            "licensing": {
+              "purchaser": {
+                "organization": {
+                  "contact": [
+                    {
+                      "name": ""
+                    }
+                  ]
+                }
+              }
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file