142 lines
5.9 KiB
JavaScript
142 lines
5.9 KiB
JavaScript
// Parcurgerea dosarului si orchestrarea detectorilor. Nimic nu se sare in tacere: fiecare
|
|
// fisier vazut ajunge intr-o singura categorie numarata, si suma se verifica la sfarsit.
|
|
import { readdirSync, statSync, readFileSync, lstatSync } from 'node:fs';
|
|
import { join, relative, sep, basename, resolve, extname } from 'node:path';
|
|
import { facContext, aplicaSuprimarea, REZOLVARI_PE_FISIER } from './context.mjs';
|
|
import { detecteazaJs } from './detect-js.mjs';
|
|
import { detecteazaPy } from './detect-py.mjs';
|
|
import { detecteazaJava } from './detect-java.mjs';
|
|
import { detecteazaGo } from './detect-go.mjs';
|
|
import { detecteazaPem } from './pem.mjs';
|
|
import { citesteManifest, NUME_MANIFEST } from './manifeste.mjs';
|
|
|
|
export const VERSIUNE = '0.2.0';
|
|
export const DOSARE_EXCLUSE_IMPLICIT = ['.git', '.hg', '.svn', 'node_modules', '__pycache__', '.venv', 'venv', '.tox', '.gradle', '.idea', '.mypy_cache', '.pytest_cache'];
|
|
|
|
const LIMBAJ = {
|
|
'.js': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript', '.jsx': 'javascript',
|
|
'.ts': 'javascript', '.tsx': 'javascript', '.mts': 'javascript', '.cts': 'javascript',
|
|
'.py': 'python', '.pyw': 'python',
|
|
'.java': 'java',
|
|
'.go': 'go',
|
|
};
|
|
const DETECTOR = { javascript: detecteazaJs, python: detecteazaPy, java: detecteazaJava, go: detecteazaGo };
|
|
const EXEMPLE = 5;
|
|
|
|
export function limbajul(cale) {
|
|
return LIMBAJ[extname(cale).toLowerCase()] || null;
|
|
}
|
|
|
|
// binar = contine un octet NUL in primii 8192 octeti
|
|
export function eBinar(buf) {
|
|
const n = Math.min(buf.length, 8192);
|
|
for (let i = 0; i < n; i++) if (buf[i] === 0) return true;
|
|
return false;
|
|
}
|
|
|
|
export function scaneaza(radacina, optiuni = {}) {
|
|
const o = {
|
|
maxFileBytes: optiuni.maxFileBytes ?? 1048576,
|
|
maxFiles: optiuni.maxFiles ?? 50000,
|
|
excludeDirs: new Set([...(optiuni.noDefaultExcludes ? [] : DOSARE_EXCLUSE_IMPLICIT), ...(optiuni.excludeDirs || [])]),
|
|
};
|
|
const root = resolve(radacina);
|
|
const st = statSync(root);
|
|
if (!st.isDirectory()) throw new Error(`not a directory: ${radacina}`);
|
|
const inceput = new Date();
|
|
const stats = {
|
|
filesSeen: 0,
|
|
codeFiles: {},
|
|
textFilesPemOnly: 0,
|
|
manifests: 0,
|
|
manifestErrors: [],
|
|
notRead: { binary: 0, tooLarge: 0, fileLimit: 0, unreadable: 0 },
|
|
notReadExamples: { binary: [], tooLarge: [], fileLimit: [], unreadable: [] },
|
|
symlinksNotFollowed: 0,
|
|
resolutionLimitFiles: 0,
|
|
resolutionLimitExamples: [],
|
|
dirsExcluded: {},
|
|
dirsUnreadable: 0,
|
|
};
|
|
const gasiri = [];
|
|
const biblioteci = [];
|
|
let cititeSauIncercate = 0;
|
|
const noteaza = (motiv, rel, extra) => {
|
|
stats.notRead[motiv]++;
|
|
if (stats.notReadExamples[motiv].length < EXEMPLE) stats.notReadExamples[motiv].push(extra ? `${rel} (${extra})` : rel);
|
|
};
|
|
|
|
const stiva = [root];
|
|
while (stiva.length) {
|
|
const dir = stiva.pop();
|
|
let intrari;
|
|
try { intrari = readdirSync(dir, { withFileTypes: true }); } catch { stats.dirsUnreadable++; continue; }
|
|
intrari.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
const subdosare = [];
|
|
for (const e of intrari) {
|
|
const abs = join(dir, e.name);
|
|
if (e.isSymbolicLink()) { stats.symlinksNotFollowed++; continue; }
|
|
if (e.isDirectory()) {
|
|
if (o.excludeDirs.has(e.name)) { stats.dirsExcluded[e.name] = (stats.dirsExcluded[e.name] || 0) + 1; continue; }
|
|
subdosare.push(abs);
|
|
continue;
|
|
}
|
|
if (!e.isFile()) continue;
|
|
stats.filesSeen++;
|
|
const rel = relative(root, abs).split(sep).join('/');
|
|
if (cititeSauIncercate >= o.maxFiles) { noteaza('fileLimit', rel); continue; }
|
|
cititeSauIncercate++;
|
|
let marime;
|
|
try { marime = lstatSync(abs).size; } catch (err) { noteaza('unreadable', rel, err.code); continue; }
|
|
if (marime > o.maxFileBytes) { noteaza('tooLarge', rel, `${marime} bytes`); continue; }
|
|
let buf;
|
|
try { buf = readFileSync(abs); } catch (err) { noteaza('unreadable', rel, err.code); continue; }
|
|
if (eBinar(buf)) { noteaza('binary', rel); continue; }
|
|
const text = buf.toString('utf8');
|
|
const limbaj = limbajul(abs);
|
|
const ctx = facContext(text, limbaj || 'text', rel);
|
|
if (limbaj) {
|
|
stats.codeFiles[limbaj] = (stats.codeFiles[limbaj] || 0) + 1;
|
|
DETECTOR[limbaj](ctx);
|
|
if (ctx.rezolvariPestePlafon) {
|
|
stats.resolutionLimitFiles++;
|
|
if (stats.resolutionLimitExamples.length < EXEMPLE) stats.resolutionLimitExamples.push(rel);
|
|
}
|
|
} else {
|
|
stats.textFilesPemOnly++;
|
|
}
|
|
detecteazaPem(text, ctx);
|
|
gasiri.push(...aplicaSuprimarea(ctx.gasiri));
|
|
if (NUME_MANIFEST.test(basename(abs))) {
|
|
stats.manifests++;
|
|
const r = citesteManifest(text, basename(abs), rel);
|
|
if (r.eroare) stats.manifestErrors.push(`${rel}: ${r.eroare}`);
|
|
biblioteci.push(...r.biblioteci);
|
|
}
|
|
}
|
|
for (let i = subdosare.length - 1; i >= 0; i--) stiva.push(subdosare[i]);
|
|
}
|
|
|
|
// garda: fiecare fisier vazut e intr-o singura categorie
|
|
const cod = Object.values(stats.codeFiles).reduce((a, b) => a + b, 0);
|
|
const nr = stats.notRead;
|
|
const suma = cod + stats.textFilesPemOnly + nr.binary + nr.tooLarge + nr.fileLimit + nr.unreadable;
|
|
if (suma !== stats.filesSeen) throw new Error(`internal accounting error: ${stats.filesSeen} files seen, ${suma} accounted for`);
|
|
stats.codeFilesTotal = cod;
|
|
|
|
gasiri.sort((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line || a.column - b.column));
|
|
biblioteci.sort((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line));
|
|
return {
|
|
tool: { name: 'aere-crypto-inventory', version: VERSIUNE },
|
|
root,
|
|
rootName: basename(root),
|
|
options: { maxFileBytes: o.maxFileBytes, maxFiles: o.maxFiles, excludeDirs: [...o.excludeDirs].sort(), maxResolvedVariablesPerFile: REZOLVARI_PE_FISIER },
|
|
startedAt: inceput.toISOString(),
|
|
finishedAt: new Date().toISOString(),
|
|
findings: gasiri,
|
|
libraries: biblioteci,
|
|
stats,
|
|
};
|
|
}
|
|
|