mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-28 11:17:07 +00:00
fix(fleet-sync): hygiene pass on receiver behavior and cleanup (#972)
A bundle of small file-local fixes to the receiver path and node-deletion flow. Changes: - F4 receiver audit log: applyIncomingSync now writes a system audit entry on every applied push so mirrored security-rule changes show up in the replica's audit panel with a clear control-side origin. - F7 pilot-agent skip: pushResource explicitly excludes pilot-agent nodes (they have no api_url for HTTP push) and warns once per node id so the operator sees they will not receive replicated policies. - B4 identity-drift notification: when targetIdentity differs from the cached fleet_self_identity, dispatch a warning so the operator can audit any identity-scoped policies that may need re-targeting. - B6 stack_pattern ReDoS guard: reject patterns with 4+ consecutive wildcards or more than 8 wildcards total. Both control-side validators (POST/PUT scan policies) and the receiver-side row validator share the helper. - B9 deleteNode cascade: clear fleet_sync_status rows for the node inside the existing transaction so the sync-status panel does not render ghost entries after a node is removed. - S6 last_error redaction: formatError strips Bearer tokens and JWT-shaped values from error messages and caps at 500 chars before storing in fleet_sync_status.last_error or logging. Tests: - 8 new vitest cases covering audit-log entry, identity-drift alert, pilot-agent warn-once, formatError redaction (Bearer + JWT), ReDoS validator rejection, and a backtracking-time smoke test. - New database-fleet-sync-cascade.test.ts: deleteNode removes fleet_sync_status rows for the deleted node and leaves siblings untouched. - Full backend suite: 1792 pass / 5 skipped.
This commit is contained in:
@@ -14,6 +14,7 @@ import { sanitizeForLog } from '../utils/safeLog';
|
||||
import { getErrorMessage } from '../utils/errors';
|
||||
import { isDebugEnabled } from '../utils/debug';
|
||||
import { blockIfReplica } from '../middleware/fleetSyncGuards';
|
||||
import { validateStackPatternForRedos } from './fleet';
|
||||
import { FINDING_SEVERITIES, POLICY_SEVERITIES } from '../utils/severity';
|
||||
|
||||
const CVE_ID_RE = /^(CVE-\d{4}-\d{4,}|GHSA-[\w-]{14,})$/;
|
||||
@@ -423,13 +424,20 @@ securityRouter.post('/policies', authMiddleware, (req: Request, res: Response):
|
||||
if (!POLICY_SEVERITIES.has(max_severity)) {
|
||||
res.status(400).json({ error: 'max_severity must be CRITICAL, HIGH, MEDIUM, or LOW' }); return;
|
||||
}
|
||||
const normalizedPattern = stack_pattern ? String(stack_pattern) : null;
|
||||
if (normalizedPattern !== null) {
|
||||
const patternError = validateStackPatternForRedos(normalizedPattern);
|
||||
if (patternError) {
|
||||
res.status(400).json({ error: patternError }); return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const resolvedNodeId = node_id != null ? Number(node_id) : null;
|
||||
const policy = DatabaseService.getInstance().createScanPolicy({
|
||||
name: name.trim(),
|
||||
node_id: resolvedNodeId,
|
||||
node_identity: FleetSyncService.resolveIdentityForNodeId(resolvedNodeId),
|
||||
stack_pattern: stack_pattern ? String(stack_pattern) : null,
|
||||
stack_pattern: normalizedPattern,
|
||||
max_severity,
|
||||
block_on_deploy: block_on_deploy ? 1 : 0,
|
||||
enabled: enabled === false ? 0 : 1,
|
||||
@@ -459,7 +467,16 @@ securityRouter.put('/policies/:id', authMiddleware, (req: Request, res: Response
|
||||
updates.node_id = resolvedNodeId;
|
||||
updates.node_identity = FleetSyncService.resolveIdentityForNodeId(resolvedNodeId);
|
||||
}
|
||||
if (body.stack_pattern !== undefined) updates.stack_pattern = body.stack_pattern ? String(body.stack_pattern) : null;
|
||||
if (body.stack_pattern !== undefined) {
|
||||
const normalizedPattern = body.stack_pattern ? String(body.stack_pattern) : null;
|
||||
if (normalizedPattern !== null) {
|
||||
const patternError = validateStackPatternForRedos(normalizedPattern);
|
||||
if (patternError) {
|
||||
res.status(400).json({ error: patternError }); return;
|
||||
}
|
||||
}
|
||||
updates.stack_pattern = normalizedPattern;
|
||||
}
|
||||
if (body.max_severity !== undefined) {
|
||||
if (!POLICY_SEVERITIES.has(body.max_severity)) {
|
||||
res.status(400).json({ error: 'max_severity must be CRITICAL, HIGH, MEDIUM, or LOW' }); return;
|
||||
|
||||
Reference in New Issue
Block a user