Merge pull request #3975 from Infisical/improve-approval-audit-logs

improvement(audit-logs): Create crud events for secret approvals on merge and improve approval audit logs
This commit is contained in:
Scott Wilson
2025-07-08 08:37:29 -07:00
committed by GitHub
4 changed files with 300 additions and 16 deletions
@@ -141,14 +141,39 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const { approval } = await server.services.secretApprovalRequest.mergeSecretApprovalRequest({ const { approval, projectId, secretMutationEvents } =
actorId: req.permission.id, await server.services.secretApprovalRequest.mergeSecretApprovalRequest({
actor: req.permission.type, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actor: req.permission.type,
actorOrgId: req.permission.orgId, actorAuthMethod: req.permission.authMethod,
approvalId: req.params.id, actorOrgId: req.permission.orgId,
bypassReason: req.body.bypassReason approvalId: req.params.id,
bypassReason: req.body.bypassReason
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
projectId,
event: {
type: EventType.SECRET_APPROVAL_MERGED,
metadata: {
mergedBy: req.permission.id,
secretApprovalRequestSlug: approval.slug,
secretApprovalRequestId: approval.id
}
}
}); });
for await (const event of secretMutationEvents) {
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
projectId,
event
});
}
return { approval }; return { approval };
} }
}); });
@@ -116,6 +116,15 @@ interface BaseAuthData {
userAgentType?: UserAgentType; userAgentType?: UserAgentType;
} }
export enum SecretApprovalEvent {
Create = "create",
Update = "update",
Delete = "delete",
CreateMany = "create-many",
UpdateMany = "update-many",
DeleteMany = "delete-many"
}
export enum UserAgentType { export enum UserAgentType {
WEB = "web", WEB = "web",
CLI = "cli", CLI = "cli",
@@ -1705,6 +1714,17 @@ interface SecretApprovalRequest {
committedBy: string; committedBy: string;
secretApprovalRequestSlug: string; secretApprovalRequestSlug: string;
secretApprovalRequestId: string; secretApprovalRequestId: string;
eventType: SecretApprovalEvent;
secretKey?: string;
secretId?: string;
secrets?: {
secretKey?: string;
secretId?: string;
environment?: string;
secretPath?: string;
}[];
environment: string;
secretPath: string;
}; };
} }
@@ -10,6 +10,7 @@ import {
TSecretApprovalRequestsSecretsInsert, TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsV2Insert TSecretApprovalRequestsSecretsV2Insert
} from "@app/db/schemas"; } from "@app/db/schemas";
import { Event, EventType } from "@app/ee/services/audit-log/audit-log-types";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
@@ -523,7 +524,7 @@ export const secretApprovalRequestServiceFactory = ({
}); });
} }
const { policy, folderId, projectId, bypassers } = secretApprovalRequest; const { policy, folderId, projectId, bypassers, environment } = secretApprovalRequest;
if (policy.deletedAt) { if (policy.deletedAt) {
throw new BadRequestError({ throw new BadRequestError({
message: "The policy associated with this secret approval request has been deleted." message: "The policy associated with this secret approval request has been deleted."
@@ -957,7 +958,112 @@ export const secretApprovalRequestServiceFactory = ({
}); });
} }
return mergeStatus; const { created, updated, deleted } = mergeStatus.secrets;
const secretMutationEvents: Event[] = [];
if (created.length) {
if (created.length > 1) {
secretMutationEvents.push({
type: EventType.CREATE_SECRETS,
metadata: {
environment,
secretPath: folder.path,
secrets: created.map((secret) => ({
secretId: secret.id,
secretVersion: 1,
// @ts-expect-error not present on v1 secrets
secretKey: secret.key as string,
// @ts-expect-error not present on v1 secrets
secretMetadata: secret.secretMetadata as ResourceMetadataDTO
}))
}
});
} else {
const [secret] = created;
secretMutationEvents.push({
type: EventType.CREATE_SECRET,
metadata: {
environment,
secretPath: folder.path,
secretId: secret.id,
secretVersion: 1,
// @ts-expect-error not present on v1 secrets
secretKey: secret.key as string,
// @ts-expect-error not present on v1 secrets
secretMetadata: secret.secretMetadata as ResourceMetadataDTO
}
});
}
}
if (updated.length) {
if (updated.length > 1) {
secretMutationEvents.push({
type: EventType.UPDATE_SECRETS,
metadata: {
environment,
secretPath: folder.path,
secrets: updated.map((secret) => ({
secretId: secret.id,
secretVersion: secret.version,
// @ts-expect-error not present on v1 secrets
secretKey: secret.key as string,
// @ts-expect-error not present on v1 secrets
secretMetadata: secret.secretMetadata as ResourceMetadataDTO
}))
}
});
} else {
const [secret] = updated;
secretMutationEvents.push({
type: EventType.UPDATE_SECRET,
metadata: {
environment,
secretPath: folder.path,
secretId: secret.id,
secretVersion: secret.version,
// @ts-expect-error not present on v1 secrets
secretKey: secret.key as string,
// @ts-expect-error not present on v1 secrets
secretMetadata: secret.secretMetadata as ResourceMetadataDTO
}
});
}
}
if (deleted.length) {
if (deleted.length > 1) {
secretMutationEvents.push({
type: EventType.DELETE_SECRETS,
metadata: {
environment,
secretPath: folder.path,
secrets: deleted.map((secret) => ({
secretId: secret.id,
secretVersion: secret.version,
// @ts-expect-error not present on v1 secrets
secretKey: secret.key as string
}))
}
});
} else {
const [secret] = deleted;
secretMutationEvents.push({
type: EventType.DELETE_SECRET,
metadata: {
environment,
secretPath: folder.path,
secretId: secret.id,
secretVersion: secret.version,
// @ts-expect-error not present on v1 secrets
secretKey: secret.key as string
}
});
}
}
return { ...mergeStatus, projectId, secretMutationEvents };
}; };
// function to save secret change to secret approval // function to save secret change to secret approval
+140 -7
View File
@@ -2,7 +2,7 @@ import picomatch from "picomatch";
import { z } from "zod"; import { z } from "zod";
import { SecretApprovalRequestsSchema, SecretsSchema, SecretType, ServiceTokenScopes } from "@app/db/schemas"; import { SecretApprovalRequestsSchema, SecretsSchema, SecretType, ServiceTokenScopes } from "@app/db/schemas";
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType, SecretApprovalEvent, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, RAW_SECRETS, SECRETS } from "@app/lib/api-docs"; import { ApiDocsTags, RAW_SECRETS, SECRETS } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
@@ -594,6 +594,23 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretReminderRepeatDays: req.body.secretReminderRepeatDays secretReminderRepeatDays: req.body.secretReminderRepeatDays
}); });
if (secretOperation.type === SecretProtectionType.Approval) { if (secretOperation.type === SecretProtectionType.Approval) {
await server.services.auditLog.createAuditLog({
projectId: req.body.workspaceId,
...req.auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath: req.body.secretPath,
environment: req.body.environment,
secretKey: req.params.secretName,
eventType: SecretApprovalEvent.Create
}
}
});
return { approval: secretOperation.approval }; return { approval: secretOperation.approval };
} }
@@ -730,6 +747,23 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}); });
if (secretOperation.type === SecretProtectionType.Approval) { if (secretOperation.type === SecretProtectionType.Approval) {
await server.services.auditLog.createAuditLog({
projectId: req.body.workspaceId,
...req.auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath: req.body.secretPath,
environment: req.body.environment,
secretKey: req.params.secretName,
eventType: SecretApprovalEvent.Update
}
}
});
return { approval: secretOperation.approval }; return { approval: secretOperation.approval };
} }
const { secret } = secretOperation; const { secret } = secretOperation;
@@ -831,6 +865,23 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
type: req.body.type type: req.body.type
}); });
if (secretOperation.type === SecretProtectionType.Approval) { if (secretOperation.type === SecretProtectionType.Approval) {
await server.services.auditLog.createAuditLog({
projectId: req.body.workspaceId,
...req.auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath: req.body.secretPath,
environment: req.body.environment,
secretKey: req.params.secretName,
eventType: SecretApprovalEvent.Delete
}
}
});
return { approval: secretOperation.approval }; return { approval: secretOperation.approval };
} }
@@ -1165,7 +1216,10 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata: { metadata: {
committedBy: approval.committerUserId, committedBy: approval.committerUserId,
secretApprovalRequestId: approval.id, secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug secretApprovalRequestSlug: approval.slug,
secretPath,
environment,
eventType: SecretApprovalEvent.Create
} }
} }
}); });
@@ -1351,7 +1405,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata: { metadata: {
committedBy: approval.committerUserId, committedBy: approval.committerUserId,
secretApprovalRequestId: approval.id, secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug secretApprovalRequestSlug: approval.slug,
secretPath,
environment,
secretKey: req.params.secretName,
eventType: SecretApprovalEvent.Update
} }
} }
}); });
@@ -1489,7 +1547,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata: { metadata: {
committedBy: approval.committerUserId, committedBy: approval.committerUserId,
secretApprovalRequestId: approval.id, secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug secretApprovalRequestSlug: approval.slug,
secretPath,
environment,
secretKey: req.params.secretName,
eventType: SecretApprovalEvent.Delete
} }
} }
}); });
@@ -1673,7 +1735,10 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata: { metadata: {
committedBy: approval.committerUserId, committedBy: approval.committerUserId,
secretApprovalRequestId: approval.id, secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug secretApprovalRequestSlug: approval.slug,
secretPath,
environment,
eventType: SecretApprovalEvent.CreateMany
} }
} }
}); });
@@ -1801,7 +1866,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata: { metadata: {
committedBy: approval.committerUserId, committedBy: approval.committerUserId,
secretApprovalRequestId: approval.id, secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug secretApprovalRequestSlug: approval.slug,
secretPath,
environment,
eventType: SecretApprovalEvent.UpdateMany,
secrets: inputSecrets.map((secret) => ({
secretKey: secret.secretName
}))
} }
} }
}); });
@@ -1920,7 +1991,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
metadata: { metadata: {
committedBy: approval.committerUserId, committedBy: approval.committerUserId,
secretApprovalRequestId: approval.id, secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug secretApprovalRequestSlug: approval.slug,
secretPath,
environment,
secrets: inputSecrets.map((secret) => ({
secretKey: secret.secretName
})),
eventType: SecretApprovalEvent.DeleteMany
} }
} }
}); });
@@ -2038,6 +2115,24 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: inputSecrets secrets: inputSecrets
}); });
if (secretOperation.type === SecretProtectionType.Approval) { if (secretOperation.type === SecretProtectionType.Approval) {
await server.services.auditLog.createAuditLog({
projectId: req.body.workspaceId,
...req.auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath,
environment,
secrets: inputSecrets.map((secret) => ({
secretKey: secret.secretKey
})),
eventType: SecretApprovalEvent.CreateMany
}
}
});
return { approval: secretOperation.approval }; return { approval: secretOperation.approval };
} }
const { secrets } = secretOperation; const { secrets } = secretOperation;
@@ -2170,6 +2265,25 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
mode: req.body.mode mode: req.body.mode
}); });
if (secretOperation.type === SecretProtectionType.Approval) { if (secretOperation.type === SecretProtectionType.Approval) {
await server.services.auditLog.createAuditLog({
projectId: req.body.workspaceId,
...req.auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath,
environment,
secrets: inputSecrets.map((secret) => ({
secretKey: secret.secretKey,
secretPath: secret.secretPath
})),
eventType: SecretApprovalEvent.UpdateMany
}
}
});
return { approval: secretOperation.approval }; return { approval: secretOperation.approval };
} }
const { secrets } = secretOperation; const { secrets } = secretOperation;
@@ -2298,6 +2412,25 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: inputSecrets secrets: inputSecrets
}); });
if (secretOperation.type === SecretProtectionType.Approval) { if (secretOperation.type === SecretProtectionType.Approval) {
await server.services.auditLog.createAuditLog({
projectId: req.body.workspaceId,
...req.auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath,
environment,
secrets: inputSecrets.map((secret) => ({
secretKey: secret.secretKey
})),
eventType: SecretApprovalEvent.DeleteMany
}
}
});
return { approval: secretOperation.approval }; return { approval: secretOperation.approval };
} }
const { secrets } = secretOperation; const { secrets } = secretOperation;