feat: add privacy-aware security manager onboarding
This commit is contained in:
46
lib/security/security-alert-policy.mjs
Normal file
46
lib/security/security-alert-policy.mjs
Normal file
@@ -0,0 +1,46 @@
|
||||
export function evaluateSecurityAlertPolicy(result, profile, now = new Date()) {
|
||||
if (!result?.notify) return { send: false, reason: 'agent_declined' };
|
||||
if (!profile) return { send: true, reason: 'no_profile' };
|
||||
|
||||
const priority = ['routine', 'priority', 'flash'].includes(result.priority) ? result.priority : 'routine';
|
||||
const preference = profile.alertPreference || 'important';
|
||||
if (preference === 'critical_only' && priority !== 'flash') {
|
||||
return { send: false, reason: 'critical_only' };
|
||||
}
|
||||
if (preference === 'important' && priority === 'routine') {
|
||||
return { send: false, reason: 'routine_suppressed' };
|
||||
}
|
||||
if (priority !== 'flash' && isWithinQuietHours(profile.quietHours, profile.timezone, now)) {
|
||||
return { send: false, reason: 'quiet_hours' };
|
||||
}
|
||||
return { send: true, reason: 'allowed' };
|
||||
}
|
||||
|
||||
export function isWithinQuietHours(value, timezone, now = new Date()) {
|
||||
const match = String(value || '').match(/^([01]\d|2[0-3]):([0-5]\d)-([01]\d|2[0-3]):([0-5]\d)$/);
|
||||
if (!match) return false;
|
||||
const localMinutes = minutesInTimezone(now, timezone);
|
||||
if (localMinutes == null) return false;
|
||||
const start = Number(match[1]) * 60 + Number(match[2]);
|
||||
const end = Number(match[3]) * 60 + Number(match[4]);
|
||||
if (start === end) return true;
|
||||
return start < end
|
||||
? localMinutes >= start && localMinutes < end
|
||||
: localMinutes >= start || localMinutes < end;
|
||||
}
|
||||
|
||||
function minutesInTimezone(date, timezone) {
|
||||
try {
|
||||
const parts = new Intl.DateTimeFormat('en-GB', {
|
||||
timeZone: timezone || 'UTC',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hourCycle: 'h23',
|
||||
}).formatToParts(date);
|
||||
const hour = Number(parts.find(part => part.type === 'hour')?.value);
|
||||
const minute = Number(parts.find(part => part.type === 'minute')?.value);
|
||||
return Number.isFinite(hour) && Number.isFinite(minute) ? hour * 60 + minute : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user