Merge pull request #4574 from Infisical/secret-update-request-fixes

fix(secret-change-requests): various UPDATE change request fixes
This commit is contained in:
Scott Wilson
2025-09-22 18:00:27 -07:00
committed by GitHub
8 changed files with 93 additions and 25 deletions

View File

@@ -0,0 +1,19 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretApprovalRequestSecretV2)) {
await knex.schema.alterTable(TableName.SecretApprovalRequestSecretV2, (t) => {
t.boolean("skipMultilineEncoding").alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretApprovalRequestSecretV2)) {
await knex.schema.alterTable(TableName.SecretApprovalRequestSecretV2, (t) => {
t.boolean("skipMultilineEncoding").defaultTo(false).alter();
});
}
}

View File

@@ -17,7 +17,7 @@ export const SecretApprovalRequestsSecretsV2Schema = z.object({
encryptedComment: zodBuffer.nullable().optional(),
reminderNote: z.string().nullable().optional(),
reminderRepeatDays: z.number().nullable().optional(),
skipMultilineEncoding: z.boolean().default(false).nullable().optional(),
skipMultilineEncoding: z.boolean().nullable().optional(),
metadata: z.unknown().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date(),

View File

@@ -320,10 +320,20 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
.array(),
secretPath: z.string(),
commits: secretRawSchema
.omit({ _id: true, environment: true, workspace: true, type: true, version: true, secretValue: true })
.omit({
_id: true,
environment: true,
workspace: true,
type: true,
version: true,
secretValue: true,
secretComment: true
})
.extend({
secretValueHidden: z.boolean(),
secretValue: z.string().optional(),
secretComment: z.string().optional(),
skipMultilineEncoding: z.boolean().nullish(),
isRotatedSecret: z.boolean().optional(),
op: z.string(),
tags: SanitizedTagSchema.array().optional(),
@@ -348,7 +358,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
secretValueHidden: z.boolean(),
secretComment: z.string().optional(),
tags: SanitizedTagSchema.array().optional(),
secretMetadata: ResourceMetadataSchema.nullish()
secretMetadata: ResourceMetadataSchema.nullish(),
skipMultilineEncoding: z.boolean().nullish()
})
.optional()
})

View File

@@ -284,7 +284,8 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
db.ref("version").withSchema(TableName.SecretVersionV2).as("secVerVersion"),
db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"),
db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"),
db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment")
db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment"),
db.ref("skipMultilineEncoding").withSchema(TableName.SecretVersionV2).as("secVerSkipMultilineEncoding")
)
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
@@ -326,14 +327,22 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
{
key: "secretVersion",
label: "secretVersion" as const,
mapper: ({ secretVersion, secVerVersion, secVerKey, secVerValue, secVerComment }) =>
mapper: ({
secretVersion,
secVerVersion,
secVerKey,
secVerValue,
secVerComment,
secVerSkipMultilineEncoding
}) =>
secretVersion
? {
version: secVerVersion,
id: secretVersion,
key: secVerKey,
encryptedValue: secVerValue,
encryptedComment: secVerComment
encryptedComment: secVerComment,
skipMultilineEncoding: secVerSkipMultilineEncoding
}
: undefined,
childrenMapper: [

View File

@@ -337,12 +337,17 @@ export const secretApprovalRequestServiceFactory = ({
? INFISICAL_SECRET_VALUE_HIDDEN_MASK
: el.secret && el.secret.isRotatedSecret
? undefined
: el.encryptedValue
: el.encryptedValue !== undefined && el.encryptedValue !== null
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
: "",
secretComment: el.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
: "",
: undefined,
secretComment:
el.encryptedComment !== undefined && el.encryptedComment !== null
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
: undefined,
skipMultilineEncoding:
el.skipMultilineEncoding !== undefined && el.skipMultilineEncoding !== null
? el.skipMultilineEncoding
: undefined,
secret: el.secret
? {
secretKey: el.secret.key,
@@ -394,7 +399,8 @@ export const secretApprovalRequestServiceFactory = ({
? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString()
: "",
tags: el.secretVersion.tags,
secretMetadata: el.oldSecretMetadata as ResourceMetadataDTO
secretMetadata: el.oldSecretMetadata as ResourceMetadataDTO,
skipMultilineEncoding: el.secretVersion.skipMultilineEncoding
}
: undefined
}));
@@ -733,9 +739,9 @@ export const secretApprovalRequestServiceFactory = ({
tx,
inputSecrets: secretUpdationCommits.map((el) => {
const encryptedValue =
!el.secret?.isRotatedSecret && typeof el.encryptedValue !== "undefined"
!el.secret?.isRotatedSecret && el.encryptedValue !== null && el.encryptedValue !== undefined
? {
encryptedValue: el.encryptedValue as Buffer,
encryptedValue: el.encryptedValue,
references: el.encryptedValue
? getAllSecretReferencesV2Bridge(
secretManagerDecryptor({
@@ -749,9 +755,9 @@ export const secretApprovalRequestServiceFactory = ({
filter: { id: el.secretId as string, type: SecretType.Shared },
data: {
reminderRepeatDays: el.reminderRepeatDays,
encryptedComment: el.encryptedComment,
encryptedComment: el.encryptedComment !== null ? el.encryptedComment : undefined,
reminderNote: el.reminderNote,
skipMultilineEncoding: el.skipMultilineEncoding,
skipMultilineEncoding: el.skipMultilineEncoding !== null ? el.skipMultilineEncoding : undefined,
key: el.key,
tags: el?.tags.map(({ id }) => id),
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
@@ -1633,11 +1639,13 @@ export const secretApprovalRequestServiceFactory = ({
key: newSecretName || secretKey,
encryptedComment: setKnexStringValue(
secretComment,
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob,
true // scott: we need to encrypt empty string on update to differentiate not updating comment vs clearing comment
),
encryptedValue: setKnexStringValue(
secretValue,
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob,
true // scott: we need to encrypt empty string on update to differentiate not updating value vs clearing value
),
reminderRepeatDays,
reminderNote,

View File

@@ -16,8 +16,12 @@ export const stripUndefinedInWhere = <T extends object>(val: T): Exclude<T, unde
// if its undefined its skipped in knex
// if its empty string its set as null
// else pass to the required one
export const setKnexStringValue = <T>(value: string | null | undefined, cb: (arg: string) => T) => {
export const setKnexStringValue = <T>(
value: string | null | undefined,
cb: (arg: string) => T,
allowEmptyString?: boolean
) => {
if (typeof value === "undefined") return;
if (value === "" || value === null) return null;
if ((value === "" && !allowEmptyString) || value === null) return null;
return cb(value);
};

View File

@@ -113,7 +113,6 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath }
projectId,
secretPath,
secretKey: secret.key,
secretValue: secret.value || "",
type: SecretType.Shared,
tagIds: secret.tags?.map((tag) => tag.id),
secretComment: secret.comment,

View File

@@ -23,6 +23,7 @@ export type Props = {
newVersion?: Omit<TSecretApprovalSecChange, "tags"> & {
tags?: WsTag[];
secretMetadata?: { key: string; value: string }[];
skipMultilineEncoding?: boolean;
};
presentSecretVersionNumber: number;
hasMerged?: boolean;
@@ -217,6 +218,14 @@ export const SecretApprovalRequestChangeItem = ({
)}
</div>
</div>
<div className="mb-2">
<div className="text-sm font-medium text-mineshaft-300">Multi-line Encoding</div>
<div className="text-sm">
{secretVersion?.skipMultilineEncoding?.toString() || (
<span className="text-sm text-mineshaft-300">-</span>
)}{" "}
</div>
</div>
</div>
) : (
<div className="text-md flex w-full items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 text-mineshaft-300 xl:w-1/2">
@@ -263,7 +272,7 @@ export const SecretApprovalRequestChangeItem = ({
isReadOnly
valueAlwaysHidden={newVersion?.secretValueHidden}
isVisible={isNewSecretValueVisible}
value={newVersion?.secretValue}
value={newVersion?.secretValue ?? secretVersion?.secretValue}
containerClassName={twMerge(
"border border-mineshaft-600 bg-bunker-700 py-1.5 text-bunker-300 hover:border-primary-400/50",
newVersion?.secretValueHidden ? "pl-8 pr-2" : "px-2"
@@ -287,7 +296,7 @@ export const SecretApprovalRequestChangeItem = ({
<div className="mb-2">
<div className="text-sm font-medium text-mineshaft-300">Comment</div>
<div className="thin-scrollbar max-h-[5rem] max-w-[34rem] overflow-y-auto break-words text-sm xl:max-w-[28rem]">
{newVersion?.secretComment || (
{(newVersion?.secretComment ?? secretVersion?.secretComment) || (
<span className="text-sm text-mineshaft-300">-</span>
)}{" "}
</div>
@@ -315,9 +324,9 @@ export const SecretApprovalRequestChangeItem = ({
</div>
<div className="mb-2">
<div className="text-sm font-medium text-mineshaft-300">Metadata</div>
{newVersion?.secretMetadata?.length ? (
{(newVersion?.secretMetadata ?? secretVersion?.secretMetadata)?.length ? (
<div className="mt-1 flex flex-wrap gap-2 text-sm text-mineshaft-300">
{newVersion.secretMetadata?.map((el) => (
{(newVersion?.secretMetadata ?? secretVersion?.secretMetadata)?.map((el) => (
<div key={el.key} className="flex items-center">
<Tag
size="xs"
@@ -353,6 +362,15 @@ export const SecretApprovalRequestChangeItem = ({
<p className="text-sm text-mineshaft-300">-</p>
)}
</div>
<div className="mb-2">
<div className="text-sm font-medium text-mineshaft-300">Multi-line Encoding</div>
<div className="text-sm">
{newVersion?.skipMultilineEncoding?.toString() ??
secretVersion?.skipMultilineEncoding?.toString() ?? (
<span className="text-sm text-mineshaft-300">-</span>
)}{" "}
</div>
</div>
</div>
) : (
<div className="text-md flex w-full items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 text-mineshaft-300 xl:w-1/2">