diff --git a/public/components/icon/icon.js b/public/components/icon/icon.js index 895049dd..ca48e145 100644 --- a/public/components/icon/icon.js +++ b/public/components/icon/icon.js @@ -59,6 +59,38 @@ const kIcons = { 46.080-19.456 47.104-52.224 17.408q-32.768 0-52.224-17.408t-20.48-47.104zM412.672 749.568v-306.176h135.168v306.176h-135.168z"/> +`, + "info-circled-filled": html` + +`, + link: html` + ` }; diff --git a/public/components/package/package.css b/public/components/package/package.css index 1247c66d..70fbdd47 100644 --- a/public/components/package/package.css +++ b/public/components/package/package.css @@ -1,7 +1,6 @@ @import url("./header/header.css"); @import url("./pannels/scripts/scripts.css"); @import url("./pannels/overview/overview.css"); -@import url("./pannels/vulnerabilities/vulnerabilities.css"); @import url("./pannels/scorecard/scorecard.css"); @import url("./pannels/warnings/warnings.css"); diff --git a/public/components/package/package.js b/public/components/package/package.js index aca76ede..81ff1083 100644 --- a/public/components/package/package.js +++ b/public/components/package/package.js @@ -98,8 +98,17 @@ export class PackageInfo { const licenses = document.createElement("package-licenses"); licenses.package = this; licenses.id = "pan-licenses"; - files.classList.add("package-container", "hidden"); + licenses.classList.add("package-container", "hidden"); panLicenses.parentElement.replaceChild(licenses, panLicenses); + + const panVulns = packageHTMLElement.querySelector("#pan-vulnerabilities"); + const vulns = document.createElement("package-vulnerabilities"); + vulns.package = this; + vulns.vulnerabilityStrategy = window.vulnerabilityStrategy; + vulns.theme = window.settings.config.theme; + vulns.id = "pan-vulnerabilities"; + vulns.classList.add("package-container", "hidden"); + panVulns.parentElement.replaceChild(vulns, panVulns); } /** @@ -162,7 +171,8 @@ export class PackageInfo { new Pannels.Overview(this).generate(clone); new Pannels.Warnings(this).generate(clone); new Pannels.Scripts(this).generate(clone); - new Pannels.Vulnerabilities(this).generate(clone); + this.addNavigationSignal(clone.getElementById("vulnerabilities-nav-menu"), + this.dependency.vulnerabilities.length); if (window.settings.config.disableExternalRequests === false) { new Pannels.Scorecard(this).generate(clone); } diff --git a/public/components/package/pannels/vulnerabilities/vulnerabilities.css b/public/components/package/pannels/vulnerabilities/vulnerabilities.css deleted file mode 100644 index 995e49a4..00000000 --- a/public/components/package/pannels/vulnerabilities/vulnerabilities.css +++ /dev/null @@ -1,194 +0,0 @@ -.vuln-strategy { - display: flex; - flex-direction: column; - height: 60px; - margin-bottom: 15px; - justify-content: center; -} - -.vuln-strategy>div { - display: flex; - justify-content: center; - align-items: center; -} - -.vuln-strategy>div+div { - margin-top: 5px; -} - -.vuln-strategy img { - width: 40px; - margin-right: 10px; -} - -.vuln-strategy .strategy { - color: #bbb; - font-size: 13px; - font-family: system-ui; - letter-spacing: 1px; -} - -.vuln-strategy .strategy>i { - margin-right: 4px; -} - -.vuln-strategy .name { - font-family: mononoki; - font-variant: small-caps; - font-weight: bold; - font-size: 20px; - text-shadow: 2px 2px 10px #00000082; -} - -.packages-vuln { - display: flex; - flex-direction: column; -} - -.packages-vuln .vuln { - border-top: 4px solid grey; - border-left: 1px solid grey; - border-right: 2px solid grey; - border-bottom: 1px solid grey; - border-radius: 8px; - box-sizing: border-box; - overflow: hidden; - color: #FFF; - display: flex; - flex-direction: column; - padding: 10px; -} - -.packages-vuln .vuln+.vuln { - margin-top: 10px; -} - -.packages-vuln .vuln.critical { - border-color: #B71C1C; - background: linear-gradient(to right, rgb(63 0 6 / 53%) 0%, rgb(0 0 0 / 0%) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#873f0006', endColorstr='#00000000', GradientType=1); -} - -.packages-vuln .vuln.high { - border-color: rgb(249 104 37); - background: linear-gradient(to right, rgb(53 38 0 / 65%) 0%, rgb(0 0 0 / 0%) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a6352600', endColorstr='#00000000', GradientType=1); -} - -.packages-vuln .vuln.medium, -.packages-vuln .vuln.moderate { - border-color: #F9A825; - background: linear-gradient(to right, rgb(65 66 0 / 65%) 0%, rgb(0 0 0 / 0%) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a6414200', endColorstr='#00000000', GradientType=1); -} - -.packages-vuln .vuln.info, -.packages-vuln .vuln.low { - border-color: #2545f9; - background: linear-gradient(to right, rgb(0 46 63 / 65%) 0%, rgb(0 0 0 / 0%) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a6002e3f', endColorstr='#00000000', GradientType=1); -} - -.packages-vuln .vuln>div { - display: flex; - height: 24px; - align-items: center; -} - -.packages-vuln .vuln .links { - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-top: 5px; -} - -.packages-vuln .vuln .links>i { - margin-right: 5px; - color: #00b8ead1; -} - -.packages-vuln .vuln .links>a { - text-decoration: none; - font-variant: all-small-caps; -} - -.packages-vuln .vuln.critical .links>a { - color: #ff9797d1; -} - -.packages-vuln .vuln.high .links>a { - color: #ffd6a1d1; -} - -.packages-vuln .vuln.medium .links>a, -.packages-vuln .vuln.modate .links>a { - color: #ffeea1d1; -} - -.packages-vuln .vuln.info .links>a, -.packages-vuln .vuln.low .links>a { - color: #a1c8ffd1; -} - -.packages-vuln .vuln>div .severity { - width: 24px; - height: inherit; - margin-right: 10px; - border-radius: 4px; - display: flex; - justify-content: center; - align-items: center; - font-family: mononoki; - font-weight: bold; - text-shadow: 2px 2px 10px #000; - color: #FFF; - border-top: 2px solid #fbffde61; - box-sizing: border-box; -} - -.packages-vuln .vuln>div>p { - display: flex; - align-items: center; - font-size: 14px; -} - -.packages-vuln .vuln>div>p.name { - font-family: mononoki; - font-size: 16px; - color: #FFF; -} - -.packages-vuln .vuln>div>span { - margin-left: auto; - background: #0000005c; - padding: 4px 5px; - margin-right: 10px; - font-family: mononoki; - font-size: 12px; - border-radius: 4px; -} - -.packages-vuln .vuln .description { - height: auto; - margin-top: 10px; - text-shadow: 2px 2px 5px #00000054; -} - -.packages-vuln .vuln>div .severity.critical { - background: #B71C1C; -} - -.packages-vuln .vuln>div .severity.high { - background: rgb(249 104 37); -} - -.packages-vuln .vuln>div .severity.medium, -.packages-vuln .vuln>div .severity.moderate { - background: #F9A825; -} - -.packages-vuln .vuln>div .severity.info, -.packages-vuln .vuln>div .severity.low { - background: #2545f9; -} diff --git a/public/components/package/pannels/vulnerabilities/vulnerabilities.js b/public/components/package/pannels/vulnerabilities/vulnerabilities.js index b8af1eff..d3086f78 100644 --- a/public/components/package/pannels/vulnerabilities/vulnerabilities.js +++ b/public/components/package/pannels/vulnerabilities/vulnerabilities.js @@ -1,96 +1,302 @@ +// Import Third-party Dependencies +import { LitElement, html, css, nothing } from "lit"; +import { when } from "lit/directives/when.js"; +import { repeat } from "lit/directives/repeat.js"; + // Import Internal Dependencies -import * as utils from "../../../../common/utils.js"; +import { EVENTS } from "../../../../core/events.js"; +import "../../../icon/icon.js"; -export class Vulnerabilities { - static href = { target: "_blank", rel: "noopener noreferrer" }; +class Vulnerabilities extends LitElement { + static styles = css` +p { + margin: 0; +} - constructor(pkg) { - this.package = pkg; - } +.vuln-strategy { + display: flex; + flex-direction: column; + height: 60px; + margin-bottom: 15px; + justify-content: center; +} - /** - * @param {!HTMLTemplateElement} clone - */ - setStrategy(clone) { - const strategy = window.vulnerabilityStrategy; - clone.querySelector(".vuln-strategy .name").textContent = strategy; - - /** @type {HTMLImageElement} */ - const strategyLogo = clone.querySelector(".vuln-strategy img"); - if (strategy === "none") { - strategyLogo.style.display = "none"; - } - else { - strategyLogo.src = strategy === "npm" ? "npm-icon.svg" : `${strategy}.png`; - } - } +.vuln-strategy>div { + display: flex; + justify-content: center; + align-items: center; +} + +.vuln-strategy>div+div { + margin-top: 5px; +} + +.vuln-strategy img { + width: 40px; + margin-right: 10px; +} + +.vuln-strategy .strategy { + color: #bbb; + font-size: 13px; + font-family: system-ui; + letter-spacing: 1px; +} + +.vuln-strategy .strategy> nsecure-icon{ + margin-right: 4px; + transform: translateY(2px); +} + +.vuln-strategy .name { + font-family: mononoki; + font-variant: small-caps; + font-weight: bold; + font-size: 20px; + text-shadow: 2px 2px 10px #00000082; +} + +.packages-vuln { + display: flex; + flex-direction: column; +} + +.packages-vuln .vuln { + border-top: 4px solid grey; + border-left: 1px solid grey; + border-right: 2px solid grey; + border-bottom: 1px solid grey; + border-radius: 8px; + box-sizing: border-box; + overflow: hidden; + color: #FFF; + display: flex; + flex-direction: column; + padding: 10px; +} + +.packages-vuln .vuln+.vuln { + margin-top: 10px; +} + +.packages-vuln .vuln.critical { + border-color: #B71C1C; + background: linear-gradient(to right, rgb(63 0 6 / 53%) 0%, rgb(0 0 0 / 0%) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#873f0006', endColorstr='#00000000', GradientType=1); +} + +.packages-vuln .vuln.high { + border-color: rgb(249 104 37); + background: linear-gradient(to right, rgb(53 38 0 / 65%) 0%, rgb(0 0 0 / 0%) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a6352600', endColorstr='#00000000', GradientType=1); +} + +.packages-vuln .vuln.medium, +.packages-vuln .vuln.moderate { + border-color: #F9A825; + background: linear-gradient(to right, rgb(65 66 0 / 65%) 0%, rgb(0 0 0 / 0%) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a6414200', endColorstr='#00000000', GradientType=1); +} + +.packages-vuln .vuln.info, +.packages-vuln .vuln.low { + border-color: #2545f9; + background: linear-gradient(to right, rgb(0 46 63 / 65%) 0%, rgb(0 0 0 / 0%) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a6002e3f', endColorstr='#00000000', GradientType=1); +} + +.packages-vuln .vuln>div { + display: flex; + height: 24px; + align-items: center; +} + +.packages-vuln .vuln .links { + display: flex; + align-items: center; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + margin-top: 5px; +} + +.packages-vuln .vuln .links> nsecure-icon { + whith: 16px; + flex: 0 0 auto; + margin-right: 5px; + color: #00b8ead1; + display: block; +} + +.packages-vuln .vuln .links>a { + flex: 1 1 auto; + text-decoration: none; + font-variant: all-small-caps; + text-overflow: ellipsis; +} + +.packages-vuln .vuln.critical .links>a { + color: #ff9797d1; +} - /** - * @param {!HTMLTemplateElement} clone - */ - generate(clone) { - this.setupSignal(clone); - this.setStrategy(clone); +.packages-vuln .vuln.high .links>a { + color: #ffd6a1d1; +} + +.packages-vuln .vuln.medium .links>a, +.packages-vuln .vuln.moderate .links>a { + color: #ffeea1d1; +} + +.packages-vuln .vuln.info .links>a, +.packages-vuln .vuln.low .links>a { + color: #a1c8ffd1; +} - clone.querySelector(".packages-vuln") - .appendChild(this.renderVulnerabilies()); +.packages-vuln .vuln>div .severity { + width: 24px; + height: inherit; + margin-right: 10px; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + font-family: mononoki; + font-weight: bold; + text-shadow: 2px 2px 10px #000; + color: #FFF; + border-top: 2px solid #fbffde61; + box-sizing: border-box; +} + +.packages-vuln .vuln>div>p { + display: flex; + align-items: center; + font-size: 14px; +} + +.packages-vuln .vuln>div>p.name { + font-family: mononoki; + font-size: 16px; + color: #FFF; +} + +.packages-vuln .vuln>div>span { + margin-left: auto; + background: #0000005c; + padding: 4px 5px; + margin-right: 10px; + font-family: mononoki; + font-size: 12px; + border-radius: 4px; +} + +.packages-vuln .vuln .description { + height: auto; + margin-top: 10px; + text-shadow: 2px 2px 5px #00000054; +} + +.vuln-strategy.dark ~ .packages-vuln .vuln .description { + color: var(--dark-theme-secondary-lighter); +} + +.packages-vuln .vuln>div .severity.critical { + background: #B71C1C; +} + +.packages-vuln .vuln>div .severity.high { + background: rgb(249 104 37); +} + +.packages-vuln .vuln>div .severity.medium, +.packages-vuln .vuln>div .severity.moderate { + background: #F9A825; +} + +.packages-vuln .vuln>div .severity.info, +.packages-vuln .vuln>div .severity.low { + background: #2545f9; +} +`; + + static properties = { + package: { type: Object }, + vulnerabilityStrategy: { type: String }, + theme: { type: String } + }; + + constructor() { + super(); + this.settingsChanged = ({ detail: { theme } }) => { + if (theme !== this.theme) { + this.theme = theme; + } + }; } - /** - * @param {!HTMLTemplateElement} clone - */ - setupSignal(clone) { - const { vulnerabilities } = this.package.dependency; - this.package.addNavigationSignal( - clone.getElementById("vulnerabilities-nav-menu"), - vulnerabilities.length - ); + connectedCallback() { + super.connectedCallback(); + window.addEventListener(EVENTS.SETTINGS_SAVED, this.settingsChanged); } - renderVulnerabilies() { + disconnectedCallback() { + window.removeEventListener(EVENTS.SETTINGS_SAVED, this.settingsChanged); + super.disconnectedCallback(); + } + + render() { const { vulnerabilities } = this.package.dependency; - const fragment = document.createDocumentFragment(); - for (const vuln of vulnerabilities) { - const severity = vuln.severity ?? "info"; - const vulnerableSemver = vuln.vulnerableRanges[0] ?? "N/A"; - - const header = utils.createDOMElement("div", { - childs: [ - utils.createDOMElement("div", { - classList: ["severity", severity], - text: severity.charAt(0).toUpperCase() - }), - utils.createDOMElement("p", { className: "name", text: vuln.package }), - utils.createDOMElement("span", { text: vulnerableSemver }) - ] - }); - const description = utils.createDOMElement("div", { - className: "description", - childs: [utils.createDOMElement("p", { text: vuln.title })] - }); - const links = utils.createDOMElement("div", { - className: "links", - childs: [ - utils.createDOMElement("i", { className: "icon-link" }), - utils.createDOMElement("a", { - text: vuln.url, - attributes: { href: vuln.url, ...Vulnerabilities.href } - }) - ] - }); - - const vulnDomElement = utils.createDOMElement("div", { - classList: ["vuln", severity], - childs: [ - header, - description, - links - ] - }); - fragment.appendChild(vulnDomElement); - } + return html` +
+
+

+ + strategy +

+
+
+ ${when(this.vulnerabilityStrategy === "none", + () => nothing, + () => html`` + ) + } +

${this.vulnerabilityStrategy}

+
+
+
+ ${repeat(vulnerabilities, + (vuln) => vuln, + (vuln) => { + const severity = vuln.severity ?? "info"; + const vulnerableSemver = vuln.vulnerableRanges[0] ?? "N/A"; - return fragment; + return html`
+
+
+ ${severity.charAt(0).toUpperCase()} +
+

${vuln.package}

+ ${vulnerableSemver} +
+
+

${vuln.title}

+
+ +
`; + } + ) + } +
+`; } } + +customElements.define("package-vulnerabilities", Vulnerabilities); diff --git a/src/commands/scanner.js b/src/commands/scanner.js index 0f2cfbca..aba8f2b2 100644 --- a/src/commands/scanner.js +++ b/src/commands/scanner.js @@ -78,12 +78,13 @@ export async function cwd(options) { } export async function from(spec, options) { - const { depth: maxDepth = Infinity, output, silent, contacts } = options; + const { depth: maxDepth = Infinity, output, silent, contacts, vulnerabilityStrategy } = options; const payload = await Scanner.from( spec, { maxDepth, + vulnerabilityStrategy, highlight: { contacts: parseContacts(contacts) }