diff --git a/i18n/english.js b/i18n/english.js index f2819ada..25882822 100644 --- a/i18n/english.js +++ b/i18n/english.js @@ -186,7 +186,9 @@ const ui = { warnings: "SAST Warnings to ignore", flags: "Flags (emojis) to ignore", network: "Network", - showFriendly: "Show friendly dependencies" + showFriendly: "Show friendly dependencies", + security: "Security", + disableExternalRequests: "Disable external requests" }, shortcuts: { title: "Shortcuts", diff --git a/i18n/french.js b/i18n/french.js index b41348fa..da4dc140 100644 --- a/i18n/french.js +++ b/i18n/french.js @@ -186,7 +186,9 @@ const ui = { warnings: "Avertissements à ignorer", flags: "Drapeau (emojis) à ignorer", network: "Réseau", - showFriendly: "Afficher les dépendances amicales" + showFriendly: "Afficher les dépendances amicales", + security: "Sécurité", + disableExternalRequests: "Désactiver les requêtes externes" }, shortcuts: { title: "Raccourcis", diff --git a/public/components/package/package.html b/public/components/package/package.html index 4cdac33b..f6115141 100644 --- a/public/components/package/package.html +++ b/public/components/package/package.html @@ -137,7 +137,7 @@

bundlephobia

-
+
N/A MIN diff --git a/public/components/package/package.js b/public/components/package/package.js index 5da6069d..e8232d33 100644 --- a/public/components/package/package.js +++ b/public/components/package/package.js @@ -82,6 +82,15 @@ export class PackageInfo { ); packageHTMLElement.setAttribute("class", "slide-in"); + if (window.settings.config.disableExternalRequests) { + const bundlephobiaElement = packageHTMLElement.querySelector("#bundlephobia-sizes"); + const bundlephobiaTitleElement = bundlephobiaElement.previousElementSibling; + bundlephobiaElement.classList.add("hidden"); + bundlephobiaTitleElement.classList.add("hidden"); + + return; + } + new Bundlephobia(this.dependencyVersion.name, this.dependencyVersion.version) .fetchDataOnHttpServer() .catch(console.error); @@ -149,7 +158,9 @@ export class PackageInfo { new Pannels.Warnings(this).generate(clone); new Pannels.Scripts(this).generate(clone); new Pannels.Vulnerabilities(this).generate(clone); - new Pannels.Scorecard(this).generate(clone); + if (window.settings.config.disableExternalRequests === false) { + new Pannels.Scorecard(this).generate(clone); + } new Pannels.Files(this).generate(clone); return clone; diff --git a/public/components/package/pannels/overview/overview.js b/public/components/package/pannels/overview/overview.js index acba12e2..809a98bc 100644 --- a/public/components/package/pannels/overview/overview.js +++ b/public/components/package/pannels/overview/overview.js @@ -32,7 +32,15 @@ export class Overview { clone.querySelector(".fields") .appendChild(this.renderTopFields()); - this.renderNPMStats(); + if (window.settings.config.disableExternalRequests === false) { + this.renderNPMStats(); + } + else { + const npmStatElement = clone.querySelector("#npm-stats"); + const npmStatTitleElement = npmStatElement.previousElementSibling; + npmStatElement.classList.add("hidden"); + npmStatTitleElement.classList.add("hidden"); + } clone.querySelector(".fields.releases") .appendChild(this.renderReleases()); @@ -47,7 +55,13 @@ export class Overview { // Fetch Github/Gitlab stats const githubLink = this.package.links.github; - if (githubLink.showInHeader) { + if (window.settings.config.disableExternalRequests) { + setTimeout(() => { + document.querySelector(".gitlab-overview")?.classList.add("hidden"); + document.querySelector(".github-overview")?.classList.add("hidden"); + }); + } + else if (githubLink.showInHeader) { setTimeout(() => { document.querySelector(".gitlab-overview")?.classList.add("hidden"); }); diff --git a/public/components/views/home/home.js b/public/components/views/home/home.js index 426d8eed..14b80b1e 100644 --- a/public/components/views/home/home.js +++ b/public/components/views/home/home.js @@ -44,7 +44,9 @@ export class HomeView { homeViewHTMLElement.innerHTML = ""; homeViewHTMLElement.appendChild(this.render()); - this.generateScorecard(); + if (window.settings.config.disableExternalRequests === false) { + this.generateScorecard(); + } this.generateHeader(); this.generateOverview(); this.generatePackagesToWatch(); @@ -133,25 +135,25 @@ export class HomeView { document.getElementById("project-links").appendChild(linksFragment); } - async generateOverview() { - const fragment = document.createDocumentFragment(); + #createOverviewDiv(icon, title, value) { + const titleDiv = utils.createDOMElement("div", { + className: "title", + childs: [ + utils.createDOMElement("i", { className: icon }), + utils.createDOMElement("p", { text: title }) + ] + }); - function createOverviewDiv(icon, title, value) { - const titleDiv = utils.createDOMElement("div", { - className: "title", - childs: [ - utils.createDOMElement("i", { className: icon }), - utils.createDOMElement("p", { text: title }) - ] - }); + return utils.createDOMElement("div", { + childs: [ + titleDiv, + utils.createDOMElement("span", { text: value }) + ] + }); + } - return utils.createDOMElement("div", { - childs: [ - titleDiv, - utils.createDOMElement("span", { text: value }) - ] - }); - } + async generateOverview() { + const fragment = document.createDocumentFragment(); const { name } = this.secureDataSet.linker.get(0); let directDependencies = 0; @@ -160,35 +162,27 @@ export class HomeView { directDependencies++; } } - fragment.appendChild(createOverviewDiv( + fragment.appendChild(this.#createOverviewDiv( "icon-cubes", `# ${window.i18n[this.lang].home.overview.dependencies}`, this.secureDataSet.dependenciesCount )); - fragment.appendChild(createOverviewDiv( + fragment.appendChild(this.#createOverviewDiv( "icon-archive", window.i18n[this.lang].home.overview.totalSize, this.secureDataSet.prettySize )); - fragment.appendChild(createOverviewDiv( + fragment.appendChild(this.#createOverviewDiv( "icon-link", `# ${window.i18n[this.lang].home.overview.directDeps}`, directDependencies )); - fragment.appendChild(createOverviewDiv( + fragment.appendChild(this.#createOverviewDiv( "icon-sitemap", `# ${window.i18n[this.lang].home.overview.transitiveDeps}`, this.secureDataSet.indirectDependencies )); const homeOverviewElement = document.querySelector(".home--overview"); homeOverviewElement.appendChild(fragment); - try { - const { downloads } = await getJSON(`/downloads/${name.replaceAll("/", "%2F")}`); - - if (downloads) { - const formattedNumber = new Intl.NumberFormat("de-DE").format(downloads); - homeOverviewElement.appendChild(createOverviewDiv( - "icon-chart-bar", window.i18n[this.lang].home.overview.downloadsLastWeek, formattedNumber - )); - } - } - catch { - // DO NOTHING + if (window.settings.config.disableExternalRequests) { + return; } + + this.generateDownloads(); } generatePackagesToWatch() { @@ -322,6 +316,29 @@ export class HomeView { .render(); } + async generateDownloads() { + const homeOverviewElement = document.querySelector(".home--overview"); + const { name } = this.secureDataSet.linker.get(0); + + try { + const { downloads } = await getJSON(`/downloads/${name.replaceAll("/", "%2F")}`); + + if (downloads) { + const downloadsElement = document.querySelector(".home--overview div:has(i.icon-chart-bar)"); + downloadsElement?.remove(); + const formattedNumber = new Intl.NumberFormat("de-DE").format(downloads); + homeOverviewElement.appendChild(this.#createOverviewDiv( + "icon-chart-bar", + window.i18n[this.lang].home.overview.downloadsLastWeek, + formattedNumber + )); + } + } + catch { + // DO NOTHING + } + } + handleReport() { document.querySelector(".home--header--report").addEventListener("click", async() => { window.popup.open( diff --git a/public/components/views/settings/settings.js b/public/components/views/settings/settings.js index 3f7ecb30..f0139a0d 100644 --- a/public/components/views/settings/settings.js +++ b/public/components/views/settings/settings.js @@ -38,7 +38,8 @@ export class Settings { shortcutsSection: document.querySelector(".shortcuts"), /** @type {HTMLInputElement} */ showFriendlyDependenciesCheckbox: document.querySelector("#show-friendly"), - themeSelector: document.querySelector("#theme_selector") + themeSelector: document.querySelector("#theme_selector"), + disableExternalRequestsCheckbox: document.querySelector("#disable-external") }; this.saveButton = document.querySelector(".save"); @@ -50,7 +51,8 @@ export class Settings { ...this.dom.warningsCheckbox, ...this.dom.flagsCheckbox, this.dom.showFriendlyDependenciesCheckbox, - this.dom.themeSelector + this.dom.themeSelector, + this.dom.disableExternalRequestsCheckbox ]; for (const formField of formFields) { formField.addEventListener("change", () => this.enableSaveButton()); @@ -200,7 +202,8 @@ export class Settings { defaultPackageMenu: this.dom.defaultPackageMenu.value || Settings.defaultMenuName, ignore: { flags: new Set(), warnings: new Set() }, showFriendlyDependencies: this.dom.showFriendlyDependenciesCheckbox.checked, - theme: this.dom.themeSelector.value + theme: this.dom.themeSelector.value, + disableExternalRequests: this.dom.disableExternalRequestsCheckbox.checked }; for (const checkbox of this.dom.warningsCheckbox) { @@ -247,5 +250,6 @@ export class Settings { } this.dom.showFriendlyDependenciesCheckbox.checked = this.config.showFriendlyDependencies; + this.dom.disableExternalRequestsCheckbox.checked = this.config.disableExternalRequests; } } diff --git a/public/main.js b/public/main.js index bc183ab8..8e0ebfc6 100644 --- a/public/main.js +++ b/public/main.js @@ -18,6 +18,7 @@ import * as utils from "./common/utils.js"; let secureDataSet; let nsn; +let homeView; let searchview; let packageInfoOpened = false; @@ -125,7 +126,7 @@ async function init(options = {}) { }); window.locker = new Locker(nsn); window.legend = new Legend({ show: window.settings.config.showFriendlyDependencies }); - new HomeView(secureDataSet, nsn); + homeView ??= new HomeView(secureDataSet, nsn); searchview ??= new SearchView(secureDataSet, nsn); window.addEventListener("package-info-closed", () => { @@ -217,6 +218,7 @@ function onSettingsSaved(defaultConfig = null) { window.settings.config.ignore.warnings = warningsToIgnore; window.settings.config.ignore.flags = flagsToIgnore; window.settings.config.theme = theme; + window.settings.config.disableExternalRequests = config.disableExternalRequests; if (theme === "dark") { document.body.classList.add("dark"); @@ -247,6 +249,10 @@ function onSettingsSaved(defaultConfig = null) { else { window.legend.hide(); } + + if (config.disableExternalRequests === false) { + homeView.generateDownloads(); + } } if (defaultConfig) { diff --git a/src/http-server/config.js b/src/http-server/config.js index 23eb7252..e9499acf 100644 --- a/src/http-server/config.js +++ b/src/http-server/config.js @@ -11,7 +11,8 @@ const experimentalWarnings = Object.entries(warnings) // CONSTANTS const kDefaultConfig = { defaultPackageMenu: "info", - ignore: { flags: [], warnings: experimentalWarnings } + ignore: { flags: [], warnings: experimentalWarnings }, + disableExternalRequests: false }; export async function get() { @@ -24,13 +25,23 @@ export async function get() { flags, warnings } = {}, - theme + theme, + disableExternalRequests = false } = config; logger.info( - `[config|get](defaultPackageMenu: ${defaultPackageMenu}|ignore-flag: ${flags}|ignore-warnings: ${warnings}|theme: ${theme})` + // eslint-disable-next-line @stylistic/max-len + `[config|get](defaultPackageMenu: ${defaultPackageMenu}|ignore-flag: ${flags}|ignore-warnings: ${warnings}|theme: ${theme}|disableExternalRequests${disableExternalRequests})` ); - return { defaultPackageMenu, ignore: { flags, warnings }, theme }; + return { + defaultPackageMenu, + ignore: { + flags, + warnings + }, + theme, + disableExternalRequests + }; } catch (err) { logger.error(`[config|get](error: ${err.message})`); diff --git a/test/config.test.js b/test/config.test.js index 022c7a74..6a777736 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -32,7 +32,8 @@ describe("config", { concurrency: 1 }, () => { defaultPackageMenu: "info", ignore: { flags: [], warnings: Object.entries(warnings) .filter(([_, { experimental }]) => experimental) - .map(([warning]) => warning) } + .map(([warning]) => warning) }, + disableExternalRequests: false }); }); @@ -43,7 +44,8 @@ describe("config", { concurrency: 1 }, () => { flags: ["foo"], warnings: ["bar"] }, - theme: "galaxy" + theme: "galaxy", + disableExternalRequests: true }; await cacache.put(CACHE_PATH, kConfigKey, JSON.stringify(expectedConfig)); const value = await get(); @@ -58,7 +60,8 @@ describe("config", { concurrency: 1 }, () => { flags: ["foz"], warnings: ["baz"] }, - theme: "galactic" + theme: "galactic", + disableExternalRequests: true }; await set(expectedConfig); const value = await get(); diff --git a/test/httpServer.test.js b/test/httpServer.test.js index 3c6ddba4..42fac9ce 100644 --- a/test/httpServer.test.js +++ b/test/httpServer.test.js @@ -218,7 +218,8 @@ describe("httpServer", { concurrency: 1 }, () => { flags: ["foo"], warnings: ["bar"] }, - theme: "galaxy" + theme: "galaxy", + disableExternalRequests: true }; await cacache.put(CACHE_PATH, kConfigKey, JSON.stringify(expectedConfig)); diff --git a/views/index.html b/views/index.html index fa758949..caead651 100644 --- a/views/index.html +++ b/views/index.html @@ -126,6 +126,11 @@

[[=z.token('settings.general.title')]]

+

[[=z.token('settings.general.security')]]:

+
+ + +

[[=z.token('settings.general.warnings')]]: