mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-27 18:57:09 +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:
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Pins the fleet_sync_status cascade behavior of DatabaseService.deleteNode.
|
||||
*
|
||||
* Without this cleanup, deleting a node from Settings → Nodes leaves orphaned
|
||||
* sync-status rows behind that the UI then renders as ghost entries.
|
||||
*/
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import { setupTestDb, cleanupTestDb } from './helpers/setupTestDb';
|
||||
|
||||
let tmpDir: string;
|
||||
let DatabaseService: typeof import('../services/DatabaseService').DatabaseService;
|
||||
|
||||
beforeAll(async () => {
|
||||
tmpDir = await setupTestDb();
|
||||
({ DatabaseService } = await import('../services/DatabaseService'));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
cleanupTestDb(tmpDir);
|
||||
});
|
||||
|
||||
describe('deleteNode fleet_sync_status cascade', () => {
|
||||
it('removes fleet_sync_status rows for the deleted node', () => {
|
||||
const db = DatabaseService.getInstance();
|
||||
const nodeId = db.addNode({
|
||||
name: 'cascade-target',
|
||||
type: 'remote',
|
||||
compose_dir: '/app/compose',
|
||||
is_default: false,
|
||||
api_url: 'https://cascade.example',
|
||||
api_token: 'tok',
|
||||
mode: 'proxy',
|
||||
});
|
||||
|
||||
// Sibling node so we can confirm its rows survive.
|
||||
const siblingId = db.addNode({
|
||||
name: 'cascade-sibling',
|
||||
type: 'remote',
|
||||
compose_dir: '/app/compose',
|
||||
is_default: false,
|
||||
api_url: 'https://sibling.example',
|
||||
api_token: 'tok',
|
||||
mode: 'proxy',
|
||||
});
|
||||
|
||||
db.recordFleetSyncFailure(nodeId, 'scan_policies', 'timeout');
|
||||
db.recordFleetSyncFailure(nodeId, 'cve_suppressions', 'timeout');
|
||||
db.recordFleetSyncSuccess(siblingId, 'scan_policies');
|
||||
|
||||
const before = db.getFleetSyncStatuses();
|
||||
expect(before.filter((s) => s.node_id === nodeId)).toHaveLength(2);
|
||||
expect(before.filter((s) => s.node_id === siblingId)).toHaveLength(1);
|
||||
|
||||
db.deleteNode(nodeId);
|
||||
|
||||
const after = db.getFleetSyncStatuses();
|
||||
expect(after.filter((s) => s.node_id === nodeId)).toHaveLength(0);
|
||||
expect(after.filter((s) => s.node_id === siblingId)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user