diff --git a/README.md b/README.md
index 0671430e..f0e81faf 100644
--- a/README.md
+++ b/README.md
@@ -112,6 +112,7 @@ For complete details on each command, refer to the following documents:
- [`config`](./docs/cli/config.md)
- [`cache`](./docs/cli/cache.md)
- [`extract integrity`](./docs/cli/extract-integrity.md)
+- [`stats`](./docs/cli/stats.md)
Each link provides access to the full documentation for the command, including additional details, options, and usage examples.
diff --git a/bin/index.js b/bin/index.js
index 6aa098d2..b8c341d7 100755
--- a/bin/index.js
+++ b/bin/index.js
@@ -135,6 +135,12 @@ prog
.example("nsecure extract integrity lodash@^4.1.2")
.action(commands.extractIntegrity.main);
+prog
+ .command("stats")
+ .describe(i18n.getTokenSync("cli.commands.stats.desc"))
+ .example("nsecure stats")
+ .action(commands.stats.main);
+
prog.parse(process.argv);
function defaultScannerCommand(name, options = {}) {
diff --git a/docs/cli/images/stats.PNG b/docs/cli/images/stats.PNG
new file mode 100644
index 00000000..baae072f
Binary files /dev/null and b/docs/cli/images/stats.PNG differ
diff --git a/docs/cli/stats.md b/docs/cli/stats.md
new file mode 100644
index 00000000..27bf143e
--- /dev/null
+++ b/docs/cli/stats.md
@@ -0,0 +1,19 @@
+## 📝 Command `stats`
+
+The `stats` displays the statistics of the last performed scan such as :
+
+- The total execution time of the scan
+- The total number of API calls made
+- every stats about the API calls made
+- The total number of errors encountered
+- every error encountered
+
+
+
+
+
+## 📜 Syntax
+
+```bash
+$ nsecure stats
+```
diff --git a/i18n/arabic.js b/i18n/arabic.js
index 18ff9c0d..25227d35 100644
--- a/i18n/arabic.js
+++ b/i18n/arabic.js
@@ -8,7 +8,7 @@ const cli = {
successfully_written_json: tS`تم كتابة ملف النتائج بنجاح في: ${0}`,
http_server_started: "تم تشغيل خادم HTTP على:",
missingEnv: tS`متغير البيئة ${0} مفقود!`,
- stat: tS`${0} ${1} في ${2}`,
+ stat: tS`${0}${1} في ${2}`,
error: {
name: tS`اسم ${0}: ${1}`,
message: tS`الرسالة: ${0}`,
@@ -88,6 +88,13 @@ const cli = {
missingSpecVersion: tS`يجب تحديد إصدار لحزمة '${0}'.`,
invalidSpec: tS`مواصفات الحزمة '${0}' غير صالحة.`,
specNotFound: tS`لم يتم العثور على مواصفات الحزمة '${0}' في سجل npm.`
+ },
+ stats: {
+ desc: "عرض إحصائيات المسح.",
+ elapsed: tS`مدة المسح: ${0}`,
+ stats: tS`عدد استدعاءات API: ${0}`,
+ error: "يجب إجراء مسح قبل عرض الإحصائيات.",
+ errors: tS`عدد الأخطاء: ${0}`
}
},
startHttp: {
diff --git a/i18n/english.js b/i18n/english.js
index 85a4f833..525ab570 100644
--- a/i18n/english.js
+++ b/i18n/english.js
@@ -10,7 +10,7 @@ const cli = {
successfully_written_json: tS`Successfully written results file at: ${0}`,
http_server_started: "HTTP Server started on:",
missingEnv: tS`Environment variable ${0} is missing!`,
- stat: tS`${0} ${1} in ${2}`,
+ stat: tS`${0}${1} in ${2}`,
error: {
name: tS`${0} name: ${1}`,
message: tS`Message: ${0}`,
@@ -90,6 +90,13 @@ const cli = {
missingSpecVersion: tS`You must specify a version for '${0}' package.`,
invalidSpec: tS`The package spec '${0}' is invalid.`,
specNotFound: tS`The package spec '${0}' could not be found from the npm registry.`
+ },
+ stats: {
+ desc: "Display the stats of a scan.",
+ elapsed: tS`Scan duration: ${0}`,
+ stats: tS`API calls count: ${0}`,
+ error: "A scan must be performed before displaying stats.",
+ errors: tS`Error count: ${0}`
}
},
startHttp: {
diff --git a/i18n/french.js b/i18n/french.js
index 419ca2f3..c20c78d6 100644
--- a/i18n/french.js
+++ b/i18n/french.js
@@ -10,7 +10,7 @@ const cli = {
successfully_written_json: tS`Ecriture du fichier de résultats réalisée avec succès ici : ${0}`,
http_server_started: "Serveur HTTP démarré sur :",
missingEnv: tS`La variable d'environnement ${0} est manquante!`,
- stat: tS`${0} ${1} en ${2}`,
+ stat: tS`${0}${1} en ${2}`,
error: {
name: tS`Nom ${0}: ${1}`,
message: tS`Message: ${0}`,
@@ -90,6 +90,13 @@ const cli = {
missingSpecVersion: tS`Vous devez spécifier une version pour le package '${0}'.`,
invalidSpec: tS`La spécification '${0}' est invalide.`,
specNotFound: tS`La spécification '${0}' n'a pas pu être trouvée dans le registre npm.`
+ },
+ stats: {
+ desc: "Afficher les statistiques d'un scan.",
+ elapsed: tS`Durée du scan: ${0}`,
+ stats: tS`Nombre d'appels API: ${0}`,
+ error: "Un scan doit être effectué avant d'afficher les statistiques.",
+ errors: tS`Nombre d'erreurs: ${0}`
}
},
startHttp: {
diff --git a/i18n/turkish.js b/i18n/turkish.js
index e1c26b80..636db10c 100644
--- a/i18n/turkish.js
+++ b/i18n/turkish.js
@@ -10,7 +10,7 @@ const cli = {
successfully_written_json: tS`Sonuç dosyası başarıyla yazıldı: ${0}`,
http_server_started: "HTTP Sunucusu başlatıldı:",
missingEnv: tS`${0} ortam değişkeni eksik!`,
- stat: tS`${0} ${1} içinde ${2}`,
+ stat: tS`${0}${1} içinde ${2}`,
error: {
name: tS`${0} adı: ${1}`,
message: tS`Mesaj: ${0}`,
@@ -90,6 +90,13 @@ const cli = {
missingSpecVersion: tS`'${0}' paketi için bir sürüm belirtmelisiniz.`,
invalidSpec: tS`'${0}' paket özelliği geçersiz.`,
specNotFound: tS`'${0}' paket özelliği npm kayıt defterinde bulunamadı.`
+ },
+ stats: {
+ desc: "Bir taramanın istatistiklerini görüntüle.",
+ elapsed: tS`Tarama süresi: ${0}`,
+ stats: tS`API çağrı sayısı: ${0}`,
+ error: "İstatistikleri görüntülemeden önce bir tarama yapılmalıdır.",
+ errors: tS`Hata sayısı: ${0}`
}
},
startHttp: {
diff --git a/src/commands/index.js b/src/commands/index.js
index 7d08a433..f0ae1341 100644
--- a/src/commands/index.js
+++ b/src/commands/index.js
@@ -8,3 +8,4 @@ export * as scorecard from "./scorecard.js";
export * as report from "./report.js";
export * as cache from "./cache.js";
export * as extractIntegrity from "./extract-integrity.js";
+export * as stats from "./stats.js";
diff --git a/src/commands/loggers/logger.js b/src/commands/loggers/logger.js
new file mode 100644
index 00000000..27fd2a3f
--- /dev/null
+++ b/src/commands/loggers/logger.js
@@ -0,0 +1,78 @@
+// Import Third-party Dependencies
+import * as i18n from "@nodesecure/i18n";
+import ms from "ms";
+
+// Import Internal Dependencies
+import kleur from "../../utils/styleText.js";
+
+export function log(token, ...args) {
+ console.log(kleur.white().bold(i18n.getTokenSync(token, ...args)));
+}
+
+export function logError(token, ...args) {
+ console.log(kleur.red().bold(i18n.getTokenSync(token, ...args)));
+}
+
+export function logScannerStat(stat, isVerbose = true) {
+ console.log(kleur.bold.white(
+ i18n.getTokenSync("cli.stat",
+ isVerbose ? kleur.blue().bold("verbose ") : "",
+ stat.name,
+ colorExecutionTime(stat.executionTime)
+ )));
+}
+
+export function logScannerError(error, phase) {
+ console.log(kleur.bold.white(
+ i18n.getTokenSync("cli.error.name",
+ kleur.red().bold("error"),
+ error.name
+ )));
+
+ if (error.message) {
+ console.log(i18n.getTokenSync("cli.error.message",
+ error.message
+ ));
+ }
+
+ if (phase) {
+ console.log(i18n.getTokenSync("cli.error.phase",
+ phase
+ ));
+ }
+
+ if (error.statusCode) {
+ console.log(i18n.getTokenSync("cli.error.statusCode",
+ error.statusCode
+ ));
+ }
+
+ console.log(i18n.getTokenSync("cli.error.executionTime",
+ colorExecutionTime(error.executionTime)
+ ));
+
+ if (error.stack) {
+ console.log(i18n.getTokenSync("cli.error.stack",
+ error.stack
+ ));
+ }
+}
+
+export function formatMs(time) {
+ return ms(Number(time.toFixed(2)));
+}
+
+function colorExecutionTime(timeMs) {
+ const formatted = formatMs(timeMs);
+ if (timeMs <= 1_000) {
+ return kleur.green().bold(formatted);
+ }
+ else if (timeMs <= 5_000) {
+ return kleur.cyan().bold(formatted);
+ }
+ else if (timeMs <= 30_000) {
+ return kleur.yellow().bold(formatted);
+ }
+
+ return kleur.red().bold(formatted);
+}
diff --git a/src/commands/scanner.js b/src/commands/scanner.js
index ba48a2b5..adbf20d1 100644
--- a/src/commands/scanner.js
+++ b/src/commands/scanner.js
@@ -7,13 +7,13 @@ import events from "node:events";
import semver from "semver";
import filenamify from "filenamify";
import { Spinner } from "@topcli/spinner";
-import ms from "ms";
import * as i18n from "@nodesecure/i18n";
import * as scanner from "@nodesecure/scanner";
// Import Internal Dependencies
import kleur from "../utils/styleText.js";
import * as http from "./http.js";
+import { logScannerStat, logScannerError, formatMs } from "./loggers/logger.js";
import { parseContacts } from "./parsers/contacts.js";
export async function auto(spec, options) {
@@ -192,77 +192,19 @@ function initLogger(spec, verbose = true) {
logger.on("stat", (stat) => {
stopSpinners();
- console.log(kleur.bold.white(
- i18n.getTokenSync("cli.stat",
- kleur.blue().bold("verbose"),
- stat.name,
- colorExecutionTime(stat.executionTime)
- )));
+ logScannerStat(stat);
startSpinners();
});
logger.on("error", (error, phase) => {
stopSpinners();
-
- console.log(kleur.bold.white(
- i18n.getTokenSync("cli.error.name",
- kleur.red().bold("error"),
- error.name
- )));
-
- if (error.message) {
- console.log(i18n.getTokenSync("cli.error.message",
- error.message
- ));
- }
-
- if (phase) {
- console.log(i18n.getTokenSync("cli.error.phase",
- phase
- ));
- }
-
- if (error.statusCode) {
- console.log(i18n.getTokenSync("cli.error.statusCode",
- error.statusCode
- ));
- }
-
- console.log(i18n.getTokenSync("cli.error.executionTime",
- kleur.cyan().bold(formatMs(error.executionTime))
- ));
-
- if (error.stack) {
- console.log(i18n.getTokenSync("cli.error.stack",
- error.stack
- ));
- }
-
+ logScannerError(error, phase);
startSpinners();
});
return logger;
}
-function formatMs(time) {
- return ms(Number(time.toFixed(2)));
-}
-
-function colorExecutionTime(timeMs) {
- const formatted = formatMs(timeMs);
- if (timeMs <= 1_000) {
- return kleur.green().bold(formatted);
- }
- else if (timeMs <= 5_000) {
- return kleur.cyan().bold(formatted);
- }
- else if (timeMs <= 30_000) {
- return kleur.yellow().bold(formatted);
- }
-
- return kleur.red().bold(formatted);
-}
-
function stopSpinners() {
spinners.forEach((spinner) => {
if (!spinner.succeeded) {
diff --git a/src/commands/stats.js b/src/commands/stats.js
new file mode 100644
index 00000000..a8cd5051
--- /dev/null
+++ b/src/commands/stats.js
@@ -0,0 +1,42 @@
+// Import Node.js Dependencies
+import { readFile } from "node:fs/promises";
+import path from "node:path";
+
+// Import Internal Dependencies
+import { logScannerStat, logScannerError, log, logError, formatMs } from "./loggers/logger.js";
+
+export async function main(options) {
+ const { getScanResult = getScanFromFile, logger = {
+ logScannerStat,
+ logScannerError,
+ log,
+ logError
+ } } = options;
+ try {
+ const scanResult = await getScanResult();
+ const { metadata } = scanResult;
+
+ logger.log("cli.commands.stats.elapsed", formatMs(metadata.executionTime));
+ logger.log("cli.commands.stats.stats", metadata.apiCallsCount);
+ metadata.apiCalls.forEach((call) => {
+ logger.logScannerStat(call, false);
+ });
+ if (metadata.errorCount === 0) {
+ return;
+ }
+ logger.log("cli.commands.stats.errors", metadata.errorCount);
+ metadata.errors.forEach((error) => {
+ logger.logScannerError(error);
+ });
+ }
+ catch {
+ logger.logError("cli.commands.stats.error");
+ }
+}
+
+async function getScanFromFile() {
+ const projectRootDir = path.join(import.meta.dirname, "..", "..");
+ const filePath = path.join(projectRootDir, "nsecure-result.json");
+
+ return JSON.parse(await readFile(filePath, "utf8"));
+}
diff --git a/test/commands/stats.test.js b/test/commands/stats.test.js
new file mode 100644
index 00000000..a6f7ad27
--- /dev/null
+++ b/test/commands/stats.test.js
@@ -0,0 +1,119 @@
+// Import Node.js Dependencies
+import assert from "node:assert";
+import { readFile } from "node:fs/promises";
+import path from "node:path";
+import { describe, test } from "node:test";
+
+// Import Internal Dependencies
+import { main } from "../../src/commands/stats.js";
+
+describe("stats", () => {
+ test("it should log stats and errors from the scan result", async(t) => {
+ const scanResult = JSON.parse(await readFile(path.join(import.meta.dirname, "..", "fixtures", "result-test3.json"), "utf8"));
+
+ async function getScanResult() {
+ return Promise.resolve(scanResult);
+ }
+
+ const logger = {
+ logScannerStat: t.mock.fn(),
+ logScannerError: t.mock.fn(),
+ log: t.mock.fn(),
+ logError: t.mock.fn()
+ };
+
+ await main({
+ getScanResult,
+ logger
+ });
+
+ assert.deepEqual(logger.log.mock.calls[0].arguments, ["cli.commands.stats.elapsed", "771ms"]);
+ assert.deepEqual(logger.log.mock.calls[1].arguments, ["cli.commands.stats.stats", 3]);
+ assert.deepEqual(logger.logScannerStat.mock.calls[0].arguments, [{
+ name: "pacote.manifest react@19.2.4",
+ startedAt: 1774601089504,
+ executionTime: 20
+ }, false]);
+ assert.deepEqual(logger.logScannerStat.mock.calls[1].arguments, [{
+ name: "pacote.extract react@19.2.4",
+ startedAt: 1774601089529,
+ executionTime: 83
+ }, false]);
+ assert.deepEqual(logger.logScannerStat.mock.calls[2].arguments, [{
+ name: "tarball.scanDirOrArchive react@19.2.4",
+ startedAt: 1774601089612,
+ executionTime: 247
+ }, false]);
+ assert.deepEqual(logger.log.mock.calls[2].arguments, ["cli.commands.stats.errors", 2]);
+ assert.deepEqual(logger.logScannerError.mock.calls[0].arguments, [{
+ name: "pacote.extract react@19.2.4"
+ }]);
+ assert.deepEqual(logger.logScannerError.mock.calls[1].arguments, [{
+ name: "tarball.scanDirOrArchive react@19.2.4",
+ message: "something went wrong !"
+ }]);
+ });
+
+ test("should display an error message when no scan has been done", async(t) => {
+ async function getScanResult() {
+ throw new Error("file does not exist");
+ }
+
+ const logger = {
+ logScannerStat: t.mock.fn(),
+ logScannerError: t.mock.fn(),
+ log: t.mock.fn(),
+ logError: t.mock.fn()
+ };
+
+ await main({
+ getScanResult,
+ logger
+ });
+
+ assert.deepEqual(logger.logError.mock.calls[0].arguments, ["cli.commands.stats.error"]);
+ assert.strictEqual(logger.log.mock.callCount(), 0);
+ assert.strictEqual(logger.logScannerStat.mock.callCount(), 0);
+ assert.strictEqual(logger.logScannerError.mock.callCount(), 0);
+ });
+
+ test("should not log the error part when there is none", async(t) => {
+ const scanResult = JSON.parse(await readFile(path.join(import.meta.dirname, "..", "fixtures", "result-test4.json"), "utf8"));
+
+ async function getScanResult() {
+ return Promise.resolve(scanResult);
+ }
+
+ const logger = {
+ logScannerStat: t.mock.fn(),
+ logScannerError: t.mock.fn(),
+ log: t.mock.fn(),
+ logError: t.mock.fn()
+ };
+
+ await main({
+ getScanResult,
+ logger
+ });
+
+ assert.deepEqual(logger.log.mock.calls[0].arguments, ["cli.commands.stats.elapsed", "771ms"]);
+ assert.deepEqual(logger.log.mock.calls[1].arguments, ["cli.commands.stats.stats", 3]);
+ assert.deepEqual(logger.logScannerStat.mock.calls[0].arguments, [{
+ name: "pacote.manifest react@19.2.4",
+ startedAt: 1774601089504,
+ executionTime: 20
+ }, false]);
+ assert.deepEqual(logger.logScannerStat.mock.calls[1].arguments, [{
+ name: "pacote.extract react@19.2.4",
+ startedAt: 1774601089529,
+ executionTime: 83
+ }, false]);
+ assert.deepEqual(logger.logScannerStat.mock.calls[2].arguments, [{
+ name: "tarball.scanDirOrArchive react@19.2.4",
+ startedAt: 1774601089612,
+ executionTime: 247
+ }, false]);
+ assert.equal(logger.log.mock.calls[2], undefined);
+ });
+});
+
diff --git a/test/fixtures/result-test3.json b/test/fixtures/result-test3.json
new file mode 100644
index 00000000..8dff31a6
--- /dev/null
+++ b/test/fixtures/result-test3.json
@@ -0,0 +1,1028 @@
+{
+ "id": "CBuXRl",
+ "rootDependency": {
+ "name": "react",
+ "version": "19.2.4",
+ "integrity": "sha512-Lfi0RfVh0gdrtD6yI6AInys+HY8Ki8E/SlIpUYm3gXzlKOXW7zvRMMC6sMQ3+3ww+ZSF5cTnOafW2T5HjtwQmg=="
+ },
+ "scannerVersion": "10.7.0",
+ "vulnerabilityStrategy": "github-advisory",
+ "warnings": [],
+ "highlighted": {
+ "contacts": [],
+ "packages": [],
+ "identifiers": []
+ },
+ "dependencies": {
+ "react": {
+ "versions": {
+ "19.2.4": {
+ "id": 0,
+ "type": "cjs",
+ "usedBy": {},
+ "isDevDependency": false,
+ "existOnRemoteRegistry": true,
+ "flags": [
+ "hasMissingOrUnusedDependency",
+ "hasWarnings"
+ ],
+ "warnings": [
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 310,
+ 10
+ ],
+ [
+ 310,
+ 30
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react.production.js"
+ },
+ {
+ "kind": "insecure-random",
+ "location": [
+ [
+ 529,
+ 43
+ ],
+ [
+ 529,
+ 56
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.insecure_random",
+ "severity": "Information",
+ "experimental": false,
+ "value": null,
+ "file": "cjs/react.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 17,
+ 10
+ ],
+ [
+ 21,
+ 11
+ ]
+ ],
+ [
+ [
+ 413,
+ 14
+ ],
+ [
+ 415,
+ 15
+ ]
+ ],
+ [
+ [
+ 1014,
+ 10
+ ],
+ [
+ 1016,
+ 11
+ ]
+ ],
+ [
+ [
+ 1176,
+ 12
+ ],
+ [
+ 1178,
+ 13
+ ]
+ ],
+ [
+ [
+ 1222,
+ 8
+ ],
+ [
+ 1224,
+ 9
+ ]
+ ],
+ [
+ [
+ 1238,
+ 8
+ ],
+ [
+ 1240,
+ 9
+ ]
+ ],
+ [
+ [
+ 1245,
+ 8
+ ],
+ [
+ 1247,
+ 9
+ ]
+ ],
+ [
+ [
+ 40,
+ 9
+ ],
+ [
+ 44,
+ 9
+ ]
+ ],
+ [
+ [
+ 112,
+ 12
+ ],
+ [
+ 114,
+ 13
+ ]
+ ],
+ [
+ [
+ 179,
+ 10
+ ],
+ [
+ 182,
+ 11
+ ]
+ ],
+ [
+ [
+ 194,
+ 8
+ ],
+ [
+ 196,
+ 9
+ ]
+ ],
+ [
+ [
+ 502,
+ 12
+ ],
+ [
+ 505,
+ 13
+ ]
+ ],
+ [
+ [
+ 507,
+ 12
+ ],
+ [
+ 510,
+ 13
+ ]
+ ],
+ [
+ [
+ 518,
+ 8
+ ],
+ [
+ 520,
+ 9
+ ]
+ ],
+ [
+ [
+ 539,
+ 16
+ ],
+ [
+ 541,
+ 17
+ ]
+ ],
+ [
+ [
+ 556,
+ 8
+ ],
+ [
+ 558,
+ 9
+ ]
+ ],
+ [
+ [
+ 741,
+ 14
+ ],
+ [
+ 741,
+ 34
+ ]
+ ],
+ [
+ [
+ 835,
+ 12
+ ],
+ [
+ 837,
+ 13
+ ]
+ ],
+ [
+ [
+ 890,
+ 14
+ ],
+ [
+ 892,
+ 15
+ ]
+ ],
+ [
+ [
+ 1064,
+ 10
+ ],
+ [
+ 1066,
+ 11
+ ]
+ ],
+ [
+ [
+ 1068,
+ 12
+ ],
+ [
+ 1071,
+ 13
+ ]
+ ],
+ [
+ [
+ 1074,
+ 12
+ ],
+ [
+ 1079,
+ 13
+ ]
+ ],
+ [
+ [
+ 1082,
+ 8
+ ],
+ [
+ 1084,
+ 9
+ ]
+ ],
+ [
+ [
+ 1126,
+ 8
+ ],
+ [
+ 1129,
+ 9
+ ]
+ ],
+ [
+ [
+ 1183,
+ 14
+ ],
+ [
+ 1185,
+ 15
+ ]
+ ],
+ [
+ [
+ 1209,
+ 8
+ ],
+ [
+ 1211,
+ 9
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.warn, console.error",
+ "file": "cjs/react.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 74,
+ 12
+ ],
+ [
+ 76,
+ 13
+ ]
+ ],
+ [
+ [
+ 141,
+ 10
+ ],
+ [
+ 144,
+ 11
+ ]
+ ],
+ [
+ [
+ 156,
+ 8
+ ],
+ [
+ 158,
+ 9
+ ]
+ ],
+ [
+ [
+ 425,
+ 8
+ ],
+ [
+ 427,
+ 9
+ ]
+ ],
+ [
+ [
+ 472,
+ 12
+ ],
+ [
+ 475,
+ 13
+ ]
+ ],
+ [
+ [
+ 477,
+ 12
+ ],
+ [
+ 480,
+ 13
+ ]
+ ],
+ [
+ [
+ 744,
+ 10
+ ],
+ [
+ 746,
+ 11
+ ]
+ ],
+ [
+ [
+ 748,
+ 12
+ ],
+ [
+ 751,
+ 13
+ ]
+ ],
+ [
+ [
+ 754,
+ 12
+ ],
+ [
+ 759,
+ 13
+ ]
+ ],
+ [
+ [
+ 762,
+ 8
+ ],
+ [
+ 764,
+ 9
+ ]
+ ],
+ [
+ [
+ 806,
+ 8
+ ],
+ [
+ 809,
+ 9
+ ]
+ ],
+ [
+ [
+ 375,
+ 14
+ ],
+ [
+ 377,
+ 15
+ ]
+ ],
+ [
+ [
+ 694,
+ 10
+ ],
+ [
+ 696,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error, console.warn",
+ "file": "cjs/react.react-server.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-runtime.react-server.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-runtime.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-dev-runtime.react-server.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-dev-runtime.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 19,
+ 8
+ ],
+ [
+ 21,
+ 9
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-compiler-runtime.development.js"
+ }
+ ],
+ "dependencyCount": 0,
+ "gitUrl": null,
+ "alias": {},
+ "description": "React is a JavaScript library for building user interfaces.",
+ "size": 172628,
+ "author": null,
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {},
+ "licenses": [
+ {
+ "licenses": {
+ "MIT": "https://spdx.org/licenses/MIT.html#licenseText"
+ },
+ "spdx": {
+ "osi": true,
+ "fsf": true,
+ "fsfAndOsi": true,
+ "includesDeprecated": false
+ },
+ "fileName": "package.json"
+ },
+ {
+ "licenses": {
+ "MIT": "https://spdx.org/licenses/MIT.html#licenseText"
+ },
+ "spdx": {
+ "osi": true,
+ "fsf": true,
+ "fsfAndOsi": true,
+ "includesDeprecated": false
+ },
+ "fileName": "LICENSE"
+ }
+ ],
+ "uniqueLicenseIds": [
+ "MIT"
+ ],
+ "composition": {
+ "extensions": [
+ ".js",
+ "",
+ ".md",
+ ".json"
+ ],
+ "files": [
+ "LICENSE",
+ "README.md",
+ "cjs/react-compiler-runtime.development.js",
+ "cjs/react-compiler-runtime.production.js",
+ "cjs/react-compiler-runtime.profiling.js",
+ "cjs/react-jsx-dev-runtime.development.js",
+ "cjs/react-jsx-dev-runtime.production.js",
+ "cjs/react-jsx-dev-runtime.profiling.js",
+ "cjs/react-jsx-dev-runtime.react-server.development.js",
+ "cjs/react-jsx-dev-runtime.react-server.production.js",
+ "cjs/react-jsx-runtime.development.js",
+ "cjs/react-jsx-runtime.production.js",
+ "cjs/react-jsx-runtime.profiling.js",
+ "cjs/react-jsx-runtime.react-server.development.js",
+ "cjs/react-jsx-runtime.react-server.production.js",
+ "cjs/react.development.js",
+ "cjs/react.production.js",
+ "cjs/react.react-server.development.js",
+ "cjs/react.react-server.production.js",
+ "compiler-runtime.js",
+ "index.js",
+ "jsx-dev-runtime.js",
+ "jsx-dev-runtime.react-server.js",
+ "jsx-runtime.js",
+ "jsx-runtime.react-server.js",
+ "package.json",
+ "react.react-server.js"
+ ],
+ "minified": [],
+ "unused": [],
+ "missing": [
+ "react"
+ ],
+ "required_files": [
+ "cjs/react.production.js",
+ "cjs/react.development.js",
+ "cjs/react.react-server.production.js",
+ "cjs/react.react-server.development.js",
+ "cjs/react-jsx-runtime.react-server.production.js",
+ "cjs/react-jsx-runtime.react-server.development.js",
+ "cjs/react-jsx-runtime.production.js",
+ "cjs/react-jsx-runtime.development.js",
+ "cjs/react-jsx-dev-runtime.react-server.production.js",
+ "cjs/react-jsx-dev-runtime.react-server.development.js",
+ "cjs/react-jsx-dev-runtime.production.js",
+ "cjs/react-jsx-dev-runtime.development.js",
+ "cjs/react-compiler-runtime.production.js",
+ "cjs/react-compiler-runtime.development.js"
+ ],
+ "required_nodejs": [],
+ "required_thirdparty": [
+ "react"
+ ],
+ "required_subpath": {}
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/facebook/react.git",
+ "directory": "packages/react"
+ },
+ "integrity": "834bff9884ee976c5d7bf1045232be43f2c64c36",
+ "links": {
+ "npm": "https://www.npmjs.com/package/react/v/19.2.4",
+ "homepage": "https://react.dev/",
+ "repository": "https://github.com/facebook/react"
+ }
+ }
+ },
+ "vulnerabilities": [],
+ "metadata": {
+ "homepage": "https://react.dev/",
+ "publishedCount": 2760,
+ "lastVersion": "19.2.4",
+ "lastUpdateAt": "2026-01-26T18:23:10.244Z",
+ "hasReceivedUpdateInOneYear": true,
+ "hasChangedAuthor": false,
+ "integrity": {
+ "19.2.4": "834bff9884ee976c5d7bf1045232be43f2c64c36"
+ },
+ "author": {
+ "name": "jeffbski",
+ "email": "jeff.barczewski@gmail.com"
+ },
+ "publishers": [
+ {
+ "name": "react-bot",
+ "email": "react-core@meta.com",
+ "version": "0.0.0-experimental-3cb2c420-20260324",
+ "at": "2026-03-25T16:40:02.078Z"
+ },
+ {
+ "name": "acdlite",
+ "email": "npm@andrewclark.io",
+ "version": "0.0.0-experimental-594093496-20230209",
+ "at": "2023-02-09T16:56:14.956Z"
+ },
+ {
+ "name": "gnoff",
+ "email": "jcs.gnoff@gmail.com",
+ "version": "18.2.0",
+ "at": "2022-06-14T19:46:38.369Z"
+ },
+ {
+ "name": "gaearon",
+ "email": "dan.abramov@gmail.com",
+ "version": "17.0.2",
+ "at": "2021-03-22T21:56:19.536Z"
+ },
+ {
+ "name": "brianvaughn",
+ "email": "briandavidvaughn@gmail.com",
+ "version": "0.0.0-experimental-27659559e",
+ "at": "2021-01-11T16:19:28.958Z"
+ },
+ {
+ "name": "lunaruan",
+ "email": "lunaris.ruan@gmail.com",
+ "version": "0.0.0-experimental-aae83a4b9",
+ "at": "2020-03-18T01:10:41.870Z"
+ },
+ {
+ "name": "threepointone",
+ "email": "threepointone@gmail.com",
+ "version": "16.13.0",
+ "at": "2020-02-26T20:19:52.312Z"
+ },
+ {
+ "name": "clemmy",
+ "email": "clement.hoang24@gmail.com",
+ "version": "16.2.0",
+ "at": "2017-11-28T21:32:28.829Z"
+ },
+ {
+ "name": "sophiebits",
+ "email": "npm@sophiebits.com",
+ "version": "15.6.2",
+ "at": "2017-09-26T00:10:25.817Z"
+ },
+ {
+ "name": "flarnie",
+ "email": "flarnie.npm@gmail.com",
+ "version": "16.0.0-rc.1",
+ "at": "2017-09-06T23:11:31.405Z"
+ },
+ {
+ "name": "spicyj",
+ "email": "ben@benalpert.com",
+ "version": "16.0.0-alpha.4",
+ "at": "2017-03-13T15:57:45.616Z"
+ },
+ {
+ "name": "tomocchino",
+ "email": "tomocchino@gmail.com",
+ "version": "15.4.0",
+ "at": "2016-11-16T14:33:05.693Z"
+ },
+ {
+ "name": "sebmarkbage",
+ "email": "sebastian@calyptus.eu",
+ "version": "15.4.0-rc.4",
+ "at": "2016-10-14T22:00:00.474Z"
+ },
+ {
+ "name": "zpao",
+ "email": "paul@oshannessy.com",
+ "version": "15.4.0-rc.2",
+ "at": "2016-10-05T22:51:07.111Z"
+ },
+ {
+ "name": "graue",
+ "email": "scott@oceanbase.org",
+ "version": "0.14.7",
+ "at": "2016-01-28T19:59:29.509Z"
+ },
+ {
+ "name": "jeffmo",
+ "email": "jeff@anafx.com",
+ "version": "0.12.1",
+ "at": "2014-11-18T06:56:11.863Z"
+ },
+ {
+ "name": "jeffbski",
+ "email": "jeff.barczewski@gmail.com",
+ "version": "0.7.1",
+ "at": "2013-05-23T19:48:26.316Z"
+ }
+ ],
+ "maintainers": [
+ {
+ "name": "fb",
+ "email": "opensource+npm@fb.com"
+ },
+ {
+ "name": "react-bot",
+ "email": "react-core@meta.com"
+ }
+ ],
+ "hasManyPublishers": false
+ }
+ }
+ },
+ "metadata": {
+ "startedAt": 1774601089503,
+ "executionTime": 771,
+ "apiCalls": [
+ {
+ "name": "pacote.manifest react@19.2.4",
+ "startedAt": 1774601089504,
+ "executionTime": 20
+ },
+ {
+ "name": "pacote.extract react@19.2.4",
+ "startedAt": 1774601089529,
+ "executionTime": 83
+ },
+ {
+ "name": "tarball.scanDirOrArchive react@19.2.4",
+ "startedAt": 1774601089612,
+ "executionTime": 247
+ }
+ ],
+ "apiCallsCount": 3,
+ "errorCount": 2,
+ "errors": [
+ {
+ "name": "pacote.extract react@19.2.4"
+ },
+ {
+ "name": "tarball.scanDirOrArchive react@19.2.4",
+ "message": "something went wrong !"
+ }
+ ]
+ }
+}
diff --git a/test/fixtures/result-test4.json b/test/fixtures/result-test4.json
new file mode 100644
index 00000000..3ebf234f
--- /dev/null
+++ b/test/fixtures/result-test4.json
@@ -0,0 +1,1020 @@
+{
+ "id": "CBuXRl",
+ "rootDependency": {
+ "name": "react",
+ "version": "19.2.4",
+ "integrity": "sha512-Lfi0RfVh0gdrtD6yI6AInys+HY8Ki8E/SlIpUYm3gXzlKOXW7zvRMMC6sMQ3+3ww+ZSF5cTnOafW2T5HjtwQmg=="
+ },
+ "scannerVersion": "10.7.0",
+ "vulnerabilityStrategy": "github-advisory",
+ "warnings": [],
+ "highlighted": {
+ "contacts": [],
+ "packages": [],
+ "identifiers": []
+ },
+ "dependencies": {
+ "react": {
+ "versions": {
+ "19.2.4": {
+ "id": 0,
+ "type": "cjs",
+ "usedBy": {},
+ "isDevDependency": false,
+ "existOnRemoteRegistry": true,
+ "flags": [
+ "hasMissingOrUnusedDependency",
+ "hasWarnings"
+ ],
+ "warnings": [
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 310,
+ 10
+ ],
+ [
+ 310,
+ 30
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react.production.js"
+ },
+ {
+ "kind": "insecure-random",
+ "location": [
+ [
+ 529,
+ 43
+ ],
+ [
+ 529,
+ 56
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.insecure_random",
+ "severity": "Information",
+ "experimental": false,
+ "value": null,
+ "file": "cjs/react.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 17,
+ 10
+ ],
+ [
+ 21,
+ 11
+ ]
+ ],
+ [
+ [
+ 413,
+ 14
+ ],
+ [
+ 415,
+ 15
+ ]
+ ],
+ [
+ [
+ 1014,
+ 10
+ ],
+ [
+ 1016,
+ 11
+ ]
+ ],
+ [
+ [
+ 1176,
+ 12
+ ],
+ [
+ 1178,
+ 13
+ ]
+ ],
+ [
+ [
+ 1222,
+ 8
+ ],
+ [
+ 1224,
+ 9
+ ]
+ ],
+ [
+ [
+ 1238,
+ 8
+ ],
+ [
+ 1240,
+ 9
+ ]
+ ],
+ [
+ [
+ 1245,
+ 8
+ ],
+ [
+ 1247,
+ 9
+ ]
+ ],
+ [
+ [
+ 40,
+ 9
+ ],
+ [
+ 44,
+ 9
+ ]
+ ],
+ [
+ [
+ 112,
+ 12
+ ],
+ [
+ 114,
+ 13
+ ]
+ ],
+ [
+ [
+ 179,
+ 10
+ ],
+ [
+ 182,
+ 11
+ ]
+ ],
+ [
+ [
+ 194,
+ 8
+ ],
+ [
+ 196,
+ 9
+ ]
+ ],
+ [
+ [
+ 502,
+ 12
+ ],
+ [
+ 505,
+ 13
+ ]
+ ],
+ [
+ [
+ 507,
+ 12
+ ],
+ [
+ 510,
+ 13
+ ]
+ ],
+ [
+ [
+ 518,
+ 8
+ ],
+ [
+ 520,
+ 9
+ ]
+ ],
+ [
+ [
+ 539,
+ 16
+ ],
+ [
+ 541,
+ 17
+ ]
+ ],
+ [
+ [
+ 556,
+ 8
+ ],
+ [
+ 558,
+ 9
+ ]
+ ],
+ [
+ [
+ 741,
+ 14
+ ],
+ [
+ 741,
+ 34
+ ]
+ ],
+ [
+ [
+ 835,
+ 12
+ ],
+ [
+ 837,
+ 13
+ ]
+ ],
+ [
+ [
+ 890,
+ 14
+ ],
+ [
+ 892,
+ 15
+ ]
+ ],
+ [
+ [
+ 1064,
+ 10
+ ],
+ [
+ 1066,
+ 11
+ ]
+ ],
+ [
+ [
+ 1068,
+ 12
+ ],
+ [
+ 1071,
+ 13
+ ]
+ ],
+ [
+ [
+ 1074,
+ 12
+ ],
+ [
+ 1079,
+ 13
+ ]
+ ],
+ [
+ [
+ 1082,
+ 8
+ ],
+ [
+ 1084,
+ 9
+ ]
+ ],
+ [
+ [
+ 1126,
+ 8
+ ],
+ [
+ 1129,
+ 9
+ ]
+ ],
+ [
+ [
+ 1183,
+ 14
+ ],
+ [
+ 1185,
+ 15
+ ]
+ ],
+ [
+ [
+ 1209,
+ 8
+ ],
+ [
+ 1211,
+ 9
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.warn, console.error",
+ "file": "cjs/react.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 74,
+ 12
+ ],
+ [
+ 76,
+ 13
+ ]
+ ],
+ [
+ [
+ 141,
+ 10
+ ],
+ [
+ 144,
+ 11
+ ]
+ ],
+ [
+ [
+ 156,
+ 8
+ ],
+ [
+ 158,
+ 9
+ ]
+ ],
+ [
+ [
+ 425,
+ 8
+ ],
+ [
+ 427,
+ 9
+ ]
+ ],
+ [
+ [
+ 472,
+ 12
+ ],
+ [
+ 475,
+ 13
+ ]
+ ],
+ [
+ [
+ 477,
+ 12
+ ],
+ [
+ 480,
+ 13
+ ]
+ ],
+ [
+ [
+ 744,
+ 10
+ ],
+ [
+ 746,
+ 11
+ ]
+ ],
+ [
+ [
+ 748,
+ 12
+ ],
+ [
+ 751,
+ 13
+ ]
+ ],
+ [
+ [
+ 754,
+ 12
+ ],
+ [
+ 759,
+ 13
+ ]
+ ],
+ [
+ [
+ 762,
+ 8
+ ],
+ [
+ 764,
+ 9
+ ]
+ ],
+ [
+ [
+ 806,
+ 8
+ ],
+ [
+ 809,
+ 9
+ ]
+ ],
+ [
+ [
+ 375,
+ 14
+ ],
+ [
+ 377,
+ 15
+ ]
+ ],
+ [
+ [
+ 694,
+ 10
+ ],
+ [
+ 696,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error, console.warn",
+ "file": "cjs/react.react-server.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-runtime.react-server.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-runtime.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-dev-runtime.react-server.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 38,
+ 12
+ ],
+ [
+ 40,
+ 13
+ ]
+ ],
+ [
+ [
+ 132,
+ 10
+ ],
+ [
+ 135,
+ 11
+ ]
+ ],
+ [
+ [
+ 147,
+ 8
+ ],
+ [
+ 149,
+ 9
+ ]
+ ],
+ [
+ [
+ 216,
+ 12
+ ],
+ [
+ 218,
+ 13
+ ]
+ ],
+ [
+ [
+ 232,
+ 10
+ ],
+ [
+ 238,
+ 11
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-jsx-dev-runtime.development.js"
+ },
+ {
+ "kind": "log-usage",
+ "location": [
+ [
+ [
+ 19,
+ 8
+ ],
+ [
+ 21,
+ 9
+ ]
+ ]
+ ],
+ "source": "JS-X-Ray",
+ "i18n": "sast_warnings.log_usage",
+ "severity": "Information",
+ "experimental": false,
+ "value": "console.error",
+ "file": "cjs/react-compiler-runtime.development.js"
+ }
+ ],
+ "dependencyCount": 0,
+ "gitUrl": null,
+ "alias": {},
+ "description": "React is a JavaScript library for building user interfaces.",
+ "size": 172628,
+ "author": null,
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "scripts": {},
+ "licenses": [
+ {
+ "licenses": {
+ "MIT": "https://spdx.org/licenses/MIT.html#licenseText"
+ },
+ "spdx": {
+ "osi": true,
+ "fsf": true,
+ "fsfAndOsi": true,
+ "includesDeprecated": false
+ },
+ "fileName": "package.json"
+ },
+ {
+ "licenses": {
+ "MIT": "https://spdx.org/licenses/MIT.html#licenseText"
+ },
+ "spdx": {
+ "osi": true,
+ "fsf": true,
+ "fsfAndOsi": true,
+ "includesDeprecated": false
+ },
+ "fileName": "LICENSE"
+ }
+ ],
+ "uniqueLicenseIds": [
+ "MIT"
+ ],
+ "composition": {
+ "extensions": [
+ ".js",
+ "",
+ ".md",
+ ".json"
+ ],
+ "files": [
+ "LICENSE",
+ "README.md",
+ "cjs/react-compiler-runtime.development.js",
+ "cjs/react-compiler-runtime.production.js",
+ "cjs/react-compiler-runtime.profiling.js",
+ "cjs/react-jsx-dev-runtime.development.js",
+ "cjs/react-jsx-dev-runtime.production.js",
+ "cjs/react-jsx-dev-runtime.profiling.js",
+ "cjs/react-jsx-dev-runtime.react-server.development.js",
+ "cjs/react-jsx-dev-runtime.react-server.production.js",
+ "cjs/react-jsx-runtime.development.js",
+ "cjs/react-jsx-runtime.production.js",
+ "cjs/react-jsx-runtime.profiling.js",
+ "cjs/react-jsx-runtime.react-server.development.js",
+ "cjs/react-jsx-runtime.react-server.production.js",
+ "cjs/react.development.js",
+ "cjs/react.production.js",
+ "cjs/react.react-server.development.js",
+ "cjs/react.react-server.production.js",
+ "compiler-runtime.js",
+ "index.js",
+ "jsx-dev-runtime.js",
+ "jsx-dev-runtime.react-server.js",
+ "jsx-runtime.js",
+ "jsx-runtime.react-server.js",
+ "package.json",
+ "react.react-server.js"
+ ],
+ "minified": [],
+ "unused": [],
+ "missing": [
+ "react"
+ ],
+ "required_files": [
+ "cjs/react.production.js",
+ "cjs/react.development.js",
+ "cjs/react.react-server.production.js",
+ "cjs/react.react-server.development.js",
+ "cjs/react-jsx-runtime.react-server.production.js",
+ "cjs/react-jsx-runtime.react-server.development.js",
+ "cjs/react-jsx-runtime.production.js",
+ "cjs/react-jsx-runtime.development.js",
+ "cjs/react-jsx-dev-runtime.react-server.production.js",
+ "cjs/react-jsx-dev-runtime.react-server.development.js",
+ "cjs/react-jsx-dev-runtime.production.js",
+ "cjs/react-jsx-dev-runtime.development.js",
+ "cjs/react-compiler-runtime.production.js",
+ "cjs/react-compiler-runtime.development.js"
+ ],
+ "required_nodejs": [],
+ "required_thirdparty": [
+ "react"
+ ],
+ "required_subpath": {}
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/facebook/react.git",
+ "directory": "packages/react"
+ },
+ "integrity": "834bff9884ee976c5d7bf1045232be43f2c64c36",
+ "links": {
+ "npm": "https://www.npmjs.com/package/react/v/19.2.4",
+ "homepage": "https://react.dev/",
+ "repository": "https://github.com/facebook/react"
+ }
+ }
+ },
+ "vulnerabilities": [],
+ "metadata": {
+ "homepage": "https://react.dev/",
+ "publishedCount": 2760,
+ "lastVersion": "19.2.4",
+ "lastUpdateAt": "2026-01-26T18:23:10.244Z",
+ "hasReceivedUpdateInOneYear": true,
+ "hasChangedAuthor": false,
+ "integrity": {
+ "19.2.4": "834bff9884ee976c5d7bf1045232be43f2c64c36"
+ },
+ "author": {
+ "name": "jeffbski",
+ "email": "jeff.barczewski@gmail.com"
+ },
+ "publishers": [
+ {
+ "name": "react-bot",
+ "email": "react-core@meta.com",
+ "version": "0.0.0-experimental-3cb2c420-20260324",
+ "at": "2026-03-25T16:40:02.078Z"
+ },
+ {
+ "name": "acdlite",
+ "email": "npm@andrewclark.io",
+ "version": "0.0.0-experimental-594093496-20230209",
+ "at": "2023-02-09T16:56:14.956Z"
+ },
+ {
+ "name": "gnoff",
+ "email": "jcs.gnoff@gmail.com",
+ "version": "18.2.0",
+ "at": "2022-06-14T19:46:38.369Z"
+ },
+ {
+ "name": "gaearon",
+ "email": "dan.abramov@gmail.com",
+ "version": "17.0.2",
+ "at": "2021-03-22T21:56:19.536Z"
+ },
+ {
+ "name": "brianvaughn",
+ "email": "briandavidvaughn@gmail.com",
+ "version": "0.0.0-experimental-27659559e",
+ "at": "2021-01-11T16:19:28.958Z"
+ },
+ {
+ "name": "lunaruan",
+ "email": "lunaris.ruan@gmail.com",
+ "version": "0.0.0-experimental-aae83a4b9",
+ "at": "2020-03-18T01:10:41.870Z"
+ },
+ {
+ "name": "threepointone",
+ "email": "threepointone@gmail.com",
+ "version": "16.13.0",
+ "at": "2020-02-26T20:19:52.312Z"
+ },
+ {
+ "name": "clemmy",
+ "email": "clement.hoang24@gmail.com",
+ "version": "16.2.0",
+ "at": "2017-11-28T21:32:28.829Z"
+ },
+ {
+ "name": "sophiebits",
+ "email": "npm@sophiebits.com",
+ "version": "15.6.2",
+ "at": "2017-09-26T00:10:25.817Z"
+ },
+ {
+ "name": "flarnie",
+ "email": "flarnie.npm@gmail.com",
+ "version": "16.0.0-rc.1",
+ "at": "2017-09-06T23:11:31.405Z"
+ },
+ {
+ "name": "spicyj",
+ "email": "ben@benalpert.com",
+ "version": "16.0.0-alpha.4",
+ "at": "2017-03-13T15:57:45.616Z"
+ },
+ {
+ "name": "tomocchino",
+ "email": "tomocchino@gmail.com",
+ "version": "15.4.0",
+ "at": "2016-11-16T14:33:05.693Z"
+ },
+ {
+ "name": "sebmarkbage",
+ "email": "sebastian@calyptus.eu",
+ "version": "15.4.0-rc.4",
+ "at": "2016-10-14T22:00:00.474Z"
+ },
+ {
+ "name": "zpao",
+ "email": "paul@oshannessy.com",
+ "version": "15.4.0-rc.2",
+ "at": "2016-10-05T22:51:07.111Z"
+ },
+ {
+ "name": "graue",
+ "email": "scott@oceanbase.org",
+ "version": "0.14.7",
+ "at": "2016-01-28T19:59:29.509Z"
+ },
+ {
+ "name": "jeffmo",
+ "email": "jeff@anafx.com",
+ "version": "0.12.1",
+ "at": "2014-11-18T06:56:11.863Z"
+ },
+ {
+ "name": "jeffbski",
+ "email": "jeff.barczewski@gmail.com",
+ "version": "0.7.1",
+ "at": "2013-05-23T19:48:26.316Z"
+ }
+ ],
+ "maintainers": [
+ {
+ "name": "fb",
+ "email": "opensource+npm@fb.com"
+ },
+ {
+ "name": "react-bot",
+ "email": "react-core@meta.com"
+ }
+ ],
+ "hasManyPublishers": false
+ }
+ }
+ },
+ "metadata": {
+ "startedAt": 1774601089503,
+ "executionTime": 771,
+ "apiCalls": [
+ {
+ "name": "pacote.manifest react@19.2.4",
+ "startedAt": 1774601089504,
+ "executionTime": 20
+ },
+ {
+ "name": "pacote.extract react@19.2.4",
+ "startedAt": 1774601089529,
+ "executionTime": 83
+ },
+ {
+ "name": "tarball.scanDirOrArchive react@19.2.4",
+ "startedAt": 1774601089612,
+ "executionTime": 247
+ }
+ ],
+ "apiCallsCount": 3,
+ "errorCount": 0,
+ "errors": []
+ }
+}