mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #2470 from akhilmhdh/fix/inline-reference-permission
feat: added validation check for secret references made in v2 engine
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import path from "node:path";
|
||||
|
||||
import { TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
@@ -375,6 +376,7 @@ type TInterpolateSecretArg = {
|
||||
decryptSecretValue: (encryptedValue?: Buffer | null) => string | undefined;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "findByFolderId">;
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
|
||||
canExpandValue: (environment: string, secretPath: string) => boolean;
|
||||
};
|
||||
|
||||
const MAX_SECRET_REFERENCE_DEPTH = 10;
|
||||
@@ -382,7 +384,8 @@ export const expandSecretReferencesFactory = ({
|
||||
projectId,
|
||||
decryptSecretValue: decryptSecret,
|
||||
secretDAL,
|
||||
folderDAL
|
||||
folderDAL,
|
||||
canExpandValue
|
||||
}: TInterpolateSecretArg) => {
|
||||
const secretCache: Record<string, Record<string, string>> = {};
|
||||
const getCacheUniqueKey = (environment: string, secretPath: string) => `${environment}-${secretPath}`;
|
||||
@@ -432,6 +435,11 @@ export const expandSecretReferencesFactory = ({
|
||||
if (entities.length === 1) {
|
||||
const [secretKey] = entities;
|
||||
|
||||
if (!canExpandValue(environment, secretPath))
|
||||
throw new UnauthorizedError({
|
||||
message: `You don't have access to secret ${secretKey} in environment ${environment} of secret path ${secretPath} `
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-continue,no-await-in-loop
|
||||
const referedValue = await fetchSecret(environment, secretPath, secretKey);
|
||||
const cacheKey = getCacheUniqueKey(environment, secretPath);
|
||||
@@ -452,6 +460,11 @@ export const expandSecretReferencesFactory = ({
|
||||
const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
|
||||
const secretReferenceKey = entities[entities.length - 1];
|
||||
|
||||
if (!canExpandValue(secretReferenceEnvironment, secretReferencePath))
|
||||
throw new UnauthorizedError({
|
||||
message: `You don't have access to secret ${secretReferenceKey} in environment ${secretReferenceEnvironment} of secret path ${secretReferencePath} `
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const referedValue = await fetchSecret(secretReferenceEnvironment, secretReferencePath, secretReferenceKey);
|
||||
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
|
||||
|
||||
@@ -152,6 +152,15 @@ export const secretV2BridgeServiceFactory = ({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
});
|
||||
references.forEach((referredSecret) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: referredSecret.environment,
|
||||
secretPath: referredSecret.secretPath
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const secret = await secretDAL.transaction((tx) =>
|
||||
fnSecretBulkInsert({
|
||||
@@ -292,6 +301,17 @@ export const secretV2BridgeServiceFactory = ({
|
||||
references: getAllNestedSecretReferences(secretValue)
|
||||
}
|
||||
: {};
|
||||
if (encryptedValue.references) {
|
||||
encryptedValue.references.forEach((referredSecret) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: referredSecret.environment,
|
||||
secretPath: referredSecret.secretPath
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
||||
fnSecretBulkUpdate({
|
||||
@@ -675,7 +695,12 @@ export const secretV2BridgeServiceFactory = ({
|
||||
projectId,
|
||||
folderDAL,
|
||||
secretDAL,
|
||||
decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined)
|
||||
decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined),
|
||||
canExpandValue: (expandEnvironment, expandSecretPath) =>
|
||||
permission.can(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath })
|
||||
)
|
||||
});
|
||||
|
||||
if (shouldExpandSecretReferences) {
|
||||
@@ -799,7 +824,12 @@ export const secretV2BridgeServiceFactory = ({
|
||||
projectId,
|
||||
folderDAL,
|
||||
secretDAL,
|
||||
decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined)
|
||||
decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined),
|
||||
canExpandValue: (expandEnvironment, expandSecretPath) =>
|
||||
permission.can(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath })
|
||||
)
|
||||
});
|
||||
|
||||
// now if secret is not found
|
||||
@@ -916,21 +946,34 @@ export const secretV2BridgeServiceFactory = ({
|
||||
|
||||
const newSecrets = await secretDAL.transaction(async (tx) =>
|
||||
fnSecretBulkInsert({
|
||||
inputSecrets: inputSecrets.map((el) => ({
|
||||
version: 1,
|
||||
encryptedComment: setKnexStringValue(
|
||||
el.secretComment,
|
||||
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
|
||||
),
|
||||
encryptedValue: el.secretValue
|
||||
? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
|
||||
: undefined,
|
||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||
key: el.secretKey,
|
||||
tagIds: el.tagIds,
|
||||
references: getAllNestedSecretReferences(el.secretValue),
|
||||
type: SecretType.Shared
|
||||
})),
|
||||
inputSecrets: inputSecrets.map((el) => {
|
||||
const references = getAllNestedSecretReferences(el.secretValue);
|
||||
references.forEach((referredSecret) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: referredSecret.environment,
|
||||
secretPath: referredSecret.secretPath
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
encryptedComment: setKnexStringValue(
|
||||
el.secretComment,
|
||||
(value) => secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob
|
||||
),
|
||||
encryptedValue: el.secretValue
|
||||
? secretManagerEncryptor({ plainText: Buffer.from(el.secretValue) }).cipherTextBlob
|
||||
: undefined,
|
||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||
key: el.secretKey,
|
||||
tagIds: el.tagIds,
|
||||
references,
|
||||
type: SecretType.Shared
|
||||
};
|
||||
}),
|
||||
folderId,
|
||||
secretDAL,
|
||||
secretVersionDAL,
|
||||
@@ -1037,6 +1080,19 @@ export const secretV2BridgeServiceFactory = ({
|
||||
references: getAllNestedSecretReferences(el.secretValue)
|
||||
}
|
||||
: {};
|
||||
|
||||
if (encryptedValue.references) {
|
||||
encryptedValue.references.forEach((referredSecret) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: referredSecret.environment,
|
||||
secretPath: referredSecret.secretPath
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
filter: { id: originalSecret.id, type: SecretType.Shared },
|
||||
data: {
|
||||
|
||||
@@ -290,7 +290,9 @@ export const secretQueueFactory = ({
|
||||
decryptSecretValue: dto.decryptor,
|
||||
secretDAL: secretV2BridgeDAL,
|
||||
folderDAL,
|
||||
projectId: dto.projectId
|
||||
projectId: dto.projectId,
|
||||
// on integration expand all secrets
|
||||
canExpandValue: () => true
|
||||
});
|
||||
// process secrets in current folder
|
||||
const secrets = await secretV2BridgeDAL.findByFolderId(dto.folderId);
|
||||
|
||||
Reference in New Issue
Block a user