Repository: sonatype-nexus-community/nancy
Pull request #106 (2020-04-01 16:55:22)

Check for existence of unmarshalled internal ID before returning
Basically we need to check if the unmarshall was successful, when sending a public application ID to Nexus IQ Server

This pull request makes the following changes:
* Quick logic check before returning
* Add some logging around the check to help someone find the issue later

cc @bhamail / @DarthHater



Patch:
diff --git a/iq/iq.go b/iq/iq.go
index 636d885..1ac2ed9 100644
--- a/iq/iq.go
+++ b/iq/iq.go
@@ -77,10 +77,10 @@ func AuditPackages(purls []string, applicationID string, config configuration.Iq
 		warnUserOfBadLifeChoices()
 	}
 
-	internalID := getInternalApplicationID(applicationID)
-	if internalID == "" {
+	internalID, err := getInternalApplicationID(applicationID)
+	if internalID == "" && err != nil {
 		LogLady.Error("Internal ID not obtained from Nexus IQ")
-		return statusURLResp, fmt.Errorf("Internal ID for %s could not be found, or Nexus IQ Server is down", applicationID)
+		return statusURLResp, err
 	}
 
 	resultsFromOssIndex, err := ossindex.AuditPackages(purls)
@@ -119,7 +119,7 @@ func AuditPackages(purls []string, applicationID string, config configuration.Iq
 	return statusURLResp, nil
 }
 
-func getInternalApplicationID(applicationID string) (internalID string) {
+func getInternalApplicationID(applicationID string) (string, error) {
 	client := &http.Client{}
 
 	req, err := http.NewRequest(
@@ -142,9 +142,25 @@ func getInternalApplicationID(applicationID string) (internalID string) {
 
 		var response applicationResponse
 		json.Unmarshal(bodyBytes, &response)
-		return response.Applications[0].ID
+
+		if response.Applications != nil && len(response.Applications) > 0 {
+			LogLady.WithFields(logrus.Fields{
+				"internal_id": response.Applications[0].ID,
+			}).Debug("Retrieved internal ID from Nexus IQ Server")
+
+			return response.Applications[0].ID, nil
+		}
+
+		LogLady.WithFields(logrus.Fields{
+			"application_id": applicationID,
+		}).Error("Unable to retrieve an internal ID for the specified public application ID")
+
+		return "", fmt.Errorf("Unable to retrieve an internal ID for the specified public application ID: %s", applicationID)
 	}
-	return ""
+	LogLady.WithFields(logrus.Fields{
+		"status_code": resp.StatusCode,
+	}).Error("Error communicating with Nexus IQ Server application endpoint")
+	return "", fmt.Errorf("Unable to communicate with Nexus IQ Server, status code returned is: %d", resp.StatusCode)
 }
 
 func submitToThirdPartyAPI(sbom string, internalID string) string {


Test patch:
diff --git a/iq/iq_test.go b/iq/iq_test.go
index 85101bf..a562232 100644
--- a/iq/iq_test.go
+++ b/iq/iq_test.go
@@ -103,6 +103,27 @@ func TestAuditPackages(t *testing.T) {
 	assert.Equal(t, result, statusExpected)
 }
 
+func TestAuditPackagesIqCannotLocateApplicationID(t *testing.T) {
+	expectedError := "Unable to retrieve an internal ID for the specified public application ID: testapp"
+	httpmock.Activate()
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://sillyplace.com:8090/api/v2/applications?publicId=testapp",
+		httpmock.NewBytesResponder(200, []byte(`{ "applications": [] }`)))
+
+	var purls []string
+	purls = append(purls, "pkg:golang/github.com/go-yaml/yaml@v2.2.2")
+	purls = append(purls, "pkg:golang/golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2")
+
+	_, err := AuditPackages(purls, "testapp", setupIqConfiguration())
+	if err == nil {
+		t.Errorf("err should not be nil, expected an err with the following text: %s", expectedError)
+	}
+	if err.Error() != expectedError {
+		t.Errorf("Error returned is not as expected. Expected: %s but got: %s", expectedError, err.Error())
+	}
+}
+
 func TestAuditPackagesIqDownOrUnreachable(t *testing.T) {
 	httpmock.Activate()
 	defer httpmock.DeactivateAndReset()