+
+
+
-
-
-
-
-
-
-
-
- Open NVD Calculator in a new tab
-
- {{ cvssVectorString }}
-
-
-
-
-
-
-
-
-
-
-
- Exploit Prediction Scoring System
-
-
-
-
-
-
-
-
-
- Advisory provided no reviewed details.
-
-
-
-
-
-
-
-
-
-
-
Risk Scores
-
-
-
-
-
{{ props.finding.confidenceLevel }}
-
-
-
-
-
-
- {{ props.finding.confidenceRationale.join(`\n\n`) }}
-
-
-
-
-
-
-
-
-
- SSVC: {{ props.currentTriage?.ssvc }}
-
+
+
+
+
+ CVSS Calculator
+
+
+ {{ cvssScoreCalc || 0 }}
+
-
-
-
-
-
-
-
-
-
- CVSS Calculator
-
-
- {{ cvssScoreCalc || 0 }}
-
-
-
-
-
-
-
- CVSS v3.1
- CVSS v4.0
-
-
-
-
-
-
-
-
-
-
- Base Metrics
-
-
-
-
- Attack Vector (AV)
-
-
- {{ option.text }}
-
-
-
-
-
-
- Attack Complexity (AC)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Privileges Required (PR)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- User Interaction (UI)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Scope (S)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
-
- Impact Metrics
-
-
-
-
- Confidentiality (C)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Integrity (I)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Availability (A)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Temporal Score Metrics
-
-
-
- Exploit Code Maturity
- (E)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Remediation Level (RL)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Report Confidence (RC)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Environmental Score Metrics
-
-
-
- Attack Vector (MAV)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Attack Complexity (MAC)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Privileges Required
- (MPR)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- User Interaction (MUI)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Scope (MS)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Confidentiality Impact
- (MC)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Integrity Impact (MI)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Availability Impact (MA)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Confidentiality
- Requirement (CR)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Integrity Requirement
- (IR)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Availability Requirement
- (AR)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Exploitability Metrics
-
-
-
- Attack
- Vector (AV)
-
-
- {{ option.text }}
-
-
-
-
-
- Attack
- Complexity (AC)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Attack
- Requirements (AT)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Privileges Required (PR)
-
-
-
- {{ option.text }}
-
-
-
-
-
- User Interaction (UI)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Vulnerable System Impact
- Metrics
-
-
-
- Confidentiality (VC)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Integrity (VI)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Availability (VA)
-
-
-
- {{ option.text }}
-
-
-
-
- Subsequent System Impact
- Metrics
-
-
-
- Confidentiality (SC)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Integrity (SI)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Availability (SA)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Supplemental Metrics
-
-
-
- Safety (S)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Automatable (AU)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Recovery (R)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Value Density (V)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Vulnerability Response
- Effort
- (RE)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Provider Urgency (U)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Environmental (Modified Base
- Metrics)
-
-
-
- Attack Vector (MAV)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Attack Complexity (MAC)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Attack Requirements
- (MAT)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Privileges Required
- (MPR)
-
-
-
- {{ option.text }}
-
-
-
-
-
- User Interaction (MUI)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Confidentiality (MVC)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Integrity (MVI)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Availability (MVA)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Confidentiality (MSC)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Integrity (MSI)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Availability (MSA)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Environmental (Security
- Requirements)
-
-
-
- Confidentiality
- Requirements
- (CR)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Integrity Requirements
- (IR)
-
-
-
- {{ option.text }}
-
-
-
-
-
- Availability
- Requirements (AR)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
- Threat Metrics
-
-
-
- Exploit Maturity (E)
-
-
-
- {{ option.text }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Save
-
-
- Cancel
-
-
-
-
-
-
-
-
CVSS
-
+
+
+
+
+ CVSS v3.1
+ CVSS v4.0
+
+
+
+
+
+
+
+
+
+
+ Base Metrics
+
+
+
+
+ Attack Vector (AV)
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Attack Complexity (AC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Privileges Required (PR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ User Interaction (UI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Scope (S)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+
+ Impact Metrics
+
+
+
+
+ Confidentiality (C)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Integrity (I)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Availability (A)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Temporal Score Metrics
+
+
+
+ Exploit Code Maturity
+ (E)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Remediation Level (RL)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Report Confidence (RC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Environmental Score Metrics
+
+
+
+ Attack Vector (MAV)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Attack Complexity (MAC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Privileges Required
+ (MPR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ User Interaction (MUI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Scope (MS)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Confidentiality Impact
+ (MC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Integrity Impact (MI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Availability Impact (MA)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Confidentiality
+ Requirement (CR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Integrity Requirement
+ (IR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Availability Requirement
+ (AR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Exploitability Metrics
+
+
+
+ Attack
+ Vector (AV)
+
+
+ {{ option.text }}
+
+
+
+
+
+ Attack
+ Complexity (AC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Attack
+ Requirements (AT)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Privileges Required (PR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ User Interaction (UI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Vulnerable System Impact
+ Metrics
+
+
+
+ Confidentiality (VC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Integrity (VI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Availability (VA)
+
+
+
+ {{ option.text }}
+
+
+
+
+ Subsequent System Impact
+ Metrics
+
+
+
+ Confidentiality (SC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Integrity (SI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Availability (SA)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Supplemental Metrics
+
+
+
+ Safety (S)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Automatable (AU)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Recovery (R)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Value Density (V)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Vulnerability Response
+ Effort
+ (RE)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Provider Urgency (U)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Environmental (Modified Base
+ Metrics)
+
+
+
+ Attack Vector (MAV)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Attack Complexity (MAC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Attack Requirements
+ (MAT)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Privileges Required
+ (MPR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ User Interaction (MUI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Confidentiality (MVC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Integrity (MVI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Availability (MVA)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Confidentiality (MSC)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Integrity (MSI)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Availability (MSA)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Environmental (Security
+ Requirements)
+
+
+
+ Confidentiality
+ Requirements
+ (CR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Integrity Requirements
+ (IR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+ Availability
+ Requirements (AR)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+ Threat Metrics
+
+
+
+ Exploit Maturity (E)
+
+
+
+ {{ option.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+ Cancel
+
+
+
+
+
+
- v{{ cvssVersion }}
-
-
-
{{
- cvssScore
- }} / 10.0
-
-
-
-
-
-
-
- EPSS Score
- {{
- parseFloat(props.currentTriage.epssScore).toFixed(5)
- }}
-
-
+
+
+
CVSS
+
-
-
-
-
-
-
- EPSS Percentile
- {{
- parseFloat(props.currentTriage.epssPercentile).toFixed(5)
- }}%
-
-
+ v{{ cvssVersion }}
+
+ {{
+ cvssScore
+ }} / 10.0
+
+
+
-
-
+
+
+
+ EPSS Score
+ {{
+ parseFloat(props.currentTriage.epssScore).toFixed(5)
+ }}
+
+
+
+
-
+
-
-
-
-
-
-
- Vulnerable Range
-
-
- {{ props.finding.vulnerableVersionRange }}
-
+
+ EPSS Percentile
+ {{
+ parseFloat(props.currentTriage.epssPercentile).toFixed(5)
+ }}%
+
+
+
-
-
-
-
- {{ version }}
-
-
-
- (Current)
-
-
- (Fix)
-
-
+
+
+
+
+
+
+
+
+
+ Vulnerable Range
+
+
+ {{ props.finding.vulnerableVersionRange }}
+
+
+
+
+
+
+ {{ version }}
+
+
+
+ (Current)
+
+
+ (Fix)
+
+
+ Vulnerable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Vulnerable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ event.value }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ ref }}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ ref }}
+
+
+
+
+
-
-
-
- {{ exploit }}
-
-
-
-
-
-
-
-
-
-
+ />
+
+ {{ exploit }}
+
+
+
+
+
+
+
+
diff --git a/src/finding.js b/src/finding.js
index c22a273..6788738 100644
--- a/src/finding.js
+++ b/src/finding.js
@@ -822,3 +822,212 @@ export const evaluateAdvisoryConfidence = advisory => {
evaluations,
};
}
+
+// Helper function to create PURL from download location
+export function createPurlFromUrl(location, name, version) {
+ if (!location || location === 'NOASSERTION') return null
+
+ try {
+ const url = new URL(location.replace(/^git\+/, ''))
+ const [owner, repo] = url.pathname.split('/').filter(Boolean)
+
+ // If it's a GitHub URL
+ if (url.hostname === 'github.com') {
+ return `pkg:github/${owner}/${repo}@${version}`
+ }
+
+ // For git URLs without specific host
+ if (location.startsWith('git://')) {
+ return `pkg:git/${owner}/${repo}@${version}`
+ }
+
+ // Generic URL
+ return `pkg:generic/${name}@${version}?download_url=${encodeURIComponent(location)}`
+
+ } catch (e) {
+ // If URL parsing fails, return null
+ return null
+ }
+}
+
+// Helper function to extract ecosystem from purl
+export function getEcosystemFromPurl(purl) {
+ if (!purl) return null
+ const match = purl.match(/pkg:([^/]+)/)
+ return match ? match[1] : null
+}
+
+// Helper function to extract license from SPDX package
+export function extractLicense(pkg) {
+ if (pkg?.licenseDeclared) {
+ return pkg.licenseDeclared
+ }
+ if (pkg?.licenseInfoFromFiles && pkg.licenseInfoFromFiles.length > 0) {
+ return pkg.licenseInfoFromFiles.join(',')
+ }
+
+ return null
+}
+
+// Helper function to extract name and version from SPDXID
+export function parsePackageRef(spdxId, name) {
+ // Format is typically "SPDXRef-name-version"
+ const match = spdxId.match(/SPDXRef-(.+)-([0-9].+)$/)
+ if (!match) return {
+ name,
+ version: spdxId.replace(`SPDXRef-${name}-`, '')
+ }
+
+ return {
+ name: match[1],
+ version: match[2]
+ }
+}
+
+export function parseSPDXComponents(spdxJson) {
+ const components = new Map()
+ const relationships = []
+ const packageEcosystem = 'generic' // TODO: There may be a SPDX solution
+
+ // First pass: Create all component records
+ spdxJson.packages.forEach(pkg => {
+ const parsedRef = parsePackageRef(pkg.SPDXID);
+ if (!parsedRef) return;
+
+ const { name, version } = parsedRef;
+ const license = extractLicense(pkg)
+
+ components.set(pkg.SPDXID, {
+ uuid: crypto.randomUUID(),
+ name,
+ version,
+ license,
+ packageEcosystem,
+ isTransitive: 1, // Default all to transitive
+ isDirect: 0, // We'll update direct deps later
+ })
+ })
+
+ // Second pass: Process relationships
+ if (spdxJson.relationships) {
+ spdxJson.relationships.forEach(rel => {
+ // Only process DEPENDS_ON relationships
+ if (rel.relationshipType !== 'DEPENDS_ON') return;
+
+ const sourceComp = components.get(rel.spdxElementId)
+ const targetComp = components.get(rel.relatedSpdxElement)
+
+ if (!sourceComp || !targetComp) return;
+
+ // If this is a dependency of the root package, mark as direct
+ if (sourceComp.name === spdxJson.name) {
+ targetComp.isDirect = 1
+ targetComp.isTransitive = 0
+ }
+
+ relationships.push({
+ uuid: crypto.randomUUID(),
+ dependsOnUuid: targetComp.uuid,
+ name: targetComp.name,
+ version: targetComp.version,
+ license: targetComp.license,
+ packageEcosystem: targetComp.packageEcosystem,
+ isTransitive: targetComp.isTransitive,
+ isDirect: targetComp.isDirect,
+ })
+ })
+ }
+
+ return relationships
+}
+
+export function parseCycloneDXComponents(cdxJson) {
+ const components = new Map()
+ const dependencies = []
+ const rootPackage = cdxJson.metadata.component
+
+ // First pass: Create all component records
+ cdxJson.components.forEach(component => {
+ if (!component?.['bom-ref']) {
+ console.log('bom-ref missing', component)
+ return;
+ }
+
+ const packageEcosystem = getEcosystemFromPurl(component.purl)
+ const name = component?.group ? [component.group, component.name].join('/') : component.name
+ const license = component.licenses.filter(l => l?.license?.id).map(l => l.license.id).join(',')
+
+ components.set(component['bom-ref'], {
+ uuid: crypto.randomUUID(),
+ name,
+ version: component.version,
+ license,
+ packageEcosystem,
+ isTransitive: 1, // Default all to transitive
+ isDirect: 0, // We'll update direct deps later
+ })
+ })
+
+ // Add root package if not already present
+ const rootRef = rootPackage['bom-ref']
+ if (!components.has(rootRef)) {
+ components.set(rootRef, {
+ uuid: crypto.randomUUID(),
+ name: rootPackage.name,
+ version: rootPackage.version,
+ license: null,
+ packageEcosystem: getEcosystemFromPurl(rootPackage.purl),
+ isTransitive: 0,
+ isDirect: 0,
+ })
+ }
+
+ // Second pass: Process dependencies
+ cdxJson.dependencies.forEach(dep => {
+ const parentRef = dep.ref
+ const parentComponent = components.get(parentRef)
+
+ if (!parentComponent) return;
+
+ // If this is the root package's dependencies, mark them as direct
+ if (parentRef === rootRef) {
+ dep.dependsOn?.forEach(childRef => {
+ const childComponent = components.get(childRef)
+ if (childComponent) {
+ childComponent.isDirect = 1
+ childComponent.isTransitive = 0
+
+ dependencies.push({
+ uuid: crypto.randomUUID(),
+ dependsOnUuid: childComponent.uuid,
+ name: childComponent.name,
+ version: childComponent.version,
+ license: childComponent.license,
+ packageEcosystem: childComponent.packageEcosystem,
+ isTransitive: 0,
+ isDirect: 1,
+ })
+ }
+ })
+ } else {
+ // Process transitive dependencies
+ dep.dependsOn?.forEach(childRef => {
+ const childComponent = components.get(childRef);
+ if (childComponent) {
+ dependencies.push({
+ uuid: crypto.randomUUID(),
+ dependsOnUuid: childComponent.uuid,
+ name: childComponent.name,
+ version: childComponent.version,
+ license: childComponent.license,
+ packageEcosystem: childComponent.packageEcosystem,
+ isTransitive: 1,
+ isDirect: 0,
+ })
+ }
+ })
+ }
+ })
+
+ return dependencies
+}
diff --git a/src/pages/Artifacts.vue b/src/pages/Artifacts.vue
index 6f5d62a..17816c3 100644
--- a/src/pages/Artifacts.vue
+++ b/src/pages/Artifacts.vue
@@ -154,17 +154,24 @@ class Controller {
if (isSARIF(json)) {
sarif.push(json)
}
- } catch (e) { }
+ } catch (e) {
+ console.log(json, e)
+ }
try {
if (isSPDX(json)) {
spdx.push(json)
}
- } catch (e) { }
+ } catch (e) {
+ console.log(json, e)
+ }
try {
if (isCDX(json)) {
cdx.push(json)
}
- } catch (e) { }
+ } catch (e) {
+ console.log(json, e)
+ // state.uploadError = typeof e === "string" ? e : `${e.code} ${e.message}`
+ }
}
}
let success = false
@@ -492,7 +499,7 @@ function updateArtifactsFromFiles(files) {
+import DependencyGraph from '@/components/DependencyGraph.vue';
import Finding from '@/components/Finding.vue';
import { useMemberStore } from '@/stores/member';
import { Client, getPastelColor, VexAnalysisState } from '@/utils';
@@ -284,7 +285,14 @@ onBeforeRouteUpdate(async (to, from) => {
- dependencies
+
+
diff --git a/src/utils.js b/src/utils.js
index 906e822..a8dd02e 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1597,8 +1597,7 @@ function validCdxComponent(o, specVersion) {
if (typeof o?.name === 'undefined' ||
typeof o?.version === 'undefined' ||
typeof o?.purl === 'undefined' ||
- typeof o?.['bom-ref'] === 'undefined' ||
- typeof o?.externalReferences === 'undefined'
+ typeof o?.['bom-ref'] === 'undefined'
) {
console.log(o)
return false
@@ -1607,8 +1606,7 @@ function validCdxComponent(o, specVersion) {
if (typeof o?.name === 'undefined' ||
typeof o?.version === 'undefined' ||
typeof o?.purl === 'undefined' ||
- typeof o?.['bom-ref'] === 'undefined' ||
- typeof o?.externalReferences === 'undefined'
+ typeof o?.['bom-ref'] === 'undefined'
) {
console.log(o)
return false
@@ -1617,14 +1615,12 @@ function validCdxComponent(o, specVersion) {
if (typeof o?.name === 'undefined' ||
typeof o?.version === 'undefined' ||
typeof o?.purl === 'undefined' ||
- typeof o?.['bom-ref'] === 'undefined' ||
- typeof o?.externalReferences === 'undefined' ||
- !o.externalReferences.length
+ typeof o?.['bom-ref'] === 'undefined'
) {
console.log(o)
return false
}
- for (const ref of o.externalReferences) {
+ for (const ref of o?.externalReferences || []) {
if (typeof ref?.type === 'undefined' || (ref.type === 'distribution' && !ref?.hashes?.length)) {
console.log(ref)
return false
@@ -1634,9 +1630,7 @@ function validCdxComponent(o, specVersion) {
return true
}
function validCdxDependency(o) {
- if (typeof o?.ref === 'undefined' ||
- typeof o?.dependsOn === 'undefined'
- ) {
+ if (typeof o?.ref === 'undefined') {
return false
}
return true
@@ -1660,8 +1654,8 @@ export const isCDX = input => {
} else {
cdx = Object.assign({}, input)
}
- if (typeof cdx?.specVersion === 'undefined' || typeof cdx?.serialNumber === 'undefined') {
- throw 'Missing specVersion or serialNumber'
+ if (typeof cdx?.specVersion === 'undefined') {
+ throw 'Missing specVersion'
}
if (!supportedVersions.includes(cdx?.specVersion)) {
throw `Provided CycloneDX version ${cdx?.specVersion} is not supported. Must be one of: ${supportedVersions}`