From 90003f9fa386c6fc77b2cc3b93c1f1af595a9419 Mon Sep 17 00:00:00 2001
From: Anton Bauhofer <anton.bauhofer@tngtech.com>
Date: Thu, 5 Oct 2023 18:01:57 +0200
Subject: [PATCH] Fix SPDX relationships in sbom

This adjusts the relationships to match the explanations at https://spdx.github.io/spdx-spec/v2.3/relationships-between-SPDX-elements/

Fixes https://github.com/npm/cli/issues/6867

Signed-off-by: Anton Bauhofer <anton.bauhofer@tngtech.com>
---
 lib/utils/sbom-spdx.js                        | 41 ++++++++++---------
 .../test/lib/commands/sbom.js.test.cjs        | 12 +++---
 .../test/lib/utils/sbom-spdx.js.test.cjs      | 12 +++---
 3 files changed, 33 insertions(+), 32 deletions(-)

diff --git a/lib/utils/sbom-spdx.js b/lib/utils/sbom-spdx.js
index 2664dabd41959..54858d73c3b49 100644
--- a/lib/utils/sbom-spdx.js
+++ b/lib/utils/sbom-spdx.js
@@ -124,31 +124,32 @@ const toSpdxItem = (node, { packageType }) => {
 }
 
 const toSpdxRelationship = (node, edge) => {
+  let type
   switch (edge.type) {
     case 'peer':
-      return {
-        spdxElementId: toSpdxID(node),
-        relatedSpdxElement: toSpdxID(edge.to),
-        relationshipType: REL_PREREQ,
-      }
+      type = REL_PREREQ
+      break
     case 'optional':
-      return {
-        spdxElementId: toSpdxID(edge.to),
-        relatedSpdxElement: toSpdxID(node),
-        relationshipType: REL_OPTIONAL,
-      }
+      type = REL_OPTIONAL
+      break
     case 'dev':
-      return {
-        spdxElementId: toSpdxID(edge.to),
-        relatedSpdxElement: toSpdxID(node),
-        relationshipType: REL_DEV,
-      }
+      type = REL_DEV
+      break
     default:
-      return {
-        spdxElementId: toSpdxID(node),
-        relatedSpdxElement: toSpdxID(edge.to),
-        relationshipType: REL_DEP,
-      }
+      type = REL_DEP
+  }
+
+  let from, to
+  if ([REL_OPTIONAL, REL_DEV].includes(type)) {
+    [from, to] = [edge.to, node]
+  } else {
+    [from, to] = [node, edge.to]
+  }
+
+  return {
+    spdxElementId: toSpdxID(from),
+    relatedSpdxElement: toSpdxID(to),
+    relationshipType: type,
   }
 }
 
diff --git a/tap-snapshots/test/lib/commands/sbom.js.test.cjs b/tap-snapshots/test/lib/commands/sbom.js.test.cjs
index 0079832f7427f..5eedbcad8235b 100644
--- a/tap-snapshots/test/lib/commands/sbom.js.test.cjs
+++ b/tap-snapshots/test/lib/commands/sbom.js.test.cjs
@@ -557,8 +557,8 @@ exports[`test/lib/commands/sbom.js TAP sbom extraneous dep > must match snapshot
       "relationshipType": "DEPENDS_ON"
     },
     {
-      "spdxElementId": "SPDXRef-Package-test-npm-ls-1.0.0",
-      "relatedSpdxElement": "SPDXRef-Package-chai-1.0.0",
+      "spdxElementId": "SPDXRef-Package-chai-1.0.0",
+      "relatedSpdxElement": "SPDXRef-Package-test-npm-ls-1.0.0",
       "relationshipType": "OPTIONAL_DEPENDENCY_OF"
     }
   ]
@@ -730,8 +730,8 @@ exports[`test/lib/commands/sbom.js TAP sbom loading a tree containing workspaces
       "relationshipType": "DEPENDS_ON"
     },
     {
-      "spdxElementId": "SPDXRef-Package-a-1.0.0",
-      "relatedSpdxElement": "SPDXRef-Package-baz-1.0.0",
+      "spdxElementId": "SPDXRef-Package-baz-1.0.0",
+      "relatedSpdxElement": "SPDXRef-Package-a-1.0.0",
       "relationshipType": "DEV_DEPENDENCY_OF"
     },
     {
@@ -1091,8 +1091,8 @@ exports[`test/lib/commands/sbom.js TAP sbom loading a tree containing workspaces
       "relationshipType": "DEPENDS_ON"
     },
     {
-      "spdxElementId": "SPDXRef-Package-a-1.0.0",
-      "relatedSpdxElement": "SPDXRef-Package-baz-1.0.0",
+      "spdxElementId": "SPDXRef-Package-baz-1.0.0",
+      "relatedSpdxElement": "SPDXRef-Package-a-1.0.0",
       "relationshipType": "DEV_DEPENDENCY_OF"
     },
     {
diff --git a/tap-snapshots/test/lib/utils/sbom-spdx.js.test.cjs b/tap-snapshots/test/lib/utils/sbom-spdx.js.test.cjs
index 890bd29b7d263..7baa0d620de36 100644
--- a/tap-snapshots/test/lib/utils/sbom-spdx.js.test.cjs
+++ b/tap-snapshots/test/lib/utils/sbom-spdx.js.test.cjs
@@ -154,13 +154,13 @@ exports[`test/lib/utils/sbom-spdx.js TAP node - with deps > must match snapshot
       "relationshipType": "HAS_PREREQUISITE"
     },
     {
-      "spdxElementId": "SPDXRef-Package-root-1.0.0",
-      "relatedSpdxElement": "SPDXRef-Package-dep2-0.0.2",
+      "spdxElementId": "SPDXRef-Package-dep2-0.0.2",
+      "relatedSpdxElement": "SPDXRef-Package-root-1.0.0",
       "relationshipType": "OPTIONAL_DEPENDENCY_OF"
     },
     {
-      "spdxElementId": "SPDXRef-Package-root-1.0.0",
-      "relatedSpdxElement": "SPDXRef-Package-dep3-0.0.3",
+      "spdxElementId": "SPDXRef-Package-dep3-0.0.3",
+      "relatedSpdxElement": "SPDXRef-Package-root-1.0.0",
       "relationshipType": "DEV_DEPENDENCY_OF"
     },
     {
@@ -174,8 +174,8 @@ exports[`test/lib/utils/sbom-spdx.js TAP node - with deps > must match snapshot
       "relationshipType": "DEPENDS_ON"
     },
     {
-      "spdxElementId": "SPDXRef-Package-root-1.0.0",
-      "relatedSpdxElement": "SPDXRef-Package-dep6-0.0.6",
+      "spdxElementId": "SPDXRef-Package-dep6-0.0.6",
+      "relatedSpdxElement": "SPDXRef-Package-root-1.0.0",
       "relationshipType": "OPTIONAL_DEPENDENCY_OF"
     }
   ]