feat: add platform audit alert notifications

This commit is contained in:
Codex
2026-06-30 06:46:35 +08:00
parent 5bb0512ba7
commit 7ab702f471
24 changed files with 1731 additions and 30 deletions

View File

@@ -321,11 +321,17 @@ function validateEnv() {
pass('env.asset_security_scan_fail_open', 'asset security scanning fails closed');
}
if (envBool('WORKER_CRM_ALLOW_INSECURE_LOCALHOST', false)) {
block('env.worker_crm_insecure_localhost', 'WORKER_CRM_ALLOW_INSECURE_LOCALHOST must be false in production');
} else {
pass('env.worker_crm_insecure_localhost', 'CRM worker insecure localhost webhook mode is disabled');
}
if (envBool('WORKER_CRM_ALLOW_INSECURE_LOCALHOST', false)) {
block('env.worker_crm_insecure_localhost', 'WORKER_CRM_ALLOW_INSECURE_LOCALHOST must be false in production');
} else {
pass('env.worker_crm_insecure_localhost', 'CRM worker insecure localhost webhook mode is disabled');
}
if (envBool('WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST', false)) {
block('env.worker_platform_audit_notification_insecure_localhost', 'WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST must be false in production');
} else {
pass('env.worker_platform_audit_notification_insecure_localhost', 'Platform audit notification worker insecure localhost webhook mode is disabled');
}
const requiredPositiveNumbers = [
'WORKER_CRM_BATCH_SIZE',
@@ -419,6 +425,51 @@ async function validateDatabase() {
pass('db.payment_provider_secrets', 'Active payment accounts have private secret rows');
}
const unsafePlatformAuditNotificationRows = await pool.query(`
select id, channel_code, provider, webhook_url
from public.platform_audit_notification_channels
where enabled = true
and (
webhook_url !~* '^https://'
or webhook_url ~* '^https?://(localhost|127\\.0\\.0\\.1|\\[?::1\\]?)'
)
`);
if (unsafePlatformAuditNotificationRows.rowCount > 0) {
block('db.platform_audit_notification_webhooks', 'Enabled platform audit notification webhooks must use production HTTPS URLs', {
count: unsafePlatformAuditNotificationRows.rowCount,
samples: unsafePlatformAuditNotificationRows.rows.slice(0, 5).map(row => ({
id: row.id,
channelCode: row.channel_code,
provider: row.provider,
})),
});
} else {
pass('db.platform_audit_notification_webhooks', 'Enabled platform audit notification webhooks use production HTTPS URLs');
}
const missingPlatformAuditNotificationSecretRows = await pool.query(`
select c.id, c.channel_code, c.provider, c.secret_ref
from public.platform_audit_notification_channels c
left join app_private.platform_secrets s
on s.secret_scope = split_part(c.secret_ref, ':', 2)
and s.secret_key = split_part(c.secret_ref, ':', 3)
where c.enabled = true
and c.provider in ('dingtalk', 'feishu')
and (c.secret_ref is null or c.secret_ref !~ '^app_private\\.platform_secrets:' or s.id is null)
`);
if (missingPlatformAuditNotificationSecretRows.rowCount > 0) {
block('db.platform_audit_notification_secrets', 'Signed platform audit notification channels require app_private.platform_secrets rows', {
count: missingPlatformAuditNotificationSecretRows.rowCount,
samples: missingPlatformAuditNotificationSecretRows.rows.slice(0, 5).map(row => ({
id: row.id,
channelCode: row.channel_code,
provider: row.provider,
})),
});
} else {
pass('db.platform_audit_notification_secrets', 'Signed platform audit notification channels have private secret rows');
}
const unverifiedDomainRows = await pool.query(`
select count(*)::int as count
from public.tenant_domains