From 1166e0e123e114f0d8d69f1bba21548011eff14c Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Fri, 17 Jan 2025 15:43:05 +0530 Subject: [PATCH 01/20] still has bug --- .../akto/action/testing/StartTestAction.java | 2 +- .../observe/api_collections/ApiDetails.jsx | 1 + .../observe/api_collections/ApiEndpoints.jsx | 1 + .../pages/observe/api_collections/RunTest.jsx | 527 +++++++-------- .../api_collections/RunTestConfiguration.jsx | 132 ++++ .../observe/api_collections/RunTestSuites.jsx | 325 +++++++++ .../component/AdvancedSettingsComponent.jsx | 102 +-- .../observe/api_collections/dummyData.json | 63 ++ .../api_collections/run_test_suites.css | 35 + .../SingleTestRunPage/SingleTestRunPage.js | 638 ++++++++++-------- 10 files changed, 1187 insertions(+), 639 deletions(-) create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 1271981c48..789d38f679 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -506,7 +506,7 @@ else if(this.testingRun.getPeriodInSeconds() > 0){ } TestingRunConfig runConfig = TestingRunConfigDao.instance.findOne( - Filters.eq("_id", this.testingRun.getTestIdConfig()), Projections.exclude("collectionWiseApiInfoKey", "testSubCategoryList") + Filters.eq("_id", this.testingRun.getTestIdConfig()), Projections.exclude("collectionWiseApiInfoKey") ); this.testingRun.setTestingRunConfig(runConfig); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx index a23c8d2f75..41bfbea0b2 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiDetails.jsx @@ -261,6 +261,7 @@ function ApiDetails(props) { endpoints={[apiDetail]} filtered={true} useLocalSubCategoryData={useLocalSubCategoryData} + preActivator={false} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx index 25c60ad8c2..192db7abf3 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiEndpoints.jsx @@ -728,6 +728,7 @@ function ApiEndpoints(props) { closeRunTest={() => setRunTests(false)} disabled={showEmptyScreen} selectedResourcesForPrimaryAction={selectedResourcesForPrimaryAction} + preActivator={false} /> ) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index e6a714b94c..2ed1aef5bb 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -1,5 +1,5 @@ -import { Box, Button, DataTable, Divider, Modal, Text, TextField, Icon, Checkbox, Badge, Banner,HorizontalGrid, HorizontalStack, Link, VerticalStack, Tooltip, Popover, ActionMenu, OptionList } from "@shopify/polaris"; -import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons" +import { Box, Button, DataTable, Divider, Modal, Text, TextField, Icon, Checkbox, Badge, Banner, InlineGrid, HorizontalStack, Link, VerticalStack, Tooltip, Popover, ActionMenu, OptionList, ActionList } from "@shopify/polaris"; +import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons"; import { useEffect, useReducer, useRef, useState } from "react"; import { default as observeApi } from "../api"; import { default as testingApi } from "../../testing/api"; @@ -12,10 +12,12 @@ import transform from "../../testing/transform"; import LocalStore from "../../../../main/LocalStorageStore"; import AdvancedSettingsComponent from "./component/AdvancedSettingsComponent"; -import {produce} from "immer" +import { produce } from "immer" +import RunTestSuites from "./RunTestSuites"; +import RunTestConfiguration from "./RunTestConfiguration"; -function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData }) { +function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testMode, testIdConfig }) { const initialState = { categories: [], @@ -38,7 +40,6 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu sendSlackAlert: false, cleanUpTestingResources: false } - const navigate = useNavigate() const [testRun, setTestRun] = useState({ @@ -48,6 +49,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const [loading, setLoading] = useState(true) const [testRolesArr, setTestRolesArr] = useState([]) const [active, setActive] = useState(runTestFromOutside || false); + const [parentActivator, setParentActivator] = useState(false) const runTestRef = useRef(null); const [searchValue, setSearchValue] = useState('') @@ -58,23 +60,36 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu title: 'Author', filterKey: 'author', options: [ - {value: 'akto', label: 'Akto default'}, - {value: 'custom', label: 'Custom tests'} + { value: 'akto', label: 'Akto default' }, + { value: 'custom', label: 'Custom tests' } ] } ] - const initialArr = ['akto','custom'] + const initialArr = ['akto', 'custom'] const [optionsSelected, setOptionsSelected] = useState(initialArr) const [slackIntegrated, setSlackIntegrated] = useState(false) - const emptyCondition = {data: {key: '', value: ''}, operator: {'type': 'ADD_HEADER'}} + const emptyCondition = { data: { key: '', value: '' }, operator: { 'type': 'ADD_HEADER' } } const [conditions, dispatchConditions] = useReducer(produce((draft, action) => func.conditionsReducer(draft, action)), [emptyCondition]); const localCategoryMap = LocalStore.getState().categoryMap const localSubCategoryMap = LocalStore.getState().subCategoryMap + useEffect(() => { + if(preActivator){ + setParentActivator(true); + if(testMode === "testSuite"){ + testSuiteToggle(true) + } + else if(testMode === "individualTest"){ + toggleRunTest() + } + } + + }, [testMode]) + function nameSuffixes(tests) { return Object.entries(tests) .filter(category => { @@ -89,9 +104,9 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } const convertToLowerCaseWithUnderscores = (inputString) => { - if(!inputString) + if (!inputString) return "" - return inputString?.toLowerCase()?.replace(/\s+/g, '_') + return inputString?.toLowerCase()?.replace(/\s+/g, '_'); } const apiCollectionName = collectionsMap[apiCollectionId] @@ -108,7 +123,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu subCategories: [], testSourceConfigs: [] } - if(!useLocalSubCategoryData) { + if (!useLocalSubCategoryData) { metaDataObj = await transform.getAllSubcategoriesData(true, "runTests") } else { metaDataObj = { @@ -126,12 +141,12 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu "value": testRole.hexId } }) - testRoles.unshift({"label": "No test role selected", "value": ""}) + testRoles.unshift({ "label": "No test role selected", "value": "" }) setTestRolesArr(testRoles) const { selectedCategory, mapCategoryToSubcategory } = populateMapCategoryToSubcategory(businessLogicSubcategories) // Store all tests const processMapCategoryToSubcategory = {} - if(Object.keys(businessLogicSubcategories).length > 0){ + if (Object.keys(businessLogicSubcategories).length > 0) { Object.keys(mapCategoryToSubcategory).map(category => { processMapCategoryToSubcategory[category] = [...mapCategoryToSubcategory[category]["all"]] }) @@ -154,7 +169,6 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const authMechanismDataResponse = await testingApi.fetchAuthMechanismData() if (authMechanismDataResponse.authMechanism) authMechanismPresent = true - setTestRun(prev => ({ ...prev, categories: categories, @@ -163,20 +177,19 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu testName: testName, authMechanismPresent: authMechanismPresent })) - setLoading(false) } useEffect(() => { fetchData() - if(runTestFromOutside === true){ + if (runTestFromOutside === true) { setActive(true) } - }, [apiCollectionName,runTestFromOutside]) + }, [apiCollectionName, runTestFromOutside]) const toggleRunTest = () => { setActive(prev => !prev) - if(active){ + if (active) { closeRunTest() } } @@ -203,6 +216,37 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } } + const [testPopover, setTestPopover] = useState(false); + + const [testSuite, testSuiteToggle] = useState(false); + + const activators = ( + setTestPopover(!testPopover)} primary disclosure>Run Tests + } + onClose={() => setTestPopover(false)} + > + testSuiteToggle(true) + }, + { + content: "Individual tests", + onAction: toggleRunTest, + active: disabled || testRun.selectedCategory.length === 0, + } + ]} + > + + + + ) + const activator = (
@@ -214,14 +258,14 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu let ans = false; sectionsForFilters.forEach((filter) => { const filterKey = filter.filterKey; - if(filterKey === 'author'){ - if(useFilterArr.includes('custom')){ + if (filterKey === 'author') { + if (useFilterArr.includes('custom')) { ans = useFilterArr.includes(test[filterKey].toLowerCase()) || test[filterKey].toLowerCase() !== 'akto' - }else{ + } else { ans = useFilterArr.length > 0 && useFilterArr.includes(test[filterKey].toLowerCase()) } } - else if(useFilterArr.includes(test[filterKey].toLowerCase())){ + else if (useFilterArr.includes(test[filterKey].toLowerCase())) { ans = true } }) @@ -239,11 +283,11 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu let localCopy = JSON.parse(JSON.stringify(testRun.tests)) localCopy[testRun.selectedCategory] = localCopy[testRun.selectedCategory].map(curTest => curTest.label === test.label ? - { - ...curTest, - selected: !curTest.selected - } : curTest - ) + { + ...curTest, + selected: !curTest.selected + } : curTest + ) const testName = convertToLowerCaseWithUnderscores(apiCollectionName) + "_" + nameSuffixes(localCopy).join("_") setTestRun(prev => ({ ...prev, @@ -256,7 +300,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu selected: !curTest.selected } : curTest) }, - testName:testName + testName: testName })) } @@ -288,17 +332,17 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu selected += 1 }) - return ([( + return [(
{ setTestRun(prev => ({ ...prev, selectedCategory: category.name })); resetSearchFunc(); setOptionsSelected(initialArr)}}> + onClick={() => { setTestRun(prev => ({ ...prev, selectedCategory: category.name })); resetSearchFunc(); setOptionsSelected(initialArr) }}>
{category.displayName} {selected} out of {total} selected
{selected > 0 && }
- )]) + )]; } else { return [] } @@ -309,9 +353,9 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu testRows = filteredTests.map(test => { const isCustom = test?.author !== "AKTO" const label = ( - + {test.label} - {isCustom ? Custom : null} + {isCustom ? Custom : null} ) return ([( @@ -354,7 +398,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu return abc }, []) - const testRunTimeOptions = [ ...runTimeMinutes, ...runTimeHours] + const testRunTimeOptions = [...runTimeMinutes, ...runTimeHours] const runTypeOptions = [{ label: "Daily", value: "Daily" }, { label: "Continuously", value: "Continuously" }, { label: "Now", value: "Now" }] @@ -383,7 +427,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } else { return
Run today at {testRun.hourlyLabel}
- + } } } @@ -400,12 +444,12 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu func.setToast(true, false, "All tests unselected") } - function checkRemoveAll(){ - const tests = {...testRun.tests} + function checkRemoveAll() { + const tests = { ...testRun.tests } let totalTests = 0 - Object.keys(tests).forEach(category =>{ + Object.keys(tests).forEach(category => { tests[category].map((test) => { - if(test.selected){ + if (test.selected) { totalTests++ } }) @@ -414,12 +458,11 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } async function handleRun() { - const { startTimestamp, recurringDaily, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, cleanUpTestingResources } = testRun + const { startTimestamp, recurringDaily, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert,cleanUpTestingResources } = testRun const collectionId = parseInt(apiCollectionId) const tests = testRun.tests const selectedTests = [] - Object.keys(tests).forEach(category => { tests[category].forEach(test => { if (test.selected) selectedTests.push(test.value) @@ -436,28 +479,28 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } else { apiInfoKeyList = selectedResourcesForPrimaryAction.map(str => { const parts = str.split('###') - + const method = parts[0] const url = parts[1] const apiCollectionId = parseInt(parts[2], 10) return { - apiCollectionId: apiCollectionId, - method: method, - url: url + apiCollectionId: apiCollectionId, + method: method, + url: url } }) } let finalAdvancedConditions = [] - if(conditions.length > 0 && conditions[0]?.data?.key?.length > 0){ + if (conditions.length > 0 && conditions[0]?.data?.key?.length > 0) { finalAdvancedConditions = transform.prepareConditionsForTesting(conditions) } if (filtered || selectedResourcesForPrimaryAction.length > 0) { await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) } else { - await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) + await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources) } setActive(false) @@ -478,8 +521,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu return obj } - function getCurrentStatus(){ - if(!testRun || testRun?.tests === undefined || testRun?.selectedCategory === undefined || testRun.tests[testRun.selectedCategory] === undefined) + function getCurrentStatus() { + if (!testRun || testRun?.tests === undefined || testRun?.selectedCategory === undefined || testRun.tests[testRun.selectedCategory] === undefined) return false; let res = true; @@ -487,7 +530,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu for (let i = 0; i < tests.length; i++) { if (tests[i].selected === false) { res = false; - break; + break; } } return res; @@ -510,19 +553,40 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } function generateLabelForSlackIntegration() { - return - - Enable - - - Slack integration to send alerts post completion - - + return ( + + + Enable + + + Slack integration to send alerts post completion + + + ); } return (
- {activator} + {!parentActivator? activators:null} + : - {!testRun.authMechanismPresent && -
- navigate("/dashboard/testing/user-config") - }} - status="critical" - > - - - Running specialized tests like Broken Object Level Authorization, - Broken User Authentication etc, require an additional attacker - authorization token. Hence before triggering Akto tests on your apis, - you may need to specify an authorization token which can be treated as - attacker token during test run. Attacker Token can be specified - manually, as well as in automated manner. We provide multiple ways to - automate Attacker token generation. - - -
-
- } - + {!testRun.authMechanismPresent && +
+ navigate("/dashboard/testing/user-config") + }} + status="critical" + > + + + Running specialized tests like Broken Object Level Authorization, + Broken User Authentication etc, require an additional attacker + authorization token. Hence before triggering Akto tests on your apis, + you may need to specify an authorization token which can be treated as + attacker token during test run. Attacker Token can be specified + manually, as well as in automated manner. We provide multiple ways to + automate Attacker token generation. + + +
+
+ } -
- Name: -
- setTestRun(prev => ({ ...prev, testName: testName }))} - /> -
- -
-
-
-
- Test Categories -
- -
- - + Name: +
+ setTestRun(prev => ({ ...prev, testName: testName }))} />
+ +
-
-
- - toggleTestsSelection(val)} +
+
+
+ Test Categories +
+ +
+ + - Tests - - - setShowFiltersOption(!showFiltersOption)}>More filters} - onClose={() => setShowFiltersOption(false)} - active={showFiltersOption} - > - - {setOptionsSelected(x); selectOnlyFilteredTests(x);}} - allowMultiple - sections={sectionsForFilters} - selected={optionsSelected} - /> - - - {showSearch ? : null} - -
- -
- +
-
-
- - - - { - let recurringDaily = false - let continuousTesting = false - - if(runType === 'Continuously'){ - continuousTesting = true; - }else if(runType === 'Daily'){ - recurringDaily = true; - } - setTestRun(prev => ({ - ...prev, - recurringDaily, - continuousTesting, - runTypeLabel: runType.label - })) - }} /> - - { - let startTimestamp - - if (hour === "Now") startTimestamp = func.timeNow() - else { - const dayStart = +func.dayStart(+new Date()); - startTimestamp = parseInt(dayStart / 1000) + parseInt(hour) * 60 * 60 - } - - const hourlyTime = getLabel(hourlyTimes, hour) - setTestRun(prev => ({ - ...prev, - startTimestamp, - hourlyLabel: hourlyTime ? hourlyTime.label : "" - })) - }} /> - { - let testRunTime - if (timeInSeconds === "Till complete") testRunTime = -1 - else testRunTime = timeInSeconds - - const testRunTimeOption = getLabel(testRunTimeOptions, timeInSeconds) - - setTestRun(prev => ({ - ...prev, - testRunTime: testRunTime, - testRunTimeLabel: testRunTimeOption.label - })) - }} /> - - -
- Select Test Role - { - let testRole - if (!(requests === "No test role selected")){testRole = requests} - const testRoleOption = getLabel(testRolesArr, requests) - - setTestRun(prev => ({ - ...prev, - testRoleId: testRole, - testRoleLabel: testRoleOption.label - })) - }} /> +
+
+ + toggleTestsSelection(val)} + /> + Tests + + + setShowFiltersOption(!showFiltersOption)} + plain>More filters} + onClose={() => setShowFiltersOption(false)} + active={showFiltersOption} + > + + { setOptionsSelected(x); selectOnlyFilteredTests(x); }} + allowMultiple + sections={sectionsForFilters} + selected={optionsSelected} + /> + + + {showSearch ? : null} + +
+ +
+ +
- -
- Max Concurrent Requests - { - let maxConcurrentRequests - if (requests === "Default") maxConcurrentRequests = -1 - else maxConcurrentRequests = requests - - const maxConcurrentRequestsOption = getLabel(maxConcurrentRequestsOptions, requests) - - setTestRun(prev => ({ - ...prev, - maxConcurrentRequests: maxConcurrentRequests, - maxConcurrentRequestsLabel: maxConcurrentRequestsOption.label - })) - }} />
- - - - setTestRun(prev => ({ ...prev, sendSlackAlert: !prev.sendSlackAlert}))} - disabled={!slackIntegrated} + - - setTestRun(prev => ({ ...prev, hasOverriddenTestAppUrl: !prev.hasOverriddenTestAppUrl }))} - /> - {testRun.hasOverriddenTestAppUrl && -
- setTestRun(prev => ({ ...prev, overriddenTestAppUrl: overriddenTestAppUrl }))} - /> -
- } -
- - {window.ACTIVE_ACCOUNT === 1723492815 && - setTestRun(prev => ({ ...prev, cleanUpTestingResources: !prev.cleanUpTestingResources }))} - /> - } - - - + }
- ) + ); } export default RunTest \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx new file mode 100644 index 0000000000..e5643b4a8c --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { VerticalStack, HorizontalGrid, Checkbox, TextField, Text } from '@shopify/polaris'; +import Dropdown from "../../../components/layouts/Dropdown"; + +const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration }) => { + return ( + + + { + let recurringDaily = false; + let continuousTesting = false; + + if (runType === 'Continuously') { + continuousTesting = true; + } else if (runType === 'Daily') { + recurringDaily = true; + } + setTestRun(prev => ({ + ...prev, + recurringDaily, + continuousTesting, + runTypeLabel: runType.label + })); + }} /> + { + let startTimestamp; + + if (hour === "Now") startTimestamp = func.timeNow(); + else { + const dayStart = +func.dayStart(+new Date()); + startTimestamp = parseInt(dayStart / 1000) + parseInt(hour) * 60 * 60; + } + + const hourlyTime = getLabel(hourlyTimes, hour); + setTestRun(prev => ({ + ...prev, + startTimestamp, + hourlyLabel: hourlyTime ? hourlyTime.label : "" + })); + }} /> + { + let testRunTime; + if (timeInSeconds === "Till complete") testRunTime = -1; + else testRunTime = timeInSeconds; + + const testRunTimeOption = getLabel(testRunTimeOptions, timeInSeconds); + + setTestRun(prev => ({ + ...prev, + testRunTime: testRunTime, + testRunTimeLabel: testRunTimeOption.label + })); + }} /> + + +
+ Select Test Role + { + let testRole; + if (!(requests === "No test role selected")) { testRole = requests; } + const testRoleOption = getLabel(testRolesArr, requests); + + setTestRun(prev => ({ + ...prev, + testRoleId: testRole, + testRoleLabel: testRoleOption.label + })); + }} /> +
+
+ Max Concurrent Requests + { + let maxConcurrentRequests; + if (requests === "Default") maxConcurrentRequests = -1; + else maxConcurrentRequests = requests; + + const maxConcurrentRequestsOption = getLabel(maxConcurrentRequestsOptions, requests); + + setTestRun(prev => ({ + ...prev, + maxConcurrentRequests: maxConcurrentRequests, + maxConcurrentRequestsLabel: maxConcurrentRequestsOption.label + })); + }} /> +
+
+ setTestRun(prev => ({ ...prev, sendSlackAlert: !prev.sendSlackAlert }))} + disabled={!slackIntegrated} + /> + + setTestRun(prev => ({ ...prev, hasOverriddenTestAppUrl: !prev.hasOverriddenTestAppUrl }))} + /> + {testRun.hasOverriddenTestAppUrl && +
+ setTestRun(prev => ({ ...prev, overriddenTestAppUrl: overriddenTestAppUrl }))} + /> +
+ } +
+
+ ); +}; + +export default RunTestConfiguration; \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx new file mode 100644 index 0000000000..12ad47b0e2 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -0,0 +1,325 @@ +import { VerticalStack, Modal, TextField, Button, Text, HorizontalStack, Collapsible, Badge, Pagination, TextContainer, Icon, Scrollable, Checkbox, Box, Tooltip, Card, MediaCard } from "@shopify/polaris"; +import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons"; +import { useEffect, useRef, useState } from "react"; +import data from "./dummyData.json" +import "./run_test_suites.css" +import RunTestConfiguration from "./RunTestConfiguration"; +import AdvancedSettingsComponent from "./component/AdvancedSettingsComponent"; + + + +function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration, dispatchConditions, conditions, handleRun, handleRemoveAll, convertToLowerCaseWithUnderscores, apiCollectionName, testIdConfig }) { + + const [owaspTop10, owaspTop10Toggle] = useState(true); + const [openConfigurations, openConfigurationsToggle] = useState(false); + const [testSuite, setTestSuite] = useState(true); + const [selectedTestSuites, setSelectedTestSuites] = useState([]); + + const owaspTop10List = { + "Broken Object Level Authorization": ["BOLA"], + "Broken Authentication": ["NO_AUTH"], + "Broken Object Property Level Authorization": ["EDE","MA"], + "Unrestricted Resource Consumption": ["RL"], + "Broken Function Level Authorization": ["BFLA"], + "Unrestricted Access to Sensitive Business Flows": ["INPUT"], + "Server Side Request Forgery": ['SSRF'], + "Security Misconfiguration": ["SM", "UHM", "VEM", "MHH", "SVD", "CORS","ILM"], + "Improper Inventory Management": ["IAM", "IIM"], + "Unsafe Consumption of APIs": ["COMMAND_INJECTION", "INJ", "CRLF", "SSTI", "LFI", "XSS", "INJECT"] + } + + + useEffect(() => { + + if (testIdConfig?.testingRunConfig?.testSubCategoryList?.length > 0) { + const testSubCategoryList = [...testIdConfig.testingRunConfig.testSubCategoryList]; + + const updatedTests = { ...testRun.tests }; + + // Reset all test selections + Object.keys(updatedTests).forEach(category => { + updatedTests[category] = updatedTests[category].map(test => ({ ...test, selected: false })); + }); + + const testSubCategorySet = new Set(testSubCategoryList); + + Object.keys(updatedTests).forEach(category => { + updatedTests[category] = updatedTests[category].map(test => ({ + ...test, + selected: testSubCategorySet.has(test.value), + })); + }); + + // Efficient deep equality check + function areObjectArraysEqual(obj1, obj2) { + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + if (keys1.length !== keys2.length) return false; + + const setKeys1 = new Set(keys1); + const setKeys2 = new Set(keys2); + if (setKeys1.size !== setKeys2.size || [...setKeys1].some(key => !setKeys2.has(key))) { + return false; + } + + for (let key of keys1) { + const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); // O(m log m) + const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); // O(m log m) + + if (arr1.length !== arr2.length || arr1.some((item, index) => item !== arr2[index])) { + return false; + } + } + + return true; + } + + + // Update state only if there is a change + if (!areObjectArraysEqual(updatedTests, testRun.tests)) { + setTestRun(prev => ({ + ...prev, + tests: updatedTests, + })); + } + } + }, [testRun]); + + + function handleTestSuiteSelection(key, data) { + let updatedSelectedTestSuites; + if (!selectedTestSuites.includes(key.replace(/\s+/g, '_'))) { + updatedSelectedTestSuites = [...selectedTestSuites, key.replace(/\s+/g, '_')]; + } else { + updatedSelectedTestSuites = selectedTestSuites.filter(item => item !== key.replace(/\s+/g, '_')); + } + + setSelectedTestSuites(updatedSelectedTestSuites); + + setTestRun(prev => { + const updatedTests = { ...prev.tests }; + data.forEach(category => { + if (updatedTests[category]) { + updatedTests[category] = updatedTests[category].map(test => ({ + ...test, + selected: !test.selected + })); + } + }); + return { + ...prev, + tests: updatedTests, + testName: convertToLowerCaseWithUnderscores(apiCollectionName) + "_" + updatedSelectedTestSuites.join("_") + }; + }); + } + + function countTestSuitesTests(data) { + let count = 0; + const test = { ...testRun.tests }; + data.forEach(category => { + if (test[category]) { + count += test[category].length; + } + }); + return count; + } + + function countAllSelectedTests() { + let count = 0; + const test = { ...testRun.tests }; + Object.keys(test).forEach(category => { + count += test[category].filter(test => test.selected).length; + }); + return count; + } + + function checkedSelected(data) { + let hasNonEmptyCategory = false; + + for (const category of data) { + if (testRun.tests[category] && testRun.tests[category].length > 0) { + hasNonEmptyCategory = true; + if (!testRun.tests[category].every(test => test.selected)) { + return false; + } + } + } + + return hasNonEmptyCategory; + } + + function checkDisableTestSuite(data){ + for (const category of data) { + if (testRun.tests[category] && testRun.tests[category].length > 0) { + return false; + } + } + return true; + } + + function renderAktoTestSuites(data) { + return ( +
+ + +
+ + + + + + + + + + + + + + + +
+
+ + + + {data?.key} + + } + helpText={`${countTestSuitesTests(data?.value)} tests`} + onChange={() => { handleTestSuiteSelection(data?.key, data?.value) }} + checked={checkedSelected(data?.value)} + disabled={checkDisableTestSuite(data?.value)} + /> + + +
+
+
+
+ ); + } + + return ( +
+ testSuiteModalToggle(false)} + title="Configure Test" + primaryAction={{ + content: 'Run test', + onAction: () => handleRun(), + }} + secondaryActions={[ + { + content: `${countAllSelectedTests()} tests selected`, + disabled: true, + plain: true, + }, + { + content: 'Cancel', + onAction: () => testSuiteModalToggle(false), + }, + + ]} + large + footer={openConfigurations ? : } + > + {!openConfigurations && + + + + +
+ } + placeholder="Search" + /> +
+ + + + + + +
+ + +
+ +
+
+ +
+ +
+ + {Object.entries(owaspTop10List).map(([key, value], index) => ( + renderAktoTestSuites({ key, value }) + ))} + +
+ +
+
+
+
+
+
+
+ } + {openConfigurations && + + + } +
+
+ ) +} + +export default RunTestSuites \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/component/AdvancedSettingsComponent.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/component/AdvancedSettingsComponent.jsx index 3b5ab7dc0f..39eedb12de 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/component/AdvancedSettingsComponent.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/component/AdvancedSettingsComponent.jsx @@ -1,11 +1,11 @@ import React, { useState } from 'react' import { Button, HorizontalGrid, HorizontalStack, TextField, VerticalStack } from '@shopify/polaris'; -import {DeleteMinor} from "@shopify/polaris-icons" +import { DeleteMinor } from "@shopify/polaris-icons" import Dropdown from '../../../../components/layouts/Dropdown'; -function AdvancedSettingsComponent({dispatchConditions, conditions, hideButton}) { - const emptyCondition = {data: {key: '', value: ''}, operator: {'type': 'ADD_HEADER'}} +function AdvancedSettingsComponent({ dispatchConditions, conditions, hideButton }) { + const emptyCondition = { data: { key: '', value: '' }, operator: { 'type': 'ADD_HEADER' } } const operatorTypeOptions = [ { value: "ADD_HEADER", label: "Add Header" }, @@ -17,23 +17,23 @@ function AdvancedSettingsComponent({dispatchConditions, conditions, hideButton}) ]; const handleTypeSelected = (type, index) => { - dispatchConditions({ type: "update", index: index, key: 'operator', obj:{"type": type} }) + dispatchConditions({ type: "update", index: index, key: 'operator', obj: { "type": type } }) } const handleValueChange = (index, value) => { - dispatchConditions({type: 'update', index: index, key: "data", obj: {"value":value } }) + dispatchConditions({ type: 'update', index: index, key: "data", obj: { "value": value } }) } const handleKeyChange = (index, value) => { - dispatchConditions({type: 'update', index: index, key: "data", obj: {"key":value } }) + dispatchConditions({ type: 'update', index: index, key: "data", obj: { "key": value } }) } const handleDelete = (index) => { - dispatchConditions({type:"delete", index: index}) + dispatchConditions({ type: "delete", index: index }) }; const handleAddField = () => { - dispatchConditions({type:"add", obj: emptyCondition}) + dispatchConditions({ type: "add", obj: emptyCondition }) }; const [showAdvancedSettings, setShowAdvancedSettings] = useState(hideButton ? hideButton : false) @@ -43,49 +43,49 @@ function AdvancedSettingsComponent({dispatchConditions, conditions, hideButton}) return ( - {hideButton ? null : } - {showAdvancedSettings ? - - {conditions.map((condition, index) => { - return( - -
- - - getLabel(condition?.operator?.type)} - selected={(type) => handleTypeSelected(type,index)} - /> - -
-
- - handleKeyChange(index,newValue)} - /> - handleValueChange(index,newValue)} - disabled={condition?.operator?.type.toLowerCase().includes("delete")} - /> - -
- -
-
: null - } + {hideButton ? null :
} + {showAdvancedSettings ? + + {conditions.map((condition, index) => { + return ( + +
+ + + getLabel(condition?.operator?.type)} + selected={(type) => handleTypeSelected(type, index)} + /> + +
+
+ + handleKeyChange(index,newValue)} + /> + handleValueChange(index,newValue)} + disabled={condition?.operator?.type.toLowerCase().includes("delete")} + /> + +
+ +
+
: null + }
) } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json new file mode 100644 index 0000000000..d4d2899035 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json @@ -0,0 +1,63 @@ +{ + "byAkto": { + "name": "By Akto", + "plans": [ + { "name": "Free Plan", "testCount": "200" }, + { "name": "PII", "testCount": "200" }, + { "name": "Intrusive", "testCount": "200" }, + { "name": "Advanced Security", "testCount": "300" }, + { "name": "Enterprise", "testCount": "500" }, + { "name": "API Gateway Monitoring", "testCount": "250" }, + { "name": "Zero Trust Security", "testCount": "350" }, + { "name": "Cloud Protection", "testCount": "400" }, + { "name": "On-Premises Security", "testCount": "150" }, + { "name": "Dynamic Scanning", "testCount": "180" } + ] + }, + "owaspTop10": { + "name":"OWASP top 10", + "plans": [ + { "name": "Broken Object Level Authentication", "severity": "High", "testCount": "150" }, + { "name": "Broken Authentication", "severity": "Medium", "testCount": "120" }, + { "name": "Sensitive Data Exposure", "severity": "High", "testCount": "180" }, + { "name": "XML External Entities (XXE)", "severity": "Medium", "testCount": "130" }, + { "name": "Security Misconfiguration", "severity": "Low", "testCount": "80" }, + { "name": "Cross-Site Scripting (XSS)", "severity": "High", "testCount": "160" }, + { "name": "Insecure Deserialization", "severity": "High", "testCount": "140" }, + { "name": "Using Components with Known Vulnerabilities", "severity": "Medium", "testCount": "110" }, + { "name": "Insufficient Logging & Monitoring", "severity": "Low", "testCount": "90" }, + { "name": "Broken Access Control", "severity": "Critical", "testCount": "200" } + ] + }, + "compliance": { + "name":"Compliance", + "plans": [ + { "name": "HIPAA", "region": "US", "testCount": "100" }, + { "name": "SOC II", "region": "Global", "testCount": "90" }, + { "name": "GDPR", "region": "EU", "testCount": "110" }, + { "name": "CCPA", "region": "US", "testCount": "95" }, + { "name": "ISO 27001", "region": "Global", "testCount": "120" }, + { "name": "PCI DSS", "region": "Global", "testCount": "115" }, + { "name": "NIST 800-53", "region": "US", "testCount": "105" }, + { "name": "FISMA", "region": "US", "testCount": "85" }, + { "name": "FedRAMP", "region": "US", "testCount": "100" }, + { "name": "SOX", "region": "Global", "testCount": "90" } + ] + }, + "custom": { + "name":"Custom", + "plans": [ + { "name": "Custom Rule Engine", "description": "Define and manage custom rules for security testing", "testCount": "50" }, + { "name": "Real-time Alerts", "description": "Get notified about vulnerabilities in real time", "testCount": "70" }, + { "name": "Detailed Reporting", "description": "Generate comprehensive security reports", "testCount": "40" }, + { "name": "Integration Support", "description": "Integrate with CI/CD tools and third-party platforms", "testCount": "60" }, + { "name": "User Behavior Analytics", "description": "Analyze patterns in user behavior for security insights", "testCount": "30" }, + { "name": "Threat Intelligence", "description": "Get data on emerging threats", "testCount": "80" }, + { "name": "Access Control Testing", "description": "Test for access control vulnerabilities", "testCount": "100" }, + { "name": "Dynamic Security Policies", "description": "Adapt policies dynamically for better security", "testCount": "45" }, + { "name": "Penetration Testing Support", "description": "Simulate real-world attacks", "testCount": "65" }, + { "name": "API Schema Validation", "description": "Validate API schemas for security compliance", "testCount": "75" } + ] + } + } + \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css new file mode 100644 index 0000000000..b6b5072d62 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css @@ -0,0 +1,35 @@ +.testSuiteCard .Polaris-Choice.Polaris-Checkbox__ChoiceLabel { + max-width: 100% !important; +} + +.testSuiteCard .Polaris-Choice__Label { + overflow: hidden !important; +} + +.createTestSuiteBox { + background: linear-gradient(90deg, #FFFFFF 0%, #F5EEFF 100%) !important; + border: 1px solid; + /* border-image-source: linear-gradient(90deg, #7F1ED9 0%, #2F55D3 27.5%, #065A3D 49.5%, #052D38 71%, #6200EA 100%); */ + border-radius: var(--p-border-radius-200) !important; + border-color: #6200EA; +} + + + +.Polaris-Modal-Dialog__Modal.Polaris-Modal-Dialog--sizeLarge > .Polaris-InlineStack > .Polaris-Box > .Polaris-InlineStack > .Polaris-InlineStack .Polaris-Button--disabled > span { + font-size: 12px !important; + font-weight: 400 !important; +} + +.testSuiteDisclosureButton svg { + fill: #5C5F62 !important; +} + +.testSuiteHorizontalScroll .Polaris-Scrollable.Polaris-Scrollable--vertical.Polaris-Scrollable--horizontal{ + scrollbar-width: none; +} + +.testSuiteCard .Polaris-VerticalStack{ + box-shadow: 0px 1px 2px 0px #00000026, 0px 0px 5px 0px #0000000D; + border-radius: 0.5rem; +} \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 28e8552157..7cbb4f76df 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -25,7 +25,8 @@ import { ReportMinor, RefreshMajor, CustomersMinor, - EditMajor + EditMajor, + PlusMinor } from '@shopify/polaris-icons'; import api from "../api"; import func from '@/util/func'; @@ -45,9 +46,11 @@ import { useSearchParams } from "react-router-dom"; import TestRunResultPage from "../TestRunResultPage/TestRunResultPage"; import { usePolling } from "../../../../main/PollingProvider"; import LocalStore from "../../../../main/LocalStorageStore"; -import {produce} from "immer" +import { produce } from "immer" import AdvancedSettingsComponent from "../../observe/api_collections/component/AdvancedSettingsComponent"; import GithubServerTable from "../../../components/tables/GithubServerTable"; +import RunTest from '../../observe/api_collections/RunTest'; +import { filter } from 'lodash'; let sortOptions = [ { label: 'Severity', value: 'severity asc', directionLabel: 'Highest severity', sortKey: 'total_severity', columnIndex: 3 }, @@ -67,9 +70,9 @@ let filters = [ label: 'Severity', title: 'Severity', choices: [ - {label: 'High', value: 'HIGH'}, - {label: 'Medium', value: 'MEDIUM'}, - {label: 'Low', value: 'LOW'} + { label: 'High', value: 'HIGH' }, + { label: 'Medium', value: 'MEDIUM' }, + { label: 'Low', value: 'LOW' } ], }, { @@ -77,11 +80,11 @@ let filters = [ label: 'Method', title: 'Method', choices: [ - {label: 'Get', value: 'GET'}, - {label: 'Post', value: 'POST'}, - {label: 'Put', value: 'PUT'}, - {label: 'Patch', value: 'PATCH'}, - {label: 'Delete', value: 'DELETE'} + { label: 'Get', value: 'GET' }, + { label: 'Post', value: 'POST' }, + { label: 'Put', value: 'PUT' }, + { label: 'Patch', value: 'PATCH' }, + { label: 'Delete', value: 'DELETE' } ], }, { @@ -107,25 +110,26 @@ let filters = [ function SingleTestRunPage() { const [testRunResultsText, setTestRunResultsText] = useState({ vulnerable: [], no_vulnerability_found: [], skipped: [], need_configurations: [], ignored_issues: [] }) - const [ selectedTestRun, setSelectedTestRun ] = useState({}); + const [selectedTestRun, setSelectedTestRun] = useState({}); const subCategoryFromSourceConfigMap = PersistStore(state => state.subCategoryFromSourceConfigMap); const subCategoryMap = LocalStore(state => state.subCategoryMap); - const params= useParams() + const params = useParams() const [loading, setLoading] = useState(false); - const [tempLoading , setTempLoading] = useState({vulnerable: false, no_vulnerability_found: false, skipped: false, running: false,need_configurations:false,ignored_issues:false}) + const [tempLoading, setTempLoading] = useState({ vulnerable: false, no_vulnerability_found: false, skipped: false, running: false, need_configurations: false, ignored_issues: false }) const [selectedTab, setSelectedTab] = useState("vulnerable") const [selected, setSelected] = useState(0) - const [workflowTest, setWorkflowTest ] = useState(false); + const [workflowTest, setWorkflowTest] = useState(false); const [secondaryPopover, setSecondaryPopover] = useState(false) - const setErrorsObject = TestingStore((state) => state.setErrorsObject) + const setErrorsObject = TestingStore((state) => state.setErrorsObject) const currentTestingRuns = [] const [updateTable, setUpdateTable] = useState("") const [testRunResultsCount, setTestRunResultsCount] = useState({}) + const [testMode, setTestMode] = useState("") - const initialTestingObj = {testsInitiated: 0,testsInsertedInDb: 0,testingRunId: -1} + const initialTestingObj = { testsInitiated: 0, testsInsertedInDb: 0, testingRunId: -1 } const [currentTestObj, setCurrentTestObj] = useState(initialTestingObj) const [missingConfigs, setMissingConfigs] = useState([]) - const [showEditableSettings, setShowEditableSettings] = useState(false) ; + const [showEditableSettings, setShowEditableSettings] = useState(false); const [testingRunConfigSettings, setTestingRunConfigSettings] = useState([]) const [testingRunConfigId, setTestingRunConfigId] = useState(-1) const apiCollectionMap = PersistStore(state => state.collectionsMap); @@ -165,7 +169,7 @@ function SingleTestRunPage() { return func.convertToDisambiguateLabel(value, func.toSentenceCase, 2) case "collectionIds": case "apiCollectionId": - return func.convertToDisambiguateLabelObj(value, apiCollectionMap, 2) + return func.convertToDisambiguateLabelObj(value, apiCollectionMap, 2) case 'categoryFilter': case 'testFilter': return func.convertToDisambiguateLabelObj(value, null, 2) @@ -177,28 +181,28 @@ function SingleTestRunPage() { const tableTabsOrder = [ "vulnerable", "need_configurations", - "skipped", + "skipped", "no_vulnerability_found", "domain_unreachable", "ignored_issues" ] - function fillTempData(data, key){ + function fillTempData(data, key) { setTestRunResultsText((prev) => { prev[key] = data; - return {...prev}; + return { ...prev }; }) } - async function setSummary(summary, initialCall=false){ + async function setSummary(summary, initialCall = false) { setTempLoading((prev) => { prev.running = false; return prev; }); clearInterval(refreshId.current); setSelectedTestRun((prev) => { - let tmp = {...summary}; - if(tmp === null || tmp?.countIssues === null || tmp?.countIssues === undefined){ + let tmp = { ...summary }; + if (tmp === null || tmp?.countIssues === null || tmp?.countIssues === undefined) { tmp.countIssues = { "HIGH": 0, "MEDIUM": 0, @@ -206,18 +210,28 @@ function SingleTestRunPage() { } } tmp.countIssues = transform.prepareCountIssues(tmp.countIssues); - prev = {...prev, ...transform.prepareDataFromSummary(tmp, prev.testRunState)} + prev = { ...prev, ...transform.prepareDataFromSummary(tmp, prev.testRunState) } - return {...prev}; + return { ...prev }; }); setCurrentSummary(summary); - if(!initialCall){ + if (!initialCall) { setUpdateTable(Date.now().toString()) } } + const [filteredEndpoints, setFilteredEndpoints] = useState([]) + useEffect(() => { setUpdateTable(Date.now().toString()) + if (testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type === "COLLECTION_WISE") { + api.fetchCollectionWiseApiEndpoints(testingRunResultSummariesObj?.testingRun?.testingEndpoints.apiCollectionId).then((res) => { + setFilteredEndpoints([...res?.listOfEndpointsInCollection]); + }) + } + else if(testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type === "CUSTOM"){ + setFilteredEndpoints([...testingRunResultSummariesObj?.testingRun?.testingEndpoints?.apisList]); + } }, [testingRunResultSummariesObj]) const fetchTestingRunResultSummaries = async () => { @@ -230,17 +244,17 @@ function SingleTestRunPage() { }) const timeNow = func.timeNow() const defaultIgnoreTime = LocalStore.getState().defaultIgnoreSummaryTime - tempTestingRunResultSummaries.sort((a,b) => { - const isAWithinTimeAndRunning = (timeNow - defaultIgnoreTime <= a.startTimestamp) && a.state === 'RUNNING'; - const isBWithinTimeAndRunning = (timeNow - defaultIgnoreTime <= b.startTimestamp) && b.state === 'RUNNING'; - - if (isAWithinTimeAndRunning && isBWithinTimeAndRunning) { - return b.startTimestamp - a.startTimestamp; - } - if (isAWithinTimeAndRunning) return -1; - if (isBWithinTimeAndRunning) return 1; + tempTestingRunResultSummaries.sort((a, b) => { + const isAWithinTimeAndRunning = (timeNow - defaultIgnoreTime <= a.startTimestamp) && a.state === 'RUNNING'; + const isBWithinTimeAndRunning = (timeNow - defaultIgnoreTime <= b.startTimestamp) && b.state === 'RUNNING'; + + if (isAWithinTimeAndRunning && isBWithinTimeAndRunning) { return b.startTimestamp - a.startTimestamp; - }) + } + if (isAWithinTimeAndRunning) return -1; + if (isBWithinTimeAndRunning) return 1; + return b.startTimestamp - a.startTimestamp; + }) if (tempTestingRunResultSummaries && tempTestingRunResultSummaries.length > 0) { setSummary(tempTestingRunResultSummaries[0], true) } @@ -252,21 +266,21 @@ function SingleTestRunPage() { let totalIgnoredIssuesCount = 0 let issuesList = [] const { testingRun, workflowTest, testingRunType } = testingRunResultSummariesObj - if(testingRun === undefined){ - return {value: [], total: 0} + if (testingRun === undefined) { + return { value: [], total: 0 } } - if(testingRun.testIdConfig === 1){ + if (testingRun.testIdConfig === 1) { setWorkflowTest(workflowTest); } let cicd = testingRunType === "CI_CD"; - const localSelectedTestRun = transform.prepareTestRun(testingRun, currentSummary , cicd, false); + const localSelectedTestRun = transform.prepareTestRun(testingRun, currentSummary, cicd, false); setTestingRunConfigSettings(testingRun.testingRunConfig?.configsAdvancedSettings || []) setTestingRunConfigId(testingRun.testingRunConfig?.id || -1) setSelectedTestRun(localSelectedTestRun); - if(localSelectedTestRun.testingRunResultSummaryHexId) { - if(selectedTab === 'ignored_issues') { + if (localSelectedTestRun.testingRunResultSummaryHexId) { + if (selectedTab === 'ignored_issues') { let ignoredTestRunResults = [] await api.fetchIssuesByStatusAndSummaryId(localSelectedTestRun.testingRunResultSummaryHexId, ["IGNORED"], sortKey, sortOrder, skip, limit, filters).then((resp) => { const ignoredIssuesTestingResult = resp?.testingRunResultList || []; @@ -279,21 +293,21 @@ function SingleTestRunPage() { await api.fetchTestingRunResults(localSelectedTestRun.testingRunResultSummaryHexId, tableTabMap[selectedTab], sortKey, sortOrder, skip, limit, filters, queryValue).then(({ testingRunResults, issueslist, errorEnums }) => { issuesList = issueslist || [] testRunResultsRes = transform.prepareTestRunResults(hexId, testingRunResults, subCategoryMap, subCategoryFromSourceConfigMap) - if(selectedTab === 'domain_unreachable' || selectedTab === 'skipped' || selectedTab === 'need_configurations') { + if (selectedTab === 'domain_unreachable' || selectedTab === 'skipped' || selectedTab === 'need_configurations') { errorEnums['UNKNOWN_ERROR_OCCURRED'] = "OOPS! Unknown error occurred." setErrorsObject(errorEnums) setMissingConfigs(transform.getMissingConfigs(testRunResultsRes)) } }) - if(!func.deepComparison(copyFilters, filters)){ + if (!func.deepComparison(copyFilters, filters)) { setCopyFilters(filters) api.fetchTestRunResultsCount(localSelectedTestRun.testingRunResultSummaryHexId).then((testCountMap) => { testRunCountMap = testCountMap || [] - testRunCountMap['VULNERABLE'] = Math.abs(testRunCountMap['VULNERABLE']-issuesList.length) + testRunCountMap['VULNERABLE'] = Math.abs(testRunCountMap['VULNERABLE'] - issuesList.length) testRunCountMap['IGNORED_ISSUES'] = (issuesList.length || 0) let countOthers = 0; Object.keys(testCountMap).forEach((x) => { - if(x !== 'ALL'){ + if (x !== 'ALL') { countOthers += testCountMap[x] } }) @@ -303,11 +317,11 @@ function SingleTestRunPage() { setPageTotalCount(testRunCountMap[tableTabMap[selectedTab]]) }) } - + } } fillTempData(testRunResultsRes, selectedTab) - return {value: transform.getPrettifiedTestRunResults(testRunResultsRes), total: selectedTab === 'ignored_issues' ? totalIgnoredIssuesCount : testRunCountMap[tableTabMap[selectedTab]]} + return { value: transform.getPrettifiedTestRunResults(testRunResultsRes), total: selectedTab === 'ignored_issues' ? totalIgnoredIssuesCount : testRunCountMap[tableTabMap[selectedTab]] } } useEffect(() => { @@ -316,12 +330,12 @@ function SingleTestRunPage() { let result = [] let store = {} Object.values(subCategoryMap).forEach((x) => { - let superCategory = x.superCategory - if (!store[superCategory.name]) { - result.push({ "label": superCategory.displayName, "value": superCategory.name }) - store[superCategory.name] = [] - } - store[superCategory.name].push(x._name); + let superCategory = x.superCategory + if (!store[superCategory.name]) { + result.push({ "label": superCategory.displayName, "value": superCategory.name }) + store[superCategory.name] = [] + } + store[superCategory.name].push(x._name); }) filters.forEach(filter => { if (filter.key === 'categoryFilter') { @@ -330,42 +344,43 @@ function SingleTestRunPage() { }) if (resultId === null || resultId.length === 0) { let found = false; - for (var ind in currentTestingRuns) { - let obj = currentTestingRuns[ind]; - if (obj.testingRunId === hexId) { - found = true; - setCurrentTestObj(prevObj => { - if (JSON.stringify(prevObj) !== JSON.stringify(obj)) { - setUpdateTable(Date.now().toString()); - return obj; - } - return prevObj; // No state change if object is the same - }); - break; + for (var ind in currentTestingRuns) { + let obj = currentTestingRuns[ind]; + if (obj.testingRunId === hexId) { + found = true; + setCurrentTestObj(prevObj => { + if (JSON.stringify(prevObj) !== JSON.stringify(obj)) { + setUpdateTable(Date.now().toString()); + return obj; } + return prevObj; // No state change if object is the same + }); + break; } + } - if (!found) { - setCurrentTestObj(prevObj => { - if (JSON.stringify(prevObj) !== JSON.stringify(initialTestingObj)) { - return initialTestingObj; - } - return prevObj; // No state change if object is the same - }); - } + if (!found) { + setCurrentTestObj(prevObj => { + if (JSON.stringify(prevObj) !== JSON.stringify(initialTestingObj)) { + return initialTestingObj; + } + return prevObj; // No state change if object is the same + }); + } } -}, []); + }, []); -const promotedBulkActions = (selectedDataHexIds) => { - return [ - { - content: `Export ${selectedDataHexIds.length} record${selectedDataHexIds.length==1 ? '' : 's'}`, - onAction: () => { - func.downloadAsCSV((testRunResultsText[selectedTab]).filter((data) => {return selectedDataHexIds.includes(data.id)}), selectedTestRun) - }, - }, -]}; + const promotedBulkActions = (selectedDataHexIds) => { + return [ + { + content: `Export ${selectedDataHexIds.length} record${selectedDataHexIds.length == 1 ? '' : 's'}`, + onAction: () => { + func.downloadAsCSV((testRunResultsText[selectedTab]).filter((data) => { return selectedDataHexIds.includes(data.id) }), selectedTestRun) + }, + }, + ] + }; function getHeadingStatus(selectedTestRun) { @@ -387,8 +402,8 @@ const promotedBulkActions = (selectedDataHexIds) => { } } - const modifyData = (data, filters) =>{ - if(filters?.urlFilters?.length > 0){ + const modifyData = (data, filters) => { + if (filters?.urlFilters?.length > 0) { let filteredData = data.map(element => { let filteredUrls = element.urls.filter(obj => filters.urlFilters.includes(obj.url)) return { @@ -399,36 +414,36 @@ const promotedBulkActions = (selectedDataHexIds) => { } }); return filteredData - }else{ + } else { return data } } - const baseUrl = window.location.origin+"/dashboard/testing/roles/details?system="; + const baseUrl = window.location.origin + "/dashboard/testing/roles/details?system="; const bannerComp = ( - missingConfigs.length > 0 ? -
- - - - - {`${missingConfigs.length} configuration${missingConfigs.length > 1 ? 's' : ''} missing: `} - - - - {missingConfigs.map((config) => { - return( - {config} - ) - })} + missingConfigs.length > 0 ? +
+ + + + + {`${missingConfigs.length} configuration${missingConfigs.length > 1 ? 's' : ''} missing: `} + + + + {missingConfigs.map((config) => { + return ( + {config} + ) + })} + - - -
: null +
+
: null ) - const definedTableTabs = ['Vulnerable', 'Need configurations','Skipped', 'No vulnerability found','Domain unreachable','Ignored Issues'] + const definedTableTabs = ['Vulnerable', 'Need configurations', 'Skipped', 'No vulnerability found', 'Domain unreachable', 'Ignored Issues'] const { tabsInfo } = useTable() const tableCountObj = func.getTabsCount(definedTableTabs, {}, Object.values(testRunResultsCount)) @@ -436,93 +451,103 @@ const promotedBulkActions = (selectedDataHexIds) => { const tableHeaders = transform.getHeaders(selectedTab) const handleSelectedTab = (selectedIndex) => { - setLoading(true) - setSelected(selectedIndex) - setUpdateTable("") - - sortOptions = sortOptions.map(option => { - if (selectedIndex === 0 || selectedIndex == 5) { - if (option.label === 'Severity') { - return { ...option, columnIndex: 3 } - } else if (option.label === 'Run time') { - return { ...option, columnIndex: 7 } - } - } else if (selectedIndex === 1) { - if (option.label === 'Run time') { - return { ...option, columnIndex: 6 } - } - } else if (selectedIndex === 2) { - if (option.label === 'Run time') { - return { ...option, columnIndex: 6 } - } - } else if (selectedIndex === 3) { - if (option.label === 'Run time') { - return { ...option, columnIndex: 6 } - } - } else if (selectedIndex === 4) { - if (option.label === 'Run time') { - return { ...option, columnIndex: 7 } - } + setLoading(true) + setSelected(selectedIndex) + setUpdateTable("") + + sortOptions = sortOptions.map(option => { + if (selectedIndex === 0 || selectedIndex == 5) { + if (option.label === 'Severity') { + return { ...option, columnIndex: 3 } + } else if (option.label === 'Run time') { + return { ...option, columnIndex: 7 } } - return option - }) + } else if (selectedIndex === 1) { + if (option.label === 'Run time') { + return { ...option, columnIndex: 6 } + } + } else if (selectedIndex === 2) { + if (option.label === 'Run time') { + return { ...option, columnIndex: 6 } + } + } else if (selectedIndex === 3) { + if (option.label === 'Run time') { + return { ...option, columnIndex: 6 } + } + } else if (selectedIndex === 4) { + if (option.label === 'Run time') { + return { ...option, columnIndex: 7 } + } + } + return option + }) - filters = filters.filter(filter => filter.key !== 'severityStatus') + filters = filters.filter(filter => filter.key !== 'severityStatus') + + if (selectedIndex === 0 || selectedIndex === 5) { + filters = [ + { + key: 'severityStatus', + label: 'Severity', + title: 'Severity', + choices: [ + { label: 'High', value: 'HIGH' }, + { label: 'Medium', value: 'MEDIUM' }, + { label: 'Low', value: 'LOW' } + ], + }, + ...filters + ] + } - if(selectedIndex === 0 || selectedIndex === 5) { - filters = [ - { - key: 'severityStatus', - label: 'Severity', - title: 'Severity', - choices: [ - {label: 'High', value: 'HIGH'}, - {label: 'Medium', value: 'MEDIUM'}, - {label: 'Low', value: 'LOW'} - ], - }, - ...filters - ] - } - - setTimeout(()=>{ - setLoading(false) - },200) + setTimeout(() => { + setLoading(false) + }, 200) + } + + function getCollectionId() { + if (testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type !== "COLLECTION_WISE") { + return testingRunResultSummariesObj?.testingRun.testingEndpoints?.apisList?.length == 1 ? testingRunResultSummariesObj?.testingRun.testingEndpoints?.apisList[0].apiCollectionId : undefined; + } + return testingRunResultSummariesObj?.testingRun?.apiCollectionId; } const resultTable = ( - modifyData(data,filters)} - notHighlightOnselected={true} - selected={selected} - tableTabs={tableTabs} - onSelect={handleSelectedTab} - filterStateUrl={"/dashboard/testing/" + selectedTestRun?.id + "/#" + selectedTab} - bannerComp={{ - "comp": bannerComp, - "selected": 1 - }} - callFromOutside={updateTable} - pageTotalCount={pageTotalCount} - /> + <> + {testMode.length > 0 ? : null} + modifyData(data, filters)} + notHighlightOnselected={true} + selected={selected} + tableTabs={tableTabs} + onSelect={handleSelectedTab} + filterStateUrl={"/dashboard/testing/" + selectedTestRun?.id + "/#" + selectedTab} + bannerComp={{ + "comp": bannerComp, + "selected": 1 + }} + callFromOutside={updateTable} + pageTotalCount={pageTotalCount} + /> + ) const workflowTestBuilder = ( @@ -538,79 +563,79 @@ const promotedBulkActions = (selectedDataHexIds) => { const metadataComponent = () => { - if(!selectedTestRun.metadata){ + if (!selectedTestRun.metadata) { return undefined } return ( - { - selectedTestRun.metadata ? Object.keys(selectedTestRun.metadata).map((key) => { - return ( - - {key} : {selectedTestRun.metadata[key]} - - ) - }) : "" - } - + { + selectedTestRun.metadata ? Object.keys(selectedTestRun.metadata).map((key) => { + return ( + + {key} : {selectedTestRun.metadata[key]} + + ) + }) : "" + } + ) } const progress = useMemo(() => { return currentTestObj.testsInitiated === 0 ? 0 : Math.floor((currentTestObj.testsInsertedInDb * 100) / currentTestObj.testsInitiated); -}, [currentTestObj.testingRunId]); + }, [currentTestObj.testingRunId]); -const runningTestsComp = useMemo(() => ( + const runningTestsComp = useMemo(() => ( currentTestObj.testingRunId !== -1 ? ( - - - {`Running ${currentTestObj.testsInitiated} tests`} -
- - {`${progress}%`} -
-
-
+ + + {`Running ${currentTestObj.testsInitiated} tests`} +
+ + {`${progress}%`} +
+
+
) : null -), [currentTestObj, progress]); + ), [currentTestObj, progress]); -const handleModifyConfig = async() => { - const settings = transform.prepareConditionsForTesting(conditions) - await api.modifyTestingRunConfig(testingRunConfigId, settings).then(() =>{ - func.setToast(true, false, "Modified testing run config successfully") - setShowEditableSettings(false) - }) -} + const handleModifyConfig = async () => { + const settings = transform.prepareConditionsForTesting(conditions) + await api.modifyTestingRunConfig(testingRunConfigId, settings).then(() => { + func.setToast(true, false, "Modified testing run config successfully") + setShowEditableSettings(false) + }) + } -const editableConfigsComp = ( - setShowEditableSettings(false)} - title={"Edit test configurations"} - primaryAction={{ + const editableConfigsComp = ( + setShowEditableSettings(false)} + title={"Edit test configurations"} + primaryAction={{ content: 'Save', onAction: () => handleModifyConfig() - }} + }} > - - + - - -) + /> + + + ) - const components = [ - runningTestsComp, , - metadataComponent(), loading ? : (!workflowTest ? resultTable : workflowTestBuilder), editableConfigsComp]; + const components = [ + runningTestsComp, , + metadataComponent(), loading ? : (!workflowTest ? resultTable : workflowTestBuilder), editableConfigsComp]; - const openVulnerabilityReport = async() => { + const openVulnerabilityReport = async () => { const currentPageKey = "/dashboard/testing/" + selectedTestRun?.id + "/#" + selectedTab let selectedFilters = filtersMap[currentPageKey]?.filters || []; let filtersObj = { @@ -620,7 +645,7 @@ const editableConfigsComp = ( selectedFilters.forEach((filter) => { filtersObj[filter.key] = filter.value }) - + await api.generatePDFReport(filtersObj, []).then((res) => { const responseId = res.split("=")[1]; window.open('/dashboard/testing/summary/' + responseId.split("}")[0], '_blank'); @@ -628,18 +653,18 @@ const editableConfigsComp = ( } const handleAddSettings = () => { - if(conditions.length === 0 && testingRunConfigSettings.length > 0){ + if (conditions.length === 0 && testingRunConfigSettings.length > 0) { testingRunConfigSettings.forEach((condition) => { const operatorType = condition.operatorType condition.operationsGroupList.forEach((obj) => { - const finalObj = {'data': obj, 'operator': {'type': operatorType}} - dispatchConditions({type:"add", obj: finalObj}) + const finalObj = { 'data': obj, 'operator': { 'type': operatorType } } + dispatchConditions({ type: "add", obj: finalObj }) }) }) } } - const handleRefreshTableCount = async(summaryHexId) => { + const handleRefreshTableCount = async (summaryHexId) => { await api.handleRefreshTableCount(summaryHexId).then((res) => { func.setToast(true, false, "Re-calculating issues count") setSecondaryPopover(false) @@ -647,17 +672,17 @@ const editableConfigsComp = ( } const EmptyData = () => { - return( -
+ return ( +
-
+
- + No test run data found @@ -673,53 +698,53 @@ const editableConfigsComp = ( } useEffect(() => { - if(Object.values(testRunResultsCount).length === 0) { - setAllResultsLength(Object.values(testRunResultsCount).reduce((acc, val) => acc+val, 0)) + if (Object.values(testRunResultsCount).length === 0) { + setAllResultsLength(Object.values(testRunResultsCount).reduce((acc, val) => acc + val, 0)) } }, [testRunResultsCount]) - const useComponents = (!workflowTest && allResultsLength === undefined && (selectedTestRun.run_type && selectedTestRun.run_type ==='One-time')) ? [] : components + const useComponents = (!workflowTest && allResultsLength === undefined && (selectedTestRun.run_type && selectedTestRun.run_type === 'One-time')) ? [] : components const headingComp = ( - { selectedTestRun?.icon && - + {selectedTestRun?.icon && + } - + { - selectedTestRun?.severity && + selectedTestRun?.severity && selectedTestRun.severity - .map((item) => - - - {item} - - - )} - + .map((item) => + + + {item} + + + )} + - + created by: {selectedTestRun.userEmail} - + - + {collectionsMap[selectedTestRun?.apiCollectionId]} - + - + {getHeadingStatus(selectedTestRun)} @@ -728,25 +753,44 @@ const editableConfigsComp = ( ) let moreActionsList = transform.getActions(selectedTestRun) - moreActionsList.push({title: 'Export', items: [ - { - content: 'Export vulnerability report', - icon: ReportMinor, - onAction: () => openVulnerabilityReport() - } - ]}) - moreActionsList.push({title: 'Update', items:[ - { - content: 'Edit testing config settings', - icon: EditMajor, - onAction: () => { setShowEditableSettings(true); handleAddSettings(); } - }, - { - content: 'Re-Calculate Issues Count', - icon: RefreshMajor, - onAction: () => {handleRefreshTableCount(currentSummary.hexId)} - } - ]}) + moreActionsList.push({ + title: 'Export', items: [ + { + content: 'Export vulnerability report', + icon: ReportMinor, + onAction: () => openVulnerabilityReport() + } + ] + }) + moreActionsList.push({ + title: 'Update', items: [ + { + content: 'Edit testing config settings', + icon: EditMajor, + onAction: () => { setShowEditableSettings(true); handleAddSettings(); } + }, + { + content: 'Re-Calculate Issues Count', + icon: RefreshMajor, + onAction: () => { handleRefreshTableCount(currentSummary.hexId) } + } + ] + }) + moreActionsList.push({ + title: 'Edit test', + items: [ + { + content: 'Add test suites', + icon: PlusMinor, + onAction: () => { setTestMode("testSuite") } + }, + { + content: 'Add individual test', + icon:PlusMinor, + onAction: () => { setTestMode("individualTest") } + } + ] + }) const moreActionsComp = ( - + ) @@ -767,13 +811,13 @@ const editableConfigsComp = ( : undefined} - secondaryActions={!workflowTest ? moreActionsComp: undefined} + }>Export results : undefined} + secondaryActions={!workflowTest ? moreActionsComp : undefined} components={useComponents} /> - + {(resultId !== null && resultId.length > 0) ? : null} ); From 09e6eae57011a966622ebdadce3d658cff086b42 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Sun, 19 Jan 2025 11:51:45 +0530 Subject: [PATCH 02/20] working but have bugs --- .../pages/observe/api_collections/RunTest.jsx | 44 ++++---- .../observe/api_collections/RunTestSuites.jsx | 101 +++++++++++++----- .../SingleTestRunPage/SingleTestRunPage.js | 28 +++-- 3 files changed, 116 insertions(+), 57 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index 2ed1aef5bb..f5fb0d4935 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -1,7 +1,7 @@ import { Box, Button, DataTable, Divider, Modal, Text, TextField, Icon, Checkbox, Badge, Banner, InlineGrid, HorizontalStack, Link, VerticalStack, Tooltip, Popover, ActionMenu, OptionList, ActionList } from "@shopify/polaris"; import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons"; import { useEffect, useReducer, useRef, useState } from "react"; -import { default as observeApi } from "../api"; +import api, { default as observeApi } from "../api"; import { default as testingApi } from "../../testing/api"; import SpinnerCentered from "../../../components/progress/SpinnerCentered" import Dropdown from "../../../components/layouts/Dropdown"; @@ -17,7 +17,7 @@ import RunTestSuites from "./RunTestSuites"; import RunTestConfiguration from "./RunTestConfiguration"; -function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testMode, testIdConfig }) { +function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testMode, testIdConfig, setTestMode }) { const initialState = { categories: [], @@ -153,17 +153,16 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } // Set if a test is selected or not - Object.keys(processMapCategoryToSubcategory).map(category => { - const selectedTests = [] - - mapCategoryToSubcategory[category]["selected"].map(test => selectedTests.push(test.value)) - processMapCategoryToSubcategory[category].forEach((test, index, arr) => { - arr[index]["selected"] = selectedTests.includes(test.value) - }) - }) - - const testName = convertToLowerCaseWithUnderscores(apiCollectionName) + "_" + nameSuffixes(processMapCategoryToSubcategory).join("_") - + // Object.keys(processMapCategoryToSubcategory).map(category => { + // const selectedTests = [] + + // mapCategoryToSubcategory[category]["selected"].map(test => selectedTests.push(test.value)) + // processMapCategoryToSubcategory[category].forEach((test, index, arr) => { + // arr[index]["selected"] = selectedTests.includes(test.value) + // }) + // }) + // because this line testSuite is getting wrong testName + const testName = convertToLowerCaseWithUnderscores(apiCollectionName); //Auth Mechanism let authMechanismPresent = false const authMechanismDataResponse = await testingApi.fetchAuthMechanismData() @@ -190,7 +189,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const toggleRunTest = () => { setActive(prev => !prev) if (active) { - closeRunTest() + if(closeRunTest !== undefined)closeRunTest() + setTestMode(""); } } @@ -462,6 +462,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const collectionId = parseInt(apiCollectionId) const tests = testRun.tests + const selectedTests = [] Object.keys(tests).forEach(category => { tests[category].forEach(test => { @@ -497,9 +498,11 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu finalAdvancedConditions = transform.prepareConditionsForTesting(conditions) } - if (filtered || selectedResourcesForPrimaryAction.length > 0) { - await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) + if (filtered || selectedResourcesForPrimaryAction?.length > 0) { + console.log(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) + await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) } else { + console.log(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources) await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources) } @@ -571,8 +574,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu + testIdConfig={testIdConfig} + initialState={initialState} + setTestMode={setTestMode} + testMode={testMode}/> 0) { const testSubCategoryList = [...testIdConfig.testingRunConfig.testSubCategoryList]; - const updatedTests = { ...testRun.tests }; + const updatedTests = { ...parentTestRun.tests }; // Reset all test selections Object.keys(updatedTests).forEach(category => { @@ -46,7 +48,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR Object.keys(updatedTests).forEach(category => { updatedTests[category] = updatedTests[category].map(test => ({ ...test, - selected: testSubCategorySet.has(test.value), + selected: testSubCategorySet.has(test.value), })); }); @@ -54,38 +56,67 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR function areObjectArraysEqual(obj1, obj2) { const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); - + if (keys1.length !== keys2.length) return false; - + const setKeys1 = new Set(keys1); const setKeys2 = new Set(keys2); if (setKeys1.size !== setKeys2.size || [...setKeys1].some(key => !setKeys2.has(key))) { return false; } - + for (let key of keys1) { const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); // O(m log m) const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); // O(m log m) - + if (arr1.length !== arr2.length || arr1.some((item, index) => item !== arr2[index])) { return false; } } - + return true; } - + // Update state only if there is a change - if (!areObjectArraysEqual(updatedTests, testRun.tests)) { + if (!areObjectArraysEqual(updatedTests, parentTestRun.tests)) { setTestRun(prev => ({ - ...prev, + ...parentTestRun, tests: updatedTests, })); - } + } } - }, [testRun]); + else { + setTestRun(prev => { + return { + ...parentTestRun + } + }); + } + const updatedName = parentTestRun.testName; + setSelectedTestSuites(prev=>{ + const updatedSelectedTestSuites = []; + Object.keys(owaspTop10List).forEach(key => { + if(updatedName.includes(key.replace(/\s+/g, '_'))){ + updatedSelectedTestSuites.push(key.replace(/\s+/g, '_')); + } + }) + return updatedSelectedTestSuites; + }); + }, [parentTestRun]); + + const [shouldCallFunction, setShouldCallFunction] = useState(false); + function handleTestSuiteRun(){ + console.log("handleTestSuiteRun"); + setParentTestRun(testRun); + setShouldCallFunction(true); + } + + useEffect(() => { + if(shouldCallFunction) handleRun(); + setShouldCallFunction(false); + }, [shouldCallFunction]); function handleTestSuiteSelection(key, data) { let updatedSelectedTestSuites; @@ -115,6 +146,18 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR }); } + function handleRemoveAll() { + setTestRun(prev => { + const tests = { ...testRun.tests } + Object.keys(tests).forEach(category => { + tests[category] = tests[category].map(test => ({ ...test, selected: false })) + }) + + return { ...prev, tests: tests} + }) + func.setToast(true, false, "All tests unselected") + } + function countTestSuitesTests(data) { let count = 0; const test = { ...testRun.tests }; @@ -137,7 +180,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR function checkedSelected(data) { let hasNonEmptyCategory = false; - + for (const category of data) { if (testRun.tests[category] && testRun.tests[category].length > 0) { hasNonEmptyCategory = true; @@ -146,11 +189,11 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR } } } - + return hasNonEmptyCategory; } - function checkDisableTestSuite(data){ + function checkDisableTestSuite(data) { for (const category of data) { if (testRun.tests[category] && testRun.tests[category].length > 0) { return false; @@ -208,24 +251,28 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR
testSuiteModalToggle(false)} + onClose={() => { + if(setTestMode)setTestMode(""); + testSuiteModalToggle(false); + }} title="Configure Test" primaryAction={{ - content: 'Run test', - onAction: () => handleRun(), + content: testMode?'Save & Re-run':'Run test', + onAction: () => handleTestSuiteRun(), + disabled: countAllSelectedTests() === 0, }} secondaryActions={[ - { + countAllSelectedTests()?{ content: `${countAllSelectedTests()} tests selected`, disabled: true, plain: true, - }, + }:null, { content: 'Cancel', onAction: () => testSuiteModalToggle(false), }, - ]} + ].filter(Boolean)} large footer={openConfigurations ? : } > @@ -241,11 +288,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, testRun, setTestR />
- - - diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 7cbb4f76df..5b27a6760a 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -224,13 +224,13 @@ function SingleTestRunPage() { useEffect(() => { setUpdateTable(Date.now().toString()) - if (testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type === "COLLECTION_WISE") { - api.fetchCollectionWiseApiEndpoints(testingRunResultSummariesObj?.testingRun?.testingEndpoints.apiCollectionId).then((res) => { + if (testingRunResultSummariesObj?.testingRun?.testingEndpoints.type === "COLLECTION_WISE") { + api.fetchCollectionWiseApiEndpoints(testingRunResultSummariesObj?.testingRun.testingEndpoints.apiCollectionId).then((res) => { setFilteredEndpoints([...res?.listOfEndpointsInCollection]); }) } - else if(testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type === "CUSTOM"){ - setFilteredEndpoints([...testingRunResultSummariesObj?.testingRun?.testingEndpoints?.apisList]); + else if (testingRunResultSummariesObj?.testingRun?.testingEndpoints.type === "CUSTOM") { + setFilteredEndpoints([...testingRunResultSummariesObj?.testingRun.testingEndpoints.apisList]); } }, [testingRunResultSummariesObj]) @@ -506,15 +506,25 @@ function SingleTestRunPage() { } function getCollectionId() { - if (testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type !== "COLLECTION_WISE") { - return testingRunResultSummariesObj?.testingRun.testingEndpoints?.apisList?.length == 1 ? testingRunResultSummariesObj?.testingRun.testingEndpoints?.apisList[0].apiCollectionId : undefined; + const testingEndpoints = testingRunResultSummariesObj?.testingRun?.testingEndpoints; + + if (!testingEndpoints) return undefined; + + if (testingEndpoints.type === "COLLECTION_WISE") { + return testingEndpoints.apiCollectionId; } - return testingRunResultSummariesObj?.testingRun?.apiCollectionId; + + return (testingEndpoints.apisList?.length === 1) ? testingEndpoints.apisList[0].apiCollectionId : undefined; } + function checkFiltered() { + return testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type !== "COLLECTION_WISE"; + } + + const resultTable = ( <> - {testMode.length > 0 ? : null} + {testMode.length > 0 ? : null} { setTestMode("individualTest") } } ] From b2ce1bc4469bf5b6a5f2a39fcedc29035f6e683d Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Sun, 19 Jan 2025 21:54:29 +0530 Subject: [PATCH 03/20] test suite added --- .../akto/action/testing/StartTestAction.java | 26 ++++++-- .../src/apps/dashboard/pages/observe/api.js | 19 ++++-- .../pages/observe/api_collections/RunTest.jsx | 24 +++---- .../observe/api_collections/RunTestSuites.jsx | 5 +- .../observe/api_collections/dummyData.json | 63 ------------------- .../api_collections/run_test_suites.css | 4 ++ 6 files changed, 51 insertions(+), 90 deletions(-) delete mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 789d38f679..2ec3320b2f 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -234,12 +234,28 @@ public String startTest() { } } + if (this.overriddenTestAppUrl != null || this.selectedTests != null) { - int id = UUID.randomUUID().hashCode() & 0xfffffff ; - TestingRunConfig testingRunConfig = new TestingRunConfig(id, null, this.selectedTests, null, this.overriddenTestAppUrl, this.testRoleId); - this.testIdConfig = testingRunConfig.getId(); - TestingRunConfigDao.instance.insertOne(testingRunConfig); - } + TestingRunConfig existingConfig = TestingRunConfigDao.instance.findOne(Filters.eq(Constants.ID, localTestingRun.getTestIdConfig())); + if (existingConfig != null) { + // If the TestingRunConfig exists, update it with new information (overridden URL or selected tests). + TestingRunConfigDao.instance.updateOne( + Filters.eq(Constants.ID, localTestingRun.getTestIdConfig()), + Updates.combine( + Updates.set("overriddenTestAppUrl", this.overriddenTestAppUrl), + Updates.set("testSubCategoryList", this.selectedTests), + Updates.set("testRoleId", this.testRoleId) + ) + ); + this.testIdConfig = existingConfig.getId(); + } + else { + int id = UUID.randomUUID().hashCode() & 0xfffffff; + TestingRunConfig testingRunConfig = new TestingRunConfig(id, null, this.selectedTests, null, this.overriddenTestAppUrl, this.testRoleId); + this.testIdConfig = testingRunConfig.getId(); + TestingRunConfigDao.instance.insertOne(testingRunConfig); + } + } } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js index f17426ffb7..793018b253 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js @@ -573,20 +573,31 @@ export default { data: {} }) }, - scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources) { + scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources, hexId) { + const requestData = { apiCollectionId, type: "COLLECTION_WISE", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources }; + + if (hexId != null && hexId != undefined) { + requestData.testingRunHexId = hexId; + } return request({ url: '/api/startTest', method: 'post', - data: { apiCollectionId, type: "COLLECTION_WISE", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources} + data: requestData }).then((resp) => { return resp }) }, - scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources) { + scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources,hexId) { + const requestData = { + apiInfoKeyList, type: "CUSTOM", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources + } + if(hexId != null && hexId != undefined) { + requestData.testingRunHexId = hexId; + } return request({ url: '/api/startTest', method: 'post', - data: {apiInfoKeyList, type: "CUSTOM", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources} + data: requestData }).then((resp) => { return resp }) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index f5fb0d4935..e4270e0389 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -152,16 +152,14 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu }) } - // Set if a test is selected or not - // Object.keys(processMapCategoryToSubcategory).map(category => { - // const selectedTests = [] - - // mapCategoryToSubcategory[category]["selected"].map(test => selectedTests.push(test.value)) - // processMapCategoryToSubcategory[category].forEach((test, index, arr) => { - // arr[index]["selected"] = selectedTests.includes(test.value) - // }) - // }) - // because this line testSuite is getting wrong testName + Object.keys(processMapCategoryToSubcategory).map(category => { + const selectedTests = [] + + mapCategoryToSubcategory[category]["selected"].map(test => selectedTests.push(test.value)) + processMapCategoryToSubcategory[category].forEach((test, index, arr) => { + arr[index]["selected"] = false + }) + }) const testName = convertToLowerCaseWithUnderscores(apiCollectionName); //Auth Mechanism let authMechanismPresent = false @@ -499,11 +497,9 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } if (filtered || selectedResourcesForPrimaryAction?.length > 0) { - console.log(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) - await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) + await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources,testIdConfig?.hexId) } else { - console.log(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources) - await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources) + await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources,testIdConfig?.hexId) } setActive(false) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index 485c398ade..0b04cadc78 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -5,7 +5,6 @@ import data from "./dummyData.json" import "./run_test_suites.css" import RunTestConfiguration from "./RunTestConfiguration"; import AdvancedSettingsComponent from "./component/AdvancedSettingsComponent"; -import { use } from "react"; @@ -13,7 +12,6 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se const [owaspTop10, owaspTop10Toggle] = useState(true); const [openConfigurations, openConfigurationsToggle] = useState(false); - const [testSuite, setTestSuite] = useState(true); const [selectedTestSuites, setSelectedTestSuites] = useState([]); const [testRun, setTestRun] = useState({...initialState}); @@ -108,7 +106,6 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se const [shouldCallFunction, setShouldCallFunction] = useState(false); function handleTestSuiteRun(){ - console.log("handleTestSuiteRun"); setParentTestRun(testRun); setShouldCallFunction(true); } @@ -308,7 +305,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se {data.owaspTop10.name} - {data.owaspTop10.plans.length} +
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json deleted file mode 100644 index d4d2899035..0000000000 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "byAkto": { - "name": "By Akto", - "plans": [ - { "name": "Free Plan", "testCount": "200" }, - { "name": "PII", "testCount": "200" }, - { "name": "Intrusive", "testCount": "200" }, - { "name": "Advanced Security", "testCount": "300" }, - { "name": "Enterprise", "testCount": "500" }, - { "name": "API Gateway Monitoring", "testCount": "250" }, - { "name": "Zero Trust Security", "testCount": "350" }, - { "name": "Cloud Protection", "testCount": "400" }, - { "name": "On-Premises Security", "testCount": "150" }, - { "name": "Dynamic Scanning", "testCount": "180" } - ] - }, - "owaspTop10": { - "name":"OWASP top 10", - "plans": [ - { "name": "Broken Object Level Authentication", "severity": "High", "testCount": "150" }, - { "name": "Broken Authentication", "severity": "Medium", "testCount": "120" }, - { "name": "Sensitive Data Exposure", "severity": "High", "testCount": "180" }, - { "name": "XML External Entities (XXE)", "severity": "Medium", "testCount": "130" }, - { "name": "Security Misconfiguration", "severity": "Low", "testCount": "80" }, - { "name": "Cross-Site Scripting (XSS)", "severity": "High", "testCount": "160" }, - { "name": "Insecure Deserialization", "severity": "High", "testCount": "140" }, - { "name": "Using Components with Known Vulnerabilities", "severity": "Medium", "testCount": "110" }, - { "name": "Insufficient Logging & Monitoring", "severity": "Low", "testCount": "90" }, - { "name": "Broken Access Control", "severity": "Critical", "testCount": "200" } - ] - }, - "compliance": { - "name":"Compliance", - "plans": [ - { "name": "HIPAA", "region": "US", "testCount": "100" }, - { "name": "SOC II", "region": "Global", "testCount": "90" }, - { "name": "GDPR", "region": "EU", "testCount": "110" }, - { "name": "CCPA", "region": "US", "testCount": "95" }, - { "name": "ISO 27001", "region": "Global", "testCount": "120" }, - { "name": "PCI DSS", "region": "Global", "testCount": "115" }, - { "name": "NIST 800-53", "region": "US", "testCount": "105" }, - { "name": "FISMA", "region": "US", "testCount": "85" }, - { "name": "FedRAMP", "region": "US", "testCount": "100" }, - { "name": "SOX", "region": "Global", "testCount": "90" } - ] - }, - "custom": { - "name":"Custom", - "plans": [ - { "name": "Custom Rule Engine", "description": "Define and manage custom rules for security testing", "testCount": "50" }, - { "name": "Real-time Alerts", "description": "Get notified about vulnerabilities in real time", "testCount": "70" }, - { "name": "Detailed Reporting", "description": "Generate comprehensive security reports", "testCount": "40" }, - { "name": "Integration Support", "description": "Integrate with CI/CD tools and third-party platforms", "testCount": "60" }, - { "name": "User Behavior Analytics", "description": "Analyze patterns in user behavior for security insights", "testCount": "30" }, - { "name": "Threat Intelligence", "description": "Get data on emerging threats", "testCount": "80" }, - { "name": "Access Control Testing", "description": "Test for access control vulnerabilities", "testCount": "100" }, - { "name": "Dynamic Security Policies", "description": "Adapt policies dynamically for better security", "testCount": "45" }, - { "name": "Penetration Testing Support", "description": "Simulate real-world attacks", "testCount": "65" }, - { "name": "API Schema Validation", "description": "Validate API schemas for security compliance", "testCount": "75" } - ] - } - } - \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css index b6b5072d62..5f7916b3ae 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css @@ -32,4 +32,8 @@ .testSuiteCard .Polaris-VerticalStack{ box-shadow: 0px 1px 2px 0px #00000026, 0px 0px 5px 0px #0000000D; border-radius: 0.5rem; +} + +.Polaris-Modal-Section{ + min-height: 75vh !important; } \ No newline at end of file From c65d38a57f13acffac7028f16279af7d472e2ec3 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Sun, 19 Jan 2025 21:56:47 +0530 Subject: [PATCH 04/20] missed console.log removed --- apps/dashboard/web/src/apps/dashboard/views/testing/api.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/api.js b/apps/dashboard/web/src/apps/dashboard/views/testing/api.js index e93e576e48..0cdf8c95ac 100644 --- a/apps/dashboard/web/src/apps/dashboard/views/testing/api.js +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/api.js @@ -65,7 +65,6 @@ export default { }, scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId) { - console.log('scheduleTestForCollection.api.js', testRoleId) return request({ url: '/api/startTest', method: 'post', From 0447af86bfda3f9fd633b60e01f4aeaeb7ca82ea Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Sun, 19 Jan 2025 22:13:05 +0530 Subject: [PATCH 05/20] type error in getCollectionId --- .../pages/testing/SingleTestRunPage/SingleTestRunPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 5b27a6760a..7cf157a9fe 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -514,7 +514,7 @@ function SingleTestRunPage() { return testingEndpoints.apiCollectionId; } - return (testingEndpoints.apisList?.length === 1) ? testingEndpoints.apisList[0].apiCollectionId : undefined; + return (testingEndpoints.apisList?.length > 0) ? testingEndpoints.apisList[0].apiCollectionId : undefined; } function checkFiltered() { From fa1afac131f3da21944e12fbd38d3b41987ef087 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Sun, 19 Jan 2025 22:15:03 +0530 Subject: [PATCH 06/20] dummyData recovered --- .../observe/api_collections/dummyData.json | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json new file mode 100644 index 0000000000..d4d2899035 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json @@ -0,0 +1,63 @@ +{ + "byAkto": { + "name": "By Akto", + "plans": [ + { "name": "Free Plan", "testCount": "200" }, + { "name": "PII", "testCount": "200" }, + { "name": "Intrusive", "testCount": "200" }, + { "name": "Advanced Security", "testCount": "300" }, + { "name": "Enterprise", "testCount": "500" }, + { "name": "API Gateway Monitoring", "testCount": "250" }, + { "name": "Zero Trust Security", "testCount": "350" }, + { "name": "Cloud Protection", "testCount": "400" }, + { "name": "On-Premises Security", "testCount": "150" }, + { "name": "Dynamic Scanning", "testCount": "180" } + ] + }, + "owaspTop10": { + "name":"OWASP top 10", + "plans": [ + { "name": "Broken Object Level Authentication", "severity": "High", "testCount": "150" }, + { "name": "Broken Authentication", "severity": "Medium", "testCount": "120" }, + { "name": "Sensitive Data Exposure", "severity": "High", "testCount": "180" }, + { "name": "XML External Entities (XXE)", "severity": "Medium", "testCount": "130" }, + { "name": "Security Misconfiguration", "severity": "Low", "testCount": "80" }, + { "name": "Cross-Site Scripting (XSS)", "severity": "High", "testCount": "160" }, + { "name": "Insecure Deserialization", "severity": "High", "testCount": "140" }, + { "name": "Using Components with Known Vulnerabilities", "severity": "Medium", "testCount": "110" }, + { "name": "Insufficient Logging & Monitoring", "severity": "Low", "testCount": "90" }, + { "name": "Broken Access Control", "severity": "Critical", "testCount": "200" } + ] + }, + "compliance": { + "name":"Compliance", + "plans": [ + { "name": "HIPAA", "region": "US", "testCount": "100" }, + { "name": "SOC II", "region": "Global", "testCount": "90" }, + { "name": "GDPR", "region": "EU", "testCount": "110" }, + { "name": "CCPA", "region": "US", "testCount": "95" }, + { "name": "ISO 27001", "region": "Global", "testCount": "120" }, + { "name": "PCI DSS", "region": "Global", "testCount": "115" }, + { "name": "NIST 800-53", "region": "US", "testCount": "105" }, + { "name": "FISMA", "region": "US", "testCount": "85" }, + { "name": "FedRAMP", "region": "US", "testCount": "100" }, + { "name": "SOX", "region": "Global", "testCount": "90" } + ] + }, + "custom": { + "name":"Custom", + "plans": [ + { "name": "Custom Rule Engine", "description": "Define and manage custom rules for security testing", "testCount": "50" }, + { "name": "Real-time Alerts", "description": "Get notified about vulnerabilities in real time", "testCount": "70" }, + { "name": "Detailed Reporting", "description": "Generate comprehensive security reports", "testCount": "40" }, + { "name": "Integration Support", "description": "Integrate with CI/CD tools and third-party platforms", "testCount": "60" }, + { "name": "User Behavior Analytics", "description": "Analyze patterns in user behavior for security insights", "testCount": "30" }, + { "name": "Threat Intelligence", "description": "Get data on emerging threats", "testCount": "80" }, + { "name": "Access Control Testing", "description": "Test for access control vulnerabilities", "testCount": "100" }, + { "name": "Dynamic Security Policies", "description": "Adapt policies dynamically for better security", "testCount": "45" }, + { "name": "Penetration Testing Support", "description": "Simulate real-world attacks", "testCount": "65" }, + { "name": "API Schema Validation", "description": "Validate API schemas for security compliance", "testCount": "75" } + ] + } + } + \ No newline at end of file From 400b0d7d15c6fb6b63f0d44de3be0d51fa8f2483 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Mon, 20 Jan 2025 11:36:30 +0530 Subject: [PATCH 07/20] dummy Data file removed --- .../observe/api_collections/RunTestSuites.jsx | 7 +-- .../observe/api_collections/dummyData.json | 63 ------------------- 2 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index 0b04cadc78..1d6cf90ee2 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -1,7 +1,6 @@ import { VerticalStack, Modal, TextField, Button, Text, HorizontalStack, Collapsible, Badge, Pagination, TextContainer, Icon, Scrollable, Checkbox, Box, Tooltip, Card, MediaCard } from "@shopify/polaris"; import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons"; import { useEffect, useRef, useState } from "react"; -import data from "./dummyData.json" import "./run_test_suites.css" import RunTestConfiguration from "./RunTestConfiguration"; import AdvancedSettingsComponent from "./component/AdvancedSettingsComponent"; @@ -64,8 +63,8 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se } for (let key of keys1) { - const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); // O(m log m) - const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); // O(m log m) + const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); + const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); if (arr1.length !== arr2.length || arr1.some((item, index) => item !== arr2[index])) { return false; @@ -303,7 +302,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se disclosure > - {data.owaspTop10.name} + {"OWASP top 10"} diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json deleted file mode 100644 index d4d2899035..0000000000 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/dummyData.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "byAkto": { - "name": "By Akto", - "plans": [ - { "name": "Free Plan", "testCount": "200" }, - { "name": "PII", "testCount": "200" }, - { "name": "Intrusive", "testCount": "200" }, - { "name": "Advanced Security", "testCount": "300" }, - { "name": "Enterprise", "testCount": "500" }, - { "name": "API Gateway Monitoring", "testCount": "250" }, - { "name": "Zero Trust Security", "testCount": "350" }, - { "name": "Cloud Protection", "testCount": "400" }, - { "name": "On-Premises Security", "testCount": "150" }, - { "name": "Dynamic Scanning", "testCount": "180" } - ] - }, - "owaspTop10": { - "name":"OWASP top 10", - "plans": [ - { "name": "Broken Object Level Authentication", "severity": "High", "testCount": "150" }, - { "name": "Broken Authentication", "severity": "Medium", "testCount": "120" }, - { "name": "Sensitive Data Exposure", "severity": "High", "testCount": "180" }, - { "name": "XML External Entities (XXE)", "severity": "Medium", "testCount": "130" }, - { "name": "Security Misconfiguration", "severity": "Low", "testCount": "80" }, - { "name": "Cross-Site Scripting (XSS)", "severity": "High", "testCount": "160" }, - { "name": "Insecure Deserialization", "severity": "High", "testCount": "140" }, - { "name": "Using Components with Known Vulnerabilities", "severity": "Medium", "testCount": "110" }, - { "name": "Insufficient Logging & Monitoring", "severity": "Low", "testCount": "90" }, - { "name": "Broken Access Control", "severity": "Critical", "testCount": "200" } - ] - }, - "compliance": { - "name":"Compliance", - "plans": [ - { "name": "HIPAA", "region": "US", "testCount": "100" }, - { "name": "SOC II", "region": "Global", "testCount": "90" }, - { "name": "GDPR", "region": "EU", "testCount": "110" }, - { "name": "CCPA", "region": "US", "testCount": "95" }, - { "name": "ISO 27001", "region": "Global", "testCount": "120" }, - { "name": "PCI DSS", "region": "Global", "testCount": "115" }, - { "name": "NIST 800-53", "region": "US", "testCount": "105" }, - { "name": "FISMA", "region": "US", "testCount": "85" }, - { "name": "FedRAMP", "region": "US", "testCount": "100" }, - { "name": "SOX", "region": "Global", "testCount": "90" } - ] - }, - "custom": { - "name":"Custom", - "plans": [ - { "name": "Custom Rule Engine", "description": "Define and manage custom rules for security testing", "testCount": "50" }, - { "name": "Real-time Alerts", "description": "Get notified about vulnerabilities in real time", "testCount": "70" }, - { "name": "Detailed Reporting", "description": "Generate comprehensive security reports", "testCount": "40" }, - { "name": "Integration Support", "description": "Integrate with CI/CD tools and third-party platforms", "testCount": "60" }, - { "name": "User Behavior Analytics", "description": "Analyze patterns in user behavior for security insights", "testCount": "30" }, - { "name": "Threat Intelligence", "description": "Get data on emerging threats", "testCount": "80" }, - { "name": "Access Control Testing", "description": "Test for access control vulnerabilities", "testCount": "100" }, - { "name": "Dynamic Security Policies", "description": "Adapt policies dynamically for better security", "testCount": "45" }, - { "name": "Penetration Testing Support", "description": "Simulate real-world attacks", "testCount": "65" }, - { "name": "API Schema Validation", "description": "Validate API schemas for security compliance", "testCount": "75" } - ] - } - } - \ No newline at end of file From 680c4c4fdddb9a1397c6cd2ce972b5c6af2bee24 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Mon, 20 Jan 2025 13:49:07 +0530 Subject: [PATCH 08/20] ui of testcard fixed --- .../observe/api_collections/RunTestSuites.jsx | 49 ++++++++----------- .../api_collections/run_test_suites.css | 4 ++ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index 1d6cf90ee2..3c8eeb4d36 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -7,12 +7,12 @@ import AdvancedSettingsComponent from "./component/AdvancedSettingsComponent"; -function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, setParentTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration, dispatchConditions, conditions, handleRun, convertToLowerCaseWithUnderscores, apiCollectionName, testIdConfig,initialState,setTestMode, testMode }) { +function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, setParentTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration, dispatchConditions, conditions, handleRun, convertToLowerCaseWithUnderscores, apiCollectionName, testIdConfig, initialState, setTestMode, testMode }) { const [owaspTop10, owaspTop10Toggle] = useState(true); const [openConfigurations, openConfigurationsToggle] = useState(false); const [selectedTestSuites, setSelectedTestSuites] = useState([]); - const [testRun, setTestRun] = useState({...initialState}); + const [testRun, setTestRun] = useState({ ...initialState }); const owaspTop10List = { "Broken Object Level Authorization": ["BOLA"], @@ -63,8 +63,8 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se } for (let key of keys1) { - const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); - const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); + const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); + const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); if (arr1.length !== arr2.length || arr1.some((item, index) => item !== arr2[index])) { return false; @@ -91,10 +91,10 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se }); } const updatedName = parentTestRun.testName; - setSelectedTestSuites(prev=>{ + setSelectedTestSuites(prev => { const updatedSelectedTestSuites = []; Object.keys(owaspTop10List).forEach(key => { - if(updatedName.includes(key.replace(/\s+/g, '_'))){ + if (updatedName.includes(key.replace(/\s+/g, '_'))) { updatedSelectedTestSuites.push(key.replace(/\s+/g, '_')); } }) @@ -104,13 +104,13 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se const [shouldCallFunction, setShouldCallFunction] = useState(false); - function handleTestSuiteRun(){ + function handleTestSuiteRun() { setParentTestRun(testRun); setShouldCallFunction(true); } useEffect(() => { - if(shouldCallFunction) handleRun(); + if (shouldCallFunction) handleRun(); setShouldCallFunction(false); }, [shouldCallFunction]); @@ -149,7 +149,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se tests[category] = tests[category].map(test => ({ ...test, selected: false })) }) - return { ...prev, tests: tests} + return { ...prev, tests: tests } }) func.setToast(true, false, "All tests unselected") } @@ -248,21 +248,21 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se { - if(setTestMode)setTestMode(""); + if (setTestMode) setTestMode(""); testSuiteModalToggle(false); }} title="Configure Test" primaryAction={{ - content: testMode?'Save & Re-run':'Run test', + content: testMode ? 'Save & Re-run' : 'Run test', onAction: () => handleTestSuiteRun(), disabled: countAllSelectedTests() === 0, }} secondaryActions={[ - countAllSelectedTests()?{ + countAllSelectedTests() ? { content: `${countAllSelectedTests()} tests selected`, disabled: true, plain: true, - }:null, + } : null, { content: 'Cancel', onAction: () => testSuiteModalToggle(false), @@ -304,7 +304,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se {"OWASP top 10"} - +
@@ -316,20 +316,13 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se >
-
- - {Object.entries(owaspTop10List).map(([key, value], index) => ( - renderAktoTestSuites({ key, value }) - ))} - -
+ + + {Object.entries(owaspTop10List).map(([key, value]) => ( + renderAktoTestSuites({ key, value }) + ))} + +
Date: Fri, 24 Jan 2025 00:43:18 +0530 Subject: [PATCH 09/20] not done completely, need api to modify test_run object in editableConfigsCom in RunTest --- .../akto/action/testing/StartTestAction.java | 39 +- .../src/apps/dashboard/pages/observe/api.js | 19 +- .../pages/observe/api_collections/RunTest.jsx | 534 +++++++++++------- .../api_collections/RunTestConfiguration.jsx | 2 +- .../observe/api_collections/RunTestSuites.jsx | 351 ++++-------- .../api_collections/run_test_suites.css | 26 - .../SingleTestRunPage/SingleTestRunPage.js | 101 ++-- .../src/apps/dashboard/pages/testing/api.js | 11 +- apps/dashboard/web/public/test_suite.svg | 16 + 9 files changed, 544 insertions(+), 555 deletions(-) create mode 100644 apps/dashboard/web/public/test_suite.svg diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index a8829871d7..70a717fbcf 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -236,25 +236,10 @@ public String startTest() { } if (this.overriddenTestAppUrl != null || this.selectedTests != null) { - TestingRunConfig existingConfig = TestingRunConfigDao.instance.findOne(Filters.eq(Constants.ID, localTestingRun.getTestIdConfig())); - if (existingConfig != null) { - // If the TestingRunConfig exists, update it with new information (overridden URL or selected tests). - TestingRunConfigDao.instance.updateOne( - Filters.eq(Constants.ID, localTestingRun.getTestIdConfig()), - Updates.combine( - Updates.set("overriddenTestAppUrl", this.overriddenTestAppUrl), - Updates.set("testSubCategoryList", this.selectedTests), - Updates.set("testRoleId", this.testRoleId) - ) - ); - this.testIdConfig = existingConfig.getId(); - } - else { - int id = UUID.randomUUID().hashCode() & 0xfffffff; - TestingRunConfig testingRunConfig = new TestingRunConfig(id, null, this.selectedTests, null, this.overriddenTestAppUrl, this.testRoleId); - this.testIdConfig = testingRunConfig.getId(); - TestingRunConfigDao.instance.insertOne(testingRunConfig); - } + int id = UUID.randomUUID().hashCode() & 0xfffffff; + TestingRunConfig testingRunConfig = new TestingRunConfig(id, null, this.selectedTests, null, this.overriddenTestAppUrl, this.testRoleId); + this.testIdConfig = testingRunConfig.getId(); + TestingRunConfigDao.instance.insertOne(testingRunConfig); } } @@ -1142,10 +1127,18 @@ public String getCurrentTestStateStatus(){ } public String modifyTestingRunConfig(){ - TestingRunConfigDao.instance.updateOne( - Filters.eq(Constants.ID, this.testingRunConfigId), - Updates.set("configsAdvancedSettings", this.testConfigsAdvancedSettings) - ); + if(this.selectedTests == null){ + TestingRunConfigDao.instance.updateOne( + Filters.eq(Constants.ID, this.testingRunConfigId), + Updates.set("configsAdvancedSettings", this.testConfigsAdvancedSettings) + ); + + } else { + TestingRunConfigDao.instance.updateOne( + Filters.eq(Constants.ID, this.testingRunConfigId), + Updates.set("testSubCategoryList", this.selectedTests) + ); + } return SUCCESS.toUpperCase(); } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js index 793018b253..f17426ffb7 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js @@ -573,31 +573,20 @@ export default { data: {} }) }, - scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources, hexId) { - const requestData = { apiCollectionId, type: "COLLECTION_WISE", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources }; - - if (hexId != null && hexId != undefined) { - requestData.testingRunHexId = hexId; - } + scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources) { return request({ url: '/api/startTest', method: 'post', - data: requestData + data: { apiCollectionId, type: "COLLECTION_WISE", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources} }).then((resp) => { return resp }) }, - scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources,hexId) { - const requestData = { - apiInfoKeyList, type: "CUSTOM", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources - } - if(hexId != null && hexId != undefined) { - requestData.testingRunHexId = hexId; - } + scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources) { return request({ url: '/api/startTest', method: 'post', - data: requestData + data: {apiInfoKeyList, type: "CUSTOM", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, source, testRoleId, continuousTesting, sendSlackAlert, testConfigsAdvancedSettings, cleanUpTestingResources} }).then((resp) => { return resp }) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index e4270e0389..17dbab29ce 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -1,5 +1,5 @@ -import { Box, Button, DataTable, Divider, Modal, Text, TextField, Icon, Checkbox, Badge, Banner, InlineGrid, HorizontalStack, Link, VerticalStack, Tooltip, Popover, ActionMenu, OptionList, ActionList } from "@shopify/polaris"; -import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons"; +import { Box, Button, DataTable, Divider, Modal, Text, TextField, Icon, Checkbox, Badge, Banner, InlineGrid, HorizontalStack, Link, VerticalStack, Tooltip, Popover, ActionMenu, OptionList, ActionList, ButtonGroup } from "@shopify/polaris"; +import { TickMinor, CancelMajor, SearchMinor, NoteMinor, AppsMinor, AppsFilledMajor } from "@shopify/polaris-icons"; import { useEffect, useReducer, useRef, useState } from "react"; import api, { default as observeApi } from "../api"; import { default as testingApi } from "../../testing/api"; @@ -17,7 +17,7 @@ import RunTestSuites from "./RunTestSuites"; import RunTestConfiguration from "./RunTestConfiguration"; -function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testMode, testIdConfig, setTestMode }) { +function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings }) { const initialState = { categories: [], @@ -76,16 +76,14 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const localCategoryMap = LocalStore.getState().categoryMap const localSubCategoryMap = LocalStore.getState().subCategoryMap + const [testMode, setTestMode] = useState(true) + const [shouldRuntestConfig, setShouldRuntestConfig] = useState(false) + + const [openConfigurations, openConfigurationsToggle] = useState(false); useEffect(() => { - if(preActivator){ + if (preActivator) { setParentActivator(true); - if(testMode === "testSuite"){ - testSuiteToggle(true) - } - else if(testMode === "individualTest"){ - toggleRunTest() - } } }, [testMode]) @@ -166,15 +164,19 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const authMechanismDataResponse = await testingApi.fetchAuthMechanismData() if (authMechanismDataResponse.authMechanism) authMechanismPresent = true - setTestRun(prev => ({ - ...prev, - categories: categories, - tests: processMapCategoryToSubcategory, - selectedCategory: Object.keys(processMapCategoryToSubcategory).length > 0 ? Object.keys(processMapCategoryToSubcategory)[0] : "", - testName: testName, - authMechanismPresent: authMechanismPresent - })) + setTestRun(prev => { + const state = { + ...prev, + categories: categories, + tests: processMapCategoryToSubcategory, + selectedCategory: Object.keys(processMapCategoryToSubcategory).length > 0 ? Object.keys(processMapCategoryToSubcategory)[0] : "", + testName: testName, + authMechanismPresent: authMechanismPresent + }; + return state; + }); setLoading(false) + setShouldRuntestConfig(true); } useEffect(() => { @@ -184,11 +186,74 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } }, [apiCollectionName, runTestFromOutside]) + + + useEffect(() => { + if (shouldRuntestConfig === false) return; + if (testIdConfig?.testingRunConfig?.testSubCategoryList?.length > 0) { + const testSubCategoryList = [...testIdConfig.testingRunConfig.testSubCategoryList]; + + const updatedTests = { ...testRun.tests }; + + // Reset all test selections + Object.keys(updatedTests).forEach(category => { + updatedTests[category] = updatedTests[category].map(test => ({ ...test, selected: false })); + }); + + const testSubCategorySet = new Set(testSubCategoryList); + + Object.keys(updatedTests).forEach(category => { + updatedTests[category] = updatedTests[category].map(test => ({ + ...test, + selected: testSubCategorySet.has(test.value), + })); + }); + + // Efficient deep equality check + function areObjectArraysEqual(obj1, obj2) { + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + if (keys1.length !== keys2.length) return false; + + const setKeys1 = new Set(keys1); + const setKeys2 = new Set(keys2); + if (setKeys1.size !== setKeys2.size || [...setKeys1].some(key => !setKeys2.has(key))) { + return false; + } + + for (let key of keys1) { + const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); + const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); + + if (arr1.length !== arr2.length || arr1.some((item, index) => item !== arr2[index])) { + return false; + } + } + + return true; + } + + // Update state only if there is a change + if (!areObjectArraysEqual(updatedTests, testRun.tests)) { + setTestRun(prev => ({ + ...testRun, + tests: updatedTests, + })); + } + } + setShouldRuntestConfig(false); + }, [shouldRuntestConfig]) + + const toggleRunTest = () => { + if (activeFromTesting) { + setActiveFromTesting(false); + return; + } setActive(prev => !prev) if (active) { - if(closeRunTest !== undefined)closeRunTest() - setTestMode(""); + if (closeRunTest !== undefined) closeRunTest() } } @@ -214,37 +279,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } } - const [testPopover, setTestPopover] = useState(false); - const [testSuite, testSuiteToggle] = useState(false); - const activators = ( - setTestPopover(!testPopover)} primary disclosure>Run Tests - } - onClose={() => setTestPopover(false)} - > - testSuiteToggle(true) - }, - { - content: "Individual tests", - onAction: toggleRunTest, - active: disabled || testRun.selectedCategory.length === 0, - } - ]} - > - - - - ) - const activator = (
@@ -456,11 +492,11 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } async function handleRun() { - const { startTimestamp, recurringDaily, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert,cleanUpTestingResources } = testRun + const { startTimestamp, recurringDaily, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, cleanUpTestingResources } = testRun const collectionId = parseInt(apiCollectionId) const tests = testRun.tests - + const selectedTests = [] Object.keys(tests).forEach(category => { tests[category].forEach(test => { @@ -469,7 +505,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu }) let apiInfoKeyList; - if(!selectedResourcesForPrimaryAction || selectedResourcesForPrimaryAction.length === 0) { + if (!selectedResourcesForPrimaryAction || selectedResourcesForPrimaryAction.length === 0) { apiInfoKeyList = endpoints.map(endpoint => ({ apiCollectionId: endpoint.apiCollectionId, method: endpoint.method, @@ -497,9 +533,18 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } if (filtered || selectedResourcesForPrimaryAction?.length > 0) { - await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources,testIdConfig?.hexId) + if (testIdConfig) { + await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, null, selectedTests) + transform.rerunTest(testIdConfig.hexId, null, true) + + } + else await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) } else { - await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions,cleanUpTestingResources,testIdConfig?.hexId) + if (testIdConfig) { + await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, null, selectedTests) + transform.rerunTest(testIdConfig.hexId, null, true) + } + else observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) } setActive(false) @@ -537,6 +582,15 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const allTestsSelectedOfCategory = getCurrentStatus() + function countAllSelectedTests() { + let count = 0; + const test = { ...testRun.tests }; + Object.keys(test).forEach(category => { + count += test[category].filter(test => test.selected).length; + }); + return count; + } + function toggleTestsSelection(val) { let copyTestRun = testRun copyTestRun.tests[testRun.selectedCategory].forEach((test) => { @@ -564,172 +618,256 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu ); } + const handleButtonClick = (check) => { + setTestMode(check); + } + + const handleModifyConfig = async () => { + const settings = transform.prepareConditionsForTesting(conditions) + await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, settings).then(() => { + func.setToast(true, false, "Modified testing run config successfully") + setShowEditableSettings(false) + }) + } + + const handleAddSettings = () => { + if (conditions.length === 0 && testingRunConfigSettings.length > 0) { + testingRunConfigSettings.forEach((condition) => { + const operatorType = condition.operatorType + condition.operationsGroupList.forEach((obj) => { + const finalObj = { 'data': obj, 'operator': { 'type': operatorType } } + dispatchConditions({ type: "add", obj: finalObj }) + }) + }) + } + } + + // still not working properly need api to modify test_run object + const editableConfigsComp = ( + setShowEditableSettings(false)} + title={"Edit test configurations"} + primaryAction={{ + content: 'Save', + onAction: () => handleModifyConfig() + }} + > + + <> + + + + + + ) + + + return (
- {!parentActivator? activators:null} - + {!parentActivator ? activator : null} + {showEditableSettings? editableConfigsComp: null} Configure test + + + + + } primaryAction={{ - content: scheduleString(), + content: activeFromTesting ? "Save & Re-run" : scheduleString(), onAction: handleRun, - disabled: !testRun.authMechanismPresent + disabled: (countAllSelectedTests() === 0) || !testRun.authMechanismPresent }} + secondaryActions={[ + countAllSelectedTests() ? { + content: `${countAllSelectedTests()} tests selected`, + disabled: true, + plain: true, + } : null, + { + content: 'Cancel', + onAction: () => toggleRunTest(), + }, + + ].filter(Boolean)} + large + + footer={testMode ? null : openConfigurations ? : } > {loading ? : - - - {!testRun.authMechanismPresent && -
- navigate("/dashboard/testing/user-config") - }} - status="critical" - > - - - Running specialized tests like Broken Object Level Authorization, - Broken User Authentication etc, require an additional attacker - authorization token. Hence before triggering Akto tests on your apis, - you may need to specify an authorization token which can be treated as - attacker token during test run. Attacker Token can be specified - manually, as well as in automated manner. We provide multiple ways to - automate Attacker token generation. - - -
-
- } - + testMode ? + + + {!testRun.authMechanismPresent && +
+ navigate("/dashboard/testing/user-config") + }} + status="critical" + > + + + Running specialized tests like Broken Object Level Authorization, + Broken User Authentication etc, require an additional attacker + authorization token. Hence before triggering Akto tests on your apis, + you may need to specify an authorization token which can be treated as + attacker token during test run. Attacker Token can be specified + manually, as well as in automated manner. We provide multiple ways to + automate Attacker token generation. + + +
+
+ } -
- Name: -
- setTestRun(prev => ({ ...prev, testName: testName }))} - /> -
- -
-
-
-
- Test Categories -
- -
- - + Name: +
+ setTestRun(prev => ({ ...prev, testName: testName }))} />
+ +
-
-
- - toggleTestsSelection(val)} +
+
+
+ Test Categories +
+ +
+ + - Tests - - - setShowFiltersOption(!showFiltersOption)} - plain>More filters} - onClose={() => setShowFiltersOption(false)} - active={showFiltersOption} - > - - { setOptionsSelected(x); selectOnlyFilteredTests(x); }} - allowMultiple - sections={sectionsForFilters} - selected={optionsSelected} - /> - - - {showSearch ? : null} - -
- -
- +
+
+ + toggleTestsSelection(val)} + /> + Tests + + + setShowFiltersOption(!showFiltersOption)} + plain>More filters} + onClose={() => setShowFiltersOption(false)} + active={showFiltersOption} + > + + { setOptionsSelected(x); selectOnlyFilteredTests(x); }} + allowMultiple + sections={sectionsForFilters} + selected={optionsSelected} + /> + + + {showSearch ? : null} + +
+ +
+ +
-
- + + + + + : + {!openConfigurations ? - - - - - + handleRun={handleRun} + handleRemoveAll={handleRemoveAll} + apiCollectionName={apiCollectionName} + setTestMode={setTestMode} + checkRemoveAll={checkRemoveAll} /> : + <> + + + + } + }
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx index e5643b4a8c..a0810b00df 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { VerticalStack, HorizontalGrid, Checkbox, TextField, Text } from '@shopify/polaris'; import Dropdown from "../../../components/layouts/Dropdown"; -const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration }) => { +const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration,getLabel }) => { return ( diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index 3c8eeb4d36..98d6a18b19 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -2,17 +2,11 @@ import { VerticalStack, Modal, TextField, Button, Text, HorizontalStack, Collaps import { TickMinor, CancelMajor, SearchMinor } from "@shopify/polaris-icons"; import { useEffect, useRef, useState } from "react"; import "./run_test_suites.css" -import RunTestConfiguration from "./RunTestConfiguration"; -import AdvancedSettingsComponent from "./component/AdvancedSettingsComponent"; - -function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, setParentTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration, dispatchConditions, conditions, handleRun, convertToLowerCaseWithUnderscores, apiCollectionName, testIdConfig, initialState, setTestMode, testMode }) { +function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handleRemoveAll }) { const [owaspTop10, owaspTop10Toggle] = useState(true); - const [openConfigurations, openConfigurationsToggle] = useState(false); - const [selectedTestSuites, setSelectedTestSuites] = useState([]); - const [testRun, setTestRun] = useState({ ...initialState }); const owaspTop10List = { "Broken Object Level Authorization": ["BOLA"], @@ -27,136 +21,53 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se "Unsafe Consumption of APIs": ["COMMAND_INJECTION", "INJ", "CRLF", "SSTI", "LFI", "XSS", "INJECT"] } - - useEffect(() => { - - if (testIdConfig?.testingRunConfig?.testSubCategoryList?.length > 0) { - const testSubCategoryList = [...testIdConfig.testingRunConfig.testSubCategoryList]; - - const updatedTests = { ...parentTestRun.tests }; - - // Reset all test selections - Object.keys(updatedTests).forEach(category => { - updatedTests[category] = updatedTests[category].map(test => ({ ...test, selected: false })); - }); - - const testSubCategorySet = new Set(testSubCategoryList); - - Object.keys(updatedTests).forEach(category => { - updatedTests[category] = updatedTests[category].map(test => ({ - ...test, - selected: testSubCategorySet.has(test.value), - })); - }); - - // Efficient deep equality check - function areObjectArraysEqual(obj1, obj2) { - const keys1 = Object.keys(obj1); - const keys2 = Object.keys(obj2); - - if (keys1.length !== keys2.length) return false; - - const setKeys1 = new Set(keys1); - const setKeys2 = new Set(keys2); - if (setKeys1.size !== setKeys2.size || [...setKeys1].some(key => !setKeys2.has(key))) { - return false; - } - - for (let key of keys1) { - const arr1 = obj1[key].map(obj => JSON.stringify(obj)).sort(); - const arr2 = obj2[key].map(obj => JSON.stringify(obj)).sort(); - - if (arr1.length !== arr2.length || arr1.some((item, index) => item !== arr2[index])) { - return false; - } - } - - return true; - } - - - // Update state only if there is a change - if (!areObjectArraysEqual(updatedTests, parentTestRun.tests)) { - setTestRun(prev => ({ - ...parentTestRun, - tests: updatedTests, - })); - } - } - else { - setTestRun(prev => { - return { - ...parentTestRun - } - }); - } - const updatedName = parentTestRun.testName; - setSelectedTestSuites(prev => { - const updatedSelectedTestSuites = []; - Object.keys(owaspTop10List).forEach(key => { - if (updatedName.includes(key.replace(/\s+/g, '_'))) { - updatedSelectedTestSuites.push(key.replace(/\s+/g, '_')); - } - }) - return updatedSelectedTestSuites; - }); - }, [parentTestRun]); - const [shouldCallFunction, setShouldCallFunction] = useState(false); - function handleTestSuiteRun() { - setParentTestRun(testRun); - setShouldCallFunction(true); - } useEffect(() => { if (shouldCallFunction) handleRun(); setShouldCallFunction(false); }, [shouldCallFunction]); - function handleTestSuiteSelection(key, data) { - let updatedSelectedTestSuites; - if (!selectedTestSuites.includes(key.replace(/\s+/g, '_'))) { - updatedSelectedTestSuites = [...selectedTestSuites, key.replace(/\s+/g, '_')]; - } else { - updatedSelectedTestSuites = selectedTestSuites.filter(item => item !== key.replace(/\s+/g, '_')); - } - - setSelectedTestSuites(updatedSelectedTestSuites); + function createAcronym(str) { + return str.split(/\s+/).map(word => word.charAt(0).toUpperCase()).join(''); + } + function handleTestSuiteSelection(key, data) { setTestRun(prev => { const updatedTests = { ...prev.tests }; + let someSelected = true data.forEach(category => { if (updatedTests[category]) { + someSelected = updatedTests[category].some(test => test.selected); updatedTests[category] = updatedTests[category].map(test => ({ ...test, - selected: !test.selected + selected: someSelected ? false : true })); } }); + + let updatedTestName = prev.testName; + const acronym = createAcronym(key); + if (someSelected) { + const regex = new RegExp(`_${acronym}`, 'g'); + updatedTestName = updatedTestName.replace(regex, ""); + } else { + updatedTestName += `_${acronym}`; + } + return { ...prev, tests: updatedTests, - testName: convertToLowerCaseWithUnderscores(apiCollectionName) + "_" + updatedSelectedTestSuites.join("_") + testName: updatedTestName }; }); } - function handleRemoveAll() { - setTestRun(prev => { - const tests = { ...testRun.tests } - Object.keys(tests).forEach(category => { - tests[category] = tests[category].map(test => ({ ...test, selected: false })) - }) - - return { ...prev, tests: tests } - }) - func.setToast(true, false, "All tests unselected") - } - function countTestSuitesTests(data) { + if (testRun === undefined) return ; let count = 0; - const test = { ...testRun.tests }; + const test = { ...testRun?.tests }; data.forEach(category => { if (test[category]) { count += test[category].length; @@ -166,6 +77,7 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se } function countAllSelectedTests() { + if (testRun === undefined) return ; let count = 0; const test = { ...testRun.tests }; Object.keys(test).forEach(category => { @@ -175,21 +87,27 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se } function checkedSelected(data) { - let hasNonEmptyCategory = false; + if (testRun === undefined) return ; + let atleastOne = false; + let allSelected = true;; for (const category of data) { if (testRun.tests[category] && testRun.tests[category].length > 0) { - hasNonEmptyCategory = true; - if (!testRun.tests[category].every(test => test.selected)) { - return false; + if (testRun.tests[category].some(test => !test.selected)) { + allSelected = false; + } + if (testRun.tests[category].some(test => test.selected)) { + atleastOne = true; } } } - - return hasNonEmptyCategory; + if (atleastOne && allSelected) return true; + else if (atleastOne) return "indeterminate"; + else return false; } function checkDisableTestSuite(data) { + if (testRun === undefined) return 0; for (const category of data) { if (testRun.tests[category] && testRun.tests[category].length > 0) { return false; @@ -200,25 +118,11 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se function renderAktoTestSuites(data) { return ( -
- +
+ -
- - - - - - - - - - - - - - - +
+
@@ -243,114 +147,83 @@ function RunTestSuites({ testSuiteModal, testSuiteModalToggle, parentTestRun, se ); } + function filterTestSuites() { + if (!searchValue || searchValue === "") return owaspTop10List; + const filtered = {}; + Object.entries(owaspTop10List).forEach(([key, value]) => { + if (key.toLowerCase().includes(searchValue.toLowerCase())) { + filtered[key] = value; + } + }); + return filtered; + } + return ( -
- { - if (setTestMode) setTestMode(""); - testSuiteModalToggle(false); - }} - title="Configure Test" - primaryAction={{ - content: testMode ? 'Save & Re-run' : 'Run test', - onAction: () => handleTestSuiteRun(), - disabled: countAllSelectedTests() === 0, - }} - secondaryActions={[ - countAllSelectedTests() ? { - content: `${countAllSelectedTests()} tests selected`, - disabled: true, - plain: true, - } : null, - { - content: 'Cancel', - onAction: () => testSuiteModalToggle(false), - }, - - ].filter(Boolean)} - large - footer={openConfigurations ? : } - > - {!openConfigurations && - - - - -
- } - placeholder="Search" - /> -
- - - - + +
+ +
+ Name: +
+ setTestRun(prev => ({ ...prev, testName: testName }))} + /> +
+ + +
+ + +
+ +
+
+ +
+ + + { + + Object.entries(owaspTop10List).map(([key, value]) => ( + renderAktoTestSuites({ key, value }) + )) + } + + - - -
- -
-
- -
- - - - {Object.entries(owaspTop10List).map(([key, value]) => ( - renderAktoTestSuites({ key, value }) - ))} - - - - -
-
-
-
- - - } - {openConfigurations && - - - } - +
+
+ +
+
+ + + +
+
) } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css index ba37c47a9c..c3c428eea7 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css @@ -6,38 +6,12 @@ overflow: hidden !important; } -.createTestSuiteBox { - background: linear-gradient(90deg, #FFFFFF 0%, #F5EEFF 100%) !important; - border: 1px solid; - /* border-image-source: linear-gradient(90deg, #7F1ED9 0%, #2F55D3 27.5%, #065A3D 49.5%, #052D38 71%, #6200EA 100%); */ - border-radius: var(--p-border-radius-200) !important; - border-color: #6200EA; -} - - - -.Polaris-Modal-Dialog__Modal.Polaris-Modal-Dialog--sizeLarge > .Polaris-InlineStack > .Polaris-Box > .Polaris-InlineStack > .Polaris-InlineStack .Polaris-Button--disabled > span { - font-size: 12px !important; - font-weight: 400 !important; -} - .testSuiteDisclosureButton svg { fill: #5C5F62 !important; } -.testSuiteHorizontalScroll .Polaris-Scrollable.Polaris-Scrollable--vertical.Polaris-Scrollable--horizontal{ - scrollbar-width: none; -} - .testSuiteCard .Polaris-VerticalStack{ box-shadow: 0px 1px 2px 0px #00000026, 0px 0px 5px 0px #0000000D; border-radius: 0.5rem; } -.Polaris-Modal-Section{ - min-height: 75vh !important; -} - -.testSuiteHorizontalScroll .Polaris-Scrollable.Polaris-Scrollable--vertical.Polaris-Scrollable--horizontal{ - display: flex; -} \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 8d25b2630b..9e261db4bc 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -26,7 +26,9 @@ import { RefreshMajor, CustomersMinor, EditMajor, - PlusMinor + PlusMinor, + SettingsMajor, + SettingsMinor } from '@shopify/polaris-icons'; import api from "../api"; import func from '@/util/func'; @@ -125,7 +127,7 @@ function SingleTestRunPage() { const currentTestingRuns = [] const [updateTable, setUpdateTable] = useState("") const [testRunResultsCount, setTestRunResultsCount] = useState({}) - const [testMode, setTestMode] = useState("") + const [testMode, setTestMode] = useState(false) const initialTestingObj = { testsInitiated: 0, testsInsertedInDb: 0, testingRunId: -1 } const [currentTestObj, setCurrentTestObj] = useState(initialTestingObj) @@ -523,10 +525,11 @@ function SingleTestRunPage() { return testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type !== "COLLECTION_WISE"; } + const [activeFromTesting, setActiveFromTesting] = useState(false) const resultTable = ( <> - {testMode.length > 0 ? : null} + { - const settings = transform.prepareConditionsForTesting(conditions) - await api.modifyTestingRunConfig(testingRunConfigId, settings).then(() => { - func.setToast(true, false, "Modified testing run config successfully") - setShowEditableSettings(false) - }) - } - - const editableConfigsComp = ( - setShowEditableSettings(false)} - title={"Edit test configurations"} - primaryAction={{ - content: 'Save', - onAction: () => handleModifyConfig() - }} - > - - - - - ) + // const handleModifyConfig = async () => { + // const settings = transform.prepareConditionsForTesting(conditions) + // await api.modifyTestingRunConfig(testingRunConfigId, settings).then(() => { + // func.setToast(true, false, "Modified testing run config successfully") + // setShowEditableSettings(false) + // }) + // } + + // const editableConfigsComp = ( + // setShowEditableSettings(false)} + // title={"Edit test configurations"} + // primaryAction={{ + // content: 'Save', + // onAction: () => handleModifyConfig() + // }} + // > + // + // + // + // + // ) const components = [ runningTestsComp, , - metadataComponent(), loading ? : (!workflowTest ? resultTable : workflowTestBuilder), editableConfigsComp]; + metadataComponent(), loading ? : (!workflowTest ? resultTable : workflowTestBuilder)]; const openVulnerabilityReport = async () => { const currentPageKey = "/dashboard/testing/" + selectedTestRun?.id + "/#" + selectedTab @@ -778,31 +781,27 @@ function SingleTestRunPage() { ] }) moreActionsList.push({ - title: 'Update', items: [ + title: 'Edit', + items: [ { - content: 'Edit testing config settings', - icon: EditMajor, - onAction: () => { setShowEditableSettings(true); handleAddSettings(); } + content: 'Tests', + icon: PlusMinor, + onAction: () => { setActiveFromTesting(true) } }, { - content: 'Re-Calculate Issues Count', - icon: RefreshMajor, - onAction: () => { handleRefreshTableCount(currentSummary.hexId) } + content: 'Configurations', + icon: SettingsMinor, + onAction: () => { setShowEditableSettings(true);} } ] }) moreActionsList.push({ - title: 'Edit test', - items: [ - { - content: 'Add test suites', - icon: PlusMinor, - onAction: () => { setTestMode("testSuite") } - }, + title: 'More', + items:[ { - content: 'Add individual test', - icon: PlusMinor, - onAction: () => { setTestMode("individualTest") } + content: 'Re-Calculate Issues Count', + icon: RefreshMajor, + onAction: () => { handleRefreshTableCount(currentSummary.hexId) } } ] }) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js index e0e6ac7d88..7dde541e63 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js @@ -475,11 +475,18 @@ export default { data: { latestTestingRunSummaryId, issueStatusQuery, sortKey, sortOrder, skip, limit, filters } }) }, - modifyTestingRunConfig(testingRunConfigId, testConfigsAdvancedSettings){ + modifyTestingRunConfig(testingRunConfigId, testConfigsAdvancedSettings,selectedTestRun) { + const requestData = { testingRunConfigId } + if(testConfigsAdvancedSettings){ + requestData.testConfigsAdvancedSettings = testConfigsAdvancedSettings; + } + if(selectedTestRun){ + requestData.selectedTests = selectedTestRun; + } return request({ url: '/api/modifyTestingRunConfig', method: 'post', - data: { testingRunConfigId, testConfigsAdvancedSettings } + data: requestData }) }, async fetchTestingRunResultsSummary(testingRunSummaryId) { diff --git a/apps/dashboard/web/public/test_suite.svg b/apps/dashboard/web/public/test_suite.svg new file mode 100644 index 0000000000..b68f8fb7e4 --- /dev/null +++ b/apps/dashboard/web/public/test_suite.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file From 80f1abd788926ff5f9a6a2468ba1d8cbd1ae3b6e Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Tue, 28 Jan 2025 13:39:33 +0530 Subject: [PATCH 10/20] test suite completed --- .../akto/action/testing/StartTestAction.java | 32 +++++--- .../pages/observe/api_collections/RunTest.jsx | 51 ++++++++----- .../api_collections/RunTestConfiguration.jsx | 6 +- .../observe/api_collections/RunTestSuites.jsx | 10 +-- .../SingleTestRunPage/SingleTestRunPage.js | 36 +-------- .../src/apps/dashboard/pages/testing/api.js | 10 +-- .../apps/dashboard/pages/testing/transform.js | 19 +++++ .../config/EditableTestingRunConfig.java | 76 +++++++++++++++++++ 8 files changed, 160 insertions(+), 80 deletions(-) create mode 100644 libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 70a717fbcf..8bed864c74 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -6,6 +6,7 @@ import com.akto.dao.testing.sources.TestSourceConfigsDao; import com.akto.dao.testing_run_findings.TestingRunIssuesDao; import com.akto.dao.testing.*; +import com.akto.dao.testing.config.EditableTestingRunConfig; import com.akto.dto.ApiInfo; import com.akto.dto.User; import com.akto.dto.ApiToken.Utility; @@ -1126,18 +1127,27 @@ public String getCurrentTestStateStatus(){ return Action.SUCCESS.toUpperCase(); } + private EditableTestingRunConfig editableTestingRunConfig; + public String modifyTestingRunConfig(){ - if(this.selectedTests == null){ - TestingRunConfigDao.instance.updateOne( - Filters.eq(Constants.ID, this.testingRunConfigId), - Updates.set("configsAdvancedSettings", this.testConfigsAdvancedSettings) + TestingRunConfigDao.instance.updateOne( + Filters.eq(Constants.ID, this.testingRunConfigId), + Updates.combine( + Updates.set("configsAdvancedSettings", this.editableTestingRunConfig.getTestConfigsAdvancedSettings()), + Updates.set("testSubCategoryList", this.editableTestingRunConfig.getSubCategoriesList()), + Updates.set("testRoleId", this.editableTestingRunConfig.getTestRoleId()), + Updates.set("overriddenTestAppUrl", this.editableTestingRunConfig.getOverriddenTestAppUrl()) + ) ); - } else { - TestingRunConfigDao.instance.updateOne( - Filters.eq(Constants.ID, this.testingRunConfigId), - Updates.set("testSubCategoryList", this.selectedTests) - ); + if(editableTestingRunConfig.getTestingRunHexId() != null){ + TestingRunDao.instance.updateOne( + Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId())), + Updates.combine( + Updates.set("testRunTime", this.editableTestingRunConfig.getTestRunTime()), + Updates.set("maxConcurrentRequests", this.editableTestingRunConfig.getMaxConcurrentRequests()) + ) + ); } return SUCCESS.toUpperCase(); } @@ -1577,6 +1587,10 @@ public void setTestingRunConfigId(int testingRunConfigId) { this.testingRunConfigId = testingRunConfigId; } + public void setEditableTestingRunConfig(EditableTestingRunConfig editableTestingRunConfig) { + this.editableTestingRunConfig = editableTestingRunConfig; + } + public Map getTestCountMap() { return testCountMap; } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index 17dbab29ce..946d0d5521 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -17,7 +17,7 @@ import RunTestSuites from "./RunTestSuites"; import RunTestConfiguration from "./RunTestConfiguration"; -function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings }) { +function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings, parentAdvanceSettingsConfig }) { const initialState = { categories: [], @@ -234,11 +234,15 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu return true; } - // Update state only if there is a change if (!areObjectArraysEqual(updatedTests, testRun.tests)) { + handleAddSettings(parentAdvanceSettingsConfig); setTestRun(prev => ({ ...testRun, tests: updatedTests, + overriddenTestAppUrl: testIdConfig.testingRunConfig.overriddenTestAppUrl, + maxConcurrentRequests: testIdConfig.maxConcurrentRequests, + testRunTime: testIdConfig.testRunTime, + testRoleId: testIdConfig.testingRunConfig.testRoleId, })); } } @@ -246,6 +250,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu }, [shouldRuntestConfig]) + const toggleRunTest = () => { if (activeFromTesting) { setActiveFromTesting(false); @@ -279,8 +284,6 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } } - const [testSuite, testSuiteToggle] = useState(false); - const activator = (
@@ -622,27 +625,34 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu setTestMode(check); } + // only for configurations const handleModifyConfig = async () => { const settings = transform.prepareConditionsForTesting(conditions) - await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, settings).then(() => { + const editableConfigObject = transform.prepareEditableConfigObject(testRun, settings, testIdConfig.hexId) + await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, editableConfigObject).then(() => { func.setToast(true, false, "Modified testing run config successfully") setShowEditableSettings(false) }) + + if(activeFromTesting){ + transform.rerunTest(testIdConfig.hexId, null, true) + } } - const handleAddSettings = () => { - if (conditions.length === 0 && testingRunConfigSettings.length > 0) { - testingRunConfigSettings.forEach((condition) => { - const operatorType = condition.operatorType - condition.operationsGroupList.forEach((obj) => { - const finalObj = { 'data': obj, 'operator': { 'type': operatorType } } - dispatchConditions({ type: "add", obj: finalObj }) + const handleAddSettings = (parentAdvanceSettingsConfig) => { + if (parentAdvanceSettingsConfig.length > 0) { + dispatchConditions({ type: "clear" }); + parentAdvanceSettingsConfig.forEach((condition) => { + const operatorType = condition.operator.type + const obj = condition.data + const finalObj = { 'data': obj, 'operator': { 'type': operatorType } } + dispatchConditions({ type: "add", obj: finalObj }) + }) - }) } - } + } + - // still not working properly need api to modify test_run object const editableConfigsComp = ( handleModifyConfig() + onAction: () => { handleModifyConfig(); }, }} > <> {!parentActivator ? activator : null} - {showEditableSettings? editableConfigsComp: null} + {showEditableSettings ? editableConfigsComp : null} } primaryAction={{ content: activeFromTesting ? "Save & Re-run" : scheduleString(), - onAction: handleRun, + onAction: activeFromTesting? handleModifyConfig: handleRun, disabled: (countAllSelectedTests() === 0) || !testRun.authMechanismPresent }} secondaryActions={[ @@ -829,6 +840,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu
: + checkRemoveAll={checkRemoveAll} handleModifyConfig={handleModifyConfig} /> : <> { +const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration,getLabel,showEditableSettings }) => { return ( @@ -25,10 +25,10 @@ const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes continuousTesting, runTypeLabel: runType.label })); - }} /> + }} disabled={showEditableSettings} /> { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index 98d6a18b19..2c00aa4a08 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from "react"; import "./run_test_suites.css" -function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handleRemoveAll }) { +function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handleRemoveAll,handleModifyConfig }) { const [owaspTop10, owaspTop10Toggle] = useState(true); @@ -21,14 +21,6 @@ function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handle "Unsafe Consumption of APIs": ["COMMAND_INJECTION", "INJ", "CRLF", "SSTI", "LFI", "XSS", "INJECT"] } - const [shouldCallFunction, setShouldCallFunction] = useState(false); - - - useEffect(() => { - if (shouldCallFunction) handleRun(); - setShouldCallFunction(false); - }, [shouldCallFunction]); - function createAcronym(str) { return str.split(/\s+/).map(word => word.charAt(0).toUpperCase()).join(''); } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 9e261db4bc..6b6fc83d95 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -328,6 +328,8 @@ function SingleTestRunPage() { return { value: transform.getPrettifiedTestRunResults(testRunResultsRes), total: selectedTab === 'ignored_issues' ? totalIgnoredIssuesCount : testRunCountMap[tableTabMap[selectedTab]] } } + useEffect(() => {handleAddSettings()}, [testingRunConfigSettings]) + useEffect(() => { fetchTestingRunResultSummaries() filters = func.getCollectionFilters(filters) @@ -529,7 +531,7 @@ function SingleTestRunPage() { const resultTable = ( <> - + { - // const settings = transform.prepareConditionsForTesting(conditions) - // await api.modifyTestingRunConfig(testingRunConfigId, settings).then(() => { - // func.setToast(true, false, "Modified testing run config successfully") - // setShowEditableSettings(false) - // }) - // } - - // const editableConfigsComp = ( - // setShowEditableSettings(false)} - // title={"Edit test configurations"} - // primaryAction={{ - // content: 'Save', - // onAction: () => handleModifyConfig() - // }} - // > - // - // - // - // - // ) const components = [ runningTestsComp, , @@ -791,7 +763,7 @@ function SingleTestRunPage() { { content: 'Configurations', icon: SettingsMinor, - onAction: () => { setShowEditableSettings(true);} + onAction: () => { setShowEditableSettings(true); handleAddSettings()} } ] }) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js index 7dde541e63..34d9b69b43 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js @@ -475,14 +475,8 @@ export default { data: { latestTestingRunSummaryId, issueStatusQuery, sortKey, sortOrder, skip, limit, filters } }) }, - modifyTestingRunConfig(testingRunConfigId, testConfigsAdvancedSettings,selectedTestRun) { - const requestData = { testingRunConfigId } - if(testConfigsAdvancedSettings){ - requestData.testConfigsAdvancedSettings = testConfigsAdvancedSettings; - } - if(selectedTestRun){ - requestData.selectedTests = selectedTestRun; - } + modifyTestingRunConfig(testingRunConfigId, editableConfigObject) { + const requestData = { testingRunConfigId, editableTestingRunConfig: editableConfigObject } return request({ url: '/api/modifyTestingRunConfig', method: 'post', diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js index 4d7fd2925b..bbe80f4b93 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js @@ -1119,6 +1119,25 @@ getMissingConfigs(testResults){ operationsGroupList: tempObj[key], }; }); + }, + prepareEditableConfigObject(testRun,settings,hexId){ + const tests = testRun.tests; + const selectedTests = [] + Object.keys(tests).forEach(category => { + tests[category].forEach(test => { + if (test.selected) selectedTests.push(test.value) + }) + }) + + return { + testConfigsAdvancedSettings:settings, + testRoleId: testRun.testRoleId, + subCategoriesList: selectedTests, + overriddenTestAppUrl: testRun.overriddenTestAppUrl, + maxConcurrentRequests: testRun.maxConcurrentRequests, + testingRunHexId: hexId, + testRunTime: testRun.testRunTime, + } } } diff --git a/libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java b/libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java new file mode 100644 index 0000000000..aacb458604 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java @@ -0,0 +1,76 @@ +package com.akto.dao.testing.config; + +import java.util.List; + +import com.akto.dto.CollectionConditions.TestConfigsAdvancedSettings; + +public class EditableTestingRunConfig { + private List subCategoriesList; + private List testConfigsAdvancedSettings; + private String overriddenTestAppUrl; + private String testRoleId; + private int maxConcurrentRequests; + private String testingRunHexId; + private int testRunTime; + + public EditableTestingRunConfig() { + + } + + public List getSubCategoriesList() { + return subCategoriesList; + } + + public int getTestRunTime() { + return testRunTime; + } + + public void setTestRunTime(int testRunTime) { + this.testRunTime = testRunTime; + } + + public void setSubCategoriesList(List subCategoriesList) { + this.subCategoriesList = subCategoriesList; + } + + public List getTestConfigsAdvancedSettings() { + return testConfigsAdvancedSettings; + } + + public void setTestConfigsAdvancedSettings(List testConfigsAdvancedSettings) { + this.testConfigsAdvancedSettings = testConfigsAdvancedSettings; + } + + public String getOverriddenTestAppUrl() { + return overriddenTestAppUrl; + } + + public void setOverriddenTestAppUrl(String overriddenTestAppUrl) { + this.overriddenTestAppUrl = overriddenTestAppUrl; + } + + public String getTestRoleId() { + return testRoleId; + } + + public void setTestRoleId(String testRoleId) { + this.testRoleId = testRoleId; + } + + public int getMaxConcurrentRequests() { + return maxConcurrentRequests; + } + + public void setMaxConcurrentRequests(int maxConcurrentRequests) { + this.maxConcurrentRequests = maxConcurrentRequests; + } + + public String getTestingRunHexId() { + return testingRunHexId; + } + + public void setTestingRunHexId(String testingRunHexId) { + this.testingRunHexId = testingRunHexId; + } +} + From ab9c27dd398f922b3b2c0d3893bd1b456740f29b Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Tue, 28 Jan 2025 17:51:38 +0530 Subject: [PATCH 11/20] unnecessary fetchSubAllCategory reduced, all values are fetched correctly now --- .../pages/observe/api_collections/RunTest.jsx | 16 ++++-- .../api_collections/RunTestConfiguration.jsx | 4 +- .../SingleTestRunPage/SingleTestRunPage.js | 49 +++++++++---------- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index 946d0d5521..c61f673825 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -121,14 +121,16 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu subCategories: [], testSourceConfigs: [] } - if (!useLocalSubCategoryData) { - metaDataObj = await transform.getAllSubcategoriesData(true, "runTests") - } else { + + if ((localCategoryMap && Object.keys(localCategoryMap).length > 0) && (localSubCategoryMap && Object.keys(localSubCategoryMap).length > 0)) { metaDataObj = { categories: Object.values(localCategoryMap), subCategories: Object.values(localSubCategoryMap), testSourceConfigs: [] } + + } else { + metaDataObj = await transform.getAllSubcategoriesData(true, "runTests") } let categories = metaDataObj.categories let businessLogicSubcategories = metaDataObj.subCategories @@ -194,7 +196,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const testSubCategoryList = [...testIdConfig.testingRunConfig.testSubCategoryList]; const updatedTests = { ...testRun.tests }; - + // console.log("updatedTests", updatedTests); // Reset all test selections Object.keys(updatedTests).forEach(category => { updatedTests[category] = updatedTests[category].map(test => ({ ...test, selected: false })); @@ -243,6 +245,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu maxConcurrentRequests: testIdConfig.maxConcurrentRequests, testRunTime: testIdConfig.testRunTime, testRoleId: testIdConfig.testingRunConfig.testRoleId, + testRunTimeLabel:getLabel(testRunTimeOptions, testIdConfig.testRunTime.toString())?.label, + testRoleLabel: getLabel(testRolesArr, testIdConfig.testingRunConfig.testRoleId).label, })); } } @@ -435,7 +439,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu return abc }, []) - const testRunTimeOptions = [...runTimeMinutes, ...runTimeHours] + const testRunTimeOptions = [{label:"Default",value:"-1"},...runTimeMinutes, ...runTimeHours] const runTypeOptions = [{ label: "Daily", value: "Daily" }, { label: "Continuously", value: "Continuously" }, { label: "Now", value: "Now" }] @@ -850,6 +854,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu maxConcurrentRequestsOptions={maxConcurrentRequestsOptions} slackIntegrated={slackIntegrated} generateLabelForSlackIntegration={generateLabelForSlackIntegration} + getLabel={getLabel} /> @@ -876,6 +881,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu maxConcurrentRequestsOptions={maxConcurrentRequestsOptions} slackIntegrated={slackIntegrated} generateLabelForSlackIntegration={generateLabelForSlackIntegration} + getLabel={getLabel} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx index efad10f53d..61b7d1acd6 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx @@ -70,7 +70,7 @@ const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes Select Test Role { let testRole; if (!(requests === "No test role selected")) { testRole = requests; } @@ -87,7 +87,7 @@ const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes Max Concurrent Requests { let maxConcurrentRequests; if (requests === "Default") maxConcurrentRequests = -1; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 6b6fc83d95..08dd6c6bcb 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -72,10 +72,10 @@ let filters = [ label: 'Severity', title: 'Severity', choices: [ - {label: 'Critical', value: 'CRITICAL'}, - {label: 'High', value: 'HIGH'}, - {label: 'Medium', value: 'MEDIUM'}, - {label: 'Low', value: 'LOW'} + { label: 'Critical', value: 'CRITICAL' }, + { label: 'High', value: 'HIGH' }, + { label: 'Medium', value: 'MEDIUM' }, + { label: 'Low', value: 'LOW' } ], }, { @@ -143,6 +143,9 @@ function SingleTestRunPage() { const [allResultsLength, setAllResultsLength] = useState(undefined) const [currentSummary, setCurrentSummary] = useState('') const [pageTotalCount, setPageTotalCount] = useState(0) + const localCategoryMap = LocalStore.getState().categoryMap + const localSubCategoryMap = LocalStore.getState().subCategoryMap + const [useLocalSubCategoryData, setUseLocalSubCategoryData] = useState(false) const tableTabMap = { vulnerable: "VULNERABLE", @@ -224,17 +227,13 @@ function SingleTestRunPage() { } } - const [filteredEndpoints, setFilteredEndpoints] = useState([]) - useEffect(() => { setUpdateTable(Date.now().toString()) - if (testingRunResultSummariesObj?.testingRun?.testingEndpoints.type === "COLLECTION_WISE") { - api.fetchCollectionWiseApiEndpoints(testingRunResultSummariesObj?.testingRun.testingEndpoints.apiCollectionId).then((res) => { - setFilteredEndpoints([...res?.listOfEndpointsInCollection]); - }) - } - else if (testingRunResultSummariesObj?.testingRun?.testingEndpoints.type === "CUSTOM") { - setFilteredEndpoints([...testingRunResultSummariesObj?.testingRun.testingEndpoints.apisList]); + if ( + (localCategoryMap && Object.keys(localCategoryMap).length > 0) && + (localSubCategoryMap && Object.keys(localSubCategoryMap).length > 0) + ) { + setUseLocalSubCategoryData(true) } }, [testingRunResultSummariesObj]) @@ -328,7 +327,7 @@ function SingleTestRunPage() { return { value: transform.getPrettifiedTestRunResults(testRunResultsRes), total: selectedTab === 'ignored_issues' ? totalIgnoredIssuesCount : testRunCountMap[tableTabMap[selectedTab]] } } - useEffect(() => {handleAddSettings()}, [testingRunConfigSettings]) + useEffect(() => { handleAddSettings() }, [testingRunConfigSettings]) useEffect(() => { fetchTestingRunResultSummaries() @@ -522,16 +521,12 @@ function SingleTestRunPage() { return (testingEndpoints.apisList?.length > 0) ? testingEndpoints.apisList[0].apiCollectionId : undefined; } - - function checkFiltered() { - return testingRunResultSummariesObj?.testingRun?.testingEndpoints?.type !== "COLLECTION_WISE"; - } - + const [activeFromTesting, setActiveFromTesting] = useState(false) const resultTable = ( <> - + { selectedTestRun?.severity && - selectedTestRun.severity.map((item) =>{ + selectedTestRun.severity.map((item) => { const sev = item.split(' ') const tempSev = sev.length > 1 ? sev[1].toUpperCase() : '' - return( + return (
- {item} + {item}
) } - + )} - + @@ -763,13 +758,13 @@ function SingleTestRunPage() { { content: 'Configurations', icon: SettingsMinor, - onAction: () => { setShowEditableSettings(true); handleAddSettings()} + onAction: () => { setShowEditableSettings(true); handleAddSettings() } } ] }) moreActionsList.push({ title: 'More', - items:[ + items: [ { content: 'Re-Calculate Issues Count', icon: RefreshMajor, From 1e459ef051c9a4cd47c1983e8d4045b38a2516e5 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Wed, 29 Jan 2025 11:51:17 +0530 Subject: [PATCH 12/20] select testModal selection minor bug fixed --- .../pages/observe/api_collections/RunTest.jsx | 17 ++++------------- .../api_collections/RunTestConfiguration.jsx | 1 + .../observe/api_collections/RunTestSuites.jsx | 5 +++-- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index c61f673825..a2f2edeeff 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -245,7 +245,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu maxConcurrentRequests: testIdConfig.maxConcurrentRequests, testRunTime: testIdConfig.testRunTime, testRoleId: testIdConfig.testingRunConfig.testRoleId, - testRunTimeLabel:getLabel(testRunTimeOptions, testIdConfig.testRunTime.toString())?.label, + testRunTimeLabel:(testIdConfig.testRunTime===-1)?"30 minutes":getLabel(testRunTimeOptions, testIdConfig.testRunTime.toString())?.label, testRoleLabel: getLabel(testRolesArr, testIdConfig.testingRunConfig.testRoleId).label, })); } @@ -439,7 +439,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu return abc }, []) - const testRunTimeOptions = [{label:"Default",value:"-1"},...runTimeMinutes, ...runTimeHours] + const testRunTimeOptions = [...runTimeMinutes, ...runTimeHours] const runTypeOptions = [{ label: "Daily", value: "Daily" }, { label: "Continuously", value: "Continuously" }, { label: "Now", value: "Now" }] @@ -540,18 +540,9 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } if (filtered || selectedResourcesForPrimaryAction?.length > 0) { - if (testIdConfig) { - await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, null, selectedTests) - transform.rerunTest(testIdConfig.hexId, null, true) - - } - else await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) + await observeApi.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, "TESTING_UI", testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) } else { - if (testIdConfig) { - await testingApi.modifyTestingRunConfig(testIdConfig?.testingRunConfig?.id, null, selectedTests) - transform.rerunTest(testIdConfig.hexId, null, true) - } - else observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) + await observeApi.scheduleTestForCollection(collectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests, overriddenTestAppUrl, testRoleId, continuousTesting, sendSlackAlert, finalAdvancedConditions, cleanUpTestingResources) } setActive(false) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx index 61b7d1acd6..044fdc95fa 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestConfiguration.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { VerticalStack, HorizontalGrid, Checkbox, TextField, Text } from '@shopify/polaris'; import Dropdown from "../../../components/layouts/Dropdown"; +import func from "@/util/func" const RunTestConfiguration = ({ testRun, setTestRun, runTypeOptions, hourlyTimes, testRunTimeOptions, testRolesArr, maxConcurrentRequestsOptions, slackIntegrated, generateLabelForSlackIntegration,getLabel,showEditableSettings }) => { return ( diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index 2c00aa4a08..e1ef0d3d0e 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -28,10 +28,11 @@ function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handle function handleTestSuiteSelection(key, data) { setTestRun(prev => { const updatedTests = { ...prev.tests }; - let someSelected = true + const someSelected = data.some(category => + updatedTests[category]?.some(test => test.selected) + ); data.forEach(category => { if (updatedTests[category]) { - someSelected = updatedTests[category].some(test => test.selected); updatedTests[category] = updatedTests[category].map(test => ({ ...test, selected: someSelected ? false : true From 53dadd1888ddb59181264f1cdb196791a36a9fca Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Wed, 29 Jan 2025 13:21:45 +0530 Subject: [PATCH 13/20] testRunType reflect --- .../dashboard/pages/observe/api_collections/RunTest.jsx | 9 ++++++++- .../pages/testing/SingleTestRunPage/SingleTestRunPage.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index a2f2edeeff..e0cb3d85be 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -17,7 +17,7 @@ import RunTestSuites from "./RunTestSuites"; import RunTestConfiguration from "./RunTestConfiguration"; -function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings, parentAdvanceSettingsConfig }) { +function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings, parentAdvanceSettingsConfig,testRunType }) { const initialState = { categories: [], @@ -238,6 +238,12 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu if (!areObjectArraysEqual(updatedTests, testRun.tests)) { handleAddSettings(parentAdvanceSettingsConfig); + const getRunTypeLabel = (runType) => { + if(!runType) return "Now"; + if (runType === "CI-CD" || runType === "ONE_TIME") return "Now"; + else if(runType === "DAILY") return "Daily"; + else if(runType === "CONTINUOUSLY") return "Continuously"; + } setTestRun(prev => ({ ...testRun, tests: updatedTests, @@ -247,6 +253,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu testRoleId: testIdConfig.testingRunConfig.testRoleId, testRunTimeLabel:(testIdConfig.testRunTime===-1)?"30 minutes":getLabel(testRunTimeOptions, testIdConfig.testRunTime.toString())?.label, testRoleLabel: getLabel(testRolesArr, testIdConfig.testingRunConfig.testRoleId).label, + runTypeLabel:getRunTypeLabel(testRunType) })); } } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 08dd6c6bcb..e962fb9fac 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -526,7 +526,7 @@ function SingleTestRunPage() { const resultTable = ( <> - + Date: Wed, 29 Jan 2025 13:25:43 +0530 Subject: [PATCH 14/20] getRunTypeLabelFixed --- .../apps/dashboard/pages/observe/api_collections/RunTest.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index e0cb3d85be..3c85ccfe4b 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -241,8 +241,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const getRunTypeLabel = (runType) => { if(!runType) return "Now"; if (runType === "CI-CD" || runType === "ONE_TIME") return "Now"; - else if(runType === "DAILY") return "Daily"; - else if(runType === "CONTINUOUSLY") return "Continuously"; + else if(runType === "RECURRING") return "Daily"; + else if(runType === "CONTINUOUS_TESTING") return "Continuously"; } setTestRun(prev => ({ ...testRun, From 6f7f1ac3f4d0dfb9a1a87324854d4b7adfa1486b Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Thu, 30 Jan 2025 11:19:23 +0530 Subject: [PATCH 15/20] Adding no upsert check --- .../main/java/com/akto/action/testing/StartTestAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 0f33bf74d5..b18ecb66b9 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -1130,7 +1130,7 @@ public String getCurrentTestStateStatus(){ private EditableTestingRunConfig editableTestingRunConfig; public String modifyTestingRunConfig(){ - TestingRunConfigDao.instance.updateOne( + TestingRunConfigDao.instance.updateOneNoUpsert( Filters.eq(Constants.ID, this.testingRunConfigId), Updates.combine( Updates.set("configsAdvancedSettings", this.editableTestingRunConfig.getTestConfigsAdvancedSettings()), @@ -1141,7 +1141,7 @@ public String modifyTestingRunConfig(){ ); if(editableTestingRunConfig.getTestingRunHexId() != null){ - TestingRunDao.instance.updateOne( + TestingRunDao.instance.updateOneNoUpsert( Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId())), Updates.combine( Updates.set("testRunTime", this.editableTestingRunConfig.getTestRunTime()), From 9bfcaf40f14972a0c3f4ae59ad4dc56fee44ea17 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Thu, 30 Jan 2025 12:55:51 +0530 Subject: [PATCH 16/20] test selected text changed in test modal --- .../pages/observe/api_collections/RunTest.jsx | 47 ++++++++++++------- .../observe/api_collections/RunTestSuites.jsx | 27 +++++++++-- .../api_collections/run_test_suites.css | 4 ++ 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index 3c85ccfe4b..e6cf032846 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -17,7 +17,7 @@ import RunTestSuites from "./RunTestSuites"; import RunTestConfiguration from "./RunTestConfiguration"; -function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings, parentAdvanceSettingsConfig,testRunType }) { +function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOutside, closeRunTest, selectedResourcesForPrimaryAction, useLocalSubCategoryData, preActivator, testIdConfig, activeFromTesting, setActiveFromTesting, showEditableSettings, setShowEditableSettings, parentAdvanceSettingsConfig, testRunType }) { const initialState = { categories: [], @@ -121,15 +121,15 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu subCategories: [], testSourceConfigs: [] } - + if ((localCategoryMap && Object.keys(localCategoryMap).length > 0) && (localSubCategoryMap && Object.keys(localSubCategoryMap).length > 0)) { metaDataObj = { categories: Object.values(localCategoryMap), subCategories: Object.values(localSubCategoryMap), testSourceConfigs: [] } - - } else { + + } else { metaDataObj = await transform.getAllSubcategoriesData(true, "runTests") } let categories = metaDataObj.categories @@ -239,10 +239,10 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu if (!areObjectArraysEqual(updatedTests, testRun.tests)) { handleAddSettings(parentAdvanceSettingsConfig); const getRunTypeLabel = (runType) => { - if(!runType) return "Now"; + if (!runType) return "Now"; if (runType === "CI-CD" || runType === "ONE_TIME") return "Now"; - else if(runType === "RECURRING") return "Daily"; - else if(runType === "CONTINUOUS_TESTING") return "Continuously"; + else if (runType === "RECURRING") return "Daily"; + else if (runType === "CONTINUOUS_TESTING") return "Continuously"; } setTestRun(prev => ({ ...testRun, @@ -251,9 +251,9 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu maxConcurrentRequests: testIdConfig.maxConcurrentRequests, testRunTime: testIdConfig.testRunTime, testRoleId: testIdConfig.testingRunConfig.testRoleId, - testRunTimeLabel:(testIdConfig.testRunTime===-1)?"30 minutes":getLabel(testRunTimeOptions, testIdConfig.testRunTime.toString())?.label, + testRunTimeLabel: (testIdConfig.testRunTime === -1) ? "30 minutes" : getLabel(testRunTimeOptions, testIdConfig.testRunTime.toString())?.label, testRoleLabel: getLabel(testRolesArr, testIdConfig.testingRunConfig.testRoleId).label, - runTypeLabel:getRunTypeLabel(testRunType) + runTypeLabel: getRunTypeLabel(testRunType) })); } } @@ -265,8 +265,19 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const toggleRunTest = () => { if (activeFromTesting) { setActiveFromTesting(false); + setShouldRuntestConfig(true); return; } + if (!activeFromTesting) { + setTestRun(prev => { + const tests = { ...testRun.tests } + Object.keys(tests).forEach(category => { + tests[category] = tests[category].map(test => ({ ...test, selected: false })) + }) + + return { ...prev, tests: tests, testName: convertToLowerCaseWithUnderscores(apiCollectionName) } + }) + } setActive(prev => !prev) if (active) { if (closeRunTest !== undefined) closeRunTest() @@ -636,7 +647,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu setShowEditableSettings(false) }) - if(activeFromTesting){ + if (activeFromTesting) { transform.rerunTest(testIdConfig.hexId, null, true) } } @@ -707,7 +718,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu } primaryAction={{ content: activeFromTesting ? "Save & Re-run" : scheduleString(), - onAction: activeFromTesting? handleModifyConfig: handleRun, + onAction: activeFromTesting ? handleModifyConfig : handleRun, disabled: (countAllSelectedTests() === 0) || !testRun.authMechanismPresent }} secondaryActions={[ @@ -767,12 +778,14 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu onChange={(testName) => setTestRun(prev => ({ ...prev, testName: testName }))} />
- - +
+ +
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx index e1ef0d3d0e..684035582a 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTestSuites.jsx @@ -109,6 +109,26 @@ function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handle return true; } + function checkifSelected(data) { + let text = `${countTestSuitesTests(data)} tests`; + let isSomeSelected = false; + let countSelected = 0; + for (const category of data) { + if (testRun.tests[category] && testRun.tests[category].length > 0) { + if (testRun.tests[category].some(test => test.selected)) { + isSomeSelected = true; + } + testRun.tests[category]?.forEach(test => { + if (test.selected) { + countSelected++; + } + }); + } + } + if(isSomeSelected === false) return text; + else return `${countSelected} out of ${countTestSuitesTests(data)} selected`; + } + function renderAktoTestSuites(data) { return (
@@ -126,7 +146,7 @@ function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handle {data?.key} } - helpText={`${countTestSuitesTests(data?.value)} tests`} + helpText={checkifSelected(data?.value)} onChange={() => { handleTestSuiteSelection(data?.key, data?.value) }} checked={checkedSelected(data?.value)} disabled={checkDisableTestSuite(data?.value)} @@ -164,12 +184,13 @@ function RunTestSuites({ testRun, setTestRun, handleRun, checkRemoveAll, handle onChange={(testName) => setTestRun(prev => ({ ...prev, testName: testName }))} />
- +
+ disabled={checkRemoveAll()}>
Clear selection
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css index c3c428eea7..4eae48793a 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/run_test_suites.css @@ -15,3 +15,7 @@ border-radius: 0.5rem; } +.removeAllButton .Polaris-Button--plain.Polaris-Button--disabled svg { + fill: var(--p-color-icon-disabled) !important; +} + From 28c20112ee8189105eb128015b6e8206d43b79b2 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Thu, 30 Jan 2025 16:45:36 +0530 Subject: [PATCH 17/20] unit test added for testModifyTestingRunConfig --- .../akto/action/testing/StartTestAction.java | 53 ++++++++----- .../action/testing/TestStartTestAction.java | 50 ++++++++++++ .../apps/dashboard/pages/testing/transform.js | 4 +- .../config/EditableTestingRunConfig.java | 76 ------------------- .../config/EditableTestingRunConfig.java | 37 +++++++++ 5 files changed, 124 insertions(+), 96 deletions(-) delete mode 100644 libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java create mode 100644 libs/dao/src/main/java/com/akto/dto/testing/config/EditableTestingRunConfig.java diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 8bed864c74..98b993fd92 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -6,7 +6,7 @@ import com.akto.dao.testing.sources.TestSourceConfigsDao; import com.akto.dao.testing_run_findings.TestingRunIssuesDao; import com.akto.dao.testing.*; -import com.akto.dao.testing.config.EditableTestingRunConfig; +import com.akto.dto.testing.config.EditableTestingRunConfig; import com.akto.dto.ApiInfo; import com.akto.dto.User; import com.akto.dto.ApiToken.Utility; @@ -1130,24 +1130,41 @@ public String getCurrentTestStateStatus(){ private EditableTestingRunConfig editableTestingRunConfig; public String modifyTestingRunConfig(){ - TestingRunConfigDao.instance.updateOne( - Filters.eq(Constants.ID, this.testingRunConfigId), - Updates.combine( - Updates.set("configsAdvancedSettings", this.editableTestingRunConfig.getTestConfigsAdvancedSettings()), - Updates.set("testSubCategoryList", this.editableTestingRunConfig.getSubCategoriesList()), - Updates.set("testRoleId", this.editableTestingRunConfig.getTestRoleId()), - Updates.set("overriddenTestAppUrl", this.editableTestingRunConfig.getOverriddenTestAppUrl()) - ) - ); + if (editableTestingRunConfig == null) { + return Action.ERROR.toUpperCase(); + } + try { + if (this.testingRunConfigId == 0) { + throw new Exception(); + } else { + TestingRunConfigDao.instance.updateOne( + Filters.eq(Constants.ID, this.testingRunConfigId), + Updates.combine( + Updates.set("configsAdvancedSettings", this.editableTestingRunConfig.getConfigsAdvancedSettings()), + Updates.set("testSubCategoryList", this.editableTestingRunConfig.getTestSubCategoryList()), + Updates.set("testRoleId", this.editableTestingRunConfig.getTestRoleId()), + Updates.set("overriddenTestAppUrl", this.editableTestingRunConfig.getOverriddenTestAppUrl()) + ) + ); + } + + if (editableTestingRunConfig.getTestingRunHexId() != null) { + String hexId = editableTestingRunConfig.getTestingRunHexId(); - if(editableTestingRunConfig.getTestingRunHexId() != null){ - TestingRunDao.instance.updateOne( - Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId())), - Updates.combine( - Updates.set("testRunTime", this.editableTestingRunConfig.getTestRunTime()), - Updates.set("maxConcurrentRequests", this.editableTestingRunConfig.getMaxConcurrentRequests()) - ) - ); + ObjectId objectId = new ObjectId(hexId); + + TestingRunDao.instance.updateOne( + Filters.eq(Constants.ID, objectId), + Updates.combine( + Updates.set("testRunTime", this.editableTestingRunConfig.getTestRunTime()), + Updates.set("maxConcurrentRequests", this.editableTestingRunConfig.getMaxConcurrentRequests()) + ) + ); + + } + + } catch (Exception e) { + return Action.ERROR.toUpperCase(); } return SUCCESS.toUpperCase(); } diff --git a/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java b/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java index 7f24640c96..4b9ca27464 100644 --- a/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java +++ b/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java @@ -8,8 +8,10 @@ import com.akto.dao.UsersDao; import com.akto.dao.billing.OrganizationsDao; import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunConfigDao; import com.akto.dao.testing.TestingRunDao; import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dto.testing.config.EditableTestingRunConfig; import com.akto.dto.AccountSettings; import com.akto.dto.ApiInfo; import com.akto.dto.ApiToken; @@ -266,4 +268,52 @@ public void testStartCICDTest() throws IOException, ServletException { } + @Test + public void testModifyTestingRunConfig() { + + int testingRunConfigId = UUID.randomUUID().hashCode() & 0xfffffff; + ObjectId testingRunHexId = new ObjectId(); + + List list1 = Arrays.asList("pkucgztk", "oionxmec", "tmptskkz", "rorcqyqf"); + List list2 = Arrays.asList("sqhrduyv", "awpqhaxz", "tdzydooe", "hxwvtoem"); + TestingRunConfig testingRunConfig = new TestingRunConfig(); + testingRunConfig.setTestRoleId("initialRole"); + testingRunConfig.setOverriddenTestAppUrl("https://initial.url"); + testingRunConfig.setTestSubCategoryList(list1); + + TestingRunConfigDao.instance.insertOne(testingRunConfig); + + TestingRun testingRun = new TestingRun(); + testingRun.setTestIdConfig(testingRunConfigId); + testingRun.setTestRunTime(1800); + testingRun.setMaxConcurrentRequests(5); + testingRun.setId(testingRunHexId); + + TestingRunDao.instance.insertOne(testingRun); + + EditableTestingRunConfig editableTestingRunConfig = new EditableTestingRunConfig(); + editableTestingRunConfig.setTestRunTime(3600); + editableTestingRunConfig.setTestRoleId("newRole"); + editableTestingRunConfig.setOverriddenTestAppUrl("https://test.url"); + editableTestingRunConfig.setMaxConcurrentRequests(10); + editableTestingRunConfig.setTestSubCategoryList(list2); + + StartTestAction startTestAction = new StartTestAction(); + startTestAction.setTestingRunConfigId(testingRunConfigId); + startTestAction.setEditableTestingRunConfig(editableTestingRunConfig); + + String result = startTestAction.modifyTestingRunConfig(); + + assertEquals(Action.SUCCESS.toUpperCase(), result); + + TestingRunConfig updatedConfig = TestingRunConfigDao.instance.findOne(Filters.eq(Constants.ID, testingRunConfigId)); + assertEquals("newRole", updatedConfig.getTestRoleId()); + assertEquals("https://test.url", updatedConfig.getOverriddenTestAppUrl()); + assertEquals(list2, updatedConfig.getTestSubCategoryList()); + + TestingRun updatedTestingRun = TestingRunDao.instance.findOne(Filters.eq(Constants.ID, testingRunHexId)); + assertEquals(3600, updatedTestingRun.getTestRunTime()); + assertEquals(10, updatedTestingRun.getMaxConcurrentRequests()); + } + } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js index bbe80f4b93..20655dc14c 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/transform.js @@ -1130,9 +1130,9 @@ getMissingConfigs(testResults){ }) return { - testConfigsAdvancedSettings:settings, + configsAdvancedSettings:settings, testRoleId: testRun.testRoleId, - subCategoriesList: selectedTests, + testSubCategoryList: selectedTests, overriddenTestAppUrl: testRun.overriddenTestAppUrl, maxConcurrentRequests: testRun.maxConcurrentRequests, testingRunHexId: hexId, diff --git a/libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java b/libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java deleted file mode 100644 index aacb458604..0000000000 --- a/libs/dao/src/main/java/com/akto/dao/testing/config/EditableTestingRunConfig.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.akto.dao.testing.config; - -import java.util.List; - -import com.akto.dto.CollectionConditions.TestConfigsAdvancedSettings; - -public class EditableTestingRunConfig { - private List subCategoriesList; - private List testConfigsAdvancedSettings; - private String overriddenTestAppUrl; - private String testRoleId; - private int maxConcurrentRequests; - private String testingRunHexId; - private int testRunTime; - - public EditableTestingRunConfig() { - - } - - public List getSubCategoriesList() { - return subCategoriesList; - } - - public int getTestRunTime() { - return testRunTime; - } - - public void setTestRunTime(int testRunTime) { - this.testRunTime = testRunTime; - } - - public void setSubCategoriesList(List subCategoriesList) { - this.subCategoriesList = subCategoriesList; - } - - public List getTestConfigsAdvancedSettings() { - return testConfigsAdvancedSettings; - } - - public void setTestConfigsAdvancedSettings(List testConfigsAdvancedSettings) { - this.testConfigsAdvancedSettings = testConfigsAdvancedSettings; - } - - public String getOverriddenTestAppUrl() { - return overriddenTestAppUrl; - } - - public void setOverriddenTestAppUrl(String overriddenTestAppUrl) { - this.overriddenTestAppUrl = overriddenTestAppUrl; - } - - public String getTestRoleId() { - return testRoleId; - } - - public void setTestRoleId(String testRoleId) { - this.testRoleId = testRoleId; - } - - public int getMaxConcurrentRequests() { - return maxConcurrentRequests; - } - - public void setMaxConcurrentRequests(int maxConcurrentRequests) { - this.maxConcurrentRequests = maxConcurrentRequests; - } - - public String getTestingRunHexId() { - return testingRunHexId; - } - - public void setTestingRunHexId(String testingRunHexId) { - this.testingRunHexId = testingRunHexId; - } -} - diff --git a/libs/dao/src/main/java/com/akto/dto/testing/config/EditableTestingRunConfig.java b/libs/dao/src/main/java/com/akto/dto/testing/config/EditableTestingRunConfig.java new file mode 100644 index 0000000000..8bcba2ef5f --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/config/EditableTestingRunConfig.java @@ -0,0 +1,37 @@ +package com.akto.dto.testing.config; +import com.akto.dto.testing.TestingRunConfig; + +public class EditableTestingRunConfig extends TestingRunConfig { + private int maxConcurrentRequests; + private String testingRunHexId; + private int testRunTime; + + public EditableTestingRunConfig() { + + } + + public int getTestRunTime() { + return testRunTime; + } + + public void setTestRunTime(int testRunTime) { + this.testRunTime = testRunTime; + } + + public int getMaxConcurrentRequests() { + return maxConcurrentRequests; + } + + public void setMaxConcurrentRequests(int maxConcurrentRequests) { + this.maxConcurrentRequests = maxConcurrentRequests; + } + + public String getTestingRunHexId() { + return testingRunHexId; + } + + public void setTestingRunHexId(String testingRunHexId) { + this.testingRunHexId = testingRunHexId; + } +} + From 14d2818b65b8410162fff3659a3978ddd7923e24 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Thu, 30 Jan 2025 19:30:59 +0530 Subject: [PATCH 18/20] actionError are added --- .../akto/action/testing/StartTestAction.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 98b993fd92..eacc680b0d 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -1131,39 +1131,47 @@ public String getCurrentTestStateStatus(){ public String modifyTestingRunConfig(){ if (editableTestingRunConfig == null) { + addActionError("Invalid editableTestingRunConfig"); return Action.ERROR.toUpperCase(); } try { if (this.testingRunConfigId == 0) { - throw new Exception(); + addActionError("Invalid testing run config id"); + return Action.ERROR.toUpperCase(); } else { - TestingRunConfigDao.instance.updateOne( - Filters.eq(Constants.ID, this.testingRunConfigId), - Updates.combine( - Updates.set("configsAdvancedSettings", this.editableTestingRunConfig.getConfigsAdvancedSettings()), - Updates.set("testSubCategoryList", this.editableTestingRunConfig.getTestSubCategoryList()), - Updates.set("testRoleId", this.editableTestingRunConfig.getTestRoleId()), - Updates.set("overriddenTestAppUrl", this.editableTestingRunConfig.getOverriddenTestAppUrl()) - ) - ); + + TestingRunConfig existingtestingRunConfig = TestingRunConfigDao.instance.findOne(Filters.eq(Constants.ID, this.testingRunConfigId)); + if (existingtestingRunConfig == null) { + addActionError("testing run config object not found"); + return Action.ERROR.toUpperCase(); + } + + existingtestingRunConfig.setConfigsAdvancedSettings(this.editableTestingRunConfig.getConfigsAdvancedSettings()); + existingtestingRunConfig.setTestSubCategoryList(this.editableTestingRunConfig.getTestSubCategoryList()); + existingtestingRunConfig.setTestRoleId(this.editableTestingRunConfig.getTestRoleId()); + existingtestingRunConfig.setOverriddenTestAppUrl(this.editableTestingRunConfig.getOverriddenTestAppUrl()); + + TestingRunConfigDao.instance.replaceOne(Filters.eq(Constants.ID, this.testingRunConfigId), existingtestingRunConfig); } if (editableTestingRunConfig.getTestingRunHexId() != null) { - String hexId = editableTestingRunConfig.getTestingRunHexId(); - ObjectId objectId = new ObjectId(hexId); - - TestingRunDao.instance.updateOne( - Filters.eq(Constants.ID, objectId), - Updates.combine( - Updates.set("testRunTime", this.editableTestingRunConfig.getTestRunTime()), - Updates.set("maxConcurrentRequests", this.editableTestingRunConfig.getMaxConcurrentRequests()) - ) - ); + TestingRun existingTestingRun = TestingRunDao.instance.findOne(Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId()))); + + if (existingTestingRun != null) { + existingTestingRun.setTestRunTime(this.editableTestingRunConfig.getTestRunTime()); + existingTestingRun.setMaxConcurrentRequests(this.editableTestingRunConfig.getMaxConcurrentRequests()); + + TestingRunDao.instance.replaceOne( + Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId())), + existingTestingRun + ); + } } } catch (Exception e) { + addActionError("Unable to modify testing run config and testing run"); return Action.ERROR.toUpperCase(); } return SUCCESS.toUpperCase(); From 0636fa379849398f49ec14f9163fc95935c24353 Mon Sep 17 00:00:00 2001 From: amitpanwar789 Date: Thu, 30 Jan 2025 20:09:37 +0530 Subject: [PATCH 19/20] null check added for all values being modified --- .../akto/action/testing/StartTestAction.java | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index d43d393df0..9a99b6809c 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -1140,18 +1140,29 @@ public String modifyTestingRunConfig(){ return Action.ERROR.toUpperCase(); } else { - TestingRunConfig existingtestingRunConfig = TestingRunConfigDao.instance.findOne(Filters.eq(Constants.ID, this.testingRunConfigId)); - if (existingtestingRunConfig == null) { - addActionError("testing run config object not found"); - return Action.ERROR.toUpperCase(); + TestingRunConfig existingTestingRunConfig = TestingRunConfigDao.instance.findOne(Filters.eq(Constants.ID, this.testingRunConfigId)); + if (existingTestingRunConfig == null) { + addActionError("Testing run config object not found for ID: " + this.testingRunConfigId); + return Action.ERROR.toUpperCase(); } - existingtestingRunConfig.setConfigsAdvancedSettings(this.editableTestingRunConfig.getConfigsAdvancedSettings()); - existingtestingRunConfig.setTestSubCategoryList(this.editableTestingRunConfig.getTestSubCategoryList()); - existingtestingRunConfig.setTestRoleId(this.editableTestingRunConfig.getTestRoleId()); - existingtestingRunConfig.setOverriddenTestAppUrl(this.editableTestingRunConfig.getOverriddenTestAppUrl()); + if (editableTestingRunConfig.getConfigsAdvancedSettings() != null) { + existingTestingRunConfig.setConfigsAdvancedSettings(editableTestingRunConfig.getConfigsAdvancedSettings()); + } + + if (editableTestingRunConfig.getTestSubCategoryList() != null) { + existingTestingRunConfig.setTestSubCategoryList(editableTestingRunConfig.getTestSubCategoryList()); + } + + if (editableTestingRunConfig.getTestRoleId() != null) { + existingTestingRunConfig.setTestRoleId(editableTestingRunConfig.getTestRoleId()); + } + + if (editableTestingRunConfig.getOverriddenTestAppUrl() != null) { + existingTestingRunConfig.setOverriddenTestAppUrl(editableTestingRunConfig.getOverriddenTestAppUrl()); + } - TestingRunConfigDao.instance.replaceOne(Filters.eq(Constants.ID, this.testingRunConfigId), existingtestingRunConfig); + TestingRunConfigDao.instance.replaceOne(Filters.eq(Constants.ID, this.testingRunConfigId), existingTestingRunConfig); } if (editableTestingRunConfig.getTestingRunHexId() != null) { @@ -1159,8 +1170,13 @@ public String modifyTestingRunConfig(){ TestingRun existingTestingRun = TestingRunDao.instance.findOne(Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId()))); if (existingTestingRun != null) { - existingTestingRun.setTestRunTime(this.editableTestingRunConfig.getTestRunTime()); - existingTestingRun.setMaxConcurrentRequests(this.editableTestingRunConfig.getMaxConcurrentRequests()); + if (editableTestingRunConfig.getTestRunTime() != 0) { + existingTestingRun.setTestRunTime(editableTestingRunConfig.getTestRunTime()); + } + + if (editableTestingRunConfig.getMaxConcurrentRequests() != 0) { + existingTestingRun.setMaxConcurrentRequests(editableTestingRunConfig.getMaxConcurrentRequests()); + } TestingRunDao.instance.replaceOne( Filters.eq(Constants.ID, new ObjectId(editableTestingRunConfig.getTestingRunHexId())), @@ -1170,6 +1186,7 @@ public String modifyTestingRunConfig(){ } + } catch (Exception e) { addActionError("Unable to modify testing run config and testing run"); return Action.ERROR.toUpperCase(); From 5480ddcdfe9364cd19eeedfa689823dae453a690 Mon Sep 17 00:00:00 2001 From: notshivansh Date: Fri, 31 Jan 2025 00:19:35 +0530 Subject: [PATCH 20/20] fix broken unit test --- .../java/com/akto/action/testing/TestStartTestAction.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java b/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java index 3bf456434a..5f5e052a0b 100644 --- a/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java +++ b/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java @@ -270,6 +270,8 @@ public void testStartCICDTest() throws IOException, ServletException { @Test public void testModifyTestingRunConfig() { + TestingRunConfigDao.instance.getMCollection().drop(); + TestingRunDao.instance.getMCollection().drop(); int testingRunConfigId = UUID.randomUUID().hashCode() & 0xfffffff; ObjectId testingRunHexId = new ObjectId(); @@ -277,6 +279,7 @@ public void testModifyTestingRunConfig() { List list1 = Arrays.asList("pkucgztk", "oionxmec", "tmptskkz", "rorcqyqf"); List list2 = Arrays.asList("sqhrduyv", "awpqhaxz", "tdzydooe", "hxwvtoem"); TestingRunConfig testingRunConfig = new TestingRunConfig(); + testingRunConfig.setId(testingRunConfigId); testingRunConfig.setTestRoleId("initialRole"); testingRunConfig.setOverriddenTestAppUrl("https://initial.url"); testingRunConfig.setTestSubCategoryList(list1); @@ -297,6 +300,7 @@ public void testModifyTestingRunConfig() { editableTestingRunConfig.setOverriddenTestAppUrl("https://test.url"); editableTestingRunConfig.setMaxConcurrentRequests(10); editableTestingRunConfig.setTestSubCategoryList(list2); + editableTestingRunConfig.setTestingRunHexId(testingRunHexId.toHexString()); StartTestAction startTestAction = new StartTestAction(); startTestAction.setTestingRunConfigId(testingRunConfigId);