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 { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync, readdirSync } from 'fs';
|
||||||
import { join, dirname } from 'path';
|
import { join, dirname } from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { execSync, spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const ROOT_DIR = join(__dirname, '..');
|
const ROOT_DIR = join(__dirname, '..');
|
||||||
@@ -132,44 +132,55 @@ function extractVersionsFromOSV(vuln) {
|
|||||||
return [...new Set(versions)];
|
return [...new Set(versions)];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFromOSV(packages) {
|
async function fetchFromOSV(projectDependencies) {
|
||||||
console.log(' 📡 Fetching from OSV (Google)...');
|
console.log(' 📡 Fetching from OSV (Google)...');
|
||||||
const results = {};
|
const results = {};
|
||||||
|
|
||||||
try {
|
if (!projectDependencies || projectDependencies.length === 0) {
|
||||||
const response = await fetch(OSV_BATCH_API, {
|
console.log(' ⚠ OSV: No dependencies to check');
|
||||||
method: 'POST',
|
return results;
|
||||||
headers: { 'Content-Type': 'application/json' },
|
}
|
||||||
body: JSON.stringify({
|
|
||||||
queries: Object.keys(packages).map(name => ({
|
|
||||||
package: { name, ecosystem: 'npm' }
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
try {
|
||||||
const data = await response.json();
|
// Query OSV for the project's actual dependencies (in batches of 1000)
|
||||||
for (const result of data.results || []) {
|
const batchSize = 1000;
|
||||||
for (const vuln of result.vulns || []) {
|
for (let i = 0; i < projectDependencies.length; i += batchSize) {
|
||||||
const summary = [vuln.summary || '', vuln.details || ''].join(' ');
|
const batch = projectDependencies.slice(i, i + batchSize);
|
||||||
if (isMalwareRelated(summary)) {
|
const response = await fetch(OSV_BATCH_API, {
|
||||||
const pkgName = vuln.affected?.[0]?.package?.name;
|
method: 'POST',
|
||||||
if (pkgName) {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
const versions = extractVersionsFromOSV(vuln);
|
body: JSON.stringify({
|
||||||
if (versions.length > 0) {
|
queries: batch.map(dep => ({
|
||||||
if (!results[pkgName]) {
|
package: { name: dep.name, ecosystem: 'npm' },
|
||||||
results[pkgName] = { versions: [], reason: '' };
|
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) {
|
} catch (e) {
|
||||||
console.log(` ⚠ OSV: Could not fetch (${e.message})`);
|
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...');
|
console.log(' 📡 Fetching from GitHub Advisory...');
|
||||||
const results = {};
|
const results = {};
|
||||||
|
|
||||||
@@ -301,28 +312,23 @@ async function fetchFromGitHubAdvisory(packages) {
|
|||||||
for (const vuln of advisory.vulnerabilities || []) {
|
for (const vuln of advisory.vulnerabilities || []) {
|
||||||
const pkgName = vuln.package?.name;
|
const pkgName = vuln.package?.name;
|
||||||
if (pkgName && vuln.package?.ecosystem === 'npm') {
|
if (pkgName && vuln.package?.ecosystem === 'npm') {
|
||||||
// Extract affected versions
|
if (!results[pkgName]) {
|
||||||
const versions = [];
|
results[pkgName] = { versions: [], versionRanges: [], reason: '' };
|
||||||
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 (versions.length > 0 || isMalwareRelated(summary)) {
|
if (vuln.vulnerable_version_range) {
|
||||||
if (!results[pkgName]) {
|
// Store the full version range for later matching
|
||||||
results[pkgName] = { versions: [], reason: '' };
|
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;
|
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
|
// Check cache first
|
||||||
if (existsSync(CACHE_FILE)) {
|
if (existsSync(CACHE_FILE)) {
|
||||||
try {
|
try {
|
||||||
@@ -345,7 +395,7 @@ async function fetchMalwareList() {
|
|||||||
if (Date.now() - cache.timestamp < CACHE_TTL_MS) {
|
if (Date.now() - cache.timestamp < CACHE_TTL_MS) {
|
||||||
console.log(' Using cached security database...');
|
console.log(' Using cached security database...');
|
||||||
console.log(` (${cache.sources?.join(', ') || 'fallback'} - cached ${Math.round((Date.now() - cache.timestamp) / 60000)} min ago)`);
|
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 {
|
} catch {
|
||||||
// Cache corrupted, will refetch
|
// Cache corrupted, will refetch
|
||||||
@@ -358,8 +408,8 @@ async function fetchMalwareList() {
|
|||||||
const packages = { ...FALLBACK_BLOCKED_PACKAGES };
|
const packages = { ...FALLBACK_BLOCKED_PACKAGES };
|
||||||
const sources = ['fallback'];
|
const sources = ['fallback'];
|
||||||
|
|
||||||
// Fetch from OSV
|
// Fetch from OSV using the project's actual dependencies
|
||||||
const osvResults = await fetchFromOSV(packages);
|
const osvResults = await fetchFromOSV(projectDependencies);
|
||||||
if (Object.keys(osvResults).length > 0) {
|
if (Object.keys(osvResults).length > 0) {
|
||||||
sources.push('OSV');
|
sources.push('OSV');
|
||||||
for (const [name, data] of Object.entries(osvResults)) {
|
for (const [name, data] of Object.entries(osvResults)) {
|
||||||
@@ -372,14 +422,15 @@ async function fetchMalwareList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch from GitHub Advisory
|
// Fetch from GitHub Advisory (returns all known npm malware, limited to first page)
|
||||||
const ghResults = await fetchFromGitHubAdvisory(packages);
|
const ghResults = await fetchFromGitHubAdvisory();
|
||||||
if (Object.keys(ghResults).length > 0) {
|
if (Object.keys(ghResults).length > 0) {
|
||||||
sources.push('GitHub');
|
sources.push('GitHub');
|
||||||
for (const [name, data] of Object.entries(ghResults)) {
|
for (const [name, data] of Object.entries(ghResults)) {
|
||||||
if (packages[name]) {
|
if (packages[name]) {
|
||||||
// Merge versions
|
// 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 {
|
} else {
|
||||||
packages[name] = data;
|
packages[name] = data;
|
||||||
}
|
}
|
||||||
@@ -402,7 +453,7 @@ async function fetchMalwareList() {
|
|||||||
// Cache write failed, continue anyway
|
// Cache write failed, continue anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
return packages;
|
return { packages, sources };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -421,8 +472,61 @@ function readJsonFile(filePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkVersion(version, blockedVersions) {
|
function compareVersions(a, b) {
|
||||||
return blockedVersions.some(blocked => blocked === version);
|
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) {
|
function checkDependencies(deps, blockedPackages, source) {
|
||||||
@@ -432,7 +536,7 @@ function checkDependencies(deps, blockedPackages, source) {
|
|||||||
for (const [name, versionSpec] of Object.entries(deps)) {
|
for (const [name, versionSpec] of Object.entries(deps)) {
|
||||||
if (blockedPackages[name]) {
|
if (blockedPackages[name]) {
|
||||||
const version = versionSpec.replace(/^[\^~>=<]*/, '').split(' ')[0];
|
const version = versionSpec.replace(/^[\^~>=<]*/, '').split(' ')[0];
|
||||||
if (checkVersion(version, blockedPackages[name].versions)) {
|
if (checkVersion(version, blockedPackages[name])) {
|
||||||
violations.push({
|
violations.push({
|
||||||
package: name,
|
package: name,
|
||||||
version,
|
version,
|
||||||
@@ -454,7 +558,7 @@ function checkLockfileDependencies(packages, blockedPackages) {
|
|||||||
const name = path.replace(/^node_modules\//, '').replace(/^.*node_modules\//, '');
|
const name = path.replace(/^node_modules\//, '').replace(/^.*node_modules\//, '');
|
||||||
|
|
||||||
if (blockedPackages[name] && info.version) {
|
if (blockedPackages[name] && info.version) {
|
||||||
if (checkVersion(info.version, blockedPackages[name].versions)) {
|
if (checkVersion(info.version, blockedPackages[name])) {
|
||||||
violations.push({
|
violations.push({
|
||||||
package: name,
|
package: name,
|
||||||
version: info.version,
|
version: info.version,
|
||||||
@@ -477,9 +581,9 @@ async function main() {
|
|||||||
console.log('='.repeat(70));
|
console.log('='.repeat(70));
|
||||||
console.log('Scanning for known supply chain attacks and compromised packages...\n');
|
console.log('Scanning for known supply chain attacks and compromised packages...\n');
|
||||||
|
|
||||||
// Fetch blocked packages list
|
// Get project dependencies and fetch blocked packages list
|
||||||
const blockedPackages = await fetchMalwareList();
|
const projectDependencies = getProjectDependencies();
|
||||||
const threatCount = Object.keys(blockedPackages).length;
|
const { packages: blockedPackages, sources } = await fetchMalwareList(projectDependencies);
|
||||||
|
|
||||||
const violations = [];
|
const violations = [];
|
||||||
|
|
||||||
@@ -572,7 +676,9 @@ async function main() {
|
|||||||
process.exit(1);
|
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');
|
console.log('='.repeat(70) + '\n');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
/**
|
/**
|
||||||
* Post-install Security Script
|
* Post-install Security Script
|
||||||
*
|
*
|
||||||
* This runs after `npm install` / `npm ci` (via prepare hook).
|
* Runs after `npm install` / `npm ci` via the prepare hook.
|
||||||
* Preinstall already checked the lockfile - this is a defense-in-depth
|
* The preinstall hook already checked package.json and lockfile - this is
|
||||||
* check against installed packages + rebuilds trusted native bindings.
|
* a defense-in-depth check against installed packages.
|
||||||
*
|
*
|
||||||
* This script:
|
* This script:
|
||||||
* 1. Runs security check against OSV + GitHub Advisory (defense in depth)
|
* 1. Runs security check against OSV + GitHub Advisory (defense in depth)
|
||||||
@@ -138,7 +138,7 @@ async function main() {
|
|||||||
trustedInstalled.forEach(pkg => console.log(` ✓ ${pkg}`));
|
trustedInstalled.forEach(pkg => console.log(` ✓ ${pkg}`));
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
run(`npm rebuild ${trustedInstalled.join(' ')}`);
|
run(`npm rebuild --ignore-scripts=false ${trustedInstalled.join(' ')}`);
|
||||||
} else {
|
} else {
|
||||||
console.log('No trusted packages require rebuilding.\n');
|
console.log('No trusted packages require rebuilding.\n');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user