Skip to content

Commit

Permalink
Issues page improvements (#66)
Browse files Browse the repository at this point in the history
* feat(style): CVSS calc layout

* feat: tab persist on refresh for issue page

* chore: refactor for dependency graph 

---------

Co-authored-by: Christopher Langton <[email protected]>
  • Loading branch information
0x73746F66 and chrisdlangton authored Nov 19, 2024
1 parent 944823e commit bfbecde
Show file tree
Hide file tree
Showing 44 changed files with 13,485 additions and 8,722 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ src/@iconify/icons-bundle.js
.wrangler/
.dev.vars
.env

# Artifacts
semgrep.sarif.json
osv.sarif.json
vulnetix.cdx.json
vulnetix.spdx.json
13 changes: 10 additions & 3 deletions .repo/scratchad.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
-- SELECT *
-- FROM Triage
-- WHERE findingUuid = "c00f2661-a1fc-4a72-83af-5894580ee510";
SELECT *
FROM Finding
WHERE affectedFunctions IS NOT NULL;
SELECT A."accessToken",
A."avatarUrl",
A."created",
A."expires",
A."installationId",
A."login",
A."memberEmail",
B."orgId"
FROM "GitHubApp" A
INNER JOIN "Member" B ON A."memberEmail" = B."email";
9 changes: 9 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
nodeLinker: node-modules

plugins:
- checksum: c738bdb53e1c886dfa387a0b25416449be043d008547da8e31237a9673e84c838084af8df4872a609b90684fce1650d6af69329884856698341da9ee7f9775e0
path: .yarn/plugins/@yarnpkg/plugin-cyclonedx.cjs
spec: "https://github.com/CycloneDX/cyclonedx-node-yarn/releases/latest/download/yarn-plugin-cyclonedx.cjs"
- checksum: 006d0325e832b7267fa88084a62d319de313ca2541dca62f8fea3125f095a4cc1b8791897f1d803d38179a972aa2152533d11525e463fb8073ca358ee8601f44
path: .yarn/plugins/@yarnpkg/plugin-spdx.cjs
spec: "https://raw.githubusercontent.com/spdx/yarn-plugin-spdx/main/bundles/@yarnpkg/plugin-spdx.js"
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ sarif: clean ## generate SARIF from Semgrep for this project
semgrep $(SEMGREP_ARGS) $(SEMGREP_RULES) | jq >semgrep.sarif.json

sbom: clean ## generate CycloneDX from NPM for this project
npm sbom --omit dev --package-lock-only --sbom-format cyclonedx | jq >npm.cdx.json
npm sbom --omit dev --package-lock-only --sbom-format spdx | jq >npm.spdx.json
yarn cyclonedx --spec-version 1.6 --prod --output-reproducible --output-file vulnetix.cdx.json
yarn spdx

deployments: ## FOR DOCO ONLY
npx wrangler pages deployment list --project-name vulnetix
Expand Down
3 changes: 0 additions & 3 deletions functions/api/[analysisState]/issues.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ export async function onRequestGet(context) {
}
},
},
omit: {
memberEmail: true,
},
include: {
triage: true,
spdx: {
Expand Down
3 changes: 0 additions & 3 deletions functions/api/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ export async function onRequestGet(context) {
where: {
orgId: verificationResult.session.orgId,
},
omit: {
memberEmail: true,
},
include: {
triage: true,
spdx: {
Expand Down
76 changes: 76 additions & 0 deletions functions/api/archive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { AuthResult, Server } from "@/utils";
import { PrismaD1 } from '@prisma/adapter-d1';
import { PrismaClient } from '@prisma/client';

export async function onRequestGet(context) {
const {
request, // same as existing Worker API
env, // same as existing Worker API
params, // if filename includes [id] or [[path]]
waitUntil, // same as ctx.waitUntil in existing Worker API
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context
try {
const adapter = new PrismaD1(env.d1db)
const prisma = new PrismaClient({
adapter,
transactionOptions: {
maxWait: 1500, // default: 2000
timeout: 2000, // default: 5000
},
})
const verificationResult = await (new Server(request, prisma)).authenticate()
if (!verificationResult.isValid) {
return Response.json({ ok: false, result: verificationResult.message })
}
const { searchParams } = new URL(request.url)
const take = parseInt(searchParams.get('take'), 10) || 50
const skip = parseInt(searchParams.get('skip'), 10) || 0
const findings = await prisma.Finding.findMany({
where: {
orgId: verificationResult.session.orgId,
AND: {
triage: {
some: { analysisState: { in: ['resolved', 'resolved_with_pedigree', 'false_positive', 'not_affected'] } }
}
},
},
include: {
triage: {
orderBy: {
triagedAt: 'desc'
}
},
spdx: {
include: {
repo: true
}
},
cdx: {
include: {
repo: true
}
},
},
take,
skip,
orderBy: {
modifiedAt: 'desc',
}
})

return Response.json({
ok: true, findings: findings.map(finding => {
finding.references = JSON.parse(finding.referencesJSON)
delete finding.referencesJSON
finding.aliases = JSON.parse(finding.aliases)
finding.cwes = JSON.parse(finding.cwes)
return finding
})
})
} catch (err) {
console.error(err)
return Response.json({ ok: false, error: { message: err }, result: AuthResult.REVOKED })
}
}
4 changes: 2 additions & 2 deletions functions/api/artifact/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export async function onRequestGet(context) {
repoName: true,
spdxVersion: true,
createdAt: true,
packagesCount: true,
dependencies: true,
},
},
sarif: {
Expand All @@ -102,7 +102,7 @@ export async function onRequestGet(context) {
repoName: true,
cdxVersion: true,
createdAt: true,
dependenciesCount: true,
dependencies: true,
},
},
vex: {
Expand Down
40 changes: 28 additions & 12 deletions functions/api/cdx.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { parseCycloneDXComponents } from "@/finding";
import { AuthResult, ensureStrReqBody, hex, isCDX, OSV, saveArtifact, Server } from "@/utils";
import { PrismaD1 } from '@prisma/adapter-d1';
import { PrismaClient } from '@prisma/client';
Expand Down Expand Up @@ -31,9 +32,6 @@ export async function onRequestGet(context) {
where: {
orgId: verificationResult.session.orgId,
},
omit: {
memberEmail: true,
},
include: {
repo: true,
artifact: {
Expand Down Expand Up @@ -100,30 +98,47 @@ export async function onRequestPost(context) {
orgId: verificationResult.session.orgId,
}
})

const artifactUuid = originalCdx?.artifactUuid || cdx.serialNumber.startsWith('urn:uuid:') ? cdx.serialNumber.substring(9) : crypto.randomUUID()
const artifactUuid = originalCdx?.artifactUuid || (cdx?.serialNumber?.startsWith('urn:uuid:') ? cdx.serialNumber.substring(9) : crypto.randomUUID())
if (!cdx?.serialNumber) {
cdx.serialNumber = `urn:uuid:${artifactUuid}`
}
const dependencies = []
for (const dep of parseCycloneDXComponents(cdx)) {
const info = await prisma.Dependency.upsert({
where: {
cdx_dep: {
cdxId,
name: dep.name,
version: dep.version,
}
},
update: {
license: dep.license,
dependsOnUuid: dep.dependsOnUuid
},
create: { ...dep, cdxId }
})
dependencies.push({ ...dep, cdxId })
console.log(`Dependency ${dep.name}@${dep.version}`, info)
}
const cdxStr = JSON.stringify(cdx)
const artifact = await saveArtifact(prisma, env.r2artifacts, cdxStr, artifactUuid, `cyclonedx`)
const cdxData = {
cdxId,
artifactUuid,
source: 'upload',
orgId: verificationResult.session.orgId,
memberEmail: verificationResult.session.memberEmail,
cdxVersion: cdx.specVersion,
serialNumber: cdx.serialNumber,
name: cdx.metadata?.component?.name,
version: cdx.metadata?.component?.version,
createdAt: (new Date(cdx.metadata.timestamp)).getTime(),
createdAt: cdx.metadata?.timestamp ? new Date(cdx.metadata.timestamp).getTime() : new Date().getTime(),
toolName: cdx.metadata.tools.map(t => `${t?.vendor} ${t?.name} ${t?.version}`.trim()).join(', '),
externalReferencesCount: cdx.metadata.component?.externalReferences?.length || 0,
componentsCount: cdx.components?.length || 0,
dependenciesCount: cdx.dependencies?.length || 0,
}
const info = await prisma.CycloneDXInfo.upsert({
where: {
cdxId,
memberEmail: verificationResult.session.memberEmail,
orgId: verificationResult.session.orgId,
},
update: {
createdAt: cdxData.createdAt,
Expand All @@ -132,6 +147,7 @@ export async function onRequestPost(context) {
create: cdxData
})
console.log(`/upload/cdx ${cdxId} kid=${verificationResult.session.kid}`, info)
cdxData.dependencies = dependencies
files.push(cdxData)

const osvQueries = cdx.components.map(component => {
Expand All @@ -157,12 +173,12 @@ export async function onRequestPost(context) {
const findingData = {
findingId,
orgId: verificationResult.session.orgId,
memberEmail: verificationResult.session.memberEmail,
source: 'osv.dev',
category: 'sca',
createdAt: (new Date()).getTime(),
modifiedAt: (new Date(vuln.modified)).getTime(),
detectionTitle: vuln.id,
detectionDescription: vuln.details,
purl: referenceLocator,
packageName: name,
packageVersion: version,
Expand Down
17 changes: 7 additions & 10 deletions functions/api/history.js → functions/api/exploitable.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,14 @@ export async function onRequestGet(context) {
const findings = await prisma.Finding.findMany({
where: {
orgId: verificationResult.session.orgId,
NOT: {
triage: {
every: { analysisState: 'in_triage', }
}
},
},
omit: {
memberEmail: true,
triage: { every: { analysisState: 'exploitable' } }
},
include: {
triage: true,
triage: {
orderBy: {
lastObserved: 'desc'
}
},
spdx: {
include: {
repo: true
Expand All @@ -55,7 +52,7 @@ export async function onRequestGet(context) {
take,
skip,
orderBy: {
createdAt: 'asc',
modifiedAt: 'desc'
}
})

Expand Down
2 changes: 1 addition & 1 deletion functions/api/github/[installation_id]/uninstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function onRequestGet(context) {

try {
const where = {
memberEmail: verificationResult.session.memberEmail,
orgId: verificationResult.session.orgId,
installationId: parseInt(params.installation_id, 10),
}
const app = await prisma.GitHubApp.findUniqueOrThrow({ where })
Expand Down
7 changes: 3 additions & 4 deletions functions/api/github/repos.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function onRequestGet(context) {
const gitRepos = []
const installs = await prisma.GitHubApp.findMany({
where: {
memberEmail: verificationResult.session.memberEmail,
orgId: verificationResult.session.orgId,
AND: { expires: { gte: (new Date()).getTime(), } }
},
})
Expand All @@ -53,14 +53,14 @@ export async function onRequestGet(context) {
await prisma.GitHubApp.update({
where: {
installationId: parseInt(app.installationId, 10),
AND: { memberEmail: app.memberEmail, },
AND: { orgId: app.orgId, },
},
data: app,
})
continue
}
delete app.accessToken
delete app.memberEmail

return Response.json({ error, app })
}
for (const repo of content) {
Expand Down Expand Up @@ -107,7 +107,6 @@ const store = async (prisma, session, repo) => {
pushedAt: (new Date(repo.pushed_at)).getTime(),
defaultBranch: repo.default_branch,
ownerId: repo.owner.id,
memberEmail: session.memberEmail,
licenseSpdxId: repo.license?.spdx_id || '',
licenseName: repo.license?.name || '',
fork: repo.fork ? 1 : 0,
Expand Down
7 changes: 2 additions & 5 deletions functions/api/github/repos/[org]/[repo]/sarif.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function onRequestGet(context) {
const errors = []
const githubApps = await prisma.GitHubApp.findMany({
where: {
memberEmail: verificationResult.session.memberEmail,
orgId: verificationResult.session.orgId,
},
})
const repoName = `${params.org}/${params.repo}`
Expand All @@ -54,14 +54,13 @@ export async function onRequestGet(context) {
await prisma.GitHubApp.update({
where: {
installationId: parseInt(app.installationId, 10),
AND: { memberEmail: app.memberEmail, },
AND: { orgId: app.orgId },
},
data: app,
})
continue
}
delete app.accessToken
delete app.memberEmail
errors.push({ error, app })
continue
}
Expand Down Expand Up @@ -116,7 +115,6 @@ const process = async (prisma, session, data, fullName) => {
fullName,
source: 'GitHub',
orgId: session.orgId,
memberEmail: session.memberEmail,
commitSha: data.report.commit_sha,
ref: data.report.ref,
createdAt: (new Date(data.report.created_at)).getTime(),
Expand Down Expand Up @@ -215,7 +213,6 @@ const process = async (prisma, session, data, fullName) => {
reportId: data.report.id.toString(),
artifactUuid: sarifId,
fullName,
memberEmail: session.memberEmail,
commitSha: data.report.commit_sha,
ref: data.report.commit_sha,
createdAt: (new Date(data.report.created_at)).getTime(),
Expand Down
Loading

0 comments on commit bfbecde

Please sign in to comment.