Files
dockhand/routes/api/settings/scanner/+server.ts
Jarek Krochmalski 62e3c6439e Initial commit
2025-12-28 21:16:03 +01:00

196 lines
6.0 KiB
TypeScript

import { json, type RequestHandler } from '@sveltejs/kit';
import { getEnvSetting, setEnvSetting, getEnvironment } from '$lib/server/db';
import {
checkScannerAvailability,
getScannerVersions,
checkScannerUpdates,
cleanupScannerVolumes,
getGlobalScannerDefaults,
type ScannerType
} from '$lib/server/scanner';
import { removeImage, listImages } from '$lib/server/docker';
import { authorize } from '$lib/server/authorize';
export interface ScannerSettings {
scanner: ScannerType;
grypeArgs: string;
trivyArgs: string;
}
export const GET: RequestHandler = async ({ url, cookies }) => {
const auth = await authorize(cookies);
const envId = url.searchParams.get('env');
const parsedEnvId = envId ? parseInt(envId) : undefined;
const checkUpdates = url.searchParams.get('checkUpdates') === 'true';
const settingsOnly = url.searchParams.get('settingsOnly') === 'true';
// Permission check with environment context
if (auth.authEnabled && !await auth.can('settings', 'view', parsedEnvId)) {
return json({ error: 'Permission denied' }, { status: 403 });
}
try {
// Get global defaults from general settings (used for reset to defaults)
const globalDefaults = await getGlobalScannerDefaults();
// Get environment-specific settings (falls back to global defaults if not set)
const settings: ScannerSettings = {
scanner: await getEnvSetting('vulnerability_scanner', parsedEnvId) || 'none',
grypeArgs: await getEnvSetting('grype_cli_args', parsedEnvId) || globalDefaults.grypeArgs,
trivyArgs: await getEnvSetting('trivy_cli_args', parsedEnvId) || globalDefaults.trivyArgs
};
// Fast path: return just settings without Docker checks
if (settingsOnly) {
return json({
settings,
defaults: globalDefaults
});
}
// Check scanner availability and versions in parallel
const [availability, versions] = await Promise.all([
checkScannerAvailability(parsedEnvId),
getScannerVersions(parsedEnvId)
]);
// Optionally check for updates (slower operation)
let updates = undefined;
if (checkUpdates) {
updates = await checkScannerUpdates(parsedEnvId);
}
return json({
settings,
availability,
versions,
updates,
defaults: globalDefaults
});
} catch (error) {
console.error('Failed to get scanner settings:', error);
return json({ error: 'Failed to get scanner settings' }, { status: 500 });
}
};
export const POST: RequestHandler = async ({ request, url, cookies }) => {
const auth = await authorize(cookies);
try {
const body = await request.json();
const { scanner, grypeArgs, trivyArgs, envId } = body;
const parsedEnvId = envId ? parseInt(envId) : undefined;
// Permission check with environment context
if (auth.authEnabled && !await auth.can('settings', 'edit', parsedEnvId)) {
return json({ error: 'Permission denied' }, { status: 403 });
}
// Validate scanner type
const validScanners: ScannerType[] = ['none', 'grype', 'trivy', 'both'];
if (scanner && !validScanners.includes(scanner)) {
return json({ error: 'Invalid scanner type' }, { status: 400 });
}
// Save environment-specific settings
if (scanner !== undefined) {
await setEnvSetting('vulnerability_scanner', scanner, parsedEnvId);
}
if (grypeArgs !== undefined) {
await setEnvSetting('grype_cli_args', grypeArgs, parsedEnvId);
}
if (trivyArgs !== undefined) {
await setEnvSetting('trivy_cli_args', trivyArgs, parsedEnvId);
}
// Get global defaults for fallback
const globalDefaults = await getGlobalScannerDefaults();
return json({
success: true,
settings: {
scanner: await getEnvSetting('vulnerability_scanner', parsedEnvId) || 'none',
grypeArgs: await getEnvSetting('grype_cli_args', parsedEnvId) || globalDefaults.grypeArgs,
trivyArgs: await getEnvSetting('trivy_cli_args', parsedEnvId) || globalDefaults.trivyArgs
}
});
} catch (error) {
console.error('Failed to save scanner settings:', error);
return json({ error: 'Failed to save scanner settings' }, { status: 500 });
}
};
export const DELETE: RequestHandler = async ({ url, cookies }) => {
const auth = await authorize(cookies);
const removeImagesFlag = url.searchParams.get('removeImages') === 'true';
const scanner = url.searchParams.get('scanner'); // 'grype', 'trivy', or null for both
const envId = url.searchParams.get('env');
const parsedEnvId = envId ? parseInt(envId) : undefined;
// Permission check with environment context
if (auth.authEnabled && !await auth.can('settings', 'edit', parsedEnvId)) {
return json({ error: 'Permission denied' }, { status: 403 });
}
try {
if (!removeImagesFlag) {
return json({ error: 'removeImages parameter required' }, { status: 400 });
}
if (!parsedEnvId) {
return json({ error: 'Environment ID required' }, { status: 400 });
}
const env = await getEnvironment(parsedEnvId);
if (!env) {
return json({ error: 'Environment not found' }, { status: 404 });
}
const images = await listImages(parsedEnvId);
const removed: string[] = [];
const errors: string[] = [];
// Determine which images to remove
const scannersToRemove: ('grype' | 'trivy')[] =
scanner === 'grype' ? ['grype'] :
scanner === 'trivy' ? ['trivy'] :
['grype', 'trivy'];
for (const scannerType of scannersToRemove) {
const imageName = scannerType === 'grype' ? 'anchore/grype' : 'aquasec/trivy';
// Find the image
const image = images.find((img) =>
img.tags?.some((tag: string) => tag.includes(imageName))
);
if (image) {
try {
await removeImage(image.id, true, parsedEnvId);
removed.push(scannerType);
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
console.error(`Failed to remove ${scannerType} image:`, err);
errors.push(`${scannerType}: ${errMsg}`);
}
}
}
// Also cleanup scanner database volumes
await cleanupScannerVolumes(parsedEnvId);
return json({
success: true,
removed,
errors: errors.length > 0 ? errors : undefined
});
} catch (error) {
console.error('Failed to remove scanner images:', error);
return json({ error: 'Failed to remove scanner images' }, { status: 500 });
}
};