Merge branch 'main' into feature/north-flank-app-connection

This commit is contained in:
Victor Santos
2025-10-24 18:07:23 -03:00
100 changed files with 4611 additions and 660 deletions
@@ -8,7 +8,7 @@ interface PrincipalArnEntity {
SessionInfo: string; // Only populated for assumed-role
}
export const extractPrincipalArnEntity = (arn: string): PrincipalArnEntity => {
export const extractPrincipalArnEntity = (arn: string, formatAsIamRole: boolean = false): PrincipalArnEntity => {
// split the ARN into parts using ":" as the delimiter
const fullParts = arn.split(":");
if (fullParts.length !== 6) {
@@ -49,7 +49,7 @@ export const extractPrincipalArnEntity = (arn: string): PrincipalArnEntity => {
}
// assumed roles use a special format where the friendly name is the role name
const [roleName, sessionId] = rest;
finalType = "assumed-role";
finalType = formatAsIamRole ? "role" : "assumed-role";
friendlyName = roleName;
sessionInfo = sessionId;
break;
@@ -83,9 +83,11 @@ export const extractPrincipalArnEntity = (arn: string): PrincipalArnEntity => {
* Extracts the identity ARN from the GetCallerIdentity response to one of the following formats:
* - arn:aws:iam::123456789012:user/MyUserName
* - arn:aws:iam::123456789012:role/MyRoleName
* - arn:aws-us-gov:iam::123456789012:user/MyUserName (GovCloud)
* - arn:aws-us-gov:iam::123456789012:role/MyRoleName (GovCloud)
*/
export const extractPrincipalArn = (arn: string) => {
const entity = extractPrincipalArnEntity(arn);
export const extractPrincipalArn = (arn: string, formatAsIamRole: boolean = false) => {
const entity = extractPrincipalArnEntity(arn, formatAsIamRole);
return `arn:aws:${entity.Service}::${entity.AccountNumber}:${entity.Type}/${entity.FriendlyName}`;
return `arn:${entity.Partition}:${formatAsIamRole ? "iam" : entity.Service}::${entity.AccountNumber}:${entity.Type}/${entity.FriendlyName}`;
};
@@ -158,7 +158,7 @@ export const identityAwsAuthServiceFactory = ({
// considers exact matches + wildcard matches
// heavily validated in router
const regex = new RE2(`^${principalArn.replaceAll("*", ".*")}$`);
return regex.test(formattedArn);
return regex.test(formattedArn) || regex.test(extractPrincipalArn(Arn, true));
});
if (!isArnAllowed) {
@@ -6,7 +6,7 @@ const twelveDigitRegex = new RE2(/^\d{12}$/);
// akhilmhdh: change this to a normal function later. Checked no redosable at the moment
const arnRegex = new RE2(
/^arn:aws:(iam|sts)::\d{12}:(user\/[a-zA-Z0-9_.@+*/-]+|role\/[a-zA-Z0-9_.@+*/-]+|assumed-role\/[a-zA-Z0-9_.@+*/-]+|\*)$/
/^arn:aws(?:-us-gov)?:(iam|sts)::\d{12}:(user\/[a-zA-Z0-9_.@+*/-]+|role\/[a-zA-Z0-9_.@+*/-]+|assumed-role\/[a-zA-Z0-9_.@+*/-]+|\*)$/
);
export const validateAccountIds = z
@@ -55,7 +55,7 @@ export const validatePrincipalArns = z
},
{
message:
"Each ARN must be in the format of 'arn:aws:iam::123456789012:user/UserName', 'arn:aws:iam::123456789012:role/RoleName', or 'arn:aws:iam::123456789012:*', 'arn:aws:sts::123456789012:assumed-role/RoleName'."
"Each ARN must be in the format of 'arn:aws:iam::123456789012:user/UserName', 'arn:aws:iam::123456789012:role/RoleName', or 'arn:aws:iam::123456789012:*', 'arn:aws:sts::123456789012:assumed-role/RoleName'. GovCloud ARNs (arn:aws-us-gov:...) are also supported."
}
)
// Transform to normalize the spaces around commas
@@ -11,6 +11,7 @@ import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { SecretNameSchema } from "@app/server/lib/schemas";
import { decryptAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns";
import { ActorType } from "@app/services/auth/auth-type";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
@@ -408,6 +409,24 @@ export const secretSyncQueueFactory = ({
if (!Object.keys(importedSecrets).length) return {};
let invalidNameCount = 0;
let errorMessage = "";
for (const [key] of Object.entries(importedSecrets)) {
const result = SecretNameSchema.safeParse(key);
if (!result.success) {
invalidNameCount += 1;
if (errorMessage === "") errorMessage = result.error.issues[0]?.message;
}
}
if (invalidNameCount > 0) {
throw new SecretSyncError({
message: `Found ${invalidNameCount} invalid secret name${invalidNameCount === 1 ? "" : "s"}. ${errorMessage}`,
shouldRetry: false
});
}
const importedSecretMap: TSecretMap = {};
const secretMap = await $getInfisicalSecrets(secretSync, false);