\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7 \8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh \00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€ \00 $\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95 59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9 C/// File Manager

File Manager

Path: /opt/alt/alt-nodejs18/root/lib/node_modules/npm/lib/utils/

Viewing File: sbom-spdx.js

const crypto = require('node:crypto')
const normalizeData = require('normalize-package-data')
const npa = require('npm-package-arg')
const ssri = require('ssri')

const SPDX_SCHEMA_VERSION = 'SPDX-2.3'
const SPDX_DATA_LICENSE = 'CC0-1.0'
const SPDX_IDENTIFER = 'SPDXRef-DOCUMENT'

const NO_ASSERTION = 'NOASSERTION'

const REL_DESCRIBES = 'DESCRIBES'
const REL_PREREQ = 'PREREQUISITE_FOR'
const REL_OPTIONAL = 'OPTIONAL_DEPENDENCY_OF'
const REL_DEV = 'DEV_DEPENDENCY_OF'
const REL_DEP = 'DEPENDENCY_OF'

const REF_CAT_PACKAGE_MANAGER = 'PACKAGE-MANAGER'
const REF_TYPE_PURL = 'purl'

const spdxOutput = ({ npm, nodes, packageType }) => {
  const rootNode = nodes.find(node => node.isRoot)
  const childNodes = nodes.filter(node => !node.isRoot && !node.isLink)
  const rootID = rootNode.pkgid
  const uuid = crypto.randomUUID()
  const ns = `http://spdx.org/spdxdocs/${npa(rootID).escapedName}-${rootNode.version}-${uuid}`

  const relationships = []
  const seen = new Set()
  for (let node of nodes) {
    if (node.isLink) {
      node = node.target
    }

    if (seen.has(node)) {
      continue
    }
    seen.add(node)

    const rels = [...node.edgesOut.values()]
      // Filter out edges that are linking to nodes not in the list
      .filter(edge => nodes.find(n => n === edge.to))
      .map(edge => toSpdxRelationship(node, edge))
      .filter(rel => rel)

    relationships.push(...rels)
  }

  const extraRelationships = nodes.filter(node => node.extraneous)
    .map(node => toSpdxRelationship(rootNode, { to: node, type: 'optional' }))

  relationships.push(...extraRelationships)

  const bom = {
    spdxVersion: SPDX_SCHEMA_VERSION,
    dataLicense: SPDX_DATA_LICENSE,
    SPDXID: SPDX_IDENTIFER,
    name: rootID,
    documentNamespace: ns,
    creationInfo: {
      created: new Date().toISOString(),
      creators: [
        `Tool: npm/cli-${npm.version}`,
      ],
    },
    documentDescribes: [toSpdxID(rootNode)],
    packages: [toSpdxItem(rootNode, { packageType }), ...childNodes.map(toSpdxItem)],
    relationships: [
      {
        spdxElementId: SPDX_IDENTIFER,
        relatedSpdxElement: toSpdxID(rootNode),
        relationshipType: REL_DESCRIBES,
      },
      ...relationships,
    ],
  }

  return bom
}

const toSpdxItem = (node, { packageType }) => {
  normalizeData(node.package)

  // Calculate purl from package spec
  let spec = npa(node.pkgid)
  spec = (spec.type === 'alias') ? spec.subSpec : spec
  const purl = npa.toPurl(spec) + (isGitNode(node) ? `?vcs_url=${node.resolved}` : '')

  /* For workspace nodes, use the location from their linkNode */
  let location = node.location
  if (node.isWorkspace && node.linksIn.size > 0) {
    location = node.linksIn.values().next().value.location
  }

  let license = node.package?.license
  if (license) {
    if (typeof license === 'object') {
      license = license.type
    }
  }

  const pkg = {
    name: node.packageName,
    SPDXID: toSpdxID(node),
    versionInfo: node.version,
    packageFileName: location,
    description: node.package?.description || undefined,
    primaryPackagePurpose: packageType ? packageType.toUpperCase() : undefined,
    downloadLocation: (node.isLink ? undefined : node.resolved) || NO_ASSERTION,
    filesAnalyzed: false,
    homepage: node.package?.homepage || NO_ASSERTION,
    licenseDeclared: license || NO_ASSERTION,
    externalRefs: [
      {
        referenceCategory: REF_CAT_PACKAGE_MANAGER,
        referenceType: REF_TYPE_PURL,
        referenceLocator: purl,
      },
    ],
  }

  if (node.integrity) {
    const integrity = ssri.parse(node.integrity, { single: true })
    pkg.checksums = [{
      algorithm: integrity.algorithm.toUpperCase(),
      checksumValue: integrity.hexDigest(),
    }]
  }
  return pkg
}

const toSpdxRelationship = (node, edge) => {
  let type
  switch (edge.type) {
    case 'peer':
      type = REL_PREREQ
      break
    case 'optional':
      type = REL_OPTIONAL
      break
    case 'dev':
      type = REL_DEV
      break
    default:
      type = REL_DEP
  }

  return {
    spdxElementId: toSpdxID(edge.to),
    relatedSpdxElement: toSpdxID(node),
    relationshipType: type,
  }
}

const toSpdxID = (node) => {
  let name = node.packageName

  // Strip leading @ for scoped packages
  name = name.replace(/^@/, '')

  // Replace slashes with dots
  name = name.replace(/\//g, '.')

  return `SPDXRef-Package-${name}-${node.version}`
}

const isGitNode = (node) => {
  if (!node.resolved) {
    return
  }

  try {
    const { type } = npa(node.resolved)
    return type === 'git' || type === 'hosted'
  } catch (err) {
    /* istanbul ignore next */
    return false
  }
}

module.exports = { spdxOutput }