Skip to content

Commit

Permalink
fix: artifact UI and cvss
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdlangton committed Oct 27, 2024
1 parent 170cffc commit 57a5bef
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 166 deletions.
4 changes: 2 additions & 2 deletions .repo/scratchad.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
-- ALTER TABLE Finding DROP COLUMN malicious;
-- ALTER TABLE Finding ADD malicious INTEGER;
SELECT *
FROM CVEMetadata
WHERE vectorString IS NOT NULL;
FROM Finding
WHERE cvssVectorString IS NOT NULL;
10 changes: 5 additions & 5 deletions functions/v1/artifact/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,18 @@ export async function onRequestGet(context) {
})

const artifacts = result.map(artifact => {
artifact.spdx = artifact.spdx.sort((a, b) => a.createdAt - b.createdAt)?.pop()
artifact.sarif = artifact.sarif.sort((a, b) => a.createdAt - b.createdAt)?.pop()
artifact.cdx = artifact.cdx.sort((a, b) => a.createdAt - b.createdAt)?.pop()
const vex = artifact.vex.sort((a, b) => a.lastObserved - b.lastObserved)?.pop()
artifact.spdx = artifact.spdx.sort((a, b) => b.createdAt - a.createdAt)?.pop()
artifact.sarif = artifact.sarif.sort((a, b) => b.createdAt - a.createdAt)?.pop()
artifact.cdx = artifact.cdx.sort((a, b) => b.createdAt - a.createdAt)?.pop()
const vex = artifact.vex.sort((a, b) => b.lastObserved - a.lastObserved)?.pop()
if (vex?.finding) {
vex.repoName = vex?.finding?.repoName
vex.source = vex?.finding?.source
vex.findingTitle = vex?.finding?.detectionTitle
delete vex.finding
}
artifact.vex = vex
artifact.downloadLink = artifact.downloadLinks.sort((a, b) => a.id - b.id)?.pop()
artifact.downloadLink = artifact.downloadLinks.sort((a, b) => b.id - a.id)?.pop()
delete artifact.downloadLinks
return artifact
})
Expand Down
56 changes: 30 additions & 26 deletions functions/v1/issue/[uuid].js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export async function onRequestGet(context) {

const cveId = finding.detectionTitle.startsWith('CVE-') ? finding.detectionTitle : osvData?.aliases?.filter(a => a.startsWith('CVE-')).pop()
let cvssVector
let cvssScore
let cvelistv5
let cve
if (cveId) {
Expand All @@ -89,13 +90,13 @@ export async function onRequestGet(context) {
cveMetadata,
containers: { cna, adp }
} = cvelistv5
const cveCvss = findVectorString(cna.metrics)
if (cveCvss.startsWith('CVSS:4/')) {
cvssVector = new CVSS40(cveCvss)
} else if (cveCvss.startsWith('CVSS:3.1/')) {
cvssVector = new CVSS31(cveCvss)
} else if (cveCvss.startsWith('CVSS:3/')) {
cvssVector = new CVSS30(cveCvss)
const cvssVector = findVectorString(cna.metrics)
if (cvssVector.startsWith('CVSS:4.0/')) {
cvssScore = new CVSS40(cvssVector).Score().toString()
} else if (cvssVector.startsWith('CVSS:3.1/')) {
cvssScore = new CVSS31(cvssVector).BaseScore().toString()
} else if (cvssVector.startsWith('CVSS:3.0/')) {
cvssScore = new CVSS30(cvssVector).BaseScore().toString()
}
if (cna?.timeline) {
finding.timelineJSON = JSON.stringify(cna.timeline.map(i => convertIsoDatesToTimestamps(i)))
Expand Down Expand Up @@ -170,10 +171,15 @@ export async function onRequestGet(context) {
epssScore = parseFloat(scores.epss)
epssPercentile = parseFloat(scores.percentile)
}
const cvss4 = osvData?.severity?.filter(i => i.score.startsWith('CVSS:4/'))?.pop()
const cvss31 = osvData?.severity?.filter(i => i.score.startsWith('CVSS:3.1/'))?.pop()
const cvss3 = osvData?.severity?.filter(i => i.score.startsWith('CVSS:3/'))?.pop()
cvssVector = !!cvss4 ? new CVSS40(cvss4.score) : !!cvss31 ? new CVSS31(cvss31.score) : cvss3 ? new CVSS30(cvss3.score) : null
const cvss = {}
if (!cvssVector) {
cvss.v4 = osvData?.severity?.filter(i => i.score.startsWith('CVSS:4/'))?.pop()
cvss.v31 = osvData?.severity?.filter(i => i.score.startsWith('CVSS:3.1/'))?.pop()
cvss.v3 = osvData?.severity?.filter(i => i.score.startsWith('CVSS:3/'))?.pop()
cvssVector = !!cvss.v4 ? cvss.v4.score : !!cvss.v31 ? cvss.v31.score : cvss.v3 ? cvss.v3.score : null
const vector = !!cvss.v4 ? new CVSS40(cvss.v4.score) : !!cvss.v31 ? new CVSS31(cvss.v31.score) : cvss.v3 ? new CVSS30(cvss.v3.score) : null
cvssScore = !!cvss.v4 ? vector.Score().toString() : !!cvss.v31 ? vector.BaseScore().toString() : cvss.v3 ? vector.BaseScore().toString() : null
}
// Decision
// Methodology
// Exploitation
Expand All @@ -186,9 +192,10 @@ export async function onRequestGet(context) {
let { analysisState = 'in_triage', triageAutomated = 0, triagedAt = null, seenAt = null } = finding?.triage || {}
if (
(cvssVector && (
['E:U', 'E:P', 'E:F', 'E:H'].some(substring => cvss3?.score?.includes(substring)) ||
['E:A', 'E:P', 'E:U'].some(substring => cvss4?.score?.includes(substring))
)) || epssPercentile > 0.2
['E:U', 'E:P', 'E:F', 'E:H'].some(substring => cvss.v3?.score?.includes(substring)) ||
['E:U', 'E:P', 'E:F', 'E:H'].some(substring => cvss.v31?.score?.includes(substring)) ||
['E:A', 'E:P', 'E:U'].some(substring => cvss.v4?.score?.includes(substring))
)) || epssPercentile > 0.27
) {
analysisState = 'exploitable'
triageAutomated = 1
Expand All @@ -199,21 +206,18 @@ export async function onRequestGet(context) {
if (seen === 1) {
seenAt = new Date().getTime()
}
let vexExist = true
if (!finding.triage.some(t => t.analysisState === analysisState)) {
vexExist = false
finding.triage.push({
analysisState,
findingUuid: uuid,
createdAt: new Date().getTime(),
lastObserved: new Date().getTime(),
})
}
const vexExist = finding.triage.some(t => t.analysisState === analysisState).length !== 0
let vexData = finding.triage.filter(t => t.analysisState === analysisState).pop()
if (!vexExist) {
vexData.analysisState = analysisState
vexData.findingUuid = finding.uuid
vexData.createdAt = new Date().getTime()
vexData.lastObserved = new Date().getTime()
}
vexData.triageAutomated = triageAutomated
vexData.triagedAt = triagedAt
vexData.cvssVector = !!cvss4 ? cvss4.score : !!cvss31 ? cvss31.score : cvss3 ? cvss3.score : null
vexData.cvssScore = !!cvss4 ? cvssVector.Score().toString() : !!cvss31 ? cvssVector.BaseScore().toString() : cvss3 ? cvssVector.BaseScore().toString() : null
vexData.cvssVector = cvssVector
vexData.cvssScore = cvssScore
if (epssPercentile) {
vexData.epssPercentile = epssPercentile.toString()
}
Expand Down
4 changes: 2 additions & 2 deletions src/layouts/components/DefaultLayoutWithVerticalNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ watch(NotificationsStore, () => {
icon: 'fluent-mdl2:set-action',
to: '/triage',
}" />
<VerticalNavLink :item="{
<!-- <VerticalNavLink :item="{
title: 'Exploitable',
icon: 'eos-icons:critical-bug-outlined',
to: '/triage/exploitable',
}" />
}" /> -->
<VerticalNavLink :item="{
title: 'History',
icon: 'mdi-clipboard-text-history',
Expand Down
170 changes: 74 additions & 96 deletions src/pages/Artifacts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,92 +49,75 @@ class Controller {
let skip = 0
while (hasMore) {
const { data } = await client.get(`/artifact/files?take=${pageSize}&skip=${skip}`)
if (data.ok) {
if (data?.artifacts) {
for (const artifact of data.artifacts) {
const { uuid, downloadLink, type, bomFormat, analysisKey } = artifact
const { contentType, url } = downloadLink
for (let group of state.artifacts) {
const ext = contentType?.includes("json") ? 'json' : 'txt'
let repoName;
let dependencies;
let results;
let versionInfo;
let findingTitle;
let analysis;
let source;
if (artifact?.cdx) {
dependencies = artifact.cdx?.dependenciesCount
repoName = artifact.cdx?.repoName
source = artifact.cdx?.source
versionInfo = `CycloneDX-${artifact.cdx?.cdxVersion}`
}
if (artifact?.spdx) {
dependencies = artifact.spdx?.packagesCount
repoName = artifact.spdx?.repoName
source = artifact.spdx?.source
versionInfo = artifact.spdx?.spdxVersion
if (data.ok && data?.artifacts) {
for (const artifact of data.artifacts) {
const { uuid, downloadLink, type, bomFormat, analysisKey } = artifact
const { contentType, url } = downloadLink
for (let group of state.artifacts) {
const ext = contentType?.includes("json") ? 'json' : 'txt'
let repoName;
let dependencies;
let results;
let versionInfo;
let findingTitle;
let analysis;
let source;
if (artifact?.cdx) {
dependencies = artifact.cdx?.dependenciesCount
repoName = artifact.cdx?.repoName
source = artifact.cdx?.source
versionInfo = `CycloneDX-${artifact.cdx?.cdxVersion}`
}
if (artifact?.spdx) {
dependencies = artifact.spdx?.packagesCount
repoName = artifact.spdx?.repoName
source = artifact.spdx?.source
versionInfo = artifact.spdx?.spdxVersion
}
if (artifact?.sarif) {
results = artifact.sarif?.resultsCount
repoName = artifact.sarif?.fullName
source = artifact.sarif?.source
versionInfo = artifact.sarif?.toolName
if (artifact.sarif?.toolVersion) {
versionInfo = `${versionInfo}-${artifact.sarif?.toolVersion}`
}
if (artifact?.sarif) {
results = artifact.sarif?.resultsCount
repoName = artifact.sarif?.fullName
source = artifact.sarif?.source
versionInfo = artifact.sarif?.toolName
if (artifact.sarif?.toolVersion) {
versionInfo = `${versionInfo}-${artifact.sarif?.toolVersion}`
}
}
if (artifact?.vex) {
findingTitle = artifact.vex?.findingTitle
source = artifact.vex?.source
if (artifact.vex?.analysisState) {
analysis = VexAnalysisState[artifact.vex.analysisState]
}
if (artifact?.vex) {
findingTitle = artifact.vex?.findingTitle
source = artifact.vex?.source
if (artifact.vex?.analysisState) {
analysis = VexAnalysisState[artifact.vex.analysisState]
}
if (artifact.vex?.analysisJustification) {
analysis = `${analysis}, ${VexAnalysisJustification[artifact.vex.analysisJustification]}`
}
if (artifact.vex?.analysisResponse) {
analysis = `${analysis}, ${VexAnalysisResponse[artifact.vex.analysisResponse]}`
}
if (artifact.vex?.analysisJustification) {
analysis = `${analysis}, ${VexAnalysisJustification[artifact.vex.analysisJustification]}`
}
const file = { contentType, ext, lastModified: artifact.date, uuid, source, url, analysis, findingTitle, dependencies, repoName, results, versionInfo }
if (group.title === "SARIF" && contentType?.includes("sarif")) {
file.title = analysisKey || `${uuid}.${ext}`
group = addFileToSourceSubgroup(group, file)
break
} else if (group.title === "VEX" && type === "VEX") {
file.title = artifact.vex.findingTitle
group = addFileToSourceSubgroup(group, file)
break
} else if (group.title === bomFormat) {
if (bomFormat === "CycloneDX") {
file.title = [artifact.cdx.name, artifact.cdx.version].filter(a => !!a).join('@')
} else if (bomFormat === "SPDX") {
file.title = [artifact.spdx.name, artifact.spdx.version].filter(a => !!a).join('@')
}
group = addFileToSourceSubgroup(group, file)
break
if (artifact.vex?.analysisResponse) {
analysis = `${analysis}, ${VexAnalysisResponse[artifact.vex.analysisResponse]}`
}
}
}
for (const group of state.artifacts) {
if (group.children.length === 0) {
group.children.push({ isEmpty: true })
const file = { contentType, ext, lastModified: artifact.date, uuid, source, url, analysis, findingTitle, dependencies, repoName, results, versionInfo }
file.key = crypto.randomUUID()
group.key = crypto.randomUUID()
if (group.title === "SARIF" && contentType?.includes("sarif")) {
file.title = analysisKey || `${uuid}.${ext}`
group = addFileToSourceSubgroup(group, file)
break
} else if (group.title === "VEX" && type === "VEX") {
file.title = artifact.vex.findingTitle
group = addFileToSourceSubgroup(group, file)
break
} else if (group.title === bomFormat) {
if (bomFormat === "CycloneDX") {
file.title = [artifact.cdx.name, artifact.cdx.version].filter(a => !!a).join('@')
} else if (bomFormat === "SPDX") {
file.title = [artifact.spdx.name, artifact.spdx.version].filter(a => !!a).join('@')
}
group = addFileToSourceSubgroup(group, file)
break
}
}
}
} else if (typeof data === "string" && !isJSON(data)) {
break
} else if (data?.error?.message) {
state.loading = false
state.error = data.error.message
return
} else if (["Expired", "Revoked", "Forbidden"].includes(data?.result)) {
state.loading = false
state.info = data.result
setTimeout(() => router.push('/logout'), 2000)
return
} else {
break
}
Expand All @@ -144,6 +127,12 @@ class Controller {
skip += pageSize
}
}
for (const group of state.artifacts) {
if (group.children.length === 0) {
group.children.push({ isEmpty: true })
}
}
state.loading = false
} catch (e) {
console.error(e)
Expand Down Expand Up @@ -321,12 +310,7 @@ const files = ref({
})
function addFileToSourceSubgroup(group, file) {
if (!file?.source) {
if (group.children.length === 1) {
const child = group.children.pop()
if (child.isEmpty) {
group.children = []
}
}
group.children = group.children.filter(item => !item?.isEmpty)
group.children.push(file)
return group
}
Expand All @@ -338,12 +322,7 @@ function addFileToSourceSubgroup(group, file) {
}
if (!sourceSubgroup.children.some(f => f.uuid === file.uuid)) {
delete file.source
if (sourceSubgroup.children.length === 1) {
const child = sourceSubgroup.children.pop()
if (child.isEmpty) {
sourceSubgroup.children = []
}
}
sourceSubgroup.children = sourceSubgroup.children.filter(item => !item?.isEmpty)
sourceSubgroup.children.push(file)
}
Expand Down Expand Up @@ -401,13 +380,11 @@ function updateArtifactsFromFiles(files) {
newFile.results = fileData.resultsCount || 0 // Assuming SARIF has a resultsCount
}
// Remove the 'isEmpty' property if it exists
uploadObject.children = uploadObject.children.filter(item => !item?.isEmpty)
// Add the new file to the upload object's children
uploadObject.children.push(newFile)
// Remove the 'isEmpty' property if it exists
if (targetArtifact.children[0].isEmpty) {
delete targetArtifact.children[0].isEmpty
}
}
}
</script>
Expand Down Expand Up @@ -581,7 +558,8 @@ function updateArtifactsFromFiles(files) {
<VTreeview
v-else
:items="state.artifacts"
item-value="title"
item-title="title"
item-value="key"
open-all
open-on-click
slim
Expand Down
Loading

0 comments on commit 57a5bef

Please sign in to comment.