n8n/packages/cli/src/audit/index.ts

69 lines
2.2 KiB
TypeScript
Raw Normal View History

feat(core): Security audit (#5034) * :sparkles: Implement security audit * :zap: Use logger * :test_tube: Fix test * :zap: Switch logger with stdout * :art: Set new logo * :zap: Fill out Public API schema * :pencil2: Fix typo * :zap: Break dependency cycle * :zap: Add security settings values * :test_tube: Test security settings * :zap: Add publicly accessible instance warning * :zap: Add metric to CLI command * :pencil2: Fix typo * :fire: Remove unneeded path alias * :blue_book: Add type import * :fire: Remove inferrable output type * :zap: Set description at correct level * :zap: Rename constant for consistency * :zap: Sort URLs * :zap: Rename local var * :zap: Shorten name * :pencil2: Improve phrasing * :zap: Improve naming * :zap: Fix casing * :pencil2: Add docline * :pencil2: Relocate comment * :zap: Add singular/plurals * :fire: Remove unneeded await * :pencil2: Improve test description * :zap: Optimize with sets * :zap: Adjust post master merge * :pencil2: Improve naming * :zap: Adjust in spy * :test_tube: Fix outdated instance test * :test_tube: Make diagnostics check consistent * :zap: Refactor `getAllExistingCreds` * :zap: Create helper `getNodeTypes` * :bug: Fix `InternalHooksManager` call * :truck: Rename `execution` to `nodes` risk * :zap: Add options to CLI command * :zap: Make days configurable * :revert: Undo changes to `BaseCommand` * :zap: Improve CLI command UX * :zap: Change no-report return value Empty array to trigger empty state on FE. * :zap: Add empty check to `reportInstanceRisk` * :test_tube: Extend Jest `expect` * :blue_book: Augment `jest.Matchers` * :test_tube: Set extend as setup file * :wrench: Override lint rule for `.d.ts` * :zap: Use new matcher * :zap: Update check * :blue_book: Improve typings * :zap: Adjust instance risk check * :pencil2: Rename `execution` → `nodes` in Public API schema * :pencil2: Add clarifying comment * :pencil2: Fix typo * :zap: Validate categories in CLI command * :pencil2: Improve naming * :pencil2: Make audit reference consistent * :blue_book: Fix typing * :zap: Use `finally` in CLI command
2023-01-05 04:28:40 -08:00
import * as Db from '@/Db';
import { separate } from '@/utils';
import config from '@/config';
import { RISK_CATEGORIES } from '@/audit/constants';
import { toReportTitle } from '@/audit/utils';
import { reportCredentialsRisk } from '@/audit/risks/credentials.risk';
import { reportDatabaseRisk } from '@/audit/risks/database.risk';
import { reportNodesRisk } from '@/audit/risks/nodes.risk';
import { reportFilesystemRisk } from '@/audit/risks/filesystem.risk';
import { reportInstanceRisk } from '@/audit/risks/instance.risk';
import type { Risk } from '@/audit/types';
export const SYNC_MAP: Record<string, Risk.SyncReportFn> = {
database: reportDatabaseRisk,
filesystem: reportFilesystemRisk,
};
export const ASYNC_MAP: Record<string, Risk.AsyncReportFn> = {
credentials: reportCredentialsRisk,
nodes: reportNodesRisk,
instance: reportInstanceRisk,
};
export const isAsync = (c: Risk.Category) => Object.keys(ASYNC_MAP).includes(c);
export async function audit(
categories: Risk.Category[] = RISK_CATEGORIES,
daysAbandonedWorkflow?: number,
) {
if (categories.length === 0) categories = RISK_CATEGORIES;
const daysFromEnv = config.getEnv('security.audit.daysAbandonedWorkflow');
if (daysAbandonedWorkflow) {
config.set('security.audit.daysAbandonedWorkflow', daysAbandonedWorkflow);
}
const workflows = await Db.collections.Workflow.find({
select: ['id', 'name', 'active', 'nodes', 'connections'],
});
const [asyncCategories, syncCategories] = separate(categories, isAsync);
const reports: Risk.Report[] = [];
if (asyncCategories.length > 0) {
const promises = asyncCategories.map(async (c) => ASYNC_MAP[c](workflows));
const asyncReports = await Promise.all(promises);
asyncReports.forEach((r) => r !== null && reports.push(r));
}
if (syncCategories.length > 0) {
const syncReports = syncCategories.map((c) => SYNC_MAP[c](workflows));
syncReports.forEach((r) => r !== null && reports.push(r));
}
if (daysAbandonedWorkflow) {
config.set('security.audit.daysAbandonedWorkflow', daysFromEnv); // restore env
}
if (reports.length === 0) return []; // trigger empty state
return reports.reduce<Risk.Audit>((acc, cur) => {
acc[toReportTitle(cur.risk)] = cur;
return acc;
}, {});
}