mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
[AAE-46514] - Fixed copilot comments
This commit is contained in:
+169
-63
@@ -33,7 +33,7 @@
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync, readdirSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { execSync, spawnSync } from 'child_process';
|
||||
import { spawnSync } from 'child_process';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT_DIR = join(__dirname, '..');
|
||||
@@ -132,44 +132,55 @@ function extractVersionsFromOSV(vuln) {
|
||||
return [...new Set(versions)];
|
||||
}
|
||||
|
||||
async function fetchFromOSV(packages) {
|
||||
async function fetchFromOSV(projectDependencies) {
|
||||
console.log(' 📡 Fetching from OSV (Google)...');
|
||||
const results = {};
|
||||
|
||||
try {
|
||||
const response = await fetch(OSV_BATCH_API, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
queries: Object.keys(packages).map(name => ({
|
||||
package: { name, ecosystem: 'npm' }
|
||||
}))
|
||||
})
|
||||
});
|
||||
if (!projectDependencies || projectDependencies.length === 0) {
|
||||
console.log(' ⚠ OSV: No dependencies to check');
|
||||
return results;
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
for (const result of data.results || []) {
|
||||
for (const vuln of result.vulns || []) {
|
||||
const summary = [vuln.summary || '', vuln.details || ''].join(' ');
|
||||
if (isMalwareRelated(summary)) {
|
||||
const pkgName = vuln.affected?.[0]?.package?.name;
|
||||
if (pkgName) {
|
||||
const versions = extractVersionsFromOSV(vuln);
|
||||
if (versions.length > 0) {
|
||||
if (!results[pkgName]) {
|
||||
results[pkgName] = { versions: [], reason: '' };
|
||||
try {
|
||||
// Query OSV for the project's actual dependencies (in batches of 1000)
|
||||
const batchSize = 1000;
|
||||
for (let i = 0; i < projectDependencies.length; i += batchSize) {
|
||||
const batch = projectDependencies.slice(i, i + batchSize);
|
||||
const response = await fetch(OSV_BATCH_API, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
queries: batch.map(dep => ({
|
||||
package: { name: dep.name, ecosystem: 'npm' },
|
||||
version: dep.version
|
||||
}))
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
for (const result of data.results || []) {
|
||||
for (const vuln of result.vulns || []) {
|
||||
const summary = [vuln.summary || '', vuln.details || ''].join(' ');
|
||||
if (isMalwareRelated(summary)) {
|
||||
const pkgName = vuln.affected?.[0]?.package?.name;
|
||||
if (pkgName) {
|
||||
const versions = extractVersionsFromOSV(vuln);
|
||||
if (versions.length > 0) {
|
||||
if (!results[pkgName]) {
|
||||
results[pkgName] = { versions: [], reason: '' };
|
||||
}
|
||||
results[pkgName].versions.push(...versions);
|
||||
results[pkgName].reason = vuln.summary || 'Malware detected by OSV';
|
||||
results[pkgName].source = 'OSV';
|
||||
}
|
||||
results[pkgName].versions.push(...versions);
|
||||
results[pkgName].reason = vuln.summary || 'Malware detected by OSV';
|
||||
results[pkgName].source = 'OSV';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(` ✓ OSV: Found ${Object.keys(results).length} malware entries`);
|
||||
}
|
||||
console.log(` ✓ OSV: Found ${Object.keys(results).length} malware entries`);
|
||||
} catch (e) {
|
||||
console.log(` ⚠ OSV: Could not fetch (${e.message})`);
|
||||
}
|
||||
@@ -275,7 +286,7 @@ async function checkWithMeterian(dependencies) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchFromGitHubAdvisory(packages) {
|
||||
async function fetchFromGitHubAdvisory() {
|
||||
console.log(' 📡 Fetching from GitHub Advisory...');
|
||||
const results = {};
|
||||
|
||||
@@ -301,28 +312,23 @@ async function fetchFromGitHubAdvisory(packages) {
|
||||
for (const vuln of advisory.vulnerabilities || []) {
|
||||
const pkgName = vuln.package?.name;
|
||||
if (pkgName && vuln.package?.ecosystem === 'npm') {
|
||||
// Extract affected versions
|
||||
const versions = [];
|
||||
if (vuln.vulnerable_version_range) {
|
||||
// Parse version range like "= 1.95.6" or ">= 1.0.0, < 1.0.1"
|
||||
const exactMatch = vuln.vulnerable_version_range.match(/^= (.+)$/);
|
||||
if (exactMatch) {
|
||||
versions.push(exactMatch[1]);
|
||||
}
|
||||
}
|
||||
if (vuln.first_patched_version?.identifier) {
|
||||
// This tells us the fixed version, not the bad ones
|
||||
// We'd need to know the introduced version too
|
||||
if (!results[pkgName]) {
|
||||
results[pkgName] = { versions: [], versionRanges: [], reason: '' };
|
||||
}
|
||||
|
||||
if (versions.length > 0 || isMalwareRelated(summary)) {
|
||||
if (!results[pkgName]) {
|
||||
results[pkgName] = { versions: [], reason: '' };
|
||||
if (vuln.vulnerable_version_range) {
|
||||
// Store the full version range for later matching
|
||||
results[pkgName].versionRanges.push(vuln.vulnerable_version_range);
|
||||
|
||||
// Also extract exact versions where possible
|
||||
const exactMatch = vuln.vulnerable_version_range.match(/^=\s*(.+)$/);
|
||||
if (exactMatch) {
|
||||
results[pkgName].versions.push(exactMatch[1].trim());
|
||||
}
|
||||
results[pkgName].versions.push(...versions);
|
||||
results[pkgName].reason = advisory.summary || 'Malware detected by GitHub Advisory';
|
||||
results[pkgName].source = 'GitHub';
|
||||
}
|
||||
|
||||
results[pkgName].reason = advisory.summary || 'Malware detected by GitHub Advisory';
|
||||
results[pkgName].source = 'GitHub';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,7 +343,51 @@ async function fetchFromGitHubAdvisory(packages) {
|
||||
return results;
|
||||
}
|
||||
|
||||
async function fetchMalwareList() {
|
||||
function getProjectDependencies() {
|
||||
const dependencies = [];
|
||||
const lockfilePath = join(ROOT_DIR, 'package-lock.json');
|
||||
|
||||
if (existsSync(lockfilePath)) {
|
||||
try {
|
||||
const lockfile = JSON.parse(readFileSync(lockfilePath, 'utf8'));
|
||||
if (lockfile.packages) {
|
||||
for (const [path, info] of Object.entries(lockfile.packages)) {
|
||||
if (!path || path === '' || !info.version) continue;
|
||||
const name = path.replace(/^node_modules\//, '').replace(/^.*node_modules\//, '');
|
||||
dependencies.push({ name, version: info.version });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
// Also check package.json for dependencies not yet in lockfile
|
||||
const packageJsonPath = join(ROOT_DIR, 'package.json');
|
||||
if (existsSync(packageJsonPath)) {
|
||||
try {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
const allDeps = {
|
||||
...packageJson.dependencies,
|
||||
...packageJson.devDependencies
|
||||
};
|
||||
for (const [name, versionSpec] of Object.entries(allDeps)) {
|
||||
if (typeof versionSpec === 'string' && !versionSpec.startsWith('file:')) {
|
||||
const version = versionSpec.replace(/^[\^~>=<]*/, '').split(' ')[0];
|
||||
if (version && !dependencies.some(d => d.name === name && d.version === version)) {
|
||||
dependencies.push({ name, version });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
async function fetchMalwareList(projectDependencies) {
|
||||
// Check cache first
|
||||
if (existsSync(CACHE_FILE)) {
|
||||
try {
|
||||
@@ -345,7 +395,7 @@ async function fetchMalwareList() {
|
||||
if (Date.now() - cache.timestamp < CACHE_TTL_MS) {
|
||||
console.log(' Using cached security database...');
|
||||
console.log(` (${cache.sources?.join(', ') || 'fallback'} - cached ${Math.round((Date.now() - cache.timestamp) / 60000)} min ago)`);
|
||||
return cache.packages;
|
||||
return { packages: cache.packages, sources: cache.sources || ['fallback'] };
|
||||
}
|
||||
} catch {
|
||||
// Cache corrupted, will refetch
|
||||
@@ -358,8 +408,8 @@ async function fetchMalwareList() {
|
||||
const packages = { ...FALLBACK_BLOCKED_PACKAGES };
|
||||
const sources = ['fallback'];
|
||||
|
||||
// Fetch from OSV
|
||||
const osvResults = await fetchFromOSV(packages);
|
||||
// Fetch from OSV using the project's actual dependencies
|
||||
const osvResults = await fetchFromOSV(projectDependencies);
|
||||
if (Object.keys(osvResults).length > 0) {
|
||||
sources.push('OSV');
|
||||
for (const [name, data] of Object.entries(osvResults)) {
|
||||
@@ -372,14 +422,15 @@ async function fetchMalwareList() {
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch from GitHub Advisory
|
||||
const ghResults = await fetchFromGitHubAdvisory(packages);
|
||||
// Fetch from GitHub Advisory (returns all known npm malware, limited to first page)
|
||||
const ghResults = await fetchFromGitHubAdvisory();
|
||||
if (Object.keys(ghResults).length > 0) {
|
||||
sources.push('GitHub');
|
||||
for (const [name, data] of Object.entries(ghResults)) {
|
||||
if (packages[name]) {
|
||||
// Merge versions
|
||||
packages[name].versions = [...new Set([...packages[name].versions, ...data.versions])];
|
||||
packages[name].versions = [...new Set([...packages[name].versions, ...(data.versions || [])])];
|
||||
packages[name].versionRanges = [...new Set([...(packages[name].versionRanges || []), ...(data.versionRanges || [])])];
|
||||
} else {
|
||||
packages[name] = data;
|
||||
}
|
||||
@@ -402,7 +453,7 @@ async function fetchMalwareList() {
|
||||
// Cache write failed, continue anyway
|
||||
}
|
||||
|
||||
return packages;
|
||||
return { packages, sources };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -421,8 +472,61 @@ function readJsonFile(filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkVersion(version, blockedVersions) {
|
||||
return blockedVersions.some(blocked => blocked === version);
|
||||
function compareVersions(a, b) {
|
||||
const partsA = a.replace(/^v/, '').split('.').map(p => parseInt(p, 10) || 0);
|
||||
const partsB = b.replace(/^v/, '').split('.').map(p => parseInt(p, 10) || 0);
|
||||
const len = Math.max(partsA.length, partsB.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const numA = partsA[i] || 0;
|
||||
const numB = partsB[i] || 0;
|
||||
if (numA > numB) return 1;
|
||||
if (numA < numB) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function matchesVersionRange(version, range) {
|
||||
// Parse version range like ">= 1.0.0, < 1.0.1" or "= 1.95.6"
|
||||
const conditions = range.split(',').map(c => c.trim());
|
||||
|
||||
for (const condition of conditions) {
|
||||
const match = condition.match(/^(>=|<=|>|<|=)?\s*(.+)$/);
|
||||
if (!match) continue;
|
||||
|
||||
const [, operator = '=', targetVersion] = match;
|
||||
const cmp = compareVersions(version, targetVersion.trim());
|
||||
|
||||
switch (operator) {
|
||||
case '=':
|
||||
if (cmp !== 0) return false;
|
||||
break;
|
||||
case '>':
|
||||
if (cmp <= 0) return false;
|
||||
break;
|
||||
case '>=':
|
||||
if (cmp < 0) return false;
|
||||
break;
|
||||
case '<':
|
||||
if (cmp >= 0) return false;
|
||||
break;
|
||||
case '<=':
|
||||
if (cmp > 0) return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkVersion(version, blockedEntry) {
|
||||
// Check exact version matches
|
||||
if (blockedEntry.versions?.some(blocked => blocked === version)) {
|
||||
return true;
|
||||
}
|
||||
// Check version ranges (from GitHub Advisory)
|
||||
if (blockedEntry.versionRanges?.some(range => matchesVersionRange(version, range))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkDependencies(deps, blockedPackages, source) {
|
||||
@@ -432,7 +536,7 @@ function checkDependencies(deps, blockedPackages, source) {
|
||||
for (const [name, versionSpec] of Object.entries(deps)) {
|
||||
if (blockedPackages[name]) {
|
||||
const version = versionSpec.replace(/^[\^~>=<]*/, '').split(' ')[0];
|
||||
if (checkVersion(version, blockedPackages[name].versions)) {
|
||||
if (checkVersion(version, blockedPackages[name])) {
|
||||
violations.push({
|
||||
package: name,
|
||||
version,
|
||||
@@ -454,7 +558,7 @@ function checkLockfileDependencies(packages, blockedPackages) {
|
||||
const name = path.replace(/^node_modules\//, '').replace(/^.*node_modules\//, '');
|
||||
|
||||
if (blockedPackages[name] && info.version) {
|
||||
if (checkVersion(info.version, blockedPackages[name].versions)) {
|
||||
if (checkVersion(info.version, blockedPackages[name])) {
|
||||
violations.push({
|
||||
package: name,
|
||||
version: info.version,
|
||||
@@ -477,9 +581,9 @@ async function main() {
|
||||
console.log('='.repeat(70));
|
||||
console.log('Scanning for known supply chain attacks and compromised packages...\n');
|
||||
|
||||
// Fetch blocked packages list
|
||||
const blockedPackages = await fetchMalwareList();
|
||||
const threatCount = Object.keys(blockedPackages).length;
|
||||
// Get project dependencies and fetch blocked packages list
|
||||
const projectDependencies = getProjectDependencies();
|
||||
const { packages: blockedPackages, sources } = await fetchMalwareList(projectDependencies);
|
||||
|
||||
const violations = [];
|
||||
|
||||
@@ -572,7 +676,9 @@ async function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`✅ Security check passed (${threatCount} known threats checked)`);
|
||||
const packageCount = Object.keys(blockedPackages).length;
|
||||
const sourceList = sources.join(' + ');
|
||||
console.log(`✅ Security check passed (checked against ${sourceList}, ${packageCount} blocked packages)`);
|
||||
console.log('='.repeat(70) + '\n');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
/**
|
||||
* Post-install Security Script
|
||||
*
|
||||
* This runs after `npm install` / `npm ci` (via prepare hook).
|
||||
* Preinstall already checked the lockfile - this is a defense-in-depth
|
||||
* check against installed packages + rebuilds trusted native bindings.
|
||||
* Runs after `npm install` / `npm ci` via the prepare hook.
|
||||
* The preinstall hook already checked package.json and lockfile - this is
|
||||
* a defense-in-depth check against installed packages.
|
||||
*
|
||||
* This script:
|
||||
* 1. Runs security check against OSV + GitHub Advisory (defense in depth)
|
||||
@@ -138,7 +138,7 @@ async function main() {
|
||||
trustedInstalled.forEach(pkg => console.log(` ✓ ${pkg}`));
|
||||
console.log('');
|
||||
|
||||
run(`npm rebuild ${trustedInstalled.join(' ')}`);
|
||||
run(`npm rebuild --ignore-scripts=false ${trustedInstalled.join(' ')}`);
|
||||
} else {
|
||||
console.log('No trusted packages require rebuilding.\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user