mirror of
https://github.com/khoaliber/dockhand.git
synced 2026-03-03 21:19:06 +00:00
Initial commit
This commit is contained in:
114
lib/server/scheduler/tasks/update-utils.ts
Normal file
114
lib/server/scheduler/tasks/update-utils.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Shared utilities for container and environment auto-update tasks.
|
||||
*/
|
||||
|
||||
import type { VulnerabilityCriteria } from '../../db';
|
||||
import type { VulnerabilitySeverity } from '../../scanner';
|
||||
|
||||
/**
|
||||
* Parse image name and tag from a full image reference.
|
||||
* Handles various formats:
|
||||
* - nginx → ["nginx", "latest"]
|
||||
* - nginx:1.25 → ["nginx", "1.25"]
|
||||
* - registry.example.com:5000/myimage:v1 → ["registry.example.com:5000/myimage", "v1"]
|
||||
* - nginx:latest-dockhand-pending → ["nginx", "latest-dockhand-pending"]
|
||||
*/
|
||||
export function parseImageNameAndTag(imageName: string): [string, string] {
|
||||
// Handle digest-based images (return as-is with empty tag)
|
||||
if (imageName.includes('@sha256:')) {
|
||||
return [imageName, ''];
|
||||
}
|
||||
|
||||
// Find the last colon that's part of the tag (not part of registry port)
|
||||
const lastColon = imageName.lastIndexOf(':');
|
||||
if (lastColon === -1) {
|
||||
return [imageName, 'latest'];
|
||||
}
|
||||
|
||||
// Check if this colon is part of a registry port
|
||||
// Registry ports appear before a slash: registry:5000/image
|
||||
const afterColon = imageName.substring(lastColon + 1);
|
||||
if (afterColon.includes('/')) {
|
||||
// The colon is part of the registry, not the tag
|
||||
return [imageName, 'latest'];
|
||||
}
|
||||
|
||||
// The colon separates repo from tag
|
||||
return [imageName.substring(0, lastColon), afterColon];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an update should be blocked based on vulnerability criteria.
|
||||
*/
|
||||
export function shouldBlockUpdate(
|
||||
criteria: VulnerabilityCriteria,
|
||||
newScanSummary: VulnerabilitySeverity,
|
||||
currentScanSummary?: VulnerabilitySeverity
|
||||
): { blocked: boolean; reason: string } {
|
||||
const totalVulns = newScanSummary.critical + newScanSummary.high + newScanSummary.medium + newScanSummary.low;
|
||||
|
||||
switch (criteria) {
|
||||
case 'any':
|
||||
if (totalVulns > 0) {
|
||||
return {
|
||||
blocked: true,
|
||||
reason: `Found ${totalVulns} vulnerabilities (${newScanSummary.critical} critical, ${newScanSummary.high} high, ${newScanSummary.medium} medium, ${newScanSummary.low} low)`
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'critical_high':
|
||||
if (newScanSummary.critical > 0 || newScanSummary.high > 0) {
|
||||
return {
|
||||
blocked: true,
|
||||
reason: `Found ${newScanSummary.critical} critical and ${newScanSummary.high} high severity vulnerabilities`
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'critical':
|
||||
if (newScanSummary.critical > 0) {
|
||||
return {
|
||||
blocked: true,
|
||||
reason: `Found ${newScanSummary.critical} critical vulnerabilities`
|
||||
};
|
||||
}
|
||||
break;
|
||||
case 'more_than_current':
|
||||
if (currentScanSummary) {
|
||||
const currentTotal = currentScanSummary.critical + currentScanSummary.high + currentScanSummary.medium + currentScanSummary.low;
|
||||
if (totalVulns > currentTotal) {
|
||||
return {
|
||||
blocked: true,
|
||||
reason: `New image has ${totalVulns} vulnerabilities vs ${currentTotal} in current image`
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'never':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return { blocked: false, reason: '' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a container is the Dockhand application itself.
|
||||
* Used to prevent Dockhand from updating its own container.
|
||||
*/
|
||||
export function isDockhandContainer(imageName: string): boolean {
|
||||
return imageName.toLowerCase().includes('fnsys/dockhand');
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine multiple scan summaries by taking the maximum of each severity level.
|
||||
*/
|
||||
export function combineScanSummaries(results: { summary: VulnerabilitySeverity }[]): VulnerabilitySeverity {
|
||||
return results.reduce((acc, result) => ({
|
||||
critical: Math.max(acc.critical, result.summary.critical),
|
||||
high: Math.max(acc.high, result.summary.high),
|
||||
medium: Math.max(acc.medium, result.summary.medium),
|
||||
low: Math.max(acc.low, result.summary.low),
|
||||
negligible: Math.max(acc.negligible, result.summary.negligible),
|
||||
unknown: Math.max(acc.unknown, result.summary.unknown)
|
||||
}), { critical: 0, high: 0, medium: 0, low: 0, negligible: 0, unknown: 0 });
|
||||
}
|
||||
Reference in New Issue
Block a user