diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0431e45..fbb9fdd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,8 @@ updates: versioning-strategy: widen schedule: interval: "weekly" + cooldown: + default-days: 5 groups: dependencies: dependency-type: "production" @@ -14,6 +16,8 @@ updates: directory: "/" schedule: interval: "monthly" + cooldown: + default-days: 5 groups: github-actions: patterns: diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index a37ce13..3ec1ee4 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -14,7 +14,7 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - node-version: [20.x, 22.x] + node-version: [24.x] os: [ubuntu-latest] fail-fast: false steps: @@ -28,8 +28,10 @@ jobs: uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: ${{ matrix.node-version }} + - name: Install chromium + run: npx puppeteer browsers install chrome - name: Install dependencies - run: npm i + run: npm install --ignore-scripts - name: Lint run: npm run lint - name: Run tests @@ -47,4 +49,4 @@ jobs: - name: Merge Dependabot PR uses: fastify/github-action-merge-dependabot@e820d631adb1d8ab16c3b93e5afe713450884a4a # v3.11.1 with: - github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.npmrc b/.npmrc index 43c97e7..61cbf3f 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,3 @@ package-lock=false +save-exact=true +ignore-scripts=true diff --git a/README.md b/README.md index e9468f4..dc9bfee 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This project is designed to generate periodic security reports in both HTML and ## Requirements -- [Node.js](https://nodejs.org/en/) v20 or higher. +- [Node.js](https://nodejs.org/en/) v24 or higher. ## Getting Started diff --git a/bin/commands/execute.ts b/bin/commands/execute.ts index eeef4c5..0e6eb93 100644 --- a/bin/commands/execute.ts +++ b/bin/commands/execute.ts @@ -9,11 +9,11 @@ import * as rc from "@nodesecure/rc"; import kleur from "kleur"; // Import Internal Dependencies -import { store } from "../../src/localStorage.js"; +import { store } from "../../src/localStorage.ts"; -import { fetchPackagesAndRepositoriesData } from "../../src/analysis/fetch.js"; -import * as CONSTANTS from "../../src/constants.js"; -import * as reporting from "../../src/reporting/index.js"; +import { fetchPackagesAndRepositoriesData } from "../../src/analysis/fetch.ts"; +import * as CONSTANTS from "../../src/constants.ts"; +import * as reporting from "../../src/reporting/index.ts"; // CONSTANTS const kReadConfigOptions = { diff --git a/bin/commands/index.ts b/bin/commands/index.ts index 6fbbdaa..d5a2abd 100644 --- a/bin/commands/index.ts +++ b/bin/commands/index.ts @@ -1,2 +1,2 @@ -export * from "./execute.js"; -export * from "./init.js"; +export * from "./execute.ts"; +export * from "./init.ts"; diff --git a/bin/index.ts b/bin/index.ts index 5c55714..d608c3e 100644 --- a/bin/index.ts +++ b/bin/index.ts @@ -8,7 +8,7 @@ import sade from "sade"; import kleur from "kleur"; // Import Internal Dependencies -import * as commands from "./commands/index.js"; +import * as commands from "./commands/index.ts"; console.log(kleur.grey().bold(`\n > Executing nreport at: ${kleur.yellow().bold(process.cwd())}\n`)); diff --git a/package.json b/package.json index 3ac0fb6..d786b4f 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,11 @@ "build:views": "rimraf dist/views && cp -r views dist/views", "build:public": "rimraf dist/public && cp -r public dist/public", "lint": "eslint src test bin scripts", - "test-only": "glob -c \"tsx --test-reporter=spec --test\" \"./test/**/*.spec.ts\"", + "test-only": "node --test-reporter=spec --test ./test/**/*.spec.ts", "test": "c8 --all --src ./src -r html npm run test-only", - "test:e2e": "glob -c \"tsx -r dotenv/config --test-reporter=spec --test\" \"./test/**/*.e2e-spec.ts\"", - "preview:light": "tsx --no-warnings ./scripts/preview.js --theme light", - "preview:dark": "tsx --no-warnings ./scripts/preview.js --theme dark", + "test:e2e": "node -r dotenv/config --test-reporter=spec --test ./test/**/*.e2e-spec.ts", + "preview:light": "node --no-warnings ./scripts/preview.js --theme light", + "preview:dark": "node --no-warnings ./scripts/preview.js --theme dark", "prepublishOnly": "npm run build" }, "files": [ @@ -62,16 +62,14 @@ }, "devDependencies": { "@openally/config.eslint": "^2.1.0", - "@openally/config.typescript": "^1.0.3", + "@openally/config.typescript": "1.2.1", "@types/node": "^24.0.1", "c8": "^10.1.2", - "glob": "^11.0.0", "open": "^10.1.0", "rimraf": "^6.0.1", - "tsx": "^4.19.2", "typescript": "^5.7.2" }, "engines": { - "node": ">=20" + "node": ">=24" } } diff --git a/scripts/preview.ts b/scripts/preview.ts index da6e563..381e451 100644 --- a/scripts/preview.ts +++ b/scripts/preview.ts @@ -7,8 +7,8 @@ import { parseArgs } from "node:util"; import open from "open"; // Import Internal Dependencies -import { HTMLTemplateGenerator } from "../src/reporting/template.js"; -import { buildFrontAssets } from "../src/reporting/html.js"; +import { HTMLTemplateGenerator } from "../src/reporting/template.ts"; +import { buildFrontAssets } from "../src/reporting/html.ts"; // CONSTANTS const kPreviewDir = path.join(import.meta.dirname, "..", "preview"); diff --git a/src/analysis/extractScannerData.ts b/src/analysis/extractScannerData.ts index facf539..f57c3c7 100644 --- a/src/analysis/extractScannerData.ts +++ b/src/analysis/extractScannerData.ts @@ -9,7 +9,7 @@ import { Extractors, type Payload, type Dependency, type DependencyVersion, type import type { RC } from "@nodesecure/rc"; // Import Internal Dependencies -import * as localStorage from "../localStorage.js"; +import * as localStorage from "../localStorage.ts"; // CONSTANTS const kFlagsList = Object.values(getManifest()); diff --git a/src/analysis/fetch.ts b/src/analysis/fetch.ts index 49ddae1..46e024e 100644 --- a/src/analysis/fetch.ts +++ b/src/analysis/fetch.ts @@ -5,11 +5,11 @@ import path from "node:path"; import kleur from "kleur"; // Import Internal Dependencies -import { buildStatsFromScannerDependencies } from "./extractScannerData.js"; -import * as scanner from "./scanner.js"; -import * as localStorage from "../localStorage.js"; -import * as utils from "../utils/index.js"; -import * as CONSTANTS from "../constants.js"; +import { buildStatsFromScannerDependencies } from "./extractScannerData.ts"; +import * as scanner from "./scanner.ts"; +import * as localStorage from "../localStorage.ts"; +import * as utils from "../utils/index.ts"; +import * as CONSTANTS from "../constants.ts"; export async function fetchPackagesAndRepositoriesData( verbose = true diff --git a/src/analysis/scanner.ts b/src/analysis/scanner.ts index 8d0d1f2..05fc7c6 100644 --- a/src/analysis/scanner.ts +++ b/src/analysis/scanner.ts @@ -7,7 +7,7 @@ import { Mutex } from "@openally/mutex"; import * as scanner from "@nodesecure/scanner"; // Import Internal Dependencies -import * as CONSTANTS from "../constants.js"; +import * as CONSTANTS from "../constants.ts"; // CONSTANTS const kMaxAnalysisLock = new Mutex({ concurrency: 2 }); diff --git a/src/api/report.ts b/src/api/report.ts index 1cec85f..da74855 100644 --- a/src/api/report.ts +++ b/src/api/report.ts @@ -8,8 +8,8 @@ import { type Payload } from "@nodesecure/scanner"; import { type RC } from "@nodesecure/rc"; // Import Internal Dependencies -import { buildStatsFromScannerDependencies } from "../analysis/extractScannerData.js"; -import { HTML, PDF } from "../reporting/index.js"; +import { buildStatsFromScannerDependencies } from "../analysis/extractScannerData.ts"; +import { HTML, PDF } from "../reporting/index.ts"; export interface ReportLocationOptions { includesPDF: boolean; diff --git a/src/index.ts b/src/index.ts index 979ba58..9112958 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export * from "./api/report.js"; +export * from "./api/report.ts"; diff --git a/src/reporting/html.ts b/src/reporting/html.ts index d6d4680..3de18ec 100644 --- a/src/reporting/html.ts +++ b/src/reporting/html.ts @@ -7,11 +7,11 @@ import esbuild from "esbuild"; import type { RC } from "@nodesecure/rc"; // Import Internal Dependencies -import * as utils from "../utils/index.js"; -import * as CONSTANTS from "../constants.js"; -import * as localStorage from "../localStorage.js"; -import { HTMLTemplateGenerator } from "./template.js"; -import type { ReportStat } from "../analysis/extractScannerData.js"; +import * as utils from "../utils/index.ts"; +import * as CONSTANTS from "../constants.ts"; +import * as localStorage from "../localStorage.ts"; +import { HTMLTemplateGenerator } from "./template.ts"; +import type { ReportStat } from "../analysis/extractScannerData.ts"; // CONSTANTS const kDateFormatter = Intl.DateTimeFormat("en-GB", { diff --git a/src/reporting/index.ts b/src/reporting/index.ts index bcdf246..8268613 100644 --- a/src/reporting/index.ts +++ b/src/reporting/index.ts @@ -2,12 +2,12 @@ import kleur from "kleur"; // Import Internal Dependencies -import * as utils from "../utils/index.js"; -import * as localStorage from "../localStorage.js"; +import * as utils from "../utils/index.ts"; +import * as localStorage from "../localStorage.ts"; // Import Reporters -import { HTML, type HTMLReportData } from "./html.js"; -import { PDF } from "./pdf.js"; +import { HTML, type HTMLReportData } from "./html.ts"; +import { PDF } from "./pdf.ts"; export async function proceed( data: HTMLReportData, diff --git a/src/reporting/pdf.ts b/src/reporting/pdf.ts index f91e908..7b7c608 100644 --- a/src/reporting/pdf.ts +++ b/src/reporting/pdf.ts @@ -6,8 +6,8 @@ import { pathToFileURL } from "node:url"; import puppeteer from "puppeteer"; // Import Internal Dependencies -import * as CONSTANTS from "../constants.js"; -import * as utils from "../utils/index.js"; +import * as CONSTANTS from "../constants.ts"; +import * as utils from "../utils/index.ts"; export interface PDFReportOptions { title: string; diff --git a/src/reporting/template.ts b/src/reporting/template.ts index 3999718..38f85e4 100644 --- a/src/reporting/template.ts +++ b/src/reporting/template.ts @@ -7,10 +7,10 @@ import compile from "zup"; import type { RC } from "@nodesecure/rc"; // Import Internal Dependencies -import * as utils from "../utils/index.js"; -import * as CONSTANTS from "../constants.js"; -import * as localStorage from "../localStorage.js"; -import type { ReportStat } from "../analysis/extractScannerData.js"; +import * as utils from "../utils/index.ts"; +import * as CONSTANTS from "../constants.ts"; +import * as localStorage from "../localStorage.ts"; +import type { ReportStat } from "../analysis/extractScannerData.ts"; const kHTMLStaticTemplate = readFileSync( path.join(CONSTANTS.DIRS.VIEWS, "template.html"), diff --git a/src/utils/charts.ts b/src/utils/charts.ts index 8d78a97..090ea4c 100644 --- a/src/utils/charts.ts +++ b/src/utils/charts.ts @@ -3,7 +3,7 @@ import { taggedString } from "@nodesecure/utils"; import type { RC } from "@nodesecure/rc"; // Import Internal Dependencies -import type { ReportStat } from "../analysis/extractScannerData.js"; +import type { ReportStat } from "../analysis/extractScannerData.ts"; // CONSTANTS const kChartTemplate = taggedString`\tcreateChart("${0}", "${4}", { labels: [${1}], interpolate: ${3}, data: [${2}] });`; diff --git a/src/utils/index.ts b/src/utils/index.ts index ba94754..a0e9e9a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,5 +1,5 @@ -export * from "./cleanReportName.js"; -export * from "./cloneGITRepository.js"; -export * from "./runInSpinner.js"; -export * from "./formatNpmPackages.js"; -export * from "./charts.js"; +export * from "./cleanReportName.ts"; +export * from "./cloneGITRepository.ts"; +export * from "./runInSpinner.ts"; +export * from "./formatNpmPackages.ts"; +export * from "./charts.ts"; diff --git a/test/api/report.spec.ts b/test/api/report.spec.ts index b5d0d5a..e708019 100644 --- a/test/api/report.spec.ts +++ b/test/api/report.spec.ts @@ -10,7 +10,7 @@ import assert from "node:assert"; import { from } from "@nodesecure/scanner"; // Import Internal Dependencies -import { report } from "../../src/index.js"; +import { report } from "../../src/index.ts"; // CONSTANTS const kReportPayload = { diff --git a/test/commands/execute.e2e-spec.ts b/test/commands/execute.e2e-spec.ts index d9129b2..0269db0 100644 --- a/test/commands/execute.e2e-spec.ts +++ b/test/commands/execute.e2e-spec.ts @@ -1,63 +1,63 @@ -// Import Node.js Dependencies -import { fileURLToPath } from "node:url"; -import path from "node:path"; -import fs from "node:fs/promises"; -import { afterEach, describe, it } from "node:test"; -import assert from "node:assert"; -import { stripVTControlCharacters } from "node:util"; - -// Import Internal Dependencies -import { filterProcessStdout } from "../helpers/reportCommandRunner.js"; -import * as CONSTANTS from "../../src/constants.js"; - -// CONSTANTS -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const kProcessDir = path.join(__dirname, "../.."); - -describe("Report execute command", () => { - afterEach(async() => await fs.rm(CONSTANTS.DIRS.CLONES, { - recursive: true, force: true - })); - - it("should execute command on fixture '.nodesecurerc'", async() => { - const options = { - cmd: "node", - args: ["dist/bin/index.js", "execute"], - cwd: kProcessDir - }; - - function byMessage(buffer) { - const message = ".*"; - const afterNonAlphaNum = String.raw`?<=[^a-zA-Z\d\s:]\s`; - const beforeTime = String.raw`?=\s\d{1,5}.\d{1,4}ms`; - const withoutDuplicates = String.raw`(?![\s\S]*\1)`; - - const matchMessage = `(${afterNonAlphaNum})(${message})(${beforeTime})|(${afterNonAlphaNum})(${message})`; - const reg = new RegExp(`(${matchMessage})${withoutDuplicates}`, "g"); - - const matchedMessages = stripVTControlCharacters(buffer.toString()).match(reg); - - return matchedMessages ?? [""]; - } - - const expectedLines = [ - `Executing nreport at: ${kProcessDir}`, - "title: Default report title", - "reporters: html,pdf", - "[Fetcher: NPM] - Fetching NPM packages metadata on the NPM Registry", - "", - "[Fetcher: NPM] - successfully executed in", - "[Fetcher: GIT] - Cloning GIT repositories", - "[Fetcher: GIT] - Fetching repositories metadata on the NPM Registry", - "[Fetcher: GIT] - successfully executed in", - "[Reporter: HTML] - Building template and assets", - "[Reporter: HTML] - successfully executed in", - "[Reporter: PDF] - Using puppeteer to convert HTML content to PDF", - "[Reporter: PDF] - successfully executed in", - "Security report successfully generated! Enjoy 🚀." - ]; - - const actualLines = await filterProcessStdout(options, byMessage); - assert.deepEqual(actualLines, expectedLines, "we are expecting these lines"); - }); -}); +// Import Node.js Dependencies +import { fileURLToPath } from "node:url"; +import path from "node:path"; +import fs from "node:fs/promises"; +import { afterEach, describe, it } from "node:test"; +import assert from "node:assert"; +import { stripVTControlCharacters } from "node:util"; + +// Import Internal Dependencies +import { filterProcessStdout } from "../helpers/reportCommandRunner.ts"; +import * as CONSTANTS from "../../src/constants.ts"; + +// CONSTANTS +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const kProcessDir = path.join(__dirname, "../.."); + +describe("Report execute command", () => { + afterEach(async() => await fs.rm(CONSTANTS.DIRS.CLONES, { + recursive: true, force: true + })); + + it("should execute command on fixture '.nodesecurerc'", async() => { + const options = { + cmd: "node", + args: ["dist/bin/index.ts", "execute"], + cwd: kProcessDir + }; + + function byMessage(buffer) { + const message = ".*"; + const afterNonAlphaNum = String.raw`?<=[^a-zA-Z\d\s:]\s`; + const beforeTime = String.raw`?=\s\d{1,5}.\d{1,4}ms`; + const withoutDuplicates = String.raw`(?![\s\S]*\1)`; + + const matchMessage = `(${afterNonAlphaNum})(${message})(${beforeTime})|(${afterNonAlphaNum})(${message})`; + const reg = new RegExp(`(${matchMessage})${withoutDuplicates}`, "g"); + + const matchedMessages = stripVTControlCharacters(buffer.toString()).match(reg); + + return matchedMessages ?? [""]; + } + + const expectedLines = [ + `Executing nreport at: ${kProcessDir}`, + "title: Default report title", + "reporters: html,pdf", + "[Fetcher: NPM] - Fetching NPM packages metadata on the NPM Registry", + "", + "[Fetcher: NPM] - successfully executed in", + "[Fetcher: GIT] - Cloning GIT repositories", + "[Fetcher: GIT] - Fetching repositories metadata on the NPM Registry", + "[Fetcher: GIT] - successfully executed in", + "[Reporter: HTML] - Building template and assets", + "[Reporter: HTML] - successfully executed in", + "[Reporter: PDF] - Using puppeteer to convert HTML content to PDF", + "[Reporter: PDF] - successfully executed in", + "Security report successfully generated! Enjoy 🚀." + ]; + + const actualLines = await filterProcessStdout(options, byMessage); + assert.deepEqual(actualLines, expectedLines, "we are expecting these lines"); + }); +}); diff --git a/test/commands/initialize.e2e-spec.ts b/test/commands/initialize.e2e-spec.ts index bdd58c9..62e4a2e 100644 --- a/test/commands/initialize.e2e-spec.ts +++ b/test/commands/initialize.e2e-spec.ts @@ -1,50 +1,50 @@ -// Import Node.js Dependencies -import { fileURLToPath } from "node:url"; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import { before, describe, it } from "node:test"; -import assert from "node:assert"; -import { stripVTControlCharacters } from "node:util"; - -// Import Internal Dependencies -import { runProcess } from "../helpers/reportCommandRunner.js"; - -// CONSTANTS -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const kBinDir = path.join(__dirname, "../..", "dist/bin/index.js"); -const kProcessDir = os.tmpdir(); -const kConfigFilePath = path.join(kProcessDir, ".nodesecurerc"); - -describe("Report init command if config does not exists", () => { - before(() => { - if (fs.existsSync(kConfigFilePath)) { - fs.unlinkSync(kConfigFilePath); - } - }); - - it("should create config if not exists", async() => { - const lines = [ - /.*/, - / > Executing nreport at: .*$/, - /.*/, - /Successfully generated NodeSecure runtime configuration at current location/, - /.*/ - ]; - - const processOptions = { - cmd: "node", - args: [kBinDir, "initialize"], - cwd: kProcessDir - }; - - for await (const line of runProcess(processOptions)) { - const regexp = lines.shift(); - assert.ok(regexp, "we are expecting this line"); - assert.ok(regexp.test(stripVTControlCharacters(line)), `line (${line}) matches ${regexp}`); - } - - // to prevent false positive if no lines have been emitted from process - assert.equal(lines.length, 0); - }); -}); +// Import Node.js Dependencies +import { fileURLToPath } from "node:url"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { before, describe, it } from "node:test"; +import assert from "node:assert"; +import { stripVTControlCharacters } from "node:util"; + +// Import Internal Dependencies +import { runProcess } from "../helpers/reportCommandRunner.ts"; + +// CONSTANTS +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const kBinDir = path.join(__dirname, "../..", "dist/bin/index.ts"); +const kProcessDir = os.tmpdir(); +const kConfigFilePath = path.join(kProcessDir, ".nodesecurerc"); + +describe("Report init command if config does not exists", () => { + before(() => { + if (fs.existsSync(kConfigFilePath)) { + fs.unlinkSync(kConfigFilePath); + } + }); + + it("should create config if not exists", async() => { + const lines = [ + /.*/, + / > Executing nreport at: .*$/, + /.*/, + /Successfully generated NodeSecure runtime configuration at current location/, + /.*/ + ]; + + const processOptions = { + cmd: "node", + args: [kBinDir, "initialize"], + cwd: kProcessDir + }; + + for await (const line of runProcess(processOptions)) { + const regexp = lines.shift(); + assert.ok(regexp, "we are expecting this line"); + assert.ok(regexp.test(stripVTControlCharacters(line)), `line (${line}) matches ${regexp}`); + } + + // to prevent false positive if no lines have been emitted from process + assert.equal(lines.length, 0); + }); +}); diff --git a/test/utils/cleanReportName.spec.ts b/test/utils/cleanReportName.spec.ts index d164f1e..82381f2 100644 --- a/test/utils/cleanReportName.spec.ts +++ b/test/utils/cleanReportName.spec.ts @@ -3,7 +3,7 @@ import { describe, it } from "node:test"; import assert from "node:assert"; // Import Internal Dependencies -import { cleanReportName } from "../../src/utils/index.js"; +import { cleanReportName } from "../../src/utils/index.ts"; describe("cleanReportName", () => { it("should remove invalid Windows characters", () => { diff --git a/test/utils/cloneGITRepository.spec.ts b/test/utils/cloneGITRepository.spec.ts index 7935c1e..90bcd11 100644 --- a/test/utils/cloneGITRepository.spec.ts +++ b/test/utils/cloneGITRepository.spec.ts @@ -6,7 +6,7 @@ import path from "node:path"; import assert from "node:assert"; // Import Internal Dependencies -import { cloneGITRepository } from "../../src/utils/index.js"; +import { cloneGITRepository } from "../../src/utils/index.ts"; describe("cloneGITRepository", () => { it("should clone a remote GIT repository", async() => { diff --git a/test/utils/formatNpmPackages.spec.ts b/test/utils/formatNpmPackages.spec.ts index 7ac711a..e77fa4e 100644 --- a/test/utils/formatNpmPackages.spec.ts +++ b/test/utils/formatNpmPackages.spec.ts @@ -3,7 +3,7 @@ import { describe, test } from "node:test"; import assert from "node:assert"; // Import Internal Dependencies -import { formatNpmPackages } from "../../src/utils/index.js"; +import { formatNpmPackages } from "../../src/utils/index.ts"; describe("formatNpmPackages", () => { test("If no organizationPrefix is provided, it should return the packages list as is.", () => { diff --git a/tsconfig.json b/tsconfig.json index ce2f747..791f9b5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@openally/config.typescript", + "extends": "@openally/config.typescript/esm-ts-next", "compilerOptions": { "outDir": "dist" },