mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 20:20:49 +00:00
Merge remote-tracking branch 'origin/main' into feature/pypi
This commit is contained in:
commit
548d416996
64 changed files with 1689 additions and 381 deletions
|
|
@ -3,6 +3,25 @@ import {
|
|||
openMalwareDatabase,
|
||||
} from "../malwareDatabase.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} PackageChange
|
||||
* @property {string} name
|
||||
* @property {string} version
|
||||
* @property {string} type
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} AuditResult
|
||||
* @property {PackageChange[]} allowedChanges
|
||||
* @property {(PackageChange & {reason: string})[]} disallowedChanges
|
||||
* @property {boolean} isAllowed
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {PackageChange[]} changes
|
||||
*
|
||||
* @returns {Promise<AuditResult>}
|
||||
*/
|
||||
export async function auditChanges(changes) {
|
||||
const allowedChanges = [];
|
||||
const disallowedChanges = [];
|
||||
|
|
@ -36,6 +55,10 @@ export async function auditChanges(changes) {
|
|||
return auditResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{name: string, version: string, type: string}[]} changes
|
||||
* @returns {Promise<{name: string, version: string, status: string}[]>}
|
||||
*/
|
||||
async function getPackagesWithMalware(changes) {
|
||||
if (changes.length === 0) {
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ import chalk from "chalk";
|
|||
import { getPackageManager } from "../packagemanager/currentPackageManager.js";
|
||||
import { ui } from "../environment/userInteraction.js";
|
||||
|
||||
/**
|
||||
* @param {string[]} args
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function shouldScanCommand(args) {
|
||||
if (!args || args.length === 0) {
|
||||
return false;
|
||||
|
|
@ -13,6 +18,11 @@ export function shouldScanCommand(args) {
|
|||
return getPackageManager().isSupportedCommand(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} args
|
||||
*
|
||||
* @returns {Promise<number | never[]>}
|
||||
*/
|
||||
export async function scanCommand(args) {
|
||||
if (!shouldScanCommand(args)) {
|
||||
return [];
|
||||
|
|
@ -23,6 +33,7 @@ export async function scanCommand(args) {
|
|||
const spinner = ui.startProcess(
|
||||
"Safe-chain: Scanning for malicious packages..."
|
||||
);
|
||||
/** @type {import("./audit/index.js").AuditResult | undefined} */
|
||||
let audit;
|
||||
|
||||
await Promise.race([
|
||||
|
|
@ -44,7 +55,7 @@ export async function scanCommand(args) {
|
|||
}
|
||||
|
||||
audit = await auditChanges(changes);
|
||||
} catch (error) {
|
||||
} catch (/** @type any */ error) {
|
||||
spinner.fail(`Safe-chain: Error while scanning.`);
|
||||
throw error;
|
||||
}
|
||||
|
|
@ -69,6 +80,12 @@ export async function scanCommand(args) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./audit/index.js").PackageChange[]} changes
|
||||
* @param spinner {import("../environment/userInteraction.js").Spinner}
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function printMaliciousChanges(changes, spinner) {
|
||||
spinner.fail("Safe-chain: " + chalk.bold("Malicious changes detected:"));
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@ import {
|
|||
import { ui } from "../environment/userInteraction.js";
|
||||
import { getEcoSystem, ECOSYSTEM_PY } from "../config/settings.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} MalwareDatabase
|
||||
* @property {function(string, string): string} getPackageStatus
|
||||
* @property {function(string, string): boolean} isMalware
|
||||
*/
|
||||
|
||||
/** @type {MalwareDatabase | null} */
|
||||
let cachedMalwareDatabase = null;
|
||||
|
||||
/**
|
||||
|
|
@ -32,6 +39,11 @@ export async function openMalwareDatabase() {
|
|||
|
||||
const malwareDatabase = await getMalwareDatabase();
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} version
|
||||
* @returns {string}
|
||||
*/
|
||||
function getPackageStatus(name, version) {
|
||||
const normalizedName = normalizePackageName(name);
|
||||
const packageData = malwareDatabase.find(
|
||||
|
|
@ -49,7 +61,7 @@ export async function openMalwareDatabase() {
|
|||
return packageData.reason;
|
||||
}
|
||||
|
||||
// This implicitely caches the malware database
|
||||
// This implicitly caches the malware database
|
||||
// that's closed over by the getPackageStatus function
|
||||
cachedMalwareDatabase = {
|
||||
getPackageStatus,
|
||||
|
|
@ -61,6 +73,9 @@ export async function openMalwareDatabase() {
|
|||
return cachedMalwareDatabase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<import("../api/aikido.js").MalwarePackage[]>}
|
||||
*/
|
||||
async function getMalwareDatabase() {
|
||||
const { malwareDatabase: cachedDatabase, version: cachedVersion } =
|
||||
readDatabaseFromLocalCache();
|
||||
|
|
@ -74,10 +89,11 @@ async function getMalwareDatabase() {
|
|||
}
|
||||
|
||||
const { malwareDatabase, version } = await fetchMalwareDatabase();
|
||||
// @ts-expect-error version can be undefined
|
||||
writeDatabaseToLocalCache(malwareDatabase, version);
|
||||
|
||||
return malwareDatabase;
|
||||
} catch (error) {
|
||||
} catch (/** @type any */ error) {
|
||||
if (cachedDatabase) {
|
||||
ui.writeWarning(
|
||||
"Failed to fetch the latest malware database. Using cached version."
|
||||
|
|
@ -88,6 +104,11 @@ async function getMalwareDatabase() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} status
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isMalwareStatus(status) {
|
||||
let malwareStatus = status.toUpperCase();
|
||||
return malwareStatus === MALWARE_STATUS_MALWARE;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue