mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-12 11:47:11 +00:00
fix(security): harden Trivy scan lifecycle, logging, and docs (#639)
* fix(security): harden Trivy scan lifecycle, logging, and docs - Call TrivyService.initialize() at startup so capability state is accurate before first request; add periodic re-detect to the scheduler so newly installed Trivy binaries light up without a restart. - Add markStaleScansAsFailed sweep (+ idx_vuln_scans_status index) to recover any scan row left in_progress after a crash or timeout; sweep runs before the paid-tier gate so every tier self-heals. - Split scanImage persistence into beginScan/finishScan so the manual scan route owns a single code path and can return a scanId synchronously while work continues asynchronously. - Validate image refs on /api/security/scan and /sbom via new utility; defense-in-depth against shell-metacharacter payloads. - Dispatch a warning-level alert when a post-deploy scan fails so the operator has a user-visible path to the failure instead of a silent log. - Share DIGEST_CACHE_TTL_MS and severity ordering across service and route layers; remove dead invalidateDetection(). - Add [Trivy:diag] logging gated behind developer_mode for support diagnostics; production logs unchanged. - Frontend: defensive toast fallback chain, sr-only SheetDescription, and a truncation badge when the 500-item detail fetch is capped. - Tests: extend trivy-service and vulnerability-db suites; add image-ref and severity unit tests. - Docs: expand vulnerability-scanning troubleshooting with recovery, re-detect, and diagnostic-log guidance; link Dockerfile comment to trivy-setup. * fix(security): drop unnecessary escape in image-ref forbidden-char regex
This commit is contained in:
@@ -14,11 +14,15 @@ import { NodeRegistry } from './NodeRegistry';
|
||||
import { NotificationService } from './NotificationService';
|
||||
import TrivyService from './TrivyService';
|
||||
|
||||
const TRIVY_REDETECT_INTERVAL_MS = 10 * 60 * 1000;
|
||||
const STALE_SCAN_THRESHOLD_MS = 15 * 60 * 1000;
|
||||
|
||||
export class SchedulerService {
|
||||
private static instance: SchedulerService;
|
||||
private intervalId: ReturnType<typeof setInterval> | null = null;
|
||||
private isProcessing = false;
|
||||
private runningTasks = new Set<number>();
|
||||
private lastTrivyRedetect = 0;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
@@ -56,6 +60,19 @@ export class SchedulerService {
|
||||
}
|
||||
}
|
||||
|
||||
private async maybeRedetectTrivy(): Promise<void> {
|
||||
const now = Date.now();
|
||||
if (now - this.lastTrivyRedetect < TRIVY_REDETECT_INTERVAL_MS) return;
|
||||
this.lastTrivyRedetect = now;
|
||||
try {
|
||||
await TrivyService.getInstance().detectTrivy();
|
||||
} catch (error) {
|
||||
if (isDebugEnabled()) {
|
||||
console.warn('[SchedulerService:debug] Trivy re-detect failed:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public calculateNextRun(cronExpression: string): number {
|
||||
const expr = CronExpressionParser.parse(cronExpression);
|
||||
return expr.next().toDate().getTime();
|
||||
@@ -68,12 +85,27 @@ export class SchedulerService {
|
||||
}
|
||||
this.isProcessing = true;
|
||||
try {
|
||||
const db = DatabaseService.getInstance();
|
||||
|
||||
// Vulnerability scanning is available on every tier, so the stale-scan sweep
|
||||
// and Trivy re-detect run before the paid-tier gate below.
|
||||
try {
|
||||
const staleScans = db.markStaleScansAsFailed(STALE_SCAN_THRESHOLD_MS);
|
||||
if (staleScans > 0) {
|
||||
console.log(
|
||||
`[SchedulerService] Marked ${staleScans} stale vulnerability scan(s) as failed`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[SchedulerService] Stale scan sweep failed:', error);
|
||||
}
|
||||
await this.maybeRedetectTrivy();
|
||||
|
||||
const ls = LicenseService.getInstance();
|
||||
const isPaid = ls.getTier() === 'paid';
|
||||
const isAdmiral = isPaid && ls.getVariant() === 'admiral';
|
||||
if (!isPaid) return;
|
||||
|
||||
const db = DatabaseService.getInstance();
|
||||
const now = Date.now();
|
||||
const dueTasks = db.getDueScheduledTasks(now);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user