Skip to content

Commit

Permalink
Db version representation (#10)
Browse files Browse the repository at this point in the history
* Update Neo4J schema to contain info about vulnerable components

* Update Neo4J schema to contain info about vulnerable components

* FIltering works

* New gitignore

* WIP: Better import

* remove file

* Add CORS support

* Iproved import flow

* ImportForm - define name and version

* WIP: Better import

remove file

Add CORS support

Iproved import flow

ImportForm - define name and version

* Fix import API endpoint

* WIP: Merge with filtering

* Exclude local .env files

* WIP:
- upload shows progress bar
- more resiliant upload form
- upload gets its own snug id
- project can be selected by URL
- minor visual changes

* Create DB contstraints

* Introduce ProjectVersion type in DB

* Update types

* WIP: rewrite DBDataHelper

* WIP: Rewrite import

* Update types

* WIP: continue with rewrite

* WIP: Fix some bugs with submission

* WIP: Can create components

* WIP: probably broken

* Working

* Working import + vulnerabilities

* WIP: working upload + visualization

* Graph cleanup

* FInalize import - progress bar

* Colors improvement

* Vulnerability detail improvement

* Link Improvement

* Different endpoint for FE /BE

---------

Co-authored-by: Matej Groman <[email protected]>
  • Loading branch information
Matej4545 and Matej Groman authored Mar 19, 2023
1 parent 9c3ce6b commit 195cb27
Show file tree
Hide file tree
Showing 47 changed files with 2,269 additions and 905 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ web_modules/
.yarn-integrity

# dotenv environment variable files
.env
.env*
.env.development.local
.env.test.local
.env.production.local
.env.local
.env*

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down Expand Up @@ -134,4 +135,5 @@ dist

/runtime

*no_git*
*no_git*

36 changes: 28 additions & 8 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
version: '3'
version: "3"

services:
neo4j:
image: neo4j:4.4.7
image: neo4j:latest
ports:
- 7474:7474
- 7687:7687
Expand All @@ -12,30 +12,50 @@ services:
- ./runtime/plugins:/var/lib/neo4j/plugins
restart: unless-stopped
environment:
- NEO4J_AUTH=neo4j/test
- NEO4J_AUTH=neo4j/${NEO4J_PASSWORD}
- NEO4J_apoc_export_file_enabled=true
- NEO4J_apoc_import_file_enabled=true
- NEO4J_apoc_import_file_use__neo4j__config=true
- NEO4JLABS_PLUGINS=["apoc"]
- NEO4J_dbms_security_procedures_unrestricted=apoc.*,algo.*

redis-cache:
image: redis:alpine
image: redis:latest
command: redis-server --requirepass ${REDIS_PASSWORD}
restart: unless-stopped
ports:
- 6379:6379
command: redis-server --save 20 1 --loglevel notice
volumes:
- cache:/data
ports:
- 6379:6379
links:
- redis-commander

redis-commander:
image: rediscommander/redis-commander:latest
restart: unless-stopped
environment:
REDIS_HOSTS: redis-cache
REDIS_HOST: redis-cache
REDIS_PORT: redis-cache:6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
HTTP_USER: root
HTTP_PASSWORD: root
ports:
- 8081:8081

depvis-next:
depends_on:
- neo4j
- redis-cache
build: ./src/depvis-next
image: depvis-next:latest
ports:
- 80:3000
- 8123:3000
restart: always
environment:
NEO4J_PASSWORD: ${NEO4J_PASSWORD}
REDIS_PASSWORD: ${REDIS_PASSWORD}
NEXT_PUBLIC_SONATYPE_OSS_AUTH: ${SONATYPE_OSS_AUTH}
volumes:
cache:
driver: local
7 changes: 6 additions & 1 deletion src/depvis-next/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ node_modules
npm-debug.log
README.md
.next
.git
.git

.env.development.local
.env.test.local
.env.production.local
.env.local
13 changes: 0 additions & 13 deletions src/depvis-next/.env.production

This file was deleted.

2 changes: 1 addition & 1 deletion src/depvis-next/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev"
"command": "npm run dev --trace-warnings"
},
{
"name": "Next.js: debug client-side",
Expand Down
26 changes: 17 additions & 9 deletions src/depvis-next/components/Details/ComponentDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { gql, useQuery } from '@apollo/client';
import { Container } from 'react-bootstrap';
import { GetComponentRepositoryURL } from '../../helpers/WorkspaceHelper';
import Loading from '../Loading/Loading';
import { DL, DLItem } from './DescriptionList';
import { gql, useQuery } from "@apollo/client";
import { Container } from "react-bootstrap";
import { GetComponentRepositoryURL } from "../../helpers/WorkspaceHelper";
import Loading from "../Loading/Loading";
import { DL, DLItem } from "./DescriptionList";

const getComponentDetailsQuery = gql`
query componentDetails($componentPurl: String, $projectId: ID) {
components(where: { purl: $componentPurl, project_SINGLE: { id: $projectId } }) {
components(
where: { purl: $componentPurl, projectVersion: { id: $projectId } }
) {
name
purl
author
Expand All @@ -27,6 +29,7 @@ const ComponentDetails = (props) => {

const renderLink = () => {
const link = GetComponentRepositoryURL(data.components[0].purl);
if (!link) return;
return (
<a href={link} target="_blank" rel="noreferrer">
{link}
Expand All @@ -35,13 +38,15 @@ const ComponentDetails = (props) => {
};
if (loading) return <Loading />;
if (!data.components[0]) {
console.error('No data found when querying backend! Below is Apollo query result');
console.error(
"No data found when querying backend! Below is Apollo query result"
);
console.error({ data: data, error: error });
return <b>No data found!</b>;
}
const component = data.components[0];
return (
<Container style={{ wordBreak: 'break-all' }} className="px-0">
<Container style={{ wordBreak: "break-all" }} className="px-0">
<h4 className="pb-3">
<b>{component.name}</b>
</h4>
Expand All @@ -50,7 +55,10 @@ const ComponentDetails = (props) => {
<DLItem label="Author" value={component.author} />
<DLItem label="Publisher" value={component.publisher} />
<DLItem label="Purl" value={component.purl} />
<DLItem label="Number of dependencies" value={component.dependsOnCount} />
<DLItem
label="Number of dependencies"
value={component.dependsOnCount}
/>
<DLItem
label="Vulnerabilities"
value={component.vulnerabilities.map((v) => (
Expand Down
65 changes: 54 additions & 11 deletions src/depvis-next/components/Details/VulnerabilityDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { gql, useQuery } from '@apollo/client';
import { Badge, Container } from 'react-bootstrap';
import Loading from '../Loading/Loading';
import { DL, DLItem } from './DescriptionList';
import { gql, useQuery } from "@apollo/client";
import Link from "next/link";
import { Badge, Container } from "react-bootstrap";
import urlJoin from "url-join";
import { vulnerabilityColorByCVSS } from "../../helpers/GraphHelper";
import Loading from "../Loading/Loading";
import { DL, DLItem } from "./DescriptionList";

const getVulnerabilityDetailsQuery = gql`
query vulnerabilityDetails($vulnerabilityId: String) {
Expand Down Expand Up @@ -38,31 +41,71 @@ const VulnerabilityDetails = (props) => {
));
};

const getCvssScoreLabel = (cvssScore) => {
if (cvssScore >= 9) return "Critical";
if (cvssScore >= 7) return "High";
if (cvssScore >= 4) return "Medium";
return "Low";
};
const renderCvss = (cvssScore) => {
return <Badge bg={cvssScore > 5 ? 'danger' : 'warning'}>{cvssScore}</Badge>;
return (
<Badge
style={{
background: `${vulnerabilityColorByCVSS(cvssScore)}`,
}}
bg=""
>
{cvssScore} - {getCvssScoreLabel(cvssScore)}
</Badge>
);
};

const renderCvssVectorAsLink = (cvssVector) => {
const cvssPortalUrl = "https://www.first.org/cvss/calculator/3.1";
const url = `${cvssPortalUrl}#${cvssVector}`;
return (
<a href={url} target="_blank" rel="noreferrer">
{cvssVector}
</a>
);
};

if (loading) return <Loading />;
if (!data.vulnerabilities[0]) {
console.error('No data found when querying backend! Below is Apollo query result');
console.error(
"No data found when querying backend! Below is Apollo query result"
);
console.error({ data: data, error: error });
return <b>No data found!</b>;
}
const vulnerability = data.vulnerabilities[0];
return (
<Container style={{ wordBreak: 'break-word' }} className="px-0">
<Container style={{ wordBreak: "break-word" }} className="px-0">
<h4 className="pb-3">
<b>{vulnerability.name}</b>
</h4>
<DL>
<DLItem label="Id" value={vulnerability.id} />
<DLItem label="CVSS Score" value={renderCvss(vulnerability.cvssScore)} alwaysShow />
<DLItem label="CVSS Vector" value={vulnerability.cvssVector} />
<DLItem
label="CVSS Score"
value={renderCvss(vulnerability.cvssScore)}
alwaysShow
/>
<DLItem
label="CVSS Vector"
value={renderCvssVectorAsLink(vulnerability.cvssVector)}
/>
<DLItem label="Description" value={vulnerability.description} />

<DLItem label="affectedVersions" value={vulnerability.affectedVersions} />
<DLItem
label="affectedVersions"
value={vulnerability.affectedVersions}
/>
<DLItem label="CWE" value={vulnerability.cwe} />
<DLItem label="References" value={renderReferences(vulnerability.references)} />
<DLItem
label="References"
value={renderReferences(vulnerability.references)}
/>
</DL>
</Container>
);
Expand Down
37 changes: 25 additions & 12 deletions src/depvis-next/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import { useState } from 'react';
import { Container, Form } from 'react-bootstrap';
import { useEffect, useState } from "react";
import { Container, Form } from "react-bootstrap";

export type DropdownItem = {
displayName: string;
id: string;
};

/**
* Dropdown component, renders set of options and fires a callback on change
* @param props one of title, onChange, defaultValue, options, disabled
* @returns
*/
export default function Dropdown(props) {
const [selected, setSelected] = useState(props.default);
const { title, onChange, defaultValue, options, disabled } = props;
const [selectedId, setSelectedId] = useState(defaultValue);
return (
<Container>
<Form>
{props.title && <Form.Label>{props.title}</Form.Label>}
{title && <Form.Label>{title}</Form.Label>}
<Form.Select
value={selected}
value={selectedId && selectedId}
disabled={disabled}
onChange={(e) => {
props.onChange(e.target.value);
setSelected(e.target.value);
setSelectedId(e.target.value);
onChange(e.target.value);
}}
>
{props.options.map((v, i) => (
<option key={i} value={v.id}>
{v.name}
</option>
))}
{options &&
options.map((v: DropdownItem, i: number) => (
<option key={i} value={v.id}>
{v.displayName}
</option>
))}
</Form.Select>
</Form>
</Container>
Expand Down
11 changes: 11 additions & 0 deletions src/depvis-next/components/Error/NoProjectFoundError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Link from 'next/link';
import { Container } from 'react-bootstrap';

export default function NoProjectFoundError(props) {
return (
<Container className="mx-auto my-5 text-center">
<p className="fs-4 fw-bold">No project found!</p>
<Link href="/upload">Upload SBOM</Link>
</Container>
);
}
17 changes: 9 additions & 8 deletions src/depvis-next/components/Graph/GraphConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export type GraphConfig = {
zoomLevel: number
color: string | Function,
label: string | Function,
linkDirectionalArrowLength: number,
linkDirectionalRelPos: number
nodeVal: number | Function
linkLength: number
}
zoomLevel: number;
color: string | Function;
label: string | Function;
linkDirectionalArrowLength: number;
linkDirectionalRelPos: number;
nodeVal: number | Function;
linkLength: number;
showOnlyVulnerable: Boolean;
};
Loading

0 comments on commit 195cb27

Please sign in to comment.