All files / services/qualimetrie bomAgent.ts

100% Statements 51/51
92.3% Branches 24/26
100% Functions 6/6
100% Lines 49/49

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 107 108 1095x   5x                                           5x       2x 2x         2x   2x 4x   4x   3x   2x           1x   1x   2x 4x   4x 4x 4x 4x 4x 1x 3x 2x   1x   4x 4x   2x     5x     1x     1x 1x   1x 1x 1x   1x 1x   2x 2x     1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 2x        
import { promisify } from 'util';
import { Command } from 'commander';
import * as licenseChecker from 'license-checker';
 
export interface BomAgentOptions {}
 
export interface BomPackage {
  name: string;
  version: string;
  license: string;
  risk: 'Low' | 'Medium' | 'High' | 'Unknown';
}
export interface BomAgentResult {
  packages: BomPackage[];
}
 
/**
 * Génère le Bill Of Materials (liste des packages et licences).
 */
 
/**
 * Exécute license-checker pour récupérer la liste des dépendances et licences,
 * et évalue le risque associé à chaque licence.
 */
export async function bomAgent(
  _opts?: BomAgentOptions,
): Promise<BomAgentResult> {
  // Programmatic retrieval via license-checker
  const initChecker = promisify(licenseChecker.init);
  const data = (await initChecker({
    start: process.cwd(),
    production: true,
    json: true,
  })) as Record<string, { licenses?: string | string[] }>;
  const results: BomPackage[] = [];
  // Risk evaluation function
  const evaluateRisk = (lic: string): BomPackage['risk'] => {
    const l = lic.toLowerCase();
    // Copyleft strong licenses
    if (l.includes('agpl') || (l.includes('gpl') && !l.includes('lgpl'))) return 'High';
    // Lesser copyleft
    if (l.includes('lgpl')) return 'Medium';
    // Permissive licenses
    if (
      l.includes('mit') ||
      l.includes('bsd') ||
      l.includes('apache') ||
      l.includes('isc')
    ) {
      return 'Low';
    }
    return 'Unknown';
  };
  for (const key of Object.keys(data)) {
    const info = data[key] as { licenses?: string | string[] };
    // key format: 'name@version'
    const at = key.lastIndexOf('@');
    const name = key.substring(0, at);
    const version = key.substring(at + 1);
    let license = '';
    if (Array.isArray(info.licenses)) {
      license = info.licenses.join(', ');
    } else if (typeof info.licenses === 'string') {
      license = info.licenses;
    } else {
      license = String(info.licenses || 'Unknown');
    }
    const risk = evaluateRisk(license);
    results.push({ name, version, license, risk });
  }
  return { packages: results };
}
 
export const cli = {
  command: 'qualimetrie:bom',
  description: 'Génère le Bill Of Materials des licences',
  builder: (cmd: Command) => cmd,
  handler: async (_opts: unknown) => {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const { bomAgent: run } = require('./bomAgent');
    const res: BomAgentResult = await run();
    // Report header
    const date = new Date().toISOString().split('T')[0];
    console.log('# 📜 Bill Of Materials Report');
    console.log(`Date: ${date}`);
    // Executive summary
    const total = res.packages.length;
    const counts = res.packages.reduce(
      (acc, pkg) => {
        acc[pkg.risk] = (acc[pkg.risk] || 0) + 1;
        return acc;
      }, {} as Record<string, number>,
    );
    console.log('## 🔎 Executive Summary');
    console.log(`- Total packages: ${total}`);
    console.log(`- High risk: ${counts['High'] || 0}`);
    console.log(`- Medium risk: ${counts['Medium'] || 0}`);
    console.log(`- Low risk: ${counts['Low'] || 0}`);
    console.log(`- Unknown risk: ${counts['Unknown'] || 0}\n`);
    // Detailed table
    console.log('## 📜 Details');
    console.log('| Package | Version | License | Risk |');
    console.log('|---|---|---|---|');
    res.packages.forEach((pkg) => {
      console.log(`| ${pkg.name} | ${pkg.version} | ${pkg.license} | ${pkg.risk} |`);
    });
  },
};