mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-10 02:41:14 +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:
@@ -0,0 +1,22 @@
|
||||
const MAX_LENGTH = 255;
|
||||
|
||||
const IMAGE_REF_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9._\-/:@+]*[a-zA-Z0-9])?$/;
|
||||
|
||||
const FORBIDDEN_CHARS = /[\s;|&`$(){}[\]<>'"\\!*?#~]/;
|
||||
|
||||
export function validateImageRef(ref: unknown): ref is string {
|
||||
if (typeof ref !== 'string') return false;
|
||||
const trimmed = ref.trim();
|
||||
if (trimmed.length === 0 || trimmed.length > MAX_LENGTH) return false;
|
||||
if (FORBIDDEN_CHARS.test(trimmed)) return false;
|
||||
if (trimmed.includes('..')) return false;
|
||||
if (!IMAGE_REF_PATTERN.test(trimmed)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function assertImageRef(ref: unknown): string {
|
||||
if (!validateImageRef(ref)) {
|
||||
throw new Error('Invalid image reference');
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { VulnSeverity } from '../services/DatabaseService';
|
||||
|
||||
export const SEVERITY_ORDER: VulnSeverity[] = [
|
||||
'UNKNOWN',
|
||||
'LOW',
|
||||
'MEDIUM',
|
||||
'HIGH',
|
||||
'CRITICAL',
|
||||
];
|
||||
|
||||
export function severityRank(severity: VulnSeverity | null | undefined): number {
|
||||
if (!severity) return -1;
|
||||
return SEVERITY_ORDER.indexOf(severity);
|
||||
}
|
||||
|
||||
export function isSeverityAtLeast(
|
||||
actual: VulnSeverity | null | undefined,
|
||||
threshold: VulnSeverity,
|
||||
): boolean {
|
||||
return severityRank(actual) >= severityRank(threshold);
|
||||
}
|
||||
Reference in New Issue
Block a user