Skip to content

Commit 89af2d4

Browse files
committed
feat(contacts): implemented parser
1 parent 2bae16f commit 89af2d4

File tree

7 files changed

+167
-9
lines changed

7 files changed

+167
-9
lines changed

bin/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ function defaultScannerCommand(name, options = {}) {
127127

128128
const cmd = prog.command(name)
129129
.option("-d, --depth", i18n.getTokenSync("cli.commands.option_depth"), Infinity)
130-
.option("--silent", i18n.getTokenSync("cli.commands.option_silent"), false);
130+
.option("--silent", i18n.getTokenSync("cli.commands.option_silent"), false)
131+
.option("-c, --contacts", i18n.getTokenSync("cli.commands.option_contacts"), "[]");
131132

132133
if (includeOutput) {
133134
cmd.option("-o, --output", i18n.getTokenSync("cli.commands.option_output"), "nsecure-result");

i18n/english.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const cli = {
1414
option_depth: "Maximum dependencies depth to fetch",
1515
option_output: "Json file output name",
1616
option_silent: "enable silent mode which disable CLI spinners",
17+
option_contacts: "List of contacts to hightlight",
1718
strategy: "Vulnerabilities source to use",
1819
cwd: {
1920
desc: "Run security analysis on the current working dir",
@@ -70,6 +71,13 @@ const cli = {
7071
startHttp: {
7172
invalidScannerVersion: tS`the payload has been scanned with version '${0}' and do not satisfies the required CLI range '${1}'`,
7273
regenerate: "please re-generate a new JSON payload using the CLI"
74+
},
75+
errors: {
76+
contacts: {
77+
should_be_valid_json: tS`Contacts: ${0}`,
78+
should_be_array: "Contacts should be an array",
79+
should_be_defined: tS`Contact at index ${0} should not be null`
80+
}
7381
}
7482
};
7583

i18n/french.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const cli = {
1414
option_depth: "Niveau de profondeur de dépendances maximum à aller chercher",
1515
option_output: "Nom de sortie du fichier json",
1616
option_silent: "Activer le mode silencieux qui désactive les spinners du CLI",
17+
option_contacts: "Liste des contacts à mettre en évidence",
1718
strategy: "Source de vulnérabilités à utiliser",
1819
cwd: {
1920
desc: "Démarre une analyse de sécurité sur le dossier courant",
@@ -70,6 +71,13 @@ const cli = {
7071
startHttp: {
7172
invalidScannerVersion: tS`le fichier d'analyse correspond à la version '${0}' du scanner et ne satisfait pas la range '${1}' attendu par la CLI`,
7273
regenerate: "veuillez re-générer un nouveau fichier d'analyse JSON en utilisant votre CLI"
74+
},
75+
errors: {
76+
contacts: {
77+
should_be_valid_json: tS`Contacts: ${0}`,
78+
should_be_array: "Contacts doit etre un array",
79+
should_be_defined: tS`Contact à index ${0} ne doit pas etre null`
80+
}
7381
}
7482
};
7583

public/components/views/home/maintainers/maintainers.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,26 @@ export class Maintainers {
2626
}
2727

2828
render() {
29-
const authors = [...this.secureDataSet.authors.entries()]
30-
.sort((left, right) => right[1].packages.size - left[1].packages.size);
29+
const authors = this.#highlightContacts([...this.secureDataSet.authors.entries()]
30+
.sort((left, right) => right[1].packages.size - left[1].packages.size));
3131

3232
document.getElementById("authors-count").innerHTML = authors.length;
3333
document.querySelector(".home--maintainers")
3434
.appendChild(this.generate(authors));
3535
}
3636

37+
#highlightContacts(authors){
38+
const highlightedContacts = new Set(this.secureDataSet.data.highlighted.contacts
39+
.map(({name})=> {
40+
return name;
41+
}));
42+
43+
const highlightedAuthors = authors.filter(([name]) => highlightedContacts.has(name));
44+
const authorsRest = authors.filter(([name]) => !highlightedContacts.has(name));
45+
46+
return [...highlightedAuthors, ...authorsRest];
47+
}
48+
3749
generate(authors) {
3850
const fragment = document.createDocumentFragment();
3951
const hideItems = authors.length > this.maximumMaintainers;

src/commands/parsers/contacts.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
export const createContactsParser = ({logError, exit}) => (json) => {
3+
const contacts = parseContacts({json,logError,exit});
4+
if(!Array.isArray(contacts)){
5+
logError("cli.errors.contacts.should_be_array");
6+
exit();
7+
}
8+
9+
let hasError = false;
10+
contacts.forEach((contact,i) => {
11+
if(!contact){
12+
hasError = true;
13+
logError("cli.errors.contacts.should_be_defined",i);
14+
}
15+
});
16+
17+
if(hasError){
18+
exit();
19+
}
20+
21+
return contacts;
22+
};
23+
24+
function parseContacts ({json,logError,exit}) {
25+
try{
26+
return JSON.parse(json);
27+
}catch(err){
28+
logError("cli.errors.contacts.should_be_valid_json",err.message);
29+
exit();
30+
}
31+
}

src/commands/scanner.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,28 @@ import * as Scanner from "@nodesecure/scanner";
1414
// Import Internal Dependencies
1515
import * as http from "./http.js";
1616
import { appCache } from "../cache.js";
17+
import { createContactsParser } from "./parsers/contacts.js";
18+
19+
const parseContacts = createContactsParser({
20+
logError: (tokenName,param) => console.log(kleur.red().bold(param ? i18n.getTokenSync(tokenName,param)
21+
: i18n.getTokenSync(tokenName))),
22+
exit: () => process.exit()
23+
});
1724

1825
export async function auto(spec, options) {
1926
const { keep, ...commandOptions } = options;
2027

28+
const optionsWithContacts = {
29+
...commandOptions,
30+
highlight:{
31+
contacts: parseContacts(options.contacts)
32+
}
33+
}
34+
2135
const payloadFile = await (
2236
typeof spec === "string" ?
23-
from(spec, commandOptions) :
24-
cwd(commandOptions)
37+
from(spec, optionsWithContacts) :
38+
cwd(optionsWithContacts)
2539
);
2640
try {
2741
if (payloadFile !== null) {
@@ -55,24 +69,28 @@ export async function cwd(options) {
5569
nolock,
5670
full,
5771
vulnerabilityStrategy,
58-
silent
72+
silent,
73+
contacts
5974
} = options;
6075

6176
const payload = await Scanner.cwd(
6277
process.cwd(),
63-
{ maxDepth, usePackageLock: !nolock, fullLockMode: full, vulnerabilityStrategy },
78+
{ maxDepth, usePackageLock: !nolock, fullLockMode: full, vulnerabilityStrategy,highlight:
79+
{contacts: parseContacts(contacts)}},
6480
initLogger(void 0, !silent)
6581
);
6682

6783
return await logAndWrite(payload, output, { local: true });
6884
}
6985

7086
export async function from(spec, options) {
71-
const { depth: maxDepth = Infinity, output, silent } = options;
87+
const { depth: maxDepth = Infinity, output, silent,contacts} = options;
7288

7389
const payload = await Scanner.from(
7490
spec,
75-
{ maxDepth },
91+
{ maxDepth,highlight: {
92+
contacts: parseContacts(contacts)
93+
} },
7694
initLogger(spec, !silent)
7795
);
7896

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Import Node.js Dependencies
2+
import { it, describe ,beforeEach} from "node:test";
3+
import assert from "node:assert/strict";
4+
5+
// Import Internal Dependencies
6+
import { createContactsParser } from "../../../src/commands/parsers/contacts.js";
7+
8+
let errors = [];
9+
10+
const exit = () => {
11+
throw new Error("process exited");
12+
}
13+
14+
beforeEach(()=>{
15+
errors = [];
16+
})
17+
18+
const logError = (token,param) => {
19+
if(param){
20+
errors.push(`${token} ${param}`);
21+
}else{
22+
errors.push(token);
23+
}
24+
};
25+
26+
27+
describe("contacts parser", () => {
28+
29+
it("should successfully parse the contacts to highlight",()=> {
30+
const parseContacts = createContactsParser({
31+
logError,
32+
exit
33+
});
34+
35+
const contactsJson = '[{"name": "contact1"},{"name":"contact2","url":"url2","email":"[email protected]"}]';
36+
37+
assert.deepEqual(parseContacts(contactsJson), [{name: "contact1"},{name:"contact2",url:"url2",email:"[email protected]"}]);
38+
assert.deepEqual(errors,[]);
39+
});
40+
41+
describe("errors", () => {
42+
it("should display an error and exit the process when the contacts is not valid json",()=> {
43+
const parseContacts = createContactsParser({
44+
logError,
45+
exit
46+
});
47+
48+
49+
const unvalidJson = '][';
50+
51+
assert.throws(()=> parseContacts(unvalidJson),{ message: "process exited"});
52+
assert.deepEqual(errors,["cli.errors.contacts.should_be_valid_json Unexpected token ']', \"][\" is not valid JSON"]);
53+
54+
});
55+
56+
it("should display an error and exit the process when the contacts is not an array",()=> {
57+
const parseContacts = createContactsParser({
58+
logError,
59+
exit
60+
});
61+
62+
const contactsJson = '{"name":"contact1"}';
63+
64+
assert.throws(()=> parseContacts(contactsJson),{ message: "process exited"});
65+
assert.deepEqual(errors,["cli.errors.contacts.should_be_array"]);
66+
});
67+
68+
it("should display an error when a contact is null",()=> {
69+
const parseContacts = createContactsParser({
70+
logError,
71+
exit
72+
});
73+
74+
const contactsJson = '[{"name": "contact1"},null,{"name":"contact2","url":"url2","email":"[email protected]"},null]';
75+
assert.throws(()=> parseContacts(contactsJson),{ message: "process exited"});
76+
assert.deepEqual(errors,["cli.errors.contacts.should_be_defined 1", "cli.errors.contacts.should_be_defined 3"]);
77+
});
78+
});
79+
});
80+

0 commit comments

Comments
 (0)