Skip to content

Commit

Permalink
feat(interface): download report (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreDemailly authored Mar 13, 2024
1 parent d8e73d1 commit 537e998
Show file tree
Hide file tree
Showing 21 changed files with 447 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ nsecure-result.json
vuln.json
tmp/
dist/
reports
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- Link vulnerabilities from the multiple sources like GitHub Advisory, Sonatype or Snyk using [Vulnera](https://github.com/NodeSecure/vulnera).
- Add flags (emojis) to each packages versions to identify well known patterns and potential security threats easily.
- First-class support of open source security initiatives like [OpenSSF Scorecard](https://github.com/ossf/scorecard).
- Generate security report (PDF).

## 🚧 Requirements

Expand Down
11 changes: 10 additions & 1 deletion i18n/english.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ const ui = {
popup: {
maintainer: {
intree: "packages in the dependency tree"
},
report: {
title: "Generate a report",
form: {
title: "Report title",
includesAllDeps: "Include all dependencies",
submit: "Generate"
}
}
},
home: {
Expand All @@ -138,7 +146,8 @@ const ui = {
totalSize: "total size",
directDeps: "direct deps",
transitiveDeps: "transitive deps",
downloadsLastWeek: "downloads last week"
downloadsLastWeek: "downloads last week",
generateReport: "Generate a report"
},
watch: "Packages in the dependency tree requiring greater attention",
criticalWarnings: "Critical Warnings",
Expand Down
11 changes: 10 additions & 1 deletion i18n/french.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ const ui = {
popup: {
maintainer: {
intree: "packages dans l'abre de dépendances"
},
report: {
title: "Générer un rapport",
form: {
title: "Titre du rapport",
includesAllDeps: "Inclure toutes les dépendances",
submit: "Générer"
}
}
},
home: {
Expand All @@ -138,7 +146,8 @@ const ui = {
totalSize: "poids total",
directDeps: "dépendances directes",
transitiveDeps: "dépendances transitives",
downloadsLastWeek: "téléchargements la semaine dernière"
downloadsLastWeek: "téléchargements la semaine dernière",
generateReport: "Générer un rapport"
},
watch: "Packages dans l'arbre de dépendance nécessitant une plus grande attention",
criticalWarnings: "Avertissements critiques",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"@nodesecure/npm-registry-sdk": "^2.1.0",
"@nodesecure/ossf-scorecard-sdk": "^3.1.0",
"@nodesecure/rc": "^1.5.0",
"@nodesecure/report": "^1.1.1",
"@nodesecure/scanner": "^5.3.0",
"@nodesecure/utils": "^1.2.0",
"@nodesecure/vuln": "^1.7.0",
Expand All @@ -95,6 +96,7 @@
"@topcli/prompts": "^1.9.0",
"@topcli/spinner": "^2.1.2",
"cacache": "^18.0.2",
"co-body": "^6.1.0",
"dotenv": "^16.4.4",
"filenamify": "^6.0.0",
"highlightjs-line-numbers.js": "^2.8.0",
Expand Down
4 changes: 4 additions & 0 deletions public/components/locker/locker.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export class Locker {
this.renderUnlock();

document.addEventListener("keydown", (event) => {
if (window.disableShortcuts) {
return;
}

const hotkeys = JSON.parse(localStorage.getItem("hotkeys"));
switch (event.key.toUpperCase()) {
case hotkeys.lock: {
Expand Down
4 changes: 4 additions & 0 deletions public/components/navigation/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export class ViewNavigation {
}

document.addEventListener("keydown", (event) => {
if (window.disableShortcuts) {
return;
}

if (window.searchbar.background.classList.contains("show")) {
return;
}
Expand Down
3 changes: 3 additions & 0 deletions public/components/popup/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class Popup {
return;
}

window.disableShortcuts = true;

this.templateName = template.name;
this.dom.popup.appendChild(template.HTMLElement);
// TODO: apply additional css customization
Expand Down Expand Up @@ -59,6 +61,7 @@ export class Popup {
return;
}

window.disableShortcuts = false;
this.dom.popup.innerHTML = "";
this.templateName = null;
this.#cleanupClickOutside();
Expand Down
19 changes: 19 additions & 0 deletions public/components/views/home/home.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import url("./maintainers/maintainers.css");
@import url("./report/report.css");

#home--view {
z-index: 10;
Expand All @@ -13,6 +14,12 @@
display: flex;
}

.home--header--aside {
display: flex;
flex-direction: column;
align-items: end;
}

.home--header--scorecard {
display: none;
flex-shrink: 0;
Expand Down Expand Up @@ -67,6 +74,18 @@
background-color: rgb(39 144 252);
}

.home--header--report {
margin-top: 8px;
border: none;
padding: 10px;
color: #fffde4;
background: #1f9ad7;
font-weight: bold;
cursor: pointer;
border-radius: 4px;
text-shadow: 1px 1px 10px #0000007d;
}

.home--header--title {
display: flex;
flex-direction: column;
Expand Down
10 changes: 10 additions & 0 deletions public/components/views/home/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { fetchScorecardData, getScoreColor, getScorecardLink } from "../../../co

// Import Components
import { Maintainers } from "./maintainers/maintainers.js";
import { PopupReport } from "./report/report.js";

// CONSTANTS
const kFlagsToWatch = new Set([
Expand Down Expand Up @@ -46,6 +47,7 @@ export class HomeView {
this.generateExtensions();
this.generateLicenses();
this.generateMaintainers();
this.handleReport();
}

generateScorecard() {
Expand Down Expand Up @@ -301,4 +303,12 @@ export class HomeView {
new Maintainers(this.secureDataSet, this.nsn)
.render();
}

handleReport() {
document.querySelector(".home--header--report").addEventListener("click", async() => {
window.popup.open(
new PopupReport(this.secureDataSet.data.rootDependencyName).render()
);
});
}
}
123 changes: 123 additions & 0 deletions public/components/views/home/report/report.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
.report--popup {
min-width: 400px;
padding: 40px;
display: flex;
flex-direction: column;
}

.report--popup>.title {
height: 2px;
background: #d3d3d387;
margin: 0 10px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}

.report--popup>.title>p {
background: #f5f4f4;
padding: 0 10px;
font-family: "roboto";
font-weight: bold;
letter-spacing: 1.2px;
color: #255471;
font-size: 20px;
}

.report--popup>form {
display: flex;
flex-direction: column;
padding: 20px;
padding-bottom: 0;
}

.report--popup>form label {
color: #546884;
margin-bottom: 8px;
font-weight: 500;
font-size: 18px;
}

.report--popup>form input {
padding: 11px 6px;
border: none;
border-left: 4px solid #546884;
margin-bottom: 10px;
border-radius: 2px;
box-shadow: 0px 3px 7px 1px rgba(0, 0, 0, 0.10);
font-size: 16px;
}

.report--popup>form>button {
border: none;
padding: 8px;
color: white;
background: #43a82f;
font-weight: bold;
cursor: pointer;
width: 120px;
margin: auto;
margin-top: 20px;
font-size: 16px;
border-radius: 4px;
}

.report--popup .spinner {
width: 7px;
height: 7px;
display: inline-block;
border-radius: 50%;
margin-right: 10px;
border: 3px solid white;
animation: spinner-from 0.8s infinite linear alternate, spinner-to 1.6s infinite linear;
}


@keyframes spinner-from {
0% {
clip-path: polygon(50% 50%, 0 0, 50% 0%, 50% 0%, 50% 0%, 50% 0%, 50% 0%);
}

12.5% {
clip-path: polygon(50% 50%, 0 0, 50% 0%, 100% 0%, 100% 0%, 100% 0%, 100% 0%);
}

25% {
clip-path: polygon(50% 50%, 0 0, 50% 0%, 100% 0%, 100% 100%, 100% 100%, 100% 100%);
}

50% {
clip-path: polygon(50% 50%, 0 0, 50% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100%);
}

62.5% {
clip-path: polygon(50% 50%, 100% 0, 100% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100%);
}

75% {
clip-path: polygon(50% 50%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 50% 100%, 0% 100%);
}

100% {
clip-path: polygon(50% 50%, 50% 100%, 50% 100%, 50% 100%, 50% 100%, 50% 100%, 0% 100%);
}
}

@keyframes spinner-to {
0% {
transform: scaleY(1) rotate(0deg);
}

49.99% {
transform: scaleY(1) rotate(135deg);
}

50% {
transform: scaleY(-1) rotate(0deg);
}

100% {
transform: scaleY(-1) rotate(-135deg);
}
}
51 changes: 51 additions & 0 deletions public/components/views/home/report/report.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Import Internal Dependencies
import { PopupTemplate } from "../../../popup/popup.js";

export class PopupReport {
constructor(rootDependencyName) {
this.rootDependencyName = rootDependencyName;
}

render() {
const templateElement = document.getElementById("report-popup-template");
/** @type {HTMLElement} */
const clone = templateElement.content.cloneNode(true);
const form = clone.querySelector("form");
clone.querySelector("#title").placeholder = `${this.rootDependencyName}'s report`;
form.addEventListener("submit", (e) => {
e.preventDefault();

form.querySelector(".spinner").classList.remove("hidden");
const title = form.querySelector("#title").value;
const includesAllDeps = form.querySelector("#includesAllDeps").checked;

fetch("/report", {
method: "POST",
body: JSON.stringify({
title,
includesAllDeps
})
}).then(async(response) => {
const { data: json } = await response.json();
const url = window.URL.createObjectURL(
new Blob(
[new Uint8Array(json.data).buffer], { type: "application/pdf" }
)
);
const link = document.createElement("a");
link.href = url;
link.target = "_blank";
link.download = `${title}.pdf`;
document.body.appendChild(link);
link.click();
}).finally(() => {
form.querySelector(".spinner").classList.add("hidden");
});
}, { once: true });

return new PopupTemplate(
"report",
clone
);
}
}
4 changes: 4 additions & 0 deletions public/components/views/settings/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export class Settings {
input.value = "";

const onKeyDown = (event) => {
if (window.disableShortcuts) {
return;
}

// Prevent the app to change view if key is equal to view's hotkey
event.preventDefault();
event.stopPropagation();
Expand Down
4 changes: 4 additions & 0 deletions public/components/wiki/wiki.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export class Wiki {
});

document.addEventListener("keydown", (event) => {
if (window.disableShortcuts) {
return;
}

const hotkeys = JSON.parse(localStorage.getItem("hotkeys"));

if (event.key.toUpperCase() === hotkeys.wiki) {
Expand Down
4 changes: 4 additions & 0 deletions public/core/network-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ export class NetworkNavigation {
this.#dependenciesMapByLevel.set(0, this.rootNodeParams);

document.addEventListener("keydown", (event) => {
if (window.disableShortcuts) {
return;
}

const isNetworkViewHidden = document.getElementById("network--view").classList.contains("hidden");
const isWikiOpen = document.getElementById("documentation-root-element").classList.contains("slide-in");
const isSearchOpen = window.searchbar.background.classList.contains("show");
Expand Down
Loading

0 comments on commit 537e998

Please sign in to comment.