diff --git a/web/ee/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx b/web/ee/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx index 118b35fb10..653d405753 100644 --- a/web/ee/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx +++ b/web/ee/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx @@ -32,6 +32,7 @@ import {LongTextCellRenderer} from "../cellRenderers/cellRenderers" import EvaluationErrorModal from "../EvaluationErrorProps/EvaluationErrorModal" import EvaluationErrorText from "../EvaluationErrorProps/EvaluationErrorText" import FilterColumns, {generateFilterItems} from "../FilterColumns/FilterColumns" +import {isValidId} from "@/oss/lib/helpers/serviceValidations" const useStyles = createUseStyles((theme: JSSTheme) => ({ table: { @@ -79,7 +80,9 @@ const EvaluationCompareMode: FC = () => { const classes = useStyles() const {appTheme} = useAppTheme() const [evaluationIdsStr = ""] = useQueryParam("evaluations") - const evaluationIdsArray = evaluationIdsStr.split(",").filter((item) => !!item) + const evaluationIdsArray = evaluationIdsStr + .split(",") + .filter((item) => !!item && isValidId(item)) const [evalIds, setEvalIds] = useState(evaluationIdsArray) const [hiddenVariants, setHiddenVariants] = useState([]) const [fetching, setFetching] = useState(false) @@ -151,7 +154,7 @@ const EvaluationCompareMode: FC = () => { }, [variants]) const evaluationIds = useMemo( - () => evaluationIdsStr.split(",").filter((item) => !!item), + () => evaluationIdsStr.split(",").filter((item) => !!item && isValidId(item)), [evaluationIdsStr], ) diff --git a/web/ee/src/services/evaluations/api/index.ts b/web/ee/src/services/evaluations/api/index.ts index 65354426ed..926b5bb108 100644 --- a/web/ee/src/services/evaluations/api/index.ts +++ b/web/ee/src/services/evaluations/api/index.ts @@ -25,6 +25,7 @@ import exactMatchImg from "@/oss/media/target.png" import similarityImg from "@/oss/media/transparency.png" import {fetchTestset} from "@/oss/services/testsets/api" import {getProjectValues} from "@/oss/state/project" +import {assertValidId, isValidId} from "@/oss/lib/helpers/serviceValidations" //Prefix convention: // - fetch: GET single entity from server @@ -159,16 +160,28 @@ export const fetchAllEvaluations = async (appId: string) => { } export const fetchEvaluation = async (evaluationId: string) => { + if (!isValidId(evaluationId)) { + throw new Error("Invalid evaluationId parameter") + } const {projectId} = getProjectValues() + const id = assertValidId(evaluationId) - const response = await axios.get(`/evaluations/${evaluationId}?project_id=${projectId}`) + const response = await axios.get(`/evaluations/${encodeURIComponent(id)}`, { + params: {project_id: projectId}, + }) return evaluationTransformer(response.data) as _Evaluation } export const fetchEvaluationStatus = async (evaluationId: string) => { + if (!isValidId(evaluationId)) { + throw new Error("Invalid evaluationId parameter") + } const {projectId} = getProjectValues() + const id = assertValidId(evaluationId) - const response = await axios.get(`/evaluations/${evaluationId}/status?project_id=${projectId}`) + const response = await axios.get(`/evaluations/${encodeURIComponent(id)}/status`, { + params: {project_id: projectId}, + }) return response.data as {status: _Evaluation["status"]} } @@ -211,11 +224,17 @@ export const deleteEvaluations = async (evaluationsIds: string[]) => { // Evaluation Scenarios export const fetchAllEvaluationScenarios = async (evaluationId: string) => { + if (!isValidId(evaluationId)) { + throw new Error("Invalid evaluationId parameter") + } const {projectId} = getProjectValues() + const id = assertValidId(evaluationId) const [{data: evaluationScenarios}, evaluation] = await Promise.all([ - axios.get(`/evaluations/${evaluationId}/evaluation_scenarios?project_id=${projectId}`), - fetchEvaluation(evaluationId), + axios.get(`/evaluations/${encodeURIComponent(id)}/evaluation_scenarios`, { + params: {project_id: projectId}, + }), + fetchEvaluation(id), ]) evaluationScenarios.forEach((scenario: _EvaluationScenario) => { @@ -239,7 +258,12 @@ export const updateScenarioStatus = async ( // Comparison export const fetchAllComparisonResults = async (evaluationIds: string[]) => { - const scenarioGroups = await Promise.all(evaluationIds.map(fetchAllEvaluationScenarios)) + // Defensive check: Only accept valid UUIDs + const validIds = evaluationIds.filter((id) => isValidId(id)) + if (validIds.length === 0) { + throw new Error("No valid evaluation IDs provided") + } + const scenarioGroups = await Promise.all(validIds.map(fetchAllEvaluationScenarios)) const testset: TestSet = await fetchTestset(scenarioGroups[0][0].evaluation?.testset?.id) const inputsNameSet = new Set() diff --git a/web/ee/src/services/human-evaluations/api/index.ts b/web/ee/src/services/human-evaluations/api/index.ts index 769582e3c3..20828c1b52 100644 --- a/web/ee/src/services/human-evaluations/api/index.ts +++ b/web/ee/src/services/human-evaluations/api/index.ts @@ -1,6 +1,7 @@ import axios from "@/oss/lib/api/assets/axiosConfig" import {EvaluationFlow, EvaluationType} from "@/oss/lib/enums" import {getAgentaApiUrl} from "@/oss/lib/helpers/api" +import {assertValidId} from "@/oss/lib/helpers/serviceValidations" import { abTestingEvaluationTransformer, fromEvaluationResponseToEvaluation, @@ -29,20 +30,25 @@ export const fetchAllLoadEvaluations = async ( projectId: string, ignoreAxiosError = false, ) => { - const response = await axios.get( - `${getAgentaApiUrl()}/human-evaluations?project_id=${projectId}&app_id=${appId}`, - { - _ignoreError: ignoreAxiosError, - } as any, - ) + const app = assertValidId(appId, "appId") + const project = assertValidId(projectId, "projectId") + + const response = await axios.get(`${getAgentaApiUrl()}/human-evaluations`, { + params: {project_id: project, app_id: app}, + _ignoreError: ignoreAxiosError, + } as any) return response.data } export const fetchLoadEvaluation = async (evaluationId: string) => { const {projectId} = getProjectValues() + const id = assertValidId(evaluationId, "evaluationId") + const project = assertValidId(projectId, "projectId") try { return await axios - .get(`${getAgentaApiUrl()}/human-evaluations/${evaluationId}?project_id=${projectId}`) + .get(`${getAgentaApiUrl()}/human-evaluations/${encodeURIComponent(id)}`, { + params: {project_id: project}, + }) .then((responseData) => { return fromEvaluationResponseToEvaluation(responseData.data) }) @@ -50,17 +56,19 @@ export const fetchLoadEvaluation = async (evaluationId: string) => { if (axios.isCancel?.(error) || (error as any)?.code === "ERR_CANCELED") { return null } - console.error(`Error fetching evaluation ${evaluationId}:`, error) + console.error(`Error fetching evaluation ${id}:`, error) return null } } export const deleteEvaluations = async (ids: string[]) => { const {projectId} = getProjectValues() + const project = assertValidId(projectId, "projectId") const response = await axios({ method: "delete", - url: `${getAgentaApiUrl()}/human-evaluations?project_id=${projectId}`, + url: `${getAgentaApiUrl()}/human-evaluations`, + params: {project_id: project}, data: {evaluations_ids: ids}, }) return response.data @@ -71,10 +79,15 @@ export const fetchAllLoadEvaluationsScenarios = async ( evaluation: Evaluation, ) => { const {projectId} = getProjectValues() + const tableId = assertValidId(evaluationTableId, "evaluationTableId") + const project = assertValidId(projectId, "projectId") return await axios .get( - `${getAgentaApiUrl()}/human-evaluations/${evaluationTableId}/evaluation_scenarios?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/${encodeURIComponent( + tableId, + )}/evaluation_scenarios`, + {params: {project_id: project}}, ) .then((responseData) => { const evaluationsRows = responseData.data.map((item: any) => { @@ -107,38 +120,45 @@ export const createNewEvaluation = async ( }, ignoreAxiosError = false, ) => { + const app = assertValidId(appId, "appId") + const testset = assertValidId(testsetId, "testsetId") + const customId = selectedCustomEvaluationID + ? assertValidId(selectedCustomEvaluationID, "customEvaluationId") + : undefined + const data = { variant_ids, inputs: inputs, - app_id: appId, + app_id: app, evaluation_type: evaluationType, evaluation_type_settings: { ...evaluationTypeSettings, - custom_code_evaluation_id: selectedCustomEvaluationID, + custom_code_evaluation_id: customId, llm_app_prompt_template: llmAppPromptTemplate, }, - testset_id: testsetId, + testset_id: testset, status: EvaluationFlow.EVALUATION_INITIALIZED, } const {projectId} = getProjectValues() + const project = assertValidId(projectId, "projectId") - const response = await axios.post( - `${getAgentaApiUrl()}/human-evaluations?project_id=${projectId}`, - data, - { - _ignoreError: ignoreAxiosError, - } as any, - ) + const response = await axios.post(`${getAgentaApiUrl()}/human-evaluations`, data, { + params: {project_id: project}, + _ignoreError: ignoreAxiosError, + } as any) return response.data.id } export const updateEvaluation = async (evaluationId: string, data: GenericObject) => { const {projectId} = getProjectValues() + const id = assertValidId(evaluationId, "evaluationId") + const project = assertValidId(projectId, "projectId") const response = await axios.put( - `${getAgentaApiUrl()}/human-evaluations/${evaluationId}?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/${encodeURIComponent(id)}`, data, + {params: {project_id: project}}, ) return response.data } @@ -150,20 +170,31 @@ export const updateEvaluationScenario = async ( evaluationType: EvaluationType, ) => { const {projectId} = getProjectValues() + const tableId = assertValidId(evaluationTableId, "evaluationTableId") + const scenarioId = assertValidId(evaluationScenarioId, "evaluationScenarioId") + const project = assertValidId(projectId, "projectId") const response = await axios.put( - `${getAgentaApiUrl()}/human-evaluations/${evaluationTableId}/evaluation_scenario/${evaluationScenarioId}/${evaluationType}?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/${encodeURIComponent( + tableId, + )}/evaluation_scenario/${encodeURIComponent(scenarioId)}/${encodeURIComponent( + evaluationType, + )}`, data, + {params: {project_id: project}}, ) return response.data } export const createEvaluationScenario = async (evaluationTableId: string, data: GenericObject) => { const {projectId} = getProjectValues() + const tableId = assertValidId(evaluationTableId, "evaluationTableId") + const project = assertValidId(projectId, "projectId") const response = await axios.post( - `${getAgentaApiUrl()}/human-evaluations/${evaluationTableId}/evaluation_scenario?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/${encodeURIComponent(tableId)}/evaluation_scenario`, data, + {params: {project_id: project}}, ) return response.data } @@ -173,21 +204,25 @@ export const createEvaluateAICritiqueForEvalScenario = async ( ignoreAxiosError = false, ) => { const {projectId} = getProjectValues() + const project = assertValidId(projectId, "projectId") const response = await axios.post( - `${getAgentaApiUrl()}/human-evaluations/evaluation_scenario/ai_critique?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/evaluation_scenario/ai_critique`, data, - {_ignoreError: ignoreAxiosError} as any, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response } export const fetchEvaluationResults = async (evaluationId: string, ignoreAxiosError = false) => { const {projectId} = getProjectValues() + const id = assertValidId(evaluationId, "evaluationId") + const project = assertValidId(projectId, "projectId") const response = await axios.get( - `${getAgentaApiUrl()}/human-evaluations/${evaluationId}/results?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/${encodeURIComponent(id)}/results`, { + params: {project_id: project}, _ignoreError: ignoreAxiosError, } as any, ) @@ -196,9 +231,14 @@ export const fetchEvaluationResults = async (evaluationId: string, ignoreAxiosEr export const fetchEvaluationScenarioResults = async (evaluation_scenario_id: string) => { const {projectId} = getProjectValues() + const scenarioId = assertValidId(evaluation_scenario_id, "evaluation_scenario_id") + const project = assertValidId(projectId, "projectId") const response = await axios.get( - `${getAgentaApiUrl()}/human-evaluations/evaluation_scenario/${evaluation_scenario_id}/score?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/evaluation_scenario/${encodeURIComponent( + scenarioId, + )}/score`, + {params: {project_id: project}}, ) return response } @@ -208,11 +248,12 @@ export const createCustomCodeEvaluation = async ( ignoreAxiosError = false, ) => { const {projectId} = getProjectValues() + const project = assertValidId(projectId, "projectId") const response = await axios.post( - `${getAgentaApiUrl()}/human-evaluations/custom_evaluation?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/custom_evaluation`, payload, - {_ignoreError: ignoreAxiosError} as any, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response } @@ -223,41 +264,53 @@ export const updateCustomEvaluationDetail = async ( ignoreAxiosError = false, ) => { const {projectId} = getProjectValues() + const customId = assertValidId(id, "custom_evaluation_id") + const project = assertValidId(projectId, "projectId") const response = await axios.put( - `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/${id}?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/${encodeURIComponent(customId)}`, payload, - {_ignoreError: ignoreAxiosError} as any, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response } export const fetchCustomEvaluations = async (app_id: string, ignoreAxiosError = false) => { const {projectId} = getProjectValues() + const appId = assertValidId(app_id, "app_id") + const project = assertValidId(projectId, "projectId") const response = await axios.get( - `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/list/${app_id}?project_id=${projectId}`, - {_ignoreError: ignoreAxiosError} as any, + `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/list/${encodeURIComponent( + appId, + )}`, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response } export const fetchCustomEvaluationDetail = async (id: string, ignoreAxiosError = false) => { const {projectId} = getProjectValues() + const customId = assertValidId(id, "custom_evaluation_id") + const project = assertValidId(projectId, "projectId") const response = await axios.get( - `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/${id}?project_id=${projectId}`, - {_ignoreError: ignoreAxiosError} as any, + `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/${encodeURIComponent(customId)}`, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response.data } export const fetchCustomEvaluationNames = async (app_id: string, ignoreAxiosError = false) => { const {projectId} = getProjectValues() + const appId = assertValidId(app_id, "app_id") + const project = assertValidId(projectId, "projectId") const response = await axios.get( - `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/${app_id}/names?project_id=${projectId}`, - {_ignoreError: ignoreAxiosError} as any, + `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/${encodeURIComponent( + appId, + )}/names`, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response } @@ -267,13 +320,15 @@ export const createExecuteCustomEvaluationCode = async ( ignoreAxiosError = false, ) => { const {projectId} = getProjectValues() + const project = assertValidId(projectId, "projectId") + const evalId = assertValidId(payload.evaluation_id, "evaluation_id") const response = await axios.post( - `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/execute/${ - payload.evaluation_id - }?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/custom_evaluation/execute/${encodeURIComponent( + evalId, + )}`, payload, - {_ignoreError: ignoreAxiosError} as any, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response } @@ -284,11 +339,15 @@ export const updateEvaluationScenarioScore = async ( ignoreAxiosError = false, ) => { const {projectId} = getProjectValues() + const scenarioId = assertValidId(evaluation_scenario_id, "evaluation_scenario_id") + const project = assertValidId(projectId, "projectId") const response = await axios.put( - `${getAgentaApiUrl()}/human-evaluations/evaluation_scenario/${evaluation_scenario_id}/score?project_id=${projectId}`, + `${getAgentaApiUrl()}/human-evaluations/evaluation_scenario/${encodeURIComponent( + scenarioId, + )}/score`, {score}, - {_ignoreError: ignoreAxiosError} as any, + {params: {project_id: project}, _ignoreError: ignoreAxiosError} as any, ) return response }