feat(notifications): secret scan alert notifications

This commit is contained in:
x032205
2025-09-16 01:27:28 -04:00
parent 66d2ecf289
commit 4431fe687d
3 changed files with 36 additions and 7 deletions
@@ -21,6 +21,8 @@ import { decryptAppConnection } from "@app/services/app-connection/app-connectio
import { TAppConnection } from "@app/services/app-connection/app-connection-types"; import { TAppConnection } from "@app/services/app-connection/app-connection-types";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
import { NotificationType } from "@app/services/notification/notification-types";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
@@ -52,6 +54,7 @@ type TSecretRotationV2QueueServiceFactoryDep = {
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">; appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">;
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">; auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "getItem">; keyStore: Pick<TKeyStoreFactory, "acquireLock" | "getItem">;
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
}; };
export type TSecretScanningV2QueueServiceFactory = Awaited<ReturnType<typeof secretScanningV2QueueServiceFactory>>; export type TSecretScanningV2QueueServiceFactory = Awaited<ReturnType<typeof secretScanningV2QueueServiceFactory>>;
@@ -65,7 +68,8 @@ export const secretScanningV2QueueServiceFactory = async ({
kmsService, kmsService,
auditLogService, auditLogService,
keyStore, keyStore,
appConnectionDAL appConnectionDAL,
notificationService
}: TSecretRotationV2QueueServiceFactoryDep) => { }: TSecretRotationV2QueueServiceFactoryDep) => {
const queueDataSourceFullScan = async ( const queueDataSourceFullScan = async (
dataSource: TSecretScanningDataSourceWithConnection, dataSource: TSecretScanningDataSourceWithConnection,
@@ -592,16 +596,38 @@ export const secretScanningV2QueueServiceFactory = async ({
const timestamp = new Date().toISOString(); const timestamp = new Date().toISOString();
const subjectLine =
payload.status === SecretScanningScanStatus.Completed
? "Incident Alert: Secret(s) Leaked"
: `Secret Scanning Failed`;
await notificationService.createUserNotifications(
recipients.map((member) => ({
userId: member.userId,
orgId: project.orgId,
type:
payload.status === SecretScanningScanStatus.Completed
? NotificationType.SECRET_SCANNING_SECRETS_DETECTED
: NotificationType.SECRET_SCANNING_SCAN_FAILED,
title: subjectLine,
body:
payload.status === SecretScanningScanStatus.Completed
? `Uncovered **${payload.numberOfSecrets}** secret(s) ${payload.isDiffScan ? " from a recent commit to" : " in"} **${resourceName}**.`
: `Encountered an error while attempting to scan the resource **${resourceName}**: ${payload.errorMessage}`,
link:
payload.status === SecretScanningScanStatus.Completed
? `/projects/secret-scanning/${projectId}/findings?search=scanId:${payload.scanId}`
: `/projects/secret-scanning/${projectId}/data-sources/${dataSource.type}/${dataSource.id}`
}))
);
await smtpService.sendMail({ await smtpService.sendMail({
recipients: recipients.map((member) => member.user.email!).filter(Boolean), recipients: recipients.map((member) => member.user.email!).filter(Boolean),
template: template:
payload.status === SecretScanningScanStatus.Completed payload.status === SecretScanningScanStatus.Completed
? SmtpTemplates.SecretScanningV2SecretsDetected ? SmtpTemplates.SecretScanningV2SecretsDetected
: SmtpTemplates.SecretScanningV2ScanFailed, : SmtpTemplates.SecretScanningV2ScanFailed,
subjectLine: subjectLine,
payload.status === SecretScanningScanStatus.Completed
? "Incident Alert: Secret(s) Leaked"
: `Secret Scanning Failed`,
substitutions: substitutions:
payload.status === SecretScanningScanStatus.Completed payload.status === SecretScanningScanStatus.Completed
? { ? {
+2 -1
View File
@@ -2030,7 +2030,8 @@ export const registerRoutes = async (
smtpService, smtpService,
kmsService, kmsService,
keyStore, keyStore,
appConnectionDAL appConnectionDAL,
notificationService
}); });
const secretScanningV2Service = secretScanningV2ServiceFactory({ const secretScanningV2Service = secretScanningV2ServiceFactory({
@@ -4,7 +4,9 @@ export enum NotificationType {
ACCESS_POLICY_BYPASSED = "access-policy-bypassed", ACCESS_POLICY_BYPASSED = "access-policy-bypassed",
SECRET_CHANGE_REQUEST = "secret-change-request", SECRET_CHANGE_REQUEST = "secret-change-request",
SECRET_CHANGE_POLICY_BYPASSED = "secret-change-policy-bypassed", SECRET_CHANGE_POLICY_BYPASSED = "secret-change-policy-bypassed",
SECRET_ROTATION_FAILED = "secret-rotation-failed" SECRET_ROTATION_FAILED = "secret-rotation-failed",
SECRET_SCANNING_SECRETS_DETECTED = "secret-scanning-secrets-detected",
SECRET_SCANNING_SCAN_FAILED = "secret-scanning-scan-failed"
} }
export interface TCreateUserNotificationDTO { export interface TCreateUserNotificationDTO {