All files / services/qualimetrie securityAgent.ts

38.98% Statements 23/59
3.84% Branches 1/26
23.07% Functions 3/13
41.81% Lines 23/55

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 1064x 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);
  },
};