// Bibliotecile criptografice DECLARATE in manifeste. Declararea nu e folosire: aceste intrari // ies ca componente "library", nu ca gasiri clasificate. import { inceputuriDeRand, randul } from './lexer.mjs'; // nume -> ce ofera (text scurt, in engleza, pentru utilizator) const NPM = { 'node-forge': 'RSA, AES, DES, MD5, SHA-1, SHA-2, X.509 (classical)', elliptic: 'ECDSA/ECDH/EdDSA on classical curves including secp256k1', secp256k1: 'secp256k1 ECDSA', 'tiny-secp256k1': 'secp256k1 ECDSA', '@noble/secp256k1': 'secp256k1 ECDSA', ethers: 'secp256k1 ECDSA accounts and signatures', web3: 'secp256k1 ECDSA accounts and signatures', viem: 'secp256k1 ECDSA accounts and signatures', 'ethereum-cryptography': 'secp256k1, keccak, AES, scrypt', jsonwebtoken: 'JWS HS*/RS*/PS*/ES*', jose: 'JWS/JWE (classical algorithms)', 'jwt-simple': 'JWS HS*/RS*', '@noble/curves': 'secp256k1, P-256/384/521, Ed25519/X25519, Ed448, BLS12-381 (classical)', '@noble/post-quantum': 'ML-KEM, ML-DSA, SLH-DSA (post-quantum)', '@noble/hashes': 'SHA-2, SHA-3, BLAKE, legacy MD5/SHA-1/RIPEMD-160', '@noble/ed25519': 'Ed25519', 'crypto-js': 'AES, DES, 3DES, RC4, MD5, SHA-1, SHA-2', tweetnacl: 'Ed25519, X25519, XSalsa20-Poly1305', 'libsodium-wrappers': 'Ed25519, X25519, XChaCha20-Poly1305, Argon2', 'node-rsa': 'RSA', jsrsasign: 'RSA, ECDSA, X.509', openpgp: 'OpenPGP: RSA, ECC, AES', bcrypt: 'bcrypt password hashing', bcryptjs: 'bcrypt password hashing', argon2: 'Argon2 password hashing', sshpk: 'SSH keys: RSA, ECDSA, Ed25519', '@peculiar/x509': 'X.509 certificates', pkijs: 'X.509/CMS', }; const PYPI = { cryptography: 'RSA, EC, DH, Ed25519/X25519, AES, hashes (pyca/cryptography)', pycryptodome: 'RSA, DSA, ECC, AES, DES, hashes', pycryptodomex: 'RSA, DSA, ECC, AES, DES, hashes', pycrypto: 'RSA, DSA, AES, DES (unmaintained since 2013)', ecdsa: 'ECDSA on classical curves', pyjwt: 'JWS HS*/RS*/ES*/PS*/EdDSA', 'python-jose': 'JWS/JWE (classical algorithms)', jwcrypto: 'JWS/JWE (classical algorithms)', pynacl: 'Ed25519, X25519', pyopenssl: 'TLS and X.509 via OpenSSL', paramiko: 'SSH: RSA, ECDSA, Ed25519, classical key exchange', 'liboqs-python': 'post-quantum KEMs and signatures (liboqs)', oqs: 'post-quantum KEMs and signatures (liboqs)', web3: 'secp256k1 ECDSA accounts', 'eth-account': 'secp256k1 ECDSA accounts', 'eth-keys': 'secp256k1 ECDSA', coincurve: 'secp256k1 ECDSA', bcrypt: 'bcrypt password hashing', 'argon2-cffi': 'Argon2 password hashing', passlib: 'password hashing', rsa: 'RSA (pure Python)', }; const MAVEN_GROUP = { 'org.bouncycastle': 'Bouncy Castle: classical and post-quantum algorithms', 'io.jsonwebtoken': 'JJWT: JWS HS*/RS*/ES*/PS*/EdDSA', 'com.nimbusds': 'Nimbus JOSE+JWT', 'com.auth0': 'java-jwt (if artifact is java-jwt)', 'org.web3j': 'secp256k1 ECDSA accounts (web3j)', 'com.google.crypto.tink': 'Tink: AEAD, signatures, hybrid encryption', 'org.conscrypt': 'Conscrypt TLS provider', }; const GO = [ [/^golang\.org\/x\/crypto$/, 'extended crypto: ssh, chacha20poly1305, curve25519, bcrypt, argon2'], [/^github\.com\/cloudflare\/circl$/, 'CIRCL: ML-KEM, ML-DSA, SLH-DSA, hybrid KEMs, classical curves'], [/^github\.com\/ethereum\/go-ethereum$/, 'secp256k1 ECDSA accounts and signatures'], [/^github\.com\/golang-jwt\/jwt(\/v\d+)?$/, 'JWS HS*/RS*/ES*/PS*/EdDSA'], [/^github\.com\/decred\/dcrd\/dcrec\/secp256k1(\/v\d+)?$/, 'secp256k1 ECDSA'], [/^github\.com\/btcsuite\/btcd\/btcec(\/v\d+)?$/, 'secp256k1 ECDSA'], [/^filippo\.io\/edwards25519$/, 'Edwards25519 group arithmetic'], [/^github\.com\/open-quantum-safe\/liboqs-go$/, 'post-quantum KEMs and signatures (liboqs)'], ]; export const NUME_MANIFEST = /^(package\.json|requirements[\w.-]*\.txt|pyproject\.toml|pom\.xml|build\.gradle(\.kts)?|go\.mod)$/; function lib(ecosistem, nume, versiune, ofera, pos, inceputuri, fisier) { const { line } = randul(inceputuri, pos); const purl = ecosistem === 'npm' ? `pkg:npm/${nume.startsWith('@') ? '%40' + nume.slice(1) : nume}${versiune && /^[\w.-]+$/.test(versiune) ? '@' + versiune : ''}` : ecosistem === 'pypi' ? `pkg:pypi/${nume}${versiune && /^[\w.-]+$/.test(versiune) ? '@' + versiune : ''}` : ecosistem === 'maven' ? `pkg:maven/${nume.replace(':', '/')}${versiune && /^[\w.-]+$/.test(versiune) ? '@' + versiune : ''}` : `pkg:golang/${nume}${versiune ? '@' + versiune : ''}`; return { file: fisier, line, ecosystem: ecosistem, name: nume, version: versiune || undefined, provides: ofera, purl }; } export function citesteManifest(text, numeFisier, rel) { const inceputuri = inceputuriDeRand(text); const r = []; if (numeFisier === 'package.json') { let j; try { j = JSON.parse(text); } catch { return { biblioteci: [], eroare: 'package.json is not valid JSON' }; } for (const sect of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) { const d = j && j[sect]; if (!d || typeof d !== 'object') continue; const ps = text.indexOf(`"${sect}"`); for (const [nume, ver] of Object.entries(d)) { const ofera = NPM[nume] || (/^@ethersproject\//.test(nume) ? 'secp256k1 ECDSA (ethers v5 module)' : null); if (!ofera) continue; const pos = text.indexOf(`"${nume}"`, ps < 0 ? 0 : ps); r.push(lib('npm', nume, String(ver).replace(/^[\^~>=<\s]+/, ''), ofera, pos < 0 ? 0 : pos, inceputuri, rel)); } } } else if (/^requirements/.test(numeFisier)) { let pos = 0; for (const linie of text.split('\n')) { const t = linie.replace(/#.*/, '').trim(); const m = /^([A-Za-z0-9_.-]+)(?:\[[^\]]*\])?\s*(?:[=<>!~]=?\s*([\w.*+-]+))?/.exec(t); if (m) { const nume = m[1].toLowerCase().replace(/_/g, '-'); if (PYPI[nume]) r.push(lib('pypi', nume, m[2], PYPI[nume], pos + linie.indexOf(m[1]), inceputuri, rel)); } pos += linie.length + 1; } } else if (numeFisier === 'pyproject.toml') { let pos = 0; let sectiune = ''; for (const linie of text.split('\n')) { const h = /^\s*\[([^\]]+)\]/.exec(linie); if (h) sectiune = h[1]; for (const m of linie.matchAll(/["']([A-Za-z0-9_.-]+)(?:\[[^\]]*\])?\s*(?:[=<>!~]=?\s*([\w.*+-]+))?[^"']*["']/g)) { const nume = m[1].toLowerCase().replace(/_/g, '-'); if (PYPI[nume]) r.push(lib('pypi', nume, m[2], PYPI[nume], pos + m.index + 1, inceputuri, rel)); } const p = /^\s*([A-Za-z0-9_.-]+)\s*=\s*(?:["']([^"']*)["']|\{)/.exec(linie); if (p && /dependencies/.test(sectiune)) { const nume = p[1].toLowerCase().replace(/_/g, '-'); if (PYPI[nume]) r.push(lib('pypi', nume, p[2] ? p[2].replace(/^[\^~>=<\s]+/, '') : undefined, PYPI[nume], pos + linie.indexOf(p[1]), inceputuri, rel)); } pos += linie.length + 1; } } else if (numeFisier === 'pom.xml') { for (const m of text.matchAll(/([\s\S]*?)<\/dependency>/g)) { const g = /\s*([^<\s]+)\s*<\/groupId>/.exec(m[1]); const a = /\s*([^<\s]+)\s*<\/artifactId>/.exec(m[1]); const v = /\s*([^<\s]+)\s*<\/version>/.exec(m[1]); if (!g || !a) continue; const ofera = MAVEN_GROUP[g[1]]; if (!ofera) continue; r.push(lib('maven', `${g[1]}:${a[1]}`, v && !/\$\{/.test(v[1]) ? v[1] : undefined, ofera, m.index + m[0].indexOf(a[0]), inceputuri, rel)); } } else if (/^build\.gradle/.test(numeFisier)) { for (const m of text.matchAll(/["']([\w.-]+):([\w.-]+)(?::([\w.-]+))?["']/g)) { const ofera = MAVEN_GROUP[m[1]]; if (ofera) r.push(lib('maven', `${m[1]}:${m[2]}`, m[3], ofera, m.index + 1, inceputuri, rel)); } } else if (numeFisier === 'go.mod') { let pos = 0; let inRequire = false; for (const linie of text.split('\n')) { const t = linie.replace(/\/\/.*/, '').trim(); if (/^require\s*\($/.test(t)) inRequire = true; else if (inRequire && t === ')') inRequire = false; const m = inRequire ? /^([\w.\-/]+)\s+(v[\w.\-+]+)/.exec(t) : /^require\s+([\w.\-/]+)\s+(v[\w.\-+]+)/.exec(t); if (m) { const intrare = GO.find(([re]) => re.test(m[1])); if (intrare) r.push(lib('golang', m[1], m[2], intrare[1], pos + linie.indexOf(m[1]), inceputuri, rel)); } pos += linie.length + 1; } } return { biblioteci: r }; }