Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 4x 4x 4x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 1x 1x 1x 1x | import { execSync } from 'child_process';
import fs from 'fs/promises';
import { Command } from 'commander';
export interface SecurityAgentOptions {}
export interface SecurityAgentResult {
/** Nombre total de vulnérabilités détectées */
total: number;
/** Nombre de vulnérabilités corrigeables */
fixable: number;
/** Répartition des vulnérabilités par sévérité */
bySeverity: { critical: number; high: number; moderate: number; low: number };
/** Top 5 des paquets les plus vulnérables */
topPackages: Array<{ package: string; count: number }>;
/** Chemin vers le rapport brut (audit-report.json) */
detailPath: string;
/** Liste brute des vulnérabilités */
vulnerabilities: unknown[];
}
/**
* Analyse les vulnérabilités via pnpm audit.
*/
export async function securityAgent(
_opts?: SecurityAgentOptions,
): Promise<SecurityAgentResult> {
const detailPath = 'audit-report.json';
try {
const cmd = 'pnpm audit --json';
const stdout = execSync(cmd, { encoding: 'utf-8' });
await fs.writeFile(detailPath, stdout, 'utf-8');
const report: any = JSON.parse(stdout);
const vulns: any[] = Array.isArray(report.vulnerabilities)
? report.vulnerabilities
: [];
const total = vulns.length;
const fixable = vulns.filter(v => v.fix || v.fixAvailable).length;
const bySeverity = { critical: 0, high: 0, moderate: 0, low: 0 };
vulns.forEach(v => {
const s = (v.severity as string)?.toLowerCase();
Iif (s in bySeverity) {
// @ts-expect-error allow indexing by dynamic severity key
bySeverity[s]++;
}
});
const pkgCounts: Record<string, number> = {};
vulns.forEach(v => {
const pkg = v.package || v.packageName || v.name;
Iif (pkg) {
pkgCounts[pkg] = (pkgCounts[pkg] || 0) + 1;
}
});
const topPackages = Object.entries(pkgCounts)
.map(([packageName, count]) => ({ package: packageName, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 5);
return { total, fixable, bySeverity, topPackages, detailPath, vulnerabilities: vulns };
} catch (err: any) {
let raw = '';
Iif (err.stdout) raw = err.stdout as string;
Iif (!raw && err.stderr) raw = err.stderr as string;
await fs.writeFile(detailPath, raw, 'utf-8');
let vulns: any[] = [];
try {
const report: any = JSON.parse(raw);
vulns = Array.isArray(report.vulnerabilities) ? report.vulnerabilities : [];
} catch {}
const total = vulns.length;
const fixable = vulns.filter(v => v.fix || v.fixAvailable).length;
const bySeverity = { critical: 0, high: 0, moderate: 0, low: 0 };
vulns.forEach(v => {
const s = (v.severity as string)?.toLowerCase();
Iif (s in bySeverity) {
// @ts-expect-error allow indexing by dynamic severity key
bySeverity[s]++;
}
});
const pkgCounts: Record<string, number> = {};
vulns.forEach(v => {
const pkg = v.package || v.packageName || v.name;
Iif (pkg) {
pkgCounts[pkg] = (pkgCounts[pkg] || 0) + 1;
}
});
const topPackages = Object.entries(pkgCounts)
.map(([packageName, count]) => ({ package: packageName, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 5);
return { total, fixable, bySeverity, topPackages, detailPath, vulnerabilities: vulns };
}
}
export const cli = {
command: 'qualimetrie:security',
description: 'Analyse les vulnérabilités de dépendances',
builder: (cmd: Command) => cmd,
handler: async (_opts: unknown) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { securityAgent: run } = require('./securityAgent');
const res = await run();
console.log(res);
},
};
|