diff --git a/frontend/src/components/v2/SecretInput/SecretInput.tsx b/frontend/src/components/v2/SecretInput/SecretInput.tsx index b91025e89..4a03533f9 100644 --- a/frontend/src/components/v2/SecretInput/SecretInput.tsx +++ b/frontend/src/components/v2/SecretInput/SecretInput.tsx @@ -5,14 +5,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { twMerge } from "tailwind-merge"; import { useWorkspace } from "@app/context"; +import { REGEX_SECRET_REFERENCE_FIND, REGEX_SECRET_REFERENCE_INVALID } from "@app/helpers/secret-reference"; import { useToggle } from "@app/hooks"; import { useGetUserWsKey } from "@app/hooks/api"; import { useGetFoldersByEnv } from "@app/hooks/api/secretFolders/queries"; import { useGetProjectSecrets } from "@app/hooks/api/secrets/queries"; -const REGEX_REFERENCE = /(\${([^}]*)})/g; -const REGEX_REFERENCE_INVALID = /(?:\/|\\|\n|\.$|^\.)/g; - const replaceContentWithDot = (str: string) => { let finalStr = ""; for (let i = 0; i < str.length; i += 1) { @@ -28,7 +26,7 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => { if (!isVisible) return replaceContentWithDot(content); let skipNext = false; - const formattedContent = content.split(REGEX_REFERENCE).flatMap((el, i) => { + const formattedContent = content.split(REGEX_SECRET_REFERENCE_FIND).flatMap((el, i) => { const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}"); if (isInterpolationSyntax) { skipNext = true; @@ -38,7 +36,7 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => { @@ -169,7 +167,7 @@ export const SecretInput = forwardRef( }, [secrets, environment, referenceKey]); function findMatch(str: string, start: number) { - const matches = [...str.matchAll(REGEX_REFERENCE)]; + const matches = [...str.matchAll(REGEX_SECRET_REFERENCE_FIND)]; for (let i = 0; i < matches.length; i += 1) { const match = matches[i]; if ( diff --git a/frontend/src/helpers/secret-reference.ts b/frontend/src/helpers/secret-reference.ts new file mode 100644 index 000000000..03af99818 --- /dev/null +++ b/frontend/src/helpers/secret-reference.ts @@ -0,0 +1,27 @@ +export const REGEX_SECRET_REFERENCE_FIND = /(\${([^}]*)})/g; +export const REGEX_SECRET_REFERENCE_INVALID = /(?:\/|\\|\n|\.$|^\.)/; + +export function isValidSecretReferenceValue(str: string): boolean { + try { + if (!str) return true; + let skipNext = false; + str.split(REGEX_SECRET_REFERENCE_FIND).flatMap((el) => { + if (skipNext) { + skipNext = false; + return []; + } + + const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}"); + if (!isInterpolationSyntax) return []; + + skipNext = true; + if (REGEX_SECRET_REFERENCE_INVALID.test(el.slice(2, -1))) + throw new Error("Invalid reference"); + + return el; + }); + return true; + } catch (e) { + return false; + } +} diff --git a/frontend/src/hooks/api/secrets/mutations.tsx b/frontend/src/hooks/api/secrets/mutations.tsx index 448daee3b..13767b13e 100644 --- a/frontend/src/hooks/api/secrets/mutations.tsx +++ b/frontend/src/hooks/api/secrets/mutations.tsx @@ -7,6 +7,7 @@ import { encryptSymmetric } from "@app/components/utilities/cryptography/crypto"; import { apiRequest } from "@app/config/request"; +import { isValidSecretReferenceValue } from "@app/helpers/secret-reference"; import { secretApprovalRequestKeys } from "../secretApprovalRequest/queries"; import { secretSnapshotKeys } from "../secretSnapshots/queries"; @@ -83,6 +84,7 @@ export const useCreateSecretV3 = ({ secretComment, skipMultilineEncoding }) => { + if (!isValidSecretReferenceValue(secretValue)) throw new Error("Invalid secret reference"); const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; const randomBytes = latestFileKey @@ -144,6 +146,7 @@ export const useUpdateSecretV3 = ({ newSecretName, skipMultilineEncoding }) => { + if (!isValidSecretReferenceValue(secretValue)) throw new Error("Invalid secret reference"); const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string; const randomBytes = latestFileKey