mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Keep track of amount of malware packages blocked
This commit is contained in:
parent
f4694ba119
commit
1f570a9f39
5 changed files with 65 additions and 8 deletions
|
|
@ -8,8 +8,11 @@
|
|||
*
|
||||
* @typedef {Object} Interceptor
|
||||
* @property {(targetUrl: string) => Promise<RequestInterceptor>} handleRequest
|
||||
* @property {(event: string, listener: (...args: any[]) => void) => Interceptor} on
|
||||
* @property {(event: string, ...args: any[]) => boolean} emit
|
||||
*/
|
||||
|
||||
import { EventEmitter } from "events";
|
||||
import { createRequestInterceptorBuilder } from "./requestInterceptorBuilder.js";
|
||||
|
||||
/**
|
||||
|
|
@ -36,9 +39,14 @@ export function createInterceptorBuilder() {
|
|||
* @returns {Interceptor}
|
||||
*/
|
||||
function buildInterceptor(requestHandlers) {
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
return {
|
||||
async handleRequest(targetUrl) {
|
||||
const reqInterceptorBuilder = createRequestInterceptorBuilder(targetUrl);
|
||||
const reqInterceptorBuilder = createRequestInterceptorBuilder(
|
||||
targetUrl,
|
||||
eventEmitter
|
||||
);
|
||||
|
||||
for (const handler of requestHandlers) {
|
||||
await handler(reqInterceptorBuilder);
|
||||
|
|
@ -46,5 +54,12 @@ function buildInterceptor(requestHandlers) {
|
|||
|
||||
return reqInterceptorBuilder.build();
|
||||
},
|
||||
on(event, listener) {
|
||||
eventEmitter.on(event, listener);
|
||||
return this;
|
||||
},
|
||||
emit(event, ...args) {
|
||||
return eventEmitter.emit(event, ...args);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ function buildNpmInterceptor(registry) {
|
|||
registry
|
||||
);
|
||||
if (await isMalwarePackage(packageName, version)) {
|
||||
req.blockRequest(403, "Forbidden - blocked by safe-chain");
|
||||
req.blockMalware(packageName, version, req.targetUrl);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ function buildNpmInterceptor(registry) {
|
|||
* @param {string} registry
|
||||
* @returns {{packageName: string | undefined, version: string | undefined}}
|
||||
*/
|
||||
export function parseNpmPackageUrl(url, registry) {
|
||||
function parseNpmPackageUrl(url, registry) {
|
||||
let packageName, version;
|
||||
if (!registry || !url.endsWith(".tgz")) {
|
||||
return { packageName, version };
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ function buildPipInterceptor(registry) {
|
|||
registry
|
||||
);
|
||||
if (await isMalwarePackage(packageName, version)) {
|
||||
req.blockRequest(403, "Forbidden - blocked by safe-chain");
|
||||
req.blockMalware(packageName, version, req.targetUrl);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* @typedef {Object} RequestInterceptorBuilder
|
||||
* @property {string} targetUrl
|
||||
* @property {(statusCode: number, message: string) => void} blockRequest
|
||||
* @property {(packageName: string | undefined, version: string | undefined, url: string) => void} blockMalware
|
||||
* @property {() => RequestInterceptor} build
|
||||
*
|
||||
* @typedef {Object} RequestInterceptor
|
||||
|
|
@ -10,17 +11,42 @@
|
|||
|
||||
/**
|
||||
* @param {string} targetUrl
|
||||
* @param {import('events').EventEmitter} eventEmitter
|
||||
* @returns {RequestInterceptorBuilder}
|
||||
*/
|
||||
export function createRequestInterceptorBuilder(targetUrl) {
|
||||
export function createRequestInterceptorBuilder(targetUrl, eventEmitter) {
|
||||
/** @type {{statusCode: number, message: string} | undefined} */
|
||||
let blockResponse = undefined;
|
||||
|
||||
/**
|
||||
* @param {number} statusCode
|
||||
* @param {string} message
|
||||
*/
|
||||
function blockRequest(statusCode, message) {
|
||||
blockResponse = { statusCode, message };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | undefined} packageName
|
||||
* @param {string | undefined} version
|
||||
* @param {string} url
|
||||
*/
|
||||
function blockMalware(packageName, version, url) {
|
||||
blockRequest(403, "Forbidden - blocked by safe-chain");
|
||||
|
||||
// Emit the malwareBlocked event
|
||||
eventEmitter.emit("malwareBlocked", {
|
||||
packageName,
|
||||
version,
|
||||
url,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
targetUrl,
|
||||
blockRequest(statusCode, message) {
|
||||
blockResponse = { statusCode, message };
|
||||
},
|
||||
blockRequest,
|
||||
blockMalware,
|
||||
build() {
|
||||
return {
|
||||
blockResponse,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { getCaCertPath } from "./certUtils.js";
|
|||
import { ui } from "../environment/userInteraction.js";
|
||||
import chalk from "chalk";
|
||||
import { createInterceptorForUrl } from "./interceptors/createInterceptorForEcoSystem.js";
|
||||
import { on } from "events";
|
||||
|
||||
const SERVER_STOP_TIMEOUT_MS = 1000;
|
||||
/**
|
||||
|
|
@ -133,6 +134,11 @@ function handleConnect(req, clientSocket, head) {
|
|||
const interceptor = createInterceptorForUrl(req.url || "");
|
||||
|
||||
if (interceptor) {
|
||||
// Subscribe to malware blocked events
|
||||
interceptor.on("malwareBlocked", (event) => {
|
||||
onMalwareBlocked(event.packageName, event.version, event.url);
|
||||
});
|
||||
|
||||
mitmConnect(req, clientSocket, interceptor);
|
||||
} else {
|
||||
// For other hosts, just tunnel the request to the destination tcp socket
|
||||
|
|
@ -141,6 +147,16 @@ function handleConnect(req, clientSocket, head) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} packageName
|
||||
* @param {string} version
|
||||
* @param {string} url
|
||||
*/
|
||||
function onMalwareBlocked(packageName, version, url) {
|
||||
state.blockedRequests.push({ packageName, version, url });
|
||||
}
|
||||
|
||||
function verifyNoMaliciousPackages() {
|
||||
if (state.blockedRequests.length === 0) {
|
||||
// No malicious packages were blocked, so nothing to block
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue