Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ For complete details on each command, refer to the following documents:
- [`cache`](./docs/cli/cache.md)
- [`extract integrity`](./docs/cli/extract-integrity.md)
- [`stats`](./docs/cli/stats.md)
- [re-highlight](./docs/cli/re-highlight.md)

Each link provides access to the full documentation for the command, including additional details, options, and usage examples.

Expand Down
9 changes: 9 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,18 @@ prog
.command("stats")
.describe(i18n.getTokenSync("cli.commands.stats.desc"))
.option("-m, --min", i18n.getTokenSync("cli.commands.stats.option_min"), undefined)
.option("-o, --output", i18n.getTokenSync("cli.commands.option_output"), "nsecure-result")
.example("nsecure stats")
.action(commands.stats.main);

prog
.command("re-highlight")
.option("-o, --output", i18n.getTokenSync("cli.commands.option_output"), "nsecure-result")
.option("-c, --contacts", i18n.getTokenSync("cli.commands.option_contacts"), [])
.option("-p, --packages", i18n.getTokenSync("cli.commands.option_packages"), [])
.example("nsecure re-highlight -c sindre sindre@gmail.com -c matteo -p lodash@^4.0.0 -p express@^4.18.0")
.action(commands.reHighlight.main);

prog.parse(process.argv);

function defaultScannerCommand(name, options = {}) {
Expand Down
19 changes: 19 additions & 0 deletions docs/cli/re-highlight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## 📝 Command `re-highlight`

The `re-highlight` command re-highlights the specified contacts and packages of the previous analysis stored in the JSON file at the root of this project.

## 📜 Syntax


```bash
$ nsecure re-highlight -c sindre sindre@gmail.com -c matteo -p lodash@^4.0.0 -p express@^4.18.0
```


## ⚙️ Available Options

| Name | Shortcut | Default Value | Description |
| ------------------------- | -------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--output` | `-o` | `nsecure-result` | Specify the output file to read from. |
| `--contacts` | `-c` | `[]` | List of contacts to highlight. |
| `--packages` | `-p` | `[]` | List of packages to highlight. |
1 change: 1 addition & 0 deletions docs/cli/stats.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ $ nsecure stats
| Name | Shortcut | Default Value | Description |
| ----- | -------- | ------------- | -------------------------------------------------------- |
| `--min` | `-m` | `undefined` | Filter API calls with execution time above ceiling (ms) |
| `--output` | `-o` | `nsecure-result` | Specify the output file to read from.
3 changes: 3 additions & 0 deletions i18n/arabic.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ const cli = {
option_min: "تصفية استدعاءات API ذات وقت التنفيذ أعلى من الحد المحدد (بالمللي ثانية)",
minNotANumber: "خطأ: يجب أن يكون --min رقماً.",
statsCeiling: tS`عدد استدعاءات API فوق ${0}: ${1}`
},
reHighlight: {
error: "يجب إجراء فحص قبل إعادة تمييز جهات الاتصال والحزم."
}
},
startHttp: {
Expand Down
3 changes: 3 additions & 0 deletions i18n/english.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ const cli = {
option_min: "Filter API calls with execution time above the specified ceiling (in ms)",
minNotANumber: "Error: --min must be a number.",
statsCeiling: tS`API calls count above ${0}: ${1}`
},
reHighlight: {
error: "A scan must be performed before re-highlighting contacts and packages."
}
},
startHttp: {
Expand Down
3 changes: 3 additions & 0 deletions i18n/french.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ const cli = {
option_min: "Filtrer les appels API avec un temps d'exécution supérieur au plafond spécifié (en ms)",
minNotANumber: "Erreur: --min doit être un nombre.",
statsCeiling: tS`Nombre d'appels API au-dessus de ${0}: ${1}`
},
reHighlight: {
error: "Une analyse doit être effectuée avant de remettre en évidence les contacts et les packages."
}
},
startHttp: {
Expand Down
3 changes: 3 additions & 0 deletions i18n/turkish.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ const cli = {
option_min: "Belirtilen tavan değerinin (ms cinsinden) üzerinde yürütme süresine sahip API çağrılarını filtrele",
minNotANumber: "Hata: --min bir sayı olmalıdır.",
statsCeiling: tS`${0} üzerindeki API çağrıları sayısı: ${1}`
},
reHighlight: {
error: "Kişileri ve paketleri yeniden vurgulamadan önce bir tarama yapılmalıdır."
}
},
startHttp: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"@nodesecure/ossf-scorecard-sdk": "4.0.1",
"@nodesecure/rc": "5.6.0",
"@nodesecure/report": "4.2.2",
"@nodesecure/scanner": "10.11.0",
"@nodesecure/scanner": "10.12.0",
"@nodesecure/server": "1.0.0",
"@nodesecure/utils": "^2.2.0",
"@nodesecure/vulnera": "3.1.0",
Expand Down
1 change: 1 addition & 0 deletions src/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ 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";
export * as reHighlight from "./re-highlight.js";
72 changes: 72 additions & 0 deletions src/commands/loggers/logger.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// Import Node.js Dependencies
import fs from "node:fs";
import path from "node:path";

// Import Third-party Dependencies
import semver from "semver";
import filenamify from "filenamify";
import * as i18n from "@nodesecure/i18n";
import ms from "ms";

Expand Down Expand Up @@ -78,3 +84,69 @@ function colorExecutionTime(timeMs) {

return kleur.red().bold(formatted);
}

export async function logAndWrite(
/** @type {import("@nodesecure/scanner").Payload} */
payload,
output = "nsecure-result",
options = {}
) {
const { local = false, showWarnings = true } = options;

if (payload === null) {
console.log(i18n.getTokenSync("cli.no_dep_to_proceed"));

return null;
}

if (showWarnings && payload.warnings.length > 0) {
console.log(`\n ${kleur.yellow().bold("Global Warning:")}\n`);
const logFn = semver.satisfies(payload.scannerVersion, ">=7.0.0") ?
logGlobalWarningsV7 :
logGlobalWarningsV6;
logFn(payload.warnings);
console.log("");
}

const ret = JSON.stringify(payload, null, 2);

if (local) {
// FIXME: would it make more sense to manage this directly within Scanner?
Object.assign(ret, { local });
}

const fileName = path.extname(output) === ".json" ?
filenamify(output) :
`${filenamify(output)}.json`;
const filePath = path.join(process.cwd(), fileName);
fs.writeFileSync(filePath, ret);

console.log("");
console.log(
kleur.white().bold(i18n.getTokenSync("cli.successfully_written_json", kleur.green().bold(filePath)))
);
console.log("");

return filePath;
}

function logGlobalWarningsV7(
/** @type {import("@nodesecure/scanner").GlobalWarning[]} */
warnings
) {
for (const warning of warnings) {
const isTypoSquatting = warning.type === "typo-squatting";

const type = kleur[isTypoSquatting ? "cyan" : "yellow"]().bold(`${warning.type}`);
console.log(kleur.gray().bold(`[${type}] ${warning.message}`));
}
}

function logGlobalWarningsV6(
/** @type {string[]} */
warnings
) {
for (const warning of warnings) {
console.log(kleur.yellow().bold(warning));
}
}
41 changes: 41 additions & 0 deletions src/commands/re-highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Import Third-party Dependencies
import { Extractors } from "@nodesecure/scanner/extractors";

// Import Internal Dependencies
import { getScanFromFile } from "../utils/getScanFromFile.js";
import { logAndWrite, logError } from "./loggers/logger.js";
import { parseContacts } from "./parsers/contacts.js";
import { parsePackages } from "./parsers/packages.js";

export async function main(options) {
const { getScanResult = getScanFromFile, logger = {
logAndWrite,
logError
}, contacts, packages, output
} = options;
try {
const scanResult = await getScanResult(output);

const extractor = new Extractors.Payload(scanResult, [
new Extractors.Probes.HighlightedContacts(parseContacts(contacts)),
new Extractors.Probes.HighlightedPackages(parsePackages(packages))
]);

const {
highlightedPackages,
illuminated
} = extractor.extractAndMerge();

await logger.logAndWrite({
...scanResult,
highlighted: {
...scanResult.highlighted,
contacts: illuminated,
packages: highlightedPackages
}
}, output, { showWarnings: false });
}
catch {
logger.logError("cli.commands.reHighlight.error");
}
}
70 changes: 1 addition & 69 deletions src/commands/scanner.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
// Import Node.js Dependencies
import fs from "node:fs";
import path from "node:path";
import events from "node:events";

// Import Third-party Dependencies
import semver from "semver";
import filenamify from "filenamify";
import { Spinner } from "@topcli/spinner";
import * as i18n from "@nodesecure/i18n";
import * as scanner from "@nodesecure/scanner";
Expand All @@ -17,6 +14,7 @@ import * as http from "./http.js";
import {
logScannerStat,
logScannerError,
logAndWrite,
formatMs
} from "./loggers/logger.js";
import { parseContacts } from "./parsers/contacts.js";
Expand Down Expand Up @@ -275,69 +273,3 @@ function startSpinners() {
}
});
}

async function logAndWrite(
/** @type {import("@nodesecure/scanner").Payload} */
payload,
output = "nsecure-result",
options = {}
) {
const { local = false } = options;

if (payload === null) {
console.log(i18n.getTokenSync("cli.no_dep_to_proceed"));

return null;
}

if (payload.warnings.length > 0) {
console.log(`\n ${kleur.yellow().bold("Global Warning:")}\n`);
const logFn = semver.satisfies(payload.scannerVersion, ">=7.0.0") ?
logGlobalWarningsV7 :
logGlobalWarningsV6;
logFn(payload.warnings);
console.log("");
}

const ret = JSON.stringify(payload, null, 2);

if (local) {
// FIXME: would it make more sense to manage this directly within Scanner?
Object.assign(ret, { local });
}

const fileName = path.extname(output) === ".json" ?
filenamify(output) :
`${filenamify(output)}.json`;
const filePath = path.join(process.cwd(), fileName);
fs.writeFileSync(filePath, ret);

console.log("");
console.log(
kleur.white().bold(i18n.getTokenSync("cli.successfully_written_json", kleur.green().bold(filePath)))
);
console.log("");

return filePath;
}

function logGlobalWarningsV7(
/** @type {import("@nodesecure/scanner").GlobalWarning[]} */
warnings
) {
for (const warning of warnings) {
const isTypoSquatting = warning.type === "typo-squatting";

const type = kleur[isTypoSquatting ? "cyan" : "yellow"]().bold(`${warning.type}`);
console.log(kleur.gray().bold(`[${type}] ${warning.message}`));
}
}

function logGlobalWarningsV6(
/** @type {string[]} */
warnings
) {
for (const warning of warnings) {
console.log(kleur.yellow().bold(warning));
}
}
16 changes: 3 additions & 13 deletions src/commands/stats.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// 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";
import { getScanFromFile } from "../utils/getScanFromFile.js";

export async function main(options) {
const { getScanResult = getScanFromFile, min, logger = {
const { getScanResult = getScanFromFile, min, output, logger = {
logScannerStat,
logScannerError,
log,
Expand All @@ -20,7 +17,7 @@ export async function main(options) {
}

try {
const scanResult = await getScanResult();
const scanResult = await getScanResult(output);
const { metadata } = scanResult;

logger.log("cli.commands.stats.elapsed", formatMs(metadata.executionTime));
Expand Down Expand Up @@ -48,10 +45,3 @@ export async function main(options) {
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"));
}
16 changes: 16 additions & 0 deletions src/utils/getScanFromFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Import Node.js Dependencies
import { readFile } from "node:fs/promises";
import path from "node:path";

// Import Third-party Dependencies
import filenamify from "filenamify";

export async function getScanFromFile(output = "nsecure-result") {
const fileName = path.extname(output) === ".json" ?
filenamify(output) :
`${filenamify(output)}.json`;

const filePath = path.join(process.cwd(), fileName);

return JSON.parse(await readFile(filePath, "utf8"));
}
Loading
Loading