diff --git a/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx b/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx index 9cb0a0bbc..c88b26a23 100644 --- a/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx +++ b/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx @@ -2,14 +2,38 @@ import React, { forwardRef, TextareaHTMLAttributes, useEffect, useRef, useState } from "react"; import { twMerge } from "tailwind-merge"; -import { - REGEX_SECRET_REFERENCE_FIND, - REGEX_SECRET_REFERENCE_INVALID -} from "@app/helpers/secret-reference"; import { useToggle } from "@app/hooks"; import SecretReferenceSelect, { ReferenceType } from "./SecretReferenceSelect"; +const REGEX_SECRET_REFERENCE_FIND = /(\${([^}]*)})/g; +const REGEX_SECRET_REFERENCE_INVALID = /(?:\/|\\|\n|\.$|^\.)/; + +const 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; + } +}; + const replaceContentWithDot = (str: string) => { let finalStr = ""; for (let i = 0; i < str.length; i += 1) { @@ -160,7 +184,6 @@ export const InfisicalSecretInput = forwardRef( const newValue = `${value.slice(0, currCaretPos)}}${value.slice(currCaretPos)}`; setValue(newValue); - // TODO: there should be a better way to do onChange?.({ target: { value: newValue } } as any); setCaretPos(currCaretPos); @@ -318,9 +341,29 @@ export const InfisicalSecretInput = forwardRef( secretPath={propSecretPath} environment={propEnvironment} open={showReferencePopup} - handleOpenChange={(isOpen) => handleReferenceOpenChange(isOpen)} + handleOpenChange={(isOpen) => { + if (!isOpen && !isValidSecretReferenceValue(value)) { + return; + } + handleReferenceOpenChange(isOpen); + }} onSelect={(refValue) => handleReferenceSelect(refValue)} onEscapeKeyDown={() => { + if (showReferencePopup && !isValidSecretReferenceValue(value)) { + // remove incomplete reference + const match = isCaretInsideReference(value, lastCaretPos); + const referenceStartIndex = match?.index || 0; + const referenceEndIndex = referenceStartIndex + (match?.[0]?.length || 0); + const [start, end] = [ + value.slice(0, referenceStartIndex), + value.slice(referenceEndIndex) + ]; + + const newValue = start + end; + setValue(newValue); + onChange?.({ target: { value: newValue } } as any); + } + const timeout = setTimeout(() => { setCaretPos(lastCaretPos); clearTimeout(timeout); diff --git a/frontend/src/helpers/secret-reference.ts b/frontend/src/helpers/secret-reference.ts deleted file mode 100644 index 03af99818..000000000 --- a/frontend/src/helpers/secret-reference.ts +++ /dev/null @@ -1,27 +0,0 @@ -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 13767b13e..448daee3b 100644 --- a/frontend/src/hooks/api/secrets/mutations.tsx +++ b/frontend/src/hooks/api/secrets/mutations.tsx @@ -7,7 +7,6 @@ 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"; @@ -84,7 +83,6 @@ 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 @@ -146,7 +144,6 @@ 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