From 5af5673fbf54d8590094d23e1a3ddcf20dac5560 Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Tue, 21 Oct 2025 12:03:03 +0530 Subject: [PATCH 01/18] UI/Backend division and filtering of Cloud and Endpoint Security --- .../com/akto/action/ApiCollectionsAction.java | 89 +++++-- .../components/layouts/leftnav/LeftNav.js | 138 +++++++++-- .../pages/dashboard/HomeDashboard.jsx | 45 +++- .../src/apps/dashboard/pages/dashboard/api.js | 4 +- .../api_collections/ApiCollections.jsx | 232 ++++++++++++++++-- .../com/akto/dto/traffic/CollectionTags.java | 3 +- 6 files changed, 452 insertions(+), 59 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java index 2c69bc06e1..bb40363b78 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java @@ -95,6 +95,8 @@ public class ApiCollectionsAction extends UserAction { int mcpDataCount; @Setter String type; + @Setter + String contextType; @Getter List auditAlerts; @@ -116,10 +118,15 @@ public void setApiList(List apiList) { * Only populates MCP URLs when dashboard context is not API security. */ private void populateCollectionUrls(ApiCollection apiCollection) { - // Do not populate MCP URLs if dashboard context is API security - if (Context.contextSource.get() != null && Context.contextSource.get() == GlobalEnums.CONTEXT_SOURCE.MCP) { - apiCollectionUrlService.populateMcpCollectionUrls(apiCollection); - }else { + try { + // Do not populate MCP URLs if dashboard context is API security + GlobalEnums.CONTEXT_SOURCE contextSource = Context.contextSource.get(); + if (contextSource != null && contextSource == GlobalEnums.CONTEXT_SOURCE.MCP) { + apiCollectionUrlService.populateMcpCollectionUrls(apiCollection); + } else { + apiCollection.setUrls(new HashSet<>()); + } + } catch (Exception e) { apiCollection.setUrls(new HashSet<>()); } } @@ -237,7 +244,18 @@ public String fetchApiStats() { } public String fetchAllCollectionsBasic() { - UsersCollectionsList.deleteContextCollectionsForUser(Context.accountId.get(), Context.contextSource.get()); + try { + // Safely delete context collections for user - handle potential null context source + Integer accountId = Context.accountId.get(); + GlobalEnums.CONTEXT_SOURCE contextSource = Context.contextSource.get(); + if (accountId != null) { + UsersCollectionsList.deleteContextCollectionsForUser(accountId, contextSource); + } + } catch (Exception e) { + // Log the error but don't fail the entire request + loggerMaker.errorAndAddToDb(e, "Error deleting context collections for user", LogDb.DASHBOARD); + } + this.apiCollections = ApiCollectionsDao.instance.findAll(Filters.empty(), Projections.exclude("urls")); this.apiCollections = fillApiCollectionsUrlCount(this.apiCollections, Filters.nin(SingleTypeInfo._API_COLLECTION_ID, deactivatedCollections)); return Action.SUCCESS.toUpperCase(); @@ -1171,7 +1189,27 @@ public String fetchMcpdata() { Bson mcpTagFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq("keyName", com.akto.util.Constants.AKTO_MCP_SERVER_TAG) ); - List mcpCollections = ApiCollectionsDao.instance.findAll(mcpTagFilter, null); + List allMcpCollections = ApiCollectionsDao.instance.findAll(mcpTagFilter, null); + + // Filter collections based on contextType (cloud vs endpoint) + List mcpCollections = allMcpCollections; + if (contextType != null && !contextType.isEmpty()) { + mcpCollections = allMcpCollections.stream() + .filter(collection -> { + boolean isEndpointCollection = collection.getTagsList() != null && + collection.getTagsList().stream().anyMatch(tag -> + "ENDPOINT".equals(tag.getSource()) + ); + + if ("endpoint".equals(contextType)) { + return isEndpointCollection; + } else { // "cloud" context + return !isEndpointCollection; + } + }) + .collect(Collectors.toList()); + } + List mcpCollectionIds = mcpCollections.stream().map(ApiCollection::getId).collect(Collectors.toList()); switch (filterType) { @@ -1224,21 +1262,24 @@ public String fetchMcpdata() { case "TOOLS": Bson toolsFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_TOOL), - Filters.ne("remarks", "Rejected") + Filters.ne("remarks", "Rejected"), + mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(toolsFilter); break; case "PROMPTS": Bson promptsFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_PROMPT), - Filters.ne("remarks", "Rejected") + Filters.ne("remarks", "Rejected"), + mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(promptsFilter); break; case "RESOURCES": Bson resourcesFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_RESOURCE), - Filters.ne("remarks", "Rejected") + Filters.ne("remarks", "Rejected"), + mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(resourcesFilter); break; @@ -1246,7 +1287,8 @@ public String fetchMcpdata() { case "MCP_SERVER": Bson mcpServerFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_SERVER), - Filters.ne("remarks", "Rejected") + Filters.ne("remarks", "Rejected"), + mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(mcpServerFilter); break; @@ -1314,11 +1356,28 @@ public String fetchMcpdata() { Bson guardRailTagFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq("keyName", Constants.AKTO_GUARD_RAIL_TAG) ); - // Use projection to only fetch IDs, reducing memory usage - List guardRailCollections = ApiCollectionsDao.instance.findAll( - guardRailTagFilter, - Projections.include(ApiCollection.ID) - ); + // Fetch collections with full data to apply context filtering + List allGuardRailCollections = ApiCollectionsDao.instance.findAll(guardRailTagFilter, null); + + // Filter collections based on contextType (cloud vs endpoint) + List guardRailCollections = allGuardRailCollections; + if (contextType != null && !contextType.isEmpty()) { + guardRailCollections = allGuardRailCollections.stream() + .filter(collection -> { + boolean isEndpointCollection = collection.getTagsList() != null && + collection.getTagsList().stream().anyMatch(tag -> + "ENDPOINT".equals(tag.getSource()) + ); + + if ("endpoint".equals(contextType)) { + return isEndpointCollection; + } else { // "cloud" context + return !isEndpointCollection; + } + }) + .collect(Collectors.toList()); + } + List guardRailCollectionIds = guardRailCollections.stream() .map(ApiCollection::getId) .collect(Collectors.toList()); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 7b107358c2..44f57460c7 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -42,6 +42,17 @@ export default function LeftNav() { const handleSelect = (selectedId) => { setLeftNavSelected(selectedId); + // Store navigation state in sessionStorage for other components to access + sessionStorage.setItem('leftNavSelected', selectedId); + + // Dispatch custom event to notify other components of navigation change + const navigationChangeEvent = new CustomEvent('navigationChanged', { + detail: { + selectedId: selectedId, + timestamp: Date.now() + } + }); + window.dispatchEvent(navigationChangeEvent); }; const handleAccountChange = async (selected) => { @@ -85,8 +96,53 @@ export default function LeftNav() { const dashboardCategory = PersistStore((state) => state.dashboardCategory) || "API Security"; + // Helper function to duplicate navigation items with different prefix and independent selection + const duplicateNavItems = (items, prefix, startKey = 100) => { + return items.map((item, index) => { + const newKey = item.key ? `${prefix}_${item.key}` : `${prefix}_${startKey + index}`; + const originalSelected = item.selected; + + return { + ...item, + key: newKey, + // Each section has independent selection state + selected: originalSelected !== undefined ? leftNavSelected === newKey : undefined, + onClick: item.onClick ? () => { + const originalHandler = item.onClick; + originalHandler(); + // Set selection specific to this section + handleSelect(newKey); + } : undefined, + subNavigationItems: item.subNavigationItems ? item.subNavigationItems.map((subItem, subIndex) => { + // Create unique keys for sub-navigation items + const originalSubSelected = subItem.selected; + const subNavKey = originalSubSelected ? + leftNavSelected.replace('dashboard_', `${prefix}_dashboard_`) : + `${prefix}_sub_${subIndex}`; + + return { + ...subItem, + // Sub-items are selected only if they match this section's prefix + selected: originalSubSelected !== undefined ? + leftNavSelected.startsWith(prefix) && + leftNavSelected === subNavKey + : undefined, + onClick: subItem.onClick ? () => { + const originalHandler = subItem.onClick; + originalHandler(); + // Extract the base selection pattern and add section prefix + const basePattern = leftNavSelected.replace(/^(cloud_|endpoint_)/, ''); + handleSelect(`${prefix}_${basePattern}`); + } : undefined, + }; + }) : undefined + }; + }); + }; + const navItems = useMemo(() => { - let items = [ + // Account dropdown (stays at the top) + const accountSection = [ { label: (!func.checkLocal()) ? ( @@ -100,6 +156,10 @@ export default function LeftNav() { ) : null }, + ]; + + // Main navigation items (to be duplicated) + const mainNavItems = [ { label: mapLabel("API Security Posture", dashboardCategory), icon: ReportFilledMinor, @@ -487,24 +547,74 @@ export default function LeftNav() { } ] }] : []) - ] + ]; + + // Add Quick Start if it doesn't exist + const quickStartItem = { + label: "Quick Start", + icon: AppsFilledMajor, + onClick: () => { + handleSelect("dashboard_quick_start") + navigate("/dashboard/quick-start") + setActive("normal") + }, + selected: leftNavSelected === "dashboard_quick_start", + key: "quick_start", + }; - const exists = items.find(item => item.key === "quick_start") + const exists = mainNavItems.find(item => item.key === "quick_start"); if (!exists) { - items.splice(1, 0, { - label: "Quick Start", - icon: AppsFilledMajor, - onClick: () => { - handleSelect("dashboard_quick_start") - navigate("/dashboard/quick-start") - setActive("normal") + mainNavItems.splice(0, 0, quickStartItem); + } + + // Conditionally create Cloud Security and Endpoint Security sections + // Only show divisions for MCP Security and Agentic Security + const shouldShowDivisions = dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security"; + + let allItems; + if (shouldShowDivisions) { + // Create Cloud Security and Endpoint Security sections + const cloudSecurityItems = duplicateNavItems(mainNavItems, "cloud", 200); + const endpointSecurityItems = duplicateNavItems(mainNavItems, "endpoint", 300); + + allItems = [ + ...accountSection, + // Cloud Security Section Header + { + label: ( + +
+ Cloud Security +
+
+ ), + key: "cloud_header", + disabled: true, + }, + ...cloudSecurityItems, + // Endpoint Security Section Header + { + label: ( + +
+ Endpoint Security +
+
+ ), + key: "endpoint_header", + disabled: true, }, - selected: leftNavSelected === "dashboard_quick_start", - key: "quick_start", - }) + ...endpointSecurityItems + ]; + } else { + // For API Security and other categories, show normal navigation without divisions + allItems = [ + ...accountSection, + ...mainNavItems + ]; } - return items + return allItems }, [dashboardCategory, leftNavSelected]) const navigationMarkup = ( diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx index fc9c6712a6..f6f9b07f91 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx @@ -377,8 +377,33 @@ function HomeDashboard() { return completeData.sort((a, b) => a[0] - b[0]); }; + const getNavigationSection = () => { + try { + const leftNavSelected = sessionStorage.getItem('leftNavSelected'); + + // Handle null/undefined cases + if (!leftNavSelected) { + return 'cloud'; + } + + const navString = leftNavSelected.toString().toLowerCase(); + + if (navString.startsWith('cloud_') || navString.includes('cloud')) { + return 'cloud'; + } else if (navString.startsWith('endpoint_') || navString.includes('endpoint')) { + return 'endpoint'; + } + + return 'cloud'; + + } catch (error) { + return 'cloud'; // Safe fallback + } + }; + const fetchData = async () => { setLoading(true) + const currentNavigationSection = getNavigationSection(); // all apis let apiPromises = [ observeApi.getUserEndpoints(), @@ -387,16 +412,16 @@ function HomeDashboard() { api.fetchEndpointsCount(startTimestamp, endTimestamp), testingApi.fetchSeverityInfoForIssues({}, [], 0), api.getApiInfoForMissingData(0, endTimestamp), - api.fetchMcpdata('TOTAL_APIS'), - api.fetchMcpdata('THIRD_PARTY_APIS'), - api.fetchMcpdata('RECENT_OPEN_ALERTS'), - api.fetchMcpdata('CRITICAL_APIS'), - api.fetchMcpdata('TOOLS'), - api.fetchMcpdata('PROMPTS'), - api.fetchMcpdata('RESOURCES'), - api.fetchMcpdata('MCP_SERVER'), - api.fetchMcpdata('POLICY_GUARDRAIL_APIS'), - api.fetchMcpdata('TOP_3_APPLICATIONS_BY_TRAFFIC') + api.fetchMcpdata('TOTAL_APIS', currentNavigationSection), + api.fetchMcpdata('THIRD_PARTY_APIS', currentNavigationSection), + api.fetchMcpdata('RECENT_OPEN_ALERTS', currentNavigationSection), + api.fetchMcpdata('CRITICAL_APIS', currentNavigationSection), + api.fetchMcpdata('TOOLS', currentNavigationSection), + api.fetchMcpdata('PROMPTS', currentNavigationSection), + api.fetchMcpdata('RESOURCES', currentNavigationSection), + api.fetchMcpdata('MCP_SERVER', currentNavigationSection), + api.fetchMcpdata('POLICY_GUARDRAIL_APIS', currentNavigationSection), + api.fetchMcpdata('TOP_3_APPLICATIONS_BY_TRAFFIC', currentNavigationSection) ]; let results = await Promise.allSettled(apiPromises); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js index c8656e0a03..ffadcd8545 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js @@ -61,11 +61,11 @@ const api = { }) }, - fetchMcpdata: async (filterType) => { + fetchMcpdata: async (filterType, contextType = null) => { return await request({ url: '/api/fetchMcpdata', method: 'post', - data: { filterType } + data: { filterType, contextType } }) }, diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx index 206d8053bf..c0bb164a4f 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx @@ -45,7 +45,7 @@ const CenterViewType = { const headers = [ - ...((isMCPSecurityCategory() || isAgenticSecurityCategory()) && func.isDemoAccount() ? [{ + ...((isMCPSecurityCategory() || isAgenticSecurityCategory()) ? [{ title: "", text: "", value: "iconComp", @@ -200,6 +200,20 @@ const resourceName = { plural: 'collections', }; +// Determine source type for filtering - check the 'source' field in tagsList +const getCollectionSource = (collection) => { + const tagsList = collection?.tagsList || []; + + // Look for any tag with source === 'ENDPOINT' in tagsList + const endpointTag = tagsList.find(tag => tag.source === 'ENDPOINT'); + + if (endpointTag) { + return 'Endpoint'; + } + + return 'Cloud'; // Default to Cloud source if no tag with source 'ENDPOINT' +}; + const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, isLoading) => { // Ensure collectionsArr is an array @@ -220,9 +234,17 @@ const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, cov nextUrl: "/dashboard/observe/inventory/"+ c.id, envTypeOriginal: c?.envType, envType: c?.envType?.map(func.formatCollectionType), - ...((isMCPSecurityCategory() || isAgenticSecurityCategory()) && func.isDemoAccount() && tagsList.includes("mcp-server") ? { - iconComp: (icon) - } : {}), + ...((isMCPSecurityCategory() || isAgenticSecurityCategory()) ? (() => { + const collectionSource = getCollectionSource(c); + const isMcpCollection = tagsList.includes("mcp-server"); + + if (collectionSource === 'Endpoint') { + return { iconComp: (icon) }; + } else if (isMcpCollection) { + return { iconComp: (icon) }; + } + return {}; + })() : {}), ...((isGenAISecurityCategory() || isAgenticSecurityCategory()) && func.isDemoAccount() && tagsList.includes("gen-ai") ? { iconComp: () } : {}), @@ -298,10 +320,48 @@ function ApiCollections(props) { const [data, setData] = useState({'all': [], 'hostname':[], 'groups': [], 'custom': [], 'deactivated': [], 'untracked': []}) const [active, setActive] = useState(false); const [loading, setLoading] = useState(false) + const [currentNavigationSection, setCurrentNavigationSection] = useState('cloud'); + const [dashboardCategory, setDashboardCategory] = useState(() => getDashboardCategory()); + + // Get current navigation section to determine filtering + const getNavigationSection = () => { + const leftNavSelected = sessionStorage.getItem('leftNavSelected') || ''; + if (leftNavSelected.startsWith('cloud_')) { + return 'cloud'; + } else if (leftNavSelected.startsWith('endpoint_')) { + return 'endpoint'; + } + return 'cloud'; // default to cloud if no specific section detected + }; + + + // Filter collections based on navigation section and source + const filterCollectionsBySection = (collections) => { + // Only apply filtering for MCP Security and Agentic Security + const shouldApplyFiltering = dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security"; + + if (!shouldApplyFiltering) { + // For API Security and other categories, return all collections without filtering + return collections; + } + + return collections.filter(collection => { + const source = getCollectionSource(collection); + + if (currentNavigationSection === 'endpoint') { + // Endpoint Security section: show only collections with source = 'Endpoint' + return source === 'Endpoint'; + } else { + // Cloud Security section: show collections with source != 'Endpoint' + return source !== 'Endpoint'; + } + }); + }; const [summaryData, setSummaryData] = useState({totalEndpoints:0 , totalTestedEndpoints: 0, totalSensitiveEndpoints: 0, totalCriticalEndpoints: 0, totalAllowedForTesting: 0}) const [hasUsageEndpoints, setHasUsageEndpoints] = useState(true) const [envTypeMap, setEnvTypeMap] = useState({}) + const [rawCollectionsData, setRawCollectionsData] = useState([]); // Store unfiltered data const [refreshData, setRefreshData] = useState(false) const [popover,setPopover] = useState(false) const [teamData, setTeamData] = useState([]) @@ -484,8 +544,14 @@ function ApiCollections(props) { if(customCollectionDataFilter){ finalArr = finalArr.filter(customCollectionDataFilter) } + + // Store raw collections data for re-filtering when navigation changes + setRawCollectionsData(finalArr); + + // Apply section-based filtering for Cloud Security vs Endpoint Security + const filteredFinalArr = filterCollectionsBySection(finalArr); - const dataObj = convertToNewData(finalArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, false); + const dataObj = convertToNewData(filteredFinalArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, false); setNormalData(dataObj.normal) // Ensure dataObj.prettify exists @@ -497,6 +563,34 @@ function ApiCollections(props) { const { envTypeObj, collectionMap, activeCollections, categorized } = categorizeCollections(dataObj.prettify); let res = categorized; + // Calculate totalAPIs from filtered collections (context-aware) + const totalFilteredAPIs = filteredFinalArr.reduce((total, collection) => { + return total + (collection.urlsCount || 0); + }, 0); + setTotalAPIs(totalFilteredAPIs); + + // Calculate initial summary with context-filtered data + const initialSummary = transform.getSummaryData(dataObj.normal); + + // Calculate critical endpoints from filtered data + const totalCriticalFromFiltered = dataObj.normal.reduce((total, collection) => { + const riskScore = riskScoreMap[collection.id] || 0; + if (riskScore >= 4) { + return total + (collection.urlsCount || 0); + } + return total; + }, 0); + + // Calculate sensitive endpoints from filtered collections + const totalSensitiveFromFiltered = dataObj.normal.reduce((total, collection) => { + const sensitiveCount = sensitiveInfoMap[collection.id] ? sensitiveInfoMap[collection.id].length : 0; + return total + sensitiveCount; + }, 0); + + initialSummary.totalCriticalEndpoints = totalCriticalFromFiltered; + initialSummary.totalSensitiveEndpoints = totalSensitiveFromFiltered; + setSummaryData(initialSummary); + // Separate active and deactivated collections const deactivatedCollectionsCopy = res.deactivated.map((c)=>{ if(deactivatedCountInfo.hasOwnProperty(c.id)){ @@ -547,15 +641,10 @@ function ApiCollections(props) { setEnvTypeMap(envTypeObj); setAllCollections(apiCollectionsResp.apiCollections || []); - // Fetch endpoints count and sensitive info asynchronously + // Fetch sensitive info asynchronously Promise.all([ - dashboardApi.fetchEndpointsCount(0, 0), shouldCallHeavyApis ? api.getSensitiveInfoForCollections() : Promise.resolve(null) - ]).then(([endpointsResponse, sensitiveResponse]) => { - // Update endpoints count - if (endpointsResponse) { - setTotalAPIs(endpointsResponse.newCount); - } + ]).then(([sensitiveResponse]) => { // Update sensitive info if available if(sensitiveResponse == null || sensitiveResponse === undefined){ @@ -574,9 +663,9 @@ function ApiCollections(props) { // Update the store with new sensitive info setLastFetchedSensitiveResp(newSensitiveInfo); - // Re-calculate data with new sensitive info + // Re-calculate data with new sensitive info (use same filtered array) const updatedDataObj = convertToNewData( - finalArr, + filteredFinalArr, newSensitiveInfo.sensitiveInfoMap || {}, severityInfoMap, coverageMap, @@ -604,10 +693,32 @@ function ApiCollections(props) { setData(updatedCategorized); - // Update summary with new sensitive endpoints count + // Calculate totalAPIs from filtered collections (context-aware) + const totalFilteredAPIs = filteredFinalArr.reduce((total, collection) => { + return total + (collection.urlsCount || 0); + }, 0); + setTotalAPIs(totalFilteredAPIs); + + // Update summary with context-filtered counts const updatedSummary = transform.getSummaryData(updatedDataObj.normal); - updatedSummary.totalCriticalEndpoints = riskScoreObj.criticalUrls; - updatedSummary.totalSensitiveEndpoints = newSensitiveInfo.sensitiveUrls; + + // Calculate critical endpoints from filtered data + const totalCriticalFromFiltered = updatedDataObj.normal.reduce((total, collection) => { + const riskScore = riskScoreMap[collection.id] || 0; + if (riskScore >= 4) { + return total + (collection.urlsCount || 0); + } + return total; + }, 0); + + // Calculate sensitive endpoints from filtered collections + const totalSensitiveFromFiltered = updatedDataObj.normal.reduce((total, collection) => { + const sensitiveCount = newSensitiveInfo.sensitiveInfoMap[collection.id] ? newSensitiveInfo.sensitiveInfoMap[collection.id].length : 0; + return total + sensitiveCount; + }, 0); + + updatedSummary.totalCriticalEndpoints = totalCriticalFromFiltered; + updatedSummary.totalSensitiveEndpoints = totalSensitiveFromFiltered; setSummaryData(updatedSummary); } } @@ -648,6 +759,93 @@ function ApiCollections(props) { fetchData() resetFunc() }, []) + + // Update dashboard category when it changes + useEffect(() => { + const currentCategory = getDashboardCategory(); + if (currentCategory !== dashboardCategory) { + setDashboardCategory(currentCategory); + } + }, [dashboardCategory]); + + // Check and update navigation section on component mount and listen for navigation changes + useEffect(() => { + const updateNavigationSection = () => { + const detectedSection = getNavigationSection(); + if (detectedSection !== currentNavigationSection) { + setCurrentNavigationSection(detectedSection); + } + }; + + // Initial check + updateNavigationSection(); + + // Listen for custom navigationChanged events dispatched from LeftNav + const handleNavigationChange = (event) => { + updateNavigationSection(); + }; + + window.addEventListener('navigationChanged', handleNavigationChange); + + return () => { + window.removeEventListener('navigationChanged', handleNavigationChange); + }; + }, [currentNavigationSection]); + + // Re-filter data when navigation section changes + useEffect(() => { + if (rawCollectionsData.length > 0) { + // Re-apply filtering with the new navigation section + const filteredArr = filterCollectionsBySection(rawCollectionsData); + + // Use stored context data for proper re-filtering + const lastFetchedSensitiveResp = PersistStore.getState().lastFetchedSensitiveResp; + const lastFetchedSeverityResp = PersistStore.getState().lastFetchedSeverityResp; + const lastFetchedResp = PersistStore.getState().lastFetchedResp; + + const sensitiveInfoMap = lastFetchedSensitiveResp?.sensitiveInfoMap || {}; + const severityInfoMap = lastFetchedSeverityResp || {}; + const riskScoreMap = lastFetchedResp?.riskScoreMap || {}; + const coverageMap = PersistStore.getState().coverageMap || {}; + + const dataObj = convertToNewData(filteredArr, sensitiveInfoMap, severityInfoMap, coverageMap, {}, riskScoreMap, false); + setNormalData(dataObj.normal); + + // Calculate context-aware totals + const totalFilteredAPIs = filteredArr.reduce((total, collection) => { + return total + (collection.urlsCount || 0); + }, 0); + setTotalAPIs(totalFilteredAPIs); + + // Update summary with context-filtered counts + const contextSummary = transform.getSummaryData(dataObj.normal); + + // Calculate critical endpoints from filtered data + const totalCriticalFromFiltered = dataObj.normal.reduce((total, collection) => { + const riskScore = riskScoreMap[collection.id] || 0; + if (riskScore >= 4) { + return total + (collection.urlsCount || 0); + } + return total; + }, 0); + + // Calculate sensitive endpoints from filtered collections + const totalSensitiveFromFiltered = dataObj.normal.reduce((total, collection) => { + const sensitiveCount = sensitiveInfoMap[collection.id] ? sensitiveInfoMap[collection.id].length : 0; + return total + sensitiveCount; + }, 0); + + contextSummary.totalCriticalEndpoints = totalCriticalFromFiltered; + contextSummary.totalSensitiveEndpoints = totalSensitiveFromFiltered; + setSummaryData(contextSummary); + + if (dataObj.prettify) { + const { envTypeObj, collectionMap, activeCollections, categorized } = categorizeCollections(dataObj.prettify); + setData(categorized); + setEnvTypeMap(envTypeObj); + } + } + }, [currentNavigationSection]); const createCollectionModalActivatorRef = useRef(); const resetResourcesSelected = () => { TableStore.getState().setSelectedItems([]) diff --git a/libs/dao/src/main/java/com/akto/dto/traffic/CollectionTags.java b/libs/dao/src/main/java/com/akto/dto/traffic/CollectionTags.java index 86355fcb57..74c42b8943 100644 --- a/libs/dao/src/main/java/com/akto/dto/traffic/CollectionTags.java +++ b/libs/dao/src/main/java/com/akto/dto/traffic/CollectionTags.java @@ -24,7 +24,8 @@ public class CollectionTags { public enum TagSource { KUBERNETES, - USER + USER, + ENDPOINT } TagSource source; From 9140657229dbba3e59b51fecc56b5a1923ae7cad Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Wed, 22 Oct 2025 00:56:41 +0530 Subject: [PATCH 02/18] ui change --- .../dashboard/pages/quick_start/transform.js | 27 ++++++++++++++++++- apps/dashboard/web/public/zscaler_logo.svg | 9 +++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 apps/dashboard/web/public/zscaler_logo.svg diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js index 244dc51a60..06ee77e898 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js @@ -842,6 +842,24 @@ const safariExtensionObj = { key: "SAFARI_BROWSER_EXTENSION", } +const cloudflareWarpObj = { + icon: '/public/cloudflare.svg', + label: "Cloudflare WARP", + text: "Cloudflare WARP setup is recommended, if you use Cloudflare WARP for secure, accelerated device-to-internet connectivity.", + docsUrl: 'https://docs.akto.io/traffic-connector', + component: , + key: "CLOUDFLARE_WARP", +} + +const zscalerObj = { + icon: '/public/zscaler_logo.svg', + label: "Zscaler", + text: "Zscaler setup is recommended,if your organization uses Zscaler for secure, zero-trust internet and app access.", + docsUrl: 'https://docs.akto.io/traffic-connector', + component: , + key: "ZSCALER", +} + const quick_start_policy_lines= [ `{`, @@ -1455,6 +1473,11 @@ const quickStartFunc = { goObj, javaObj, nodejsObj, pythonObj ]; + // Secure Web Networks + const secureWebNetworks = [ + cloudflareWarpObj, zscalerObj + ]; + const browserExtensions = [ chromeExtensionObj, firefoxExtensionObj, safariExtensionObj ] @@ -1496,6 +1519,7 @@ const quickStartFunc = { "Akto SDK": aktoSdk, "Virtual Machines": vm, "Source Code": sourceCode, + "Secure Web Networks": secureWebNetworks } return connectors; @@ -1519,7 +1543,8 @@ const quickStartFunc = { openApiObj, beanStalkObj, eksObj, dockerObj, envoyObj, mcpScanObj, mcpProxyObj, mcpGateway, mcpWrapperObj, impervaImportObj, harFileUploadObj, kongObj, tcpObj, mirroringObj, hybridSaasObj, apiInventoryFromSourceCodeObj, ebpfObj, ebpfMTLSObj, istioObj, pythonObj, awsApiGatewayObj, awsLambdaObj, - apigeeObj, iisObj, azureObj, cloudflareObj, f5Obj, goObj, haproxyObj, javaObj, kongmeshObj, layer7Obj, nodejsObj, openshiftObj, threescaleObj, githubObj, gitlabObj, bitbucketObj, aktoJaxObj + apigeeObj, iisObj, azureObj, cloudflareObj, f5Obj, goObj, haproxyObj, javaObj, kongmeshObj, layer7Obj, nodejsObj, openshiftObj, threescaleObj, githubObj, gitlabObj, bitbucketObj, aktoJaxObj, + cloudflareWarpObj, zscalerObj ] if(isGenAISecurityCategory() || isAgenticSecurityCategory()){ diff --git a/apps/dashboard/web/public/zscaler_logo.svg b/apps/dashboard/web/public/zscaler_logo.svg new file mode 100644 index 0000000000..cb38a9bc04 --- /dev/null +++ b/apps/dashboard/web/public/zscaler_logo.svg @@ -0,0 +1,9 @@ + + logo (34)-svg + + + + + \ No newline at end of file From 0153bff2fca989d898f7f12ecbc0f80018f4c98c Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Wed, 22 Oct 2025 01:08:05 +0530 Subject: [PATCH 03/18] ui change --- .../src/main/java/com/akto/action/IngestionAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java b/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java index b448ce37d6..2394d1c63a 100644 --- a/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java +++ b/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java @@ -52,7 +52,7 @@ private boolean sendLogsToCustomAccount(List batchData){ public String ingestData() { try { if(sendLogsToCustomAccount(batchData)){ - System.setProperty("DATABASE_ABSTRACTOR_SERVICE_TOKEN", "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoiaW52aXRlX3VzZXIiLCJhY2NvdW50SWQiOjE2NjI2ODA0NjMsImlhdCI6MTc2MDU5NzM0OCwiZXhwIjoxNzc2MzIyMTQ4fQ.b-aqZEiTinzE1tavKDe6t7Ec7TsnsGoVRdxCiMmeOM20JcJ7aEgOZaJxD7O9zyoD6AEXmpEghd04wGhGCECBOKWivDS8Y_fdatLw8R7hH0Y-pu8QEMC1whbXXJrNhsRGXihLIiQ80nDKbrv6ObbyDwy4NPYoCFK8Mpu2i4W8qZHBJXnxmVkCp8Cp_LyeDLotXvc8DAp9huHASil0BSOxiUwHsw3Efk4BkRlHADfAwGFz4j-ozdbiK0SHHvOZNicl1wgpvDk0nHRLhIg3Ynx-Fk4Pp0agb0MCpS55-CRMBbx3zy9xRdkhIGdOydEzZKK5p311hwPnxxeL6Dp1C2f89g"); + } printLogs("ingestData batch size " + batchData.size()); From f4bd5efcb60c6838009e9089a6886937f8cf7a1a Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 13:01:20 +0530 Subject: [PATCH 04/18] Dual left navigation changes --- .../com/akto/action/AccessTokenAction.java | 1 + .../com/akto/filter/UserDetailsFilter.java | 5 + .../components/layouts/header/Headers.js | 1 + .../components/layouts/leftnav/LeftNav.js | 27 +- .../api_collections/ApiCollections.jsx | 268 +++--------------- .../web/src/apps/main/PersistStore.js | 12 +- .../web/src/apps/main/labelHelper.js | 27 ++ .../web/polaris_web/web/src/util/request.js | 2 + .../java/com/akto/dao/context/Context.java | 10 + .../akto/dto/rbac/UsersCollectionsList.java | 38 ++- .../java/com/akto/util/enums/GlobalEnums.java | 4 + .../com/akto/usage/UsageMetricCalculator.java | 8 +- 12 files changed, 171 insertions(+), 232 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java b/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java index 865b2ba81d..ca3a9ad4d3 100644 --- a/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java @@ -22,6 +22,7 @@ public class AccessTokenAction implements Action, ServletResponseAware, ServletR public static final String ACCESS_TOKEN_HEADER_NAME = "access-token"; public static final String CONTEXT_SOURCE_HEADER = "x-context-source"; public static final String AKTO_SESSION_TOKEN = "x-akto-session-token"; + public static final String LEFT_NAV_CATEGORY_HEADER = "x-left-nav-category"; @Override public String execute() { diff --git a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java index 71502ceb2b..673868b57d 100644 --- a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java +++ b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java @@ -83,6 +83,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo String accessTokenFromResponse = httpServletResponse.getHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); String accessTokenFromRequest = httpServletRequest.getHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); String contextSourceFromRequest = httpServletRequest.getHeader(AccessTokenAction.CONTEXT_SOURCE_HEADER); + String leftNavCategoryFromRequest = httpServletRequest.getHeader(AccessTokenAction.LEFT_NAV_CATEGORY_HEADER); String aktoSessionTokenFromRequest = httpServletRequest.getHeader(AccessTokenAction.AKTO_SESSION_TOKEN); @@ -108,6 +109,10 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } } + if(!StringUtils.isEmpty(leftNavCategoryFromRequest)) { + Context.leftNavCategory.set(leftNavCategoryFromRequest); + } + if(StringUtils.isNotEmpty(aktoSessionTokenFromRequest) && httpServletRequest.getRequestURI().contains("agent")){ try { Jws claims = JwtAuthenticator.authenticate(aktoSessionTokenFromRequest); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js index df3df0134f..8cf832e484 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js @@ -126,6 +126,7 @@ export default function Header() { LocalStore.getState().setCategoryMap({}); LocalStore.getState().setSubCategoryMap({}); SessionStore.getState().setThreatFiltersMap({}); + PersistStore.getState().setLeftNavCategory('Cloud Security'); setDashboardCategory(value); window.location.reload(); window.location.href("/dashboard/observe/inventory") diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 52dd49de02..5837a2feb0 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -22,7 +22,7 @@ import func from "@/util/func"; import Dropdown from "../Dropdown"; import SessionStore from "../../../../main/SessionStore"; import IssuesStore from "../../../pages/issues/issuesStore"; -import { CATEGORY_API_SECURITY, mapLabel } from "../../../../main/labelHelper"; +import { CATEGORY_API_SECURITY, mapLabel, shouldShowLeftNavSwitch, LEFT_NAV_CLOUD_SECURITY, LEFT_NAV_ENDPOINT_SECURITY, getLeftNavCategory } from "../../../../main/labelHelper"; export default function LeftNav() { const navigate = useNavigate(); @@ -39,6 +39,8 @@ export default function LeftNav() { const resetStore = LocalStore(state => state.resetStore); const resetSession = SessionStore(state => state.resetStore); const resetFields = IssuesStore(state => state.resetStore); + const leftNavCategory = PersistStore((state) => state.leftNavCategory) || LEFT_NAV_CLOUD_SECURITY; + const setLeftNavCategory = PersistStore((state) => state.setLeftNavCategory); const handleSelect = (selectedId) => { setLeftNavSelected(selectedId); @@ -55,6 +57,13 @@ export default function LeftNav() { window.dispatchEvent(navigationChangeEvent); }; + + const handleLeftNavCategoryChange = (selected) => { + setLeftNavCategory(selected); + // Force refresh of current page to reload data with new left nav category + window.location.reload(); + }; + const handleAccountChange = async (selected) => { resetAll(); resetStore(); @@ -108,6 +117,13 @@ export default function LeftNav() { // Each section has independent selection state selected: originalSelected !== undefined ? leftNavSelected === newKey : undefined, onClick: item.onClick ? () => { + // Set the left nav category based on section + if (prefix === "cloud") { + setLeftNavCategory("Cloud Security"); + } else if (prefix === "endpoint") { + setLeftNavCategory("Endpoint Security"); + } + const originalHandler = item.onClick; originalHandler(); // Set selection specific to this section @@ -128,6 +144,13 @@ export default function LeftNav() { leftNavSelected === subNavKey : undefined, onClick: subItem.onClick ? () => { + // Set the left nav category based on section + if (prefix === "cloud") { + setLeftNavCategory("Cloud Security"); + } else if (prefix === "endpoint") { + setLeftNavCategory("Endpoint Security"); + } + const originalHandler = subItem.onClick; originalHandler(); // Extract the base selection pattern and add section prefix @@ -657,7 +680,7 @@ export default function LeftNav() { } return allItems - }, [dashboardCategory, leftNavSelected]) + }, [dashboardCategory, leftNavSelected, leftNavCategory]) const navigationMarkup = (
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx index 02bb77426d..a20ebb9c36 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx @@ -35,8 +35,8 @@ import ReactFlow, { } from 'react-flow-renderer'; import SetUserEnvPopupComponent from "./component/SetUserEnvPopupComponent"; -import { getDashboardCategory, mapLabel, isMCPSecurityCategory, isAgenticSecurityCategory, isGenAISecurityCategory } from "../../../../main/labelHelper"; - +import { getDashboardCategory, mapLabel, isMCPSecurityCategory, isAgenticSecurityCategory, isGenAISecurityCategory, shouldShowLeftNavSwitch, isEndpointSecurityLeftNav, getLeftNavCategory } from "../../../../main/labelHelper"; + const CenterViewType = { Table: 0, Tree: 1, @@ -45,7 +45,7 @@ const CenterViewType = { const headers = [ - ...((isMCPSecurityCategory() || isAgenticSecurityCategory()) ? [{ + ...(shouldShowLeftNavSwitch() ? [{ title: "", text: "", value: "iconComp", @@ -200,19 +200,26 @@ const resourceName = { plural: 'collections', }; -// Determine source type for filtering - check the 'source' field in tagsList -const getCollectionSource = (collection) => { - const tagsList = collection?.tagsList || []; - - // Look for any tag with source === 'ENDPOINT' in tagsList - const endpointTag = tagsList.find(tag => tag.source === 'ENDPOINT'); - - if (endpointTag) { - return 'Endpoint'; +const filterCollectionsByLeftNav = (collectionsArr) => { + if (!shouldShowLeftNavSwitch()) { + return collectionsArr; } - - return 'Cloud'; // Default to Cloud source if no tag with source 'ENDPOINT' -}; + + const leftNavCategory = getLeftNavCategory(); + + return collectionsArr.filter((collection) => { + const tagsList = collection?.tagsList || []; + const tagsString = JSON.stringify(tagsList); + + if (leftNavCategory === 'Endpoint Security') { + // Show collections where tagsList contains source: "Endpoint" + return tagsString.includes('"source":"Endpoint"') || tagsString.includes('source') && tagsString.includes('Endpoint'); + } else { + // Cloud Security - show collections where tagsList does NOT contain source: "Endpoint" + return !(tagsString.includes('"source":"Endpoint"') || (tagsString.includes('source') && tagsString.includes('Endpoint'))); + } + }); +} const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, isLoading) => { @@ -222,7 +229,10 @@ const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, cov return { prettify: [], normal: [] }; } - const newData = collectionsArr.map((c) => { + // Filter collections based on left navigation category + const filteredCollections = filterCollectionsByLeftNav(collectionsArr); + + const newData = filteredCollections.map((c) => { if(c.deactivated){ c.rowStatus = 'critical' c.disableClick = true @@ -234,17 +244,9 @@ const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, cov nextUrl: "/dashboard/observe/inventory/"+ c.id, envTypeOriginal: c?.envType, envType: c?.envType?.map(func.formatCollectionType), - ...((isMCPSecurityCategory() || isAgenticSecurityCategory()) ? (() => { - const collectionSource = getCollectionSource(c); - const isMcpCollection = tagsList.includes("mcp-server"); - - if (collectionSource === 'Endpoint') { - return { iconComp: (icon) }; - } else if (isMcpCollection) { - return { iconComp: (icon) }; - } - return {}; - })() : {}), + ...(shouldShowLeftNavSwitch() ? { + iconComp: (icon) + } : {}), ...((isGenAISecurityCategory() || isAgenticSecurityCategory()) && func.isDemoAccount() && tagsList.includes("gen-ai") ? { iconComp: () } : {}), @@ -274,12 +276,12 @@ const categorizeCollections = (prettifyArray) => { const activeCollections = []; const deactivatedCollectionsData = []; const collectionMap = new Map(); - + prettifyArray.forEach((c) => { // Build environment map envTypeObj[c.id] = c.envTypeOriginal; collectionMap.set(c.id, c); - + // Categorize collections in single pass if (!c.deactivated) { activeCollections.push(c); @@ -320,48 +322,10 @@ function ApiCollections(props) { const [data, setData] = useState({'all': [], 'hostname':[], 'groups': [], 'custom': [], 'deactivated': [], 'untracked': []}) const [active, setActive] = useState(false); const [loading, setLoading] = useState(false) - const [currentNavigationSection, setCurrentNavigationSection] = useState('cloud'); - const [dashboardCategory, setDashboardCategory] = useState(() => getDashboardCategory()); - - // Get current navigation section to determine filtering - const getNavigationSection = () => { - const leftNavSelected = sessionStorage.getItem('leftNavSelected') || ''; - if (leftNavSelected.startsWith('cloud_')) { - return 'cloud'; - } else if (leftNavSelected.startsWith('endpoint_')) { - return 'endpoint'; - } - return 'cloud'; // default to cloud if no specific section detected - }; - - // Filter collections based on navigation section and source - const filterCollectionsBySection = (collections) => { - // Only apply filtering for MCP Security and Agentic Security - const shouldApplyFiltering = dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security"; - - if (!shouldApplyFiltering) { - // For API Security and other categories, return all collections without filtering - return collections; - } - - return collections.filter(collection => { - const source = getCollectionSource(collection); - - if (currentNavigationSection === 'endpoint') { - // Endpoint Security section: show only collections with source = 'Endpoint' - return source === 'Endpoint'; - } else { - // Cloud Security section: show collections with source != 'Endpoint' - return source !== 'Endpoint'; - } - }); - }; - const [summaryData, setSummaryData] = useState({totalEndpoints:0 , totalTestedEndpoints: 0, totalSensitiveEndpoints: 0, totalCriticalEndpoints: 0, totalAllowedForTesting: 0}) const [hasUsageEndpoints, setHasUsageEndpoints] = useState(true) const [envTypeMap, setEnvTypeMap] = useState({}) - const [rawCollectionsData, setRawCollectionsData] = useState([]); // Store unfiltered data const [refreshData, setRefreshData] = useState(false) const [popover,setPopover] = useState(false) const [teamData, setTeamData] = useState([]) @@ -544,14 +508,8 @@ function ApiCollections(props) { if(customCollectionDataFilter){ finalArr = finalArr.filter(customCollectionDataFilter) } - - // Store raw collections data for re-filtering when navigation changes - setRawCollectionsData(finalArr); - - // Apply section-based filtering for Cloud Security vs Endpoint Security - const filteredFinalArr = filterCollectionsBySection(finalArr); - - const dataObj = convertToNewData(filteredFinalArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, false); + + const dataObj = convertToNewData(finalArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, false); setNormalData(dataObj.normal) // Ensure dataObj.prettify exists @@ -563,34 +521,6 @@ function ApiCollections(props) { const { envTypeObj, collectionMap, activeCollections, categorized } = categorizeCollections(dataObj.prettify); let res = categorized; - // Calculate totalAPIs from filtered collections (context-aware) - const totalFilteredAPIs = filteredFinalArr.reduce((total, collection) => { - return total + (collection.urlsCount || 0); - }, 0); - setTotalAPIs(totalFilteredAPIs); - - // Calculate initial summary with context-filtered data - const initialSummary = transform.getSummaryData(dataObj.normal); - - // Calculate critical endpoints from filtered data - const totalCriticalFromFiltered = dataObj.normal.reduce((total, collection) => { - const riskScore = riskScoreMap[collection.id] || 0; - if (riskScore >= 4) { - return total + (collection.urlsCount || 0); - } - return total; - }, 0); - - // Calculate sensitive endpoints from filtered collections - const totalSensitiveFromFiltered = dataObj.normal.reduce((total, collection) => { - const sensitiveCount = sensitiveInfoMap[collection.id] ? sensitiveInfoMap[collection.id].length : 0; - return total + sensitiveCount; - }, 0); - - initialSummary.totalCriticalEndpoints = totalCriticalFromFiltered; - initialSummary.totalSensitiveEndpoints = totalSensitiveFromFiltered; - setSummaryData(initialSummary); - // Separate active and deactivated collections const deactivatedCollectionsCopy = res.deactivated.map((c)=>{ if(deactivatedCountInfo.hasOwnProperty(c.id)){ @@ -640,12 +570,17 @@ function ApiCollections(props) { setData(res); setEnvTypeMap(envTypeObj); setAllCollections(apiCollectionsResp.apiCollections || []); - - // Fetch sensitive info asynchronously + + // Fetch endpoints count and sensitive info asynchronously Promise.all([ + dashboardApi.fetchEndpointsCount(0, 0), shouldCallHeavyApis ? api.getSensitiveInfoForCollections() : Promise.resolve(null) - ]).then(([sensitiveResponse]) => { - + ]).then(([endpointsResponse, sensitiveResponse]) => { + // Update endpoints count + if (endpointsResponse) { + setTotalAPIs(endpointsResponse.newCount); + } + // Update sensitive info if available if(sensitiveResponse == null || sensitiveResponse === undefined){ sensitiveResponse = { @@ -665,7 +600,7 @@ function ApiCollections(props) { // Re-calculate data with new sensitive info (use same filtered array) const updatedDataObj = convertToNewData( - filteredFinalArr, + finalArr, newSensitiveInfo.sensitiveInfoMap || {}, severityInfoMap, coverageMap, @@ -692,33 +627,11 @@ function ApiCollections(props) { updatedCategorized['untracked'] = untrackedCollections; setData(updatedCategorized); - - // Calculate totalAPIs from filtered collections (context-aware) - const totalFilteredAPIs = filteredFinalArr.reduce((total, collection) => { - return total + (collection.urlsCount || 0); - }, 0); - setTotalAPIs(totalFilteredAPIs); - - // Update summary with context-filtered counts + + // Update summary with new sensitive endpoints count const updatedSummary = transform.getSummaryData(updatedDataObj.normal); - - // Calculate critical endpoints from filtered data - const totalCriticalFromFiltered = updatedDataObj.normal.reduce((total, collection) => { - const riskScore = riskScoreMap[collection.id] || 0; - if (riskScore >= 4) { - return total + (collection.urlsCount || 0); - } - return total; - }, 0); - - // Calculate sensitive endpoints from filtered collections - const totalSensitiveFromFiltered = updatedDataObj.normal.reduce((total, collection) => { - const sensitiveCount = newSensitiveInfo.sensitiveInfoMap[collection.id] ? newSensitiveInfo.sensitiveInfoMap[collection.id].length : 0; - return total + sensitiveCount; - }, 0); - - updatedSummary.totalCriticalEndpoints = totalCriticalFromFiltered; - updatedSummary.totalSensitiveEndpoints = totalSensitiveFromFiltered; + updatedSummary.totalCriticalEndpoints = riskScoreObj.criticalUrls; + updatedSummary.totalSensitiveEndpoints = newSensitiveInfo.sensitiveUrls; setSummaryData(updatedSummary); } } @@ -759,93 +672,6 @@ function ApiCollections(props) { fetchData() resetFunc() }, []) - - // Update dashboard category when it changes - useEffect(() => { - const currentCategory = getDashboardCategory(); - if (currentCategory !== dashboardCategory) { - setDashboardCategory(currentCategory); - } - }, [dashboardCategory]); - - // Check and update navigation section on component mount and listen for navigation changes - useEffect(() => { - const updateNavigationSection = () => { - const detectedSection = getNavigationSection(); - if (detectedSection !== currentNavigationSection) { - setCurrentNavigationSection(detectedSection); - } - }; - - // Initial check - updateNavigationSection(); - - // Listen for custom navigationChanged events dispatched from LeftNav - const handleNavigationChange = (event) => { - updateNavigationSection(); - }; - - window.addEventListener('navigationChanged', handleNavigationChange); - - return () => { - window.removeEventListener('navigationChanged', handleNavigationChange); - }; - }, [currentNavigationSection]); - - // Re-filter data when navigation section changes - useEffect(() => { - if (rawCollectionsData.length > 0) { - // Re-apply filtering with the new navigation section - const filteredArr = filterCollectionsBySection(rawCollectionsData); - - // Use stored context data for proper re-filtering - const lastFetchedSensitiveResp = PersistStore.getState().lastFetchedSensitiveResp; - const lastFetchedSeverityResp = PersistStore.getState().lastFetchedSeverityResp; - const lastFetchedResp = PersistStore.getState().lastFetchedResp; - - const sensitiveInfoMap = lastFetchedSensitiveResp?.sensitiveInfoMap || {}; - const severityInfoMap = lastFetchedSeverityResp || {}; - const riskScoreMap = lastFetchedResp?.riskScoreMap || {}; - const coverageMap = PersistStore.getState().coverageMap || {}; - - const dataObj = convertToNewData(filteredArr, sensitiveInfoMap, severityInfoMap, coverageMap, {}, riskScoreMap, false); - setNormalData(dataObj.normal); - - // Calculate context-aware totals - const totalFilteredAPIs = filteredArr.reduce((total, collection) => { - return total + (collection.urlsCount || 0); - }, 0); - setTotalAPIs(totalFilteredAPIs); - - // Update summary with context-filtered counts - const contextSummary = transform.getSummaryData(dataObj.normal); - - // Calculate critical endpoints from filtered data - const totalCriticalFromFiltered = dataObj.normal.reduce((total, collection) => { - const riskScore = riskScoreMap[collection.id] || 0; - if (riskScore >= 4) { - return total + (collection.urlsCount || 0); - } - return total; - }, 0); - - // Calculate sensitive endpoints from filtered collections - const totalSensitiveFromFiltered = dataObj.normal.reduce((total, collection) => { - const sensitiveCount = sensitiveInfoMap[collection.id] ? sensitiveInfoMap[collection.id].length : 0; - return total + sensitiveCount; - }, 0); - - contextSummary.totalCriticalEndpoints = totalCriticalFromFiltered; - contextSummary.totalSensitiveEndpoints = totalSensitiveFromFiltered; - setSummaryData(contextSummary); - - if (dataObj.prettify) { - const { envTypeObj, collectionMap, activeCollections, categorized } = categorizeCollections(dataObj.prettify); - setData(categorized); - setEnvTypeMap(envTypeObj); - } - } - }, [currentNavigationSection]); const createCollectionModalActivatorRef = useRef(); const resetResourcesSelected = () => { TableStore.getState().setSelectedItems([]) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js b/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js index 008efe32cc..d2ef96ac57 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js @@ -61,6 +61,7 @@ const initialState = { sendEventOnLogin: false, tableSelectedTab: {}, dashboardCategory: 'API Security', + leftNavCategory: 'Cloud Security', }; let persistStore = (set, get) => ({ @@ -221,6 +222,14 @@ let persistStore = (set, get) => ({ console.error("Error setting dashboardCategory:", error); } }, + setLeftNavCategory: (leftNavCategory) => { + try { + set({ leftNavCategory }); + } catch (error) { + console.error("Error setting leftNavCategory:", error); + } + }, + }); persistStore = devtools(persistStore); @@ -241,7 +250,8 @@ persistStore = persist(persistStore, { trafficAlerts: state.trafficAlerts, sendEventOnLogin: state.sendEventOnLogin, tableSelectedTab: state.tableSelectedTab, - dashboardCategory: state.dashboardCategory + dashboardCategory: state.dashboardCategory, + leftNavCategory: state.leftNavCategory }) }); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js b/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js index 81bbc26451..501369f40c 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js @@ -15,6 +15,8 @@ export const CATEGORY_MCP_SECURITY = 'MCP Security'; export const CATEGORY_GEN_AI = 'Gen AI'; export const CATEGORY_API_SECURITY = 'API Security'; export const CATEGORY_AGENTIC_SECURITY = 'Agentic Security'; +export const LEFT_NAV_CLOUD_SECURITY = 'Cloud Security'; +export const LEFT_NAV_ENDPOINT_SECURITY = 'Endpoint Security'; export function getDashboardCategory() { try { @@ -44,3 +46,28 @@ export function isApiSecurityCategory() { export function isAgenticSecurityCategory() { return isCategory(CATEGORY_AGENTIC_SECURITY); } + +export function getLeftNavCategory() { + try { + const category = PersistStore.getState().leftNavCategory + return category + } catch(e){ + return LEFT_NAV_CLOUD_SECURITY + } +} + +export function isLeftNavCategory(category) { + return getLeftNavCategory() === category; +} + +export function isCloudSecurityLeftNav() { + return isLeftNavCategory(LEFT_NAV_CLOUD_SECURITY); +} + +export function isEndpointSecurityLeftNav() { + return isLeftNavCategory(LEFT_NAV_ENDPOINT_SECURITY); +} + +export function shouldShowLeftNavSwitch() { + return isMCPSecurityCategory() || isAgenticSecurityCategory(); +} diff --git a/apps/dashboard/web/polaris_web/web/src/util/request.js b/apps/dashboard/web/polaris_web/web/src/util/request.js index d83c57d793..03128225d1 100644 --- a/apps/dashboard/web/polaris_web/web/src/util/request.js +++ b/apps/dashboard/web/polaris_web/web/src/util/request.js @@ -101,6 +101,7 @@ service.interceptors.request.use((config) => { config.headers['Content-Type'] = 'application/json' config.headers["access-token"] = SessionStore.getState().accessToken const currentCategory = PersistStore.getState().dashboardCategory || "API Security"; + const leftNavCategory = PersistStore.getState().leftNavCategory || "Cloud Security"; let contextSource = "API"; if (currentCategory === "API Security") { contextSource = "API"; @@ -110,6 +111,7 @@ service.interceptors.request.use((config) => { contextSource = "AGENTIC"; } config.headers['x-context-source'] = contextSource; + config.headers['x-left-nav-category'] = leftNavCategory; if (window.ACTIVE_ACCOUNT) { diff --git a/libs/dao/src/main/java/com/akto/dao/context/Context.java b/libs/dao/src/main/java/com/akto/dao/context/Context.java index f618a24268..7e18319800 100644 --- a/libs/dao/src/main/java/com/akto/dao/context/Context.java +++ b/libs/dao/src/main/java/com/akto/dao/context/Context.java @@ -12,11 +12,13 @@ public class Context { public static ThreadLocal accountId = new ThreadLocal(); public static ThreadLocal userId = new ThreadLocal(); public static ThreadLocal contextSource = new ThreadLocal(); +public static ThreadLocal leftNavCategory = new ThreadLocal(); public static void resetContextThreadLocals() { accountId.remove(); userId.remove(); contextSource.remove(); + leftNavCategory.remove(); } public static int getId() { @@ -29,6 +31,14 @@ public static void dummy() { } + public static String getLeftNavCategory() { + try { + return leftNavCategory.get(); + } catch (Exception e) { + return null; + } + } + public static int today() { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd"); LocalDateTime now = LocalDateTime.now(); diff --git a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java index 32b5222931..804b6804aa 100644 --- a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java +++ b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java @@ -82,8 +82,9 @@ public static List getCollectionsIdForUser(int userId, int accountId) { collectionList = collectionIdEntry.getFirst(); } + String leftNavCat = Context.getLeftNavCategory(); // since this function is used everywhere for the queries, taking context collections into account here - Set contextCollections = getContextCollectionsForUser(accountId, Context.contextSource.get()); + Set contextCollections = getContextCollectionsForUser(accountId, Context.contextSource.get(), leftNavCat); if(collectionList == null) { collectionList = contextCollections.stream() .collect(Collectors.toList()); @@ -106,7 +107,7 @@ public static void deleteContextCollectionsForUser(int accountId, CONTEXT_SOURCE contextCollectionsMap.remove(key); } - public static Set getContextCollectionsForUser(int accountId, CONTEXT_SOURCE source) { + public static Set getContextCollectionsForUser(int accountId, CONTEXT_SOURCE source, String leftNavCategory) { if(source == null) { source = CONTEXT_SOURCE.API; } @@ -115,7 +116,7 @@ public static Set getContextCollectionsForUser(int accountId, CONTEXT_S Set collectionList = new HashSet<>(); if (collectionIdEntry == null || (Context.now() - collectionIdEntry.getSecond() > CONTEXT_EXPIRY_TIME)) { - collectionList = getContextCollections(source); + collectionList = getContextCollections(source, leftNavCategory); } else { collectionList = collectionIdEntry.getFirst(); } @@ -123,7 +124,7 @@ public static Set getContextCollectionsForUser(int accountId, CONTEXT_S return collectionList; } - public static Set getContextCollections(CONTEXT_SOURCE source) { + public static Set getContextCollections(CONTEXT_SOURCE source, String leftNavCategory) { Set collectionIds = new HashSet<>(); Bson finalFilter = Filters.or( Filters.exists(ApiCollection.TAGS_STRING, false), @@ -159,6 +160,35 @@ public static Set getContextCollections(CONTEXT_SOURCE source) { default: break; } + + if (leftNavCategory != null && !leftNavCategory.isEmpty()) { + Bson leftNavFilter = null; + if ("Endpoint Security".equalsIgnoreCase(leftNavCategory)) { + // For Endpoint Security: filter collections where tags_string has source = "Endpoint" + leftNavFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, + Filters.and( + Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), + Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) + ) + ); + } else if ("Cloud Security".equalsIgnoreCase(leftNavCategory)) { + // For Cloud Security: filter collections where tags_string has source != "Endpoint" (or no source tag) + leftNavFilter = Filters.or( + Filters.not(Filters.exists(ApiCollection.TAGS_STRING)), + Filters.not(Filters.elemMatch(ApiCollection.TAGS_STRING, + Filters.and( + Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), + Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) + ) + )) + ); + } + + if (leftNavFilter != null) { + finalFilter = Filters.and(finalFilter, leftNavFilter); + } + } + MongoCursor cursor = ApiCollectionsDao.instance.getMCollection().find(finalFilter).projection(Projections.include(Constants.ID)).iterator(); while (cursor.hasNext()) { collectionIds.add(cursor.next().getId()); diff --git a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java index 478d776cd1..06e44b66eb 100644 --- a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java +++ b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java @@ -157,5 +157,9 @@ public enum CONTEXT_SOURCE { API, MCP, GEN_AI, AGENTIC } + public enum LEFT_NAVIGATION_SOURCE { + CLOUD_SECURITY, ENDPOINT_SECURITY; + } + /* ********************************************************************** */ } diff --git a/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java b/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java index 337841c506..dad45d1155 100644 --- a/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java +++ b/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java @@ -122,19 +122,19 @@ public static Set getDemosAndDeactivated() { } public static Set getMcpCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.MCP); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.MCP, Context.getLeftNavCategory()); } public static Set getGenAiCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.GEN_AI); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.GEN_AI, Context.getLeftNavCategory()); } public static Set getApiCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.API); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.API, Context.getLeftNavCategory()); } public static Set getAgenticCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.AGENTIC); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.AGENTIC, Context.getLeftNavCategory()); } public static List getInvalidTestErrors() { From 369fd3b04be3f3f23dee3ba1283cb8cf5adbc64f Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 16:38:28 +0530 Subject: [PATCH 05/18] subcategory in dashboard changes --- .../com/akto/action/AccessTokenAction.java | 2 +- .../com/akto/action/ApiCollectionsAction.java | 63 +++------------ .../com/akto/filter/UserDetailsFilter.java | 21 ++++- .../components/layouts/header/Headers.js | 13 ++- .../components/layouts/leftnav/LeftNav.js | 20 ++--- .../pages/dashboard/HomeDashboard.jsx | 45 +++-------- .../src/apps/dashboard/pages/dashboard/api.js | 4 +- .../api_collections/ApiCollections.jsx | 27 +------ .../web/src/apps/main/PersistStore.js | 10 +-- .../web/src/apps/main/labelHelper.js | 18 ++--- .../web/polaris_web/web/src/util/request.js | 4 +- libs/dao/src/main/java/com/akto/DaoInit.java | 3 +- .../java/com/akto/dao/context/Context.java | 10 ++- .../akto/dto/rbac/UsersCollectionsList.java | 80 ++++++++++--------- .../java/com/akto/util/enums/GlobalEnums.java | 2 +- .../com/akto/usage/UsageMetricCalculator.java | 8 +- 16 files changed, 138 insertions(+), 192 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java b/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java index ca3a9ad4d3..87ff229f69 100644 --- a/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java @@ -22,7 +22,7 @@ public class AccessTokenAction implements Action, ServletResponseAware, ServletR public static final String ACCESS_TOKEN_HEADER_NAME = "access-token"; public static final String CONTEXT_SOURCE_HEADER = "x-context-source"; public static final String AKTO_SESSION_TOKEN = "x-akto-session-token"; - public static final String LEFT_NAV_CATEGORY_HEADER = "x-left-nav-category"; + public static final String SUB_CATEGORY_HEADER = "x-sub-category"; @Override public String execute() { diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java index f904af5ea8..5b82b64f9d 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java @@ -1189,26 +1189,8 @@ public String fetchMcpdata() { Bson mcpTagFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq("keyName", com.akto.util.Constants.AKTO_MCP_SERVER_TAG) ); - List allMcpCollections = ApiCollectionsDao.instance.findAll(mcpTagFilter, null); - - // Filter collections based on contextType (cloud vs endpoint) - List mcpCollections = allMcpCollections; - if (contextType != null && !contextType.isEmpty()) { - mcpCollections = allMcpCollections.stream() - .filter(collection -> { - boolean isEndpointCollection = collection.getTagsList() != null && - collection.getTagsList().stream().anyMatch(tag -> - "ENDPOINT".equals(tag.getSource()) - ); - - if ("endpoint".equals(contextType)) { - return isEndpointCollection; - } else { // "cloud" context - return !isEndpointCollection; - } - }) - .collect(Collectors.toList()); - } + + List mcpCollections = ApiCollectionsDao.instance.findAll(mcpTagFilter, null); List mcpCollectionIds = mcpCollections.stream().map(ApiCollection::getId).collect(Collectors.toList()); @@ -1262,24 +1244,21 @@ public String fetchMcpdata() { case "TOOLS": Bson toolsFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_TOOL), - Filters.ne("remarks", "Rejected"), - mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) + Filters.ne("remarks", "Rejected") ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(toolsFilter); break; case "PROMPTS": Bson promptsFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_PROMPT), - Filters.ne("remarks", "Rejected"), - mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) + Filters.ne("remarks", "Rejected") ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(promptsFilter); break; case "RESOURCES": Bson resourcesFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_RESOURCE), - Filters.ne("remarks", "Rejected"), - mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) + Filters.ne("remarks", "Rejected") ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(resourcesFilter); break; @@ -1287,8 +1266,7 @@ public String fetchMcpdata() { case "MCP_SERVER": Bson mcpServerFilter = Filters.and( Filters.eq("type", Constants.AKTO_MCP_SERVER), - Filters.ne("remarks", "Rejected"), - mcpCollectionIds.isEmpty() ? Filters.exists("hostCollectionId") : Filters.in("hostCollectionId", mcpCollectionIds) + Filters.ne("remarks", "Rejected") ); this.mcpDataCount = (int) McpAuditInfoDao.instance.count(mcpServerFilter); break; @@ -1356,28 +1334,13 @@ public String fetchMcpdata() { Bson guardRailTagFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq("keyName", Constants.AKTO_GUARD_RAIL_TAG) ); - // Fetch collections with full data to apply context filtering - List allGuardRailCollections = ApiCollectionsDao.instance.findAll(guardRailTagFilter, null); - - // Filter collections based on contextType (cloud vs endpoint) - List guardRailCollections = allGuardRailCollections; - if (contextType != null && !contextType.isEmpty()) { - guardRailCollections = allGuardRailCollections.stream() - .filter(collection -> { - boolean isEndpointCollection = collection.getTagsList() != null && - collection.getTagsList().stream().anyMatch(tag -> - "ENDPOINT".equals(tag.getSource()) - ); - - if ("endpoint".equals(contextType)) { - return isEndpointCollection; - } else { // "cloud" context - return !isEndpointCollection; - } - }) - .collect(Collectors.toList()); - } - + + // Use projection to only fetch IDs, reducing memory usage + List guardRailCollections = ApiCollectionsDao.instance.findAll( + guardRailTagFilter, + Projections.include(ApiCollection.ID) + ); + List guardRailCollectionIds = guardRailCollections.stream() .map(ApiCollection::getId) .collect(Collectors.toList()); diff --git a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java index 673868b57d..00239d803f 100644 --- a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java +++ b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java @@ -83,7 +83,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo String accessTokenFromResponse = httpServletResponse.getHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); String accessTokenFromRequest = httpServletRequest.getHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); String contextSourceFromRequest = httpServletRequest.getHeader(AccessTokenAction.CONTEXT_SOURCE_HEADER); - String leftNavCategoryFromRequest = httpServletRequest.getHeader(AccessTokenAction.LEFT_NAV_CATEGORY_HEADER); + String subCategoryFromRequest = httpServletRequest.getHeader(AccessTokenAction.SUB_CATEGORY_HEADER); String aktoSessionTokenFromRequest = httpServletRequest.getHeader(AccessTokenAction.AKTO_SESSION_TOKEN); @@ -109,8 +109,23 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } } - if(!StringUtils.isEmpty(leftNavCategoryFromRequest)) { - Context.leftNavCategory.set(leftNavCategoryFromRequest); + if(!StringUtils.isEmpty(subCategoryFromRequest)) { + try { + // Convert from frontend string format to enum format + GlobalEnums.SUB_CATEGORY_SOURCE subCategoryEnum; + if ("Cloud Security".equalsIgnoreCase(subCategoryFromRequest)) { + subCategoryEnum = GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY; + } else if ("Endpoint Security".equalsIgnoreCase(subCategoryFromRequest)) { + subCategoryEnum = GlobalEnums.SUB_CATEGORY_SOURCE.ENDPOINT_SECURITY; + } else { + // Default fallback + subCategoryEnum = GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY; + } + Context.subCategory.set(subCategoryEnum); + } catch (Exception e) { + // Fallback to default if conversion fails + Context.subCategory.set(GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY); + } } if(StringUtils.isNotEmpty(aktoSessionTokenFromRequest) && httpServletRequest.getRequestURI().contains("agent")){ diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js index 8cf832e484..ca91959d77 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js @@ -118,6 +118,9 @@ export default function Header() { } const handleDashboardChange = (value) => { + // Preserve current subcategory selection + const currentSubCategory = PersistStore.getState().subCategory || 'Cloud Security'; + PersistStore.getState().setAllCollections([]); PersistStore.getState().setCollectionsMap({}); PersistStore.getState().setHostNameMap({}); @@ -126,7 +129,15 @@ export default function Header() { LocalStore.getState().setCategoryMap({}); LocalStore.getState().setSubCategoryMap({}); SessionStore.getState().setThreatFiltersMap({}); - PersistStore.getState().setLeftNavCategory('Cloud Security'); + + // Only set subcategory for dashboard categories that support subcategories + if (value === "MCP Security" || value === "Agentic Security") { + PersistStore.getState().setSubCategory(currentSubCategory); + } else { + // For API Security and other categories, reset to default + PersistStore.getState().setSubCategory('Cloud Security'); + } + setDashboardCategory(value); window.location.reload(); window.location.href("/dashboard/observe/inventory") diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 5837a2feb0..7b79c63bab 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -22,7 +22,7 @@ import func from "@/util/func"; import Dropdown from "../Dropdown"; import SessionStore from "../../../../main/SessionStore"; import IssuesStore from "../../../pages/issues/issuesStore"; -import { CATEGORY_API_SECURITY, mapLabel, shouldShowLeftNavSwitch, LEFT_NAV_CLOUD_SECURITY, LEFT_NAV_ENDPOINT_SECURITY, getLeftNavCategory } from "../../../../main/labelHelper"; +import { CATEGORY_API_SECURITY, mapLabel, shouldShowLeftNavSwitch, SUB_CATEGORY_CLOUD_SECURITY, SUB_CATEGORY_ENDPOINT_SECURITY, getSubCategory } from "../../../../main/labelHelper"; export default function LeftNav() { const navigate = useNavigate(); @@ -39,8 +39,8 @@ export default function LeftNav() { const resetStore = LocalStore(state => state.resetStore); const resetSession = SessionStore(state => state.resetStore); const resetFields = IssuesStore(state => state.resetStore); - const leftNavCategory = PersistStore((state) => state.leftNavCategory) || LEFT_NAV_CLOUD_SECURITY; - const setLeftNavCategory = PersistStore((state) => state.setLeftNavCategory); + const subCategory = PersistStore((state) => state.subCategory) || SUB_CATEGORY_CLOUD_SECURITY; + const setSubCategory = PersistStore((state) => state.setSubCategory); const handleSelect = (selectedId) => { setLeftNavSelected(selectedId); @@ -58,8 +58,8 @@ export default function LeftNav() { }; - const handleLeftNavCategoryChange = (selected) => { - setLeftNavCategory(selected); + const handleSubCategoryChange = (selected) => { + setSubCategory(selected); // Force refresh of current page to reload data with new left nav category window.location.reload(); }; @@ -119,9 +119,9 @@ export default function LeftNav() { onClick: item.onClick ? () => { // Set the left nav category based on section if (prefix === "cloud") { - setLeftNavCategory("Cloud Security"); + setSubCategory("Cloud Security"); } else if (prefix === "endpoint") { - setLeftNavCategory("Endpoint Security"); + setSubCategory("Endpoint Security"); } const originalHandler = item.onClick; @@ -146,9 +146,9 @@ export default function LeftNav() { onClick: subItem.onClick ? () => { // Set the left nav category based on section if (prefix === "cloud") { - setLeftNavCategory("Cloud Security"); + setSubCategory("Cloud Security"); } else if (prefix === "endpoint") { - setLeftNavCategory("Endpoint Security"); + setSubCategory("Endpoint Security"); } const originalHandler = subItem.onClick; @@ -680,7 +680,7 @@ export default function LeftNav() { } return allItems - }, [dashboardCategory, leftNavSelected, leftNavCategory]) + }, [dashboardCategory, leftNavSelected, subCategory]) const navigationMarkup = (
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx index f6f9b07f91..fc9c6712a6 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx @@ -377,33 +377,8 @@ function HomeDashboard() { return completeData.sort((a, b) => a[0] - b[0]); }; - const getNavigationSection = () => { - try { - const leftNavSelected = sessionStorage.getItem('leftNavSelected'); - - // Handle null/undefined cases - if (!leftNavSelected) { - return 'cloud'; - } - - const navString = leftNavSelected.toString().toLowerCase(); - - if (navString.startsWith('cloud_') || navString.includes('cloud')) { - return 'cloud'; - } else if (navString.startsWith('endpoint_') || navString.includes('endpoint')) { - return 'endpoint'; - } - - return 'cloud'; - - } catch (error) { - return 'cloud'; // Safe fallback - } - }; - const fetchData = async () => { setLoading(true) - const currentNavigationSection = getNavigationSection(); // all apis let apiPromises = [ observeApi.getUserEndpoints(), @@ -412,16 +387,16 @@ function HomeDashboard() { api.fetchEndpointsCount(startTimestamp, endTimestamp), testingApi.fetchSeverityInfoForIssues({}, [], 0), api.getApiInfoForMissingData(0, endTimestamp), - api.fetchMcpdata('TOTAL_APIS', currentNavigationSection), - api.fetchMcpdata('THIRD_PARTY_APIS', currentNavigationSection), - api.fetchMcpdata('RECENT_OPEN_ALERTS', currentNavigationSection), - api.fetchMcpdata('CRITICAL_APIS', currentNavigationSection), - api.fetchMcpdata('TOOLS', currentNavigationSection), - api.fetchMcpdata('PROMPTS', currentNavigationSection), - api.fetchMcpdata('RESOURCES', currentNavigationSection), - api.fetchMcpdata('MCP_SERVER', currentNavigationSection), - api.fetchMcpdata('POLICY_GUARDRAIL_APIS', currentNavigationSection), - api.fetchMcpdata('TOP_3_APPLICATIONS_BY_TRAFFIC', currentNavigationSection) + api.fetchMcpdata('TOTAL_APIS'), + api.fetchMcpdata('THIRD_PARTY_APIS'), + api.fetchMcpdata('RECENT_OPEN_ALERTS'), + api.fetchMcpdata('CRITICAL_APIS'), + api.fetchMcpdata('TOOLS'), + api.fetchMcpdata('PROMPTS'), + api.fetchMcpdata('RESOURCES'), + api.fetchMcpdata('MCP_SERVER'), + api.fetchMcpdata('POLICY_GUARDRAIL_APIS'), + api.fetchMcpdata('TOP_3_APPLICATIONS_BY_TRAFFIC') ]; let results = await Promise.allSettled(apiPromises); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js index ffadcd8545..c8656e0a03 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/api.js @@ -61,11 +61,11 @@ const api = { }) }, - fetchMcpdata: async (filterType, contextType = null) => { + fetchMcpdata: async (filterType) => { return await request({ url: '/api/fetchMcpdata', method: 'post', - data: { filterType, contextType } + data: { filterType } }) }, diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx index a20ebb9c36..edae9327a8 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx @@ -35,7 +35,7 @@ import ReactFlow, { } from 'react-flow-renderer'; import SetUserEnvPopupComponent from "./component/SetUserEnvPopupComponent"; -import { getDashboardCategory, mapLabel, isMCPSecurityCategory, isAgenticSecurityCategory, isGenAISecurityCategory, shouldShowLeftNavSwitch, isEndpointSecurityLeftNav, getLeftNavCategory } from "../../../../main/labelHelper"; +import { getDashboardCategory, mapLabel, isMCPSecurityCategory, isAgenticSecurityCategory, isGenAISecurityCategory, shouldShowLeftNavSwitch, isEndpointSecurityLeftNav, getSubCategory } from "../../../../main/labelHelper"; const CenterViewType = { Table: 0, @@ -200,26 +200,6 @@ const resourceName = { plural: 'collections', }; -const filterCollectionsByLeftNav = (collectionsArr) => { - if (!shouldShowLeftNavSwitch()) { - return collectionsArr; - } - - const leftNavCategory = getLeftNavCategory(); - - return collectionsArr.filter((collection) => { - const tagsList = collection?.tagsList || []; - const tagsString = JSON.stringify(tagsList); - - if (leftNavCategory === 'Endpoint Security') { - // Show collections where tagsList contains source: "Endpoint" - return tagsString.includes('"source":"Endpoint"') || tagsString.includes('source') && tagsString.includes('Endpoint'); - } else { - // Cloud Security - show collections where tagsList does NOT contain source: "Endpoint" - return !(tagsString.includes('"source":"Endpoint"') || (tagsString.includes('source') && tagsString.includes('Endpoint'))); - } - }); -} const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, coverageMap, trafficInfoMap, riskScoreMap, isLoading) => { @@ -229,10 +209,7 @@ const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, cov return { prettify: [], normal: [] }; } - // Filter collections based on left navigation category - const filteredCollections = filterCollectionsByLeftNav(collectionsArr); - - const newData = filteredCollections.map((c) => { + const newData = collectionsArr.map((c) => { if(c.deactivated){ c.rowStatus = 'critical' c.disableClick = true diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js b/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js index d2ef96ac57..60b1742c0b 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js @@ -61,7 +61,7 @@ const initialState = { sendEventOnLogin: false, tableSelectedTab: {}, dashboardCategory: 'API Security', - leftNavCategory: 'Cloud Security', + subCategory: 'Cloud Security', }; let persistStore = (set, get) => ({ @@ -222,11 +222,11 @@ let persistStore = (set, get) => ({ console.error("Error setting dashboardCategory:", error); } }, - setLeftNavCategory: (leftNavCategory) => { + setSubCategory: (subCategory) => { try { - set({ leftNavCategory }); + set({ subCategory }); } catch (error) { - console.error("Error setting leftNavCategory:", error); + console.error("Error setting subCategory:", error); } }, @@ -251,7 +251,7 @@ persistStore = persist(persistStore, { sendEventOnLogin: state.sendEventOnLogin, tableSelectedTab: state.tableSelectedTab, dashboardCategory: state.dashboardCategory, - leftNavCategory: state.leftNavCategory + subCategory: state.subCategory }) }); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js b/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js index 501369f40c..51e71b97e0 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js @@ -15,8 +15,8 @@ export const CATEGORY_MCP_SECURITY = 'MCP Security'; export const CATEGORY_GEN_AI = 'Gen AI'; export const CATEGORY_API_SECURITY = 'API Security'; export const CATEGORY_AGENTIC_SECURITY = 'Agentic Security'; -export const LEFT_NAV_CLOUD_SECURITY = 'Cloud Security'; -export const LEFT_NAV_ENDPOINT_SECURITY = 'Endpoint Security'; +export const SUB_CATEGORY_CLOUD_SECURITY = 'Cloud Security'; +export const SUB_CATEGORY_ENDPOINT_SECURITY = 'Endpoint Security'; export function getDashboardCategory() { try { @@ -47,25 +47,25 @@ export function isAgenticSecurityCategory() { return isCategory(CATEGORY_AGENTIC_SECURITY); } -export function getLeftNavCategory() { +export function getSubCategory() { try { - const category = PersistStore.getState().leftNavCategory + const category = PersistStore.getState().subCategory return category } catch(e){ - return LEFT_NAV_CLOUD_SECURITY + return SUB_CATEGORY_CLOUD_SECURITY } } -export function isLeftNavCategory(category) { - return getLeftNavCategory() === category; +export function isSubCategory(category) { + return getSubCategory() === category; } export function isCloudSecurityLeftNav() { - return isLeftNavCategory(LEFT_NAV_CLOUD_SECURITY); + return isLeftNavCategory(SUB_CATEGORY_CLOUD_SECURITY); } export function isEndpointSecurityLeftNav() { - return isLeftNavCategory(LEFT_NAV_ENDPOINT_SECURITY); + return isLeftNavCategory(SUB_CATEGORY_ENDPOINT_SECURITY); } export function shouldShowLeftNavSwitch() { diff --git a/apps/dashboard/web/polaris_web/web/src/util/request.js b/apps/dashboard/web/polaris_web/web/src/util/request.js index 03128225d1..13101a1c7f 100644 --- a/apps/dashboard/web/polaris_web/web/src/util/request.js +++ b/apps/dashboard/web/polaris_web/web/src/util/request.js @@ -101,7 +101,7 @@ service.interceptors.request.use((config) => { config.headers['Content-Type'] = 'application/json' config.headers["access-token"] = SessionStore.getState().accessToken const currentCategory = PersistStore.getState().dashboardCategory || "API Security"; - const leftNavCategory = PersistStore.getState().leftNavCategory || "Cloud Security"; + const subCategory = PersistStore.getState().subCategory || "Cloud Security"; let contextSource = "API"; if (currentCategory === "API Security") { contextSource = "API"; @@ -111,7 +111,7 @@ service.interceptors.request.use((config) => { contextSource = "AGENTIC"; } config.headers['x-context-source'] = contextSource; - config.headers['x-left-nav-category'] = leftNavCategory; + config.headers['x-sub-category'] = subCategory; if (window.ACTIVE_ACCOUNT) { diff --git a/libs/dao/src/main/java/com/akto/DaoInit.java b/libs/dao/src/main/java/com/akto/DaoInit.java index cfef934603..23aa4b392a 100644 --- a/libs/dao/src/main/java/com/akto/DaoInit.java +++ b/libs/dao/src/main/java/com/akto/DaoInit.java @@ -399,7 +399,8 @@ public static CodecRegistry createCodecRegistry(){ new EnumCodec<>(ModelType.class), new EnumCodec<>(ModuleInfo.ModuleType.class), new EnumCodec<>(TLSAuthParam.CertificateType.class), - new EnumCodec<>(TicketSource.class) + new EnumCodec<>(TicketSource.class), + new EnumCodec<>(CollectionTags.TagSource.class) ); return fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), pojoCodecRegistry, diff --git a/libs/dao/src/main/java/com/akto/dao/context/Context.java b/libs/dao/src/main/java/com/akto/dao/context/Context.java index 7e18319800..5498a2ce71 100644 --- a/libs/dao/src/main/java/com/akto/dao/context/Context.java +++ b/libs/dao/src/main/java/com/akto/dao/context/Context.java @@ -2,7 +2,9 @@ import com.akto.dao.AccountsDao; import com.akto.dto.Account; +import com.akto.util.enums.GlobalEnums; import com.akto.util.enums.GlobalEnums.CONTEXT_SOURCE; +import com.akto.util.enums.GlobalEnums.SUB_CATEGORY_SOURCE; import java.math.BigDecimal; import java.time.*; @@ -12,13 +14,13 @@ public class Context { public static ThreadLocal accountId = new ThreadLocal(); public static ThreadLocal userId = new ThreadLocal(); public static ThreadLocal contextSource = new ThreadLocal(); -public static ThreadLocal leftNavCategory = new ThreadLocal(); +public static ThreadLocal subCategory = new ThreadLocal<>(); public static void resetContextThreadLocals() { accountId.remove(); userId.remove(); contextSource.remove(); - leftNavCategory.remove(); + subCategory.remove(); } public static int getId() { @@ -31,9 +33,9 @@ public static void dummy() { } - public static String getLeftNavCategory() { + public static SUB_CATEGORY_SOURCE getSubCategory() { try { - return leftNavCategory.get(); + return subCategory.get(); } catch (Exception e) { return null; } diff --git a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java index 804b6804aa..dda062d900 100644 --- a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java +++ b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java @@ -9,6 +9,7 @@ import com.akto.dto.traffic.CollectionTags; import com.akto.util.Constants; import com.akto.util.Pair; +import com.akto.util.enums.GlobalEnums; import com.akto.util.enums.GlobalEnums.CONTEXT_SOURCE; import com.mongodb.client.MongoCursor; import com.mongodb.client.model.Filters; @@ -27,7 +28,7 @@ public class UsersCollectionsList { private static final ConcurrentHashMap, Pair, Integer>> usersCollectionMap = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap, Pair, Integer>> contextCollectionsMap = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap, Integer>> contextCollectionsMap = new ConcurrentHashMap<>(); private static final int EXPIRY_TIME = 15 * 60; private static final int CONTEXT_EXPIRY_TIME = 120; @@ -82,9 +83,9 @@ public static List getCollectionsIdForUser(int userId, int accountId) { collectionList = collectionIdEntry.getFirst(); } - String leftNavCat = Context.getLeftNavCategory(); + GlobalEnums.SUB_CATEGORY_SOURCE subCategory = Context.getSubCategory(); // since this function is used everywhere for the queries, taking context collections into account here - Set contextCollections = getContextCollectionsForUser(accountId, Context.contextSource.get(), leftNavCat); + Set contextCollections = getContextCollectionsForUser(accountId, Context.contextSource.get(), subCategory); if(collectionList == null) { collectionList = contextCollections.stream() .collect(Collectors.toList()); @@ -103,20 +104,25 @@ public static void deleteContextCollectionsForUser(int accountId, CONTEXT_SOURCE if(contextCollectionsMap.isEmpty()) { return; } - Pair key = new Pair<>(accountId, source); - contextCollectionsMap.remove(key); + // Remove all cache entries for this accountId and source (regardless of leftNavCategory) + final String keyPrefix = accountId + "_" + source + "_"; + contextCollectionsMap.entrySet().removeIf(entry -> + entry.getKey().startsWith(keyPrefix)); } - public static Set getContextCollectionsForUser(int accountId, CONTEXT_SOURCE source, String leftNavCategory) { + public static Set getContextCollectionsForUser(int accountId, CONTEXT_SOURCE source, GlobalEnums.SUB_CATEGORY_SOURCE subCategory) { if(source == null) { source = CONTEXT_SOURCE.API; } - Pair key = new Pair<>(accountId, source); - Pair, Integer> collectionIdEntry = contextCollectionsMap.get(key); - Set collectionList = new HashSet<>(); + // Create cache key that includes leftNavCategory to ensure proper filtering + String cacheKey = accountId + "_" + source + "_" + (subCategory != null ? subCategory : "null"); + Pair, Integer> collectionIdEntry = contextCollectionsMap.get(cacheKey); + Set collectionList; if (collectionIdEntry == null || (Context.now() - collectionIdEntry.getSecond() > CONTEXT_EXPIRY_TIME)) { - collectionList = getContextCollections(source, leftNavCategory); + collectionList = getContextCollections(source, subCategory); + // Cache the result with the new key + contextCollectionsMap.put(cacheKey, new Pair<>(collectionList, Context.now())); } else { collectionList = collectionIdEntry.getFirst(); } @@ -124,7 +130,7 @@ public static Set getContextCollectionsForUser(int accountId, CONTEXT_S return collectionList; } - public static Set getContextCollections(CONTEXT_SOURCE source, String leftNavCategory) { + public static Set getContextCollections(CONTEXT_SOURCE source, GlobalEnums.SUB_CATEGORY_SOURCE subCategory ) { Set collectionIds = new HashSet<>(); Bson finalFilter = Filters.or( Filters.exists(ApiCollection.TAGS_STRING, false), @@ -133,6 +139,30 @@ public static Set getContextCollections(CONTEXT_SOURCE source, String Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_GEN_AI_TAG)) ) ); + + if (subCategory != null) { + + if ((subCategory.equals(GlobalEnums.SUB_CATEGORY_SOURCE.ENDPOINT_SECURITY))) { + // For Endpoint Security: filter collections where tags_string has source = "Endpoint" + finalFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, + Filters.and( + Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), + Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) + ) + ); + } else if (subCategory.equals(GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY)) { + // For Cloud Security: exclude collections that have source = "ENDPOINT" tag + finalFilter = Filters.not( + Filters.elemMatch(ApiCollection.TAGS_STRING, + Filters.and( + Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), + Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) + ) + ) + ); + } + } + switch (source) { case MCP: finalFilter = Filters.and( @@ -161,34 +191,6 @@ public static Set getContextCollections(CONTEXT_SOURCE source, String break; } - if (leftNavCategory != null && !leftNavCategory.isEmpty()) { - Bson leftNavFilter = null; - if ("Endpoint Security".equalsIgnoreCase(leftNavCategory)) { - // For Endpoint Security: filter collections where tags_string has source = "Endpoint" - leftNavFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, - Filters.and( - Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), - Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) - ) - ); - } else if ("Cloud Security".equalsIgnoreCase(leftNavCategory)) { - // For Cloud Security: filter collections where tags_string has source != "Endpoint" (or no source tag) - leftNavFilter = Filters.or( - Filters.not(Filters.exists(ApiCollection.TAGS_STRING)), - Filters.not(Filters.elemMatch(ApiCollection.TAGS_STRING, - Filters.and( - Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), - Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) - ) - )) - ); - } - - if (leftNavFilter != null) { - finalFilter = Filters.and(finalFilter, leftNavFilter); - } - } - MongoCursor cursor = ApiCollectionsDao.instance.getMCollection().find(finalFilter).projection(Projections.include(Constants.ID)).iterator(); while (cursor.hasNext()) { collectionIds.add(cursor.next().getId()); diff --git a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java index 06e44b66eb..eef2f8058b 100644 --- a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java +++ b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java @@ -157,7 +157,7 @@ public enum CONTEXT_SOURCE { API, MCP, GEN_AI, AGENTIC } - public enum LEFT_NAVIGATION_SOURCE { + public enum SUB_CATEGORY_SOURCE { CLOUD_SECURITY, ENDPOINT_SECURITY; } diff --git a/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java b/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java index dad45d1155..48696507a7 100644 --- a/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java +++ b/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java @@ -122,19 +122,19 @@ public static Set getDemosAndDeactivated() { } public static Set getMcpCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.MCP, Context.getLeftNavCategory()); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.MCP, Context.getSubCategory()); } public static Set getGenAiCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.GEN_AI, Context.getLeftNavCategory()); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.GEN_AI, Context.getSubCategory()); } public static Set getApiCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.API, Context.getLeftNavCategory()); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.API, null); } public static Set getAgenticCollections() { - return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.AGENTIC, Context.getLeftNavCategory()); + return UsersCollectionsList.getContextCollections(CONTEXT_SOURCE.AGENTIC, Context.getSubCategory()); } public static List getInvalidTestErrors() { From 745f89d9dfb09c1f71734b52b5a8817159a0661f Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 18:58:24 +0530 Subject: [PATCH 06/18] subcategory in dashboard changes --- .../com/akto/action/ApiCollectionsAction.java | 6 +-- .../com/akto/filter/UserDetailsFilter.java | 21 +++------ .../api_collections/ApiCollections.jsx | 3 +- .../dashboard/pages/quick_start/transform.js | 18 ------- .../web/src/apps/main/PersistStore.js | 2 +- .../web/src/apps/main/labelHelper.js | 8 ---- .../web/polaris_web/web/src/util/request.js | 11 ++++- .../akto/dto/rbac/UsersCollectionsList.java | 47 +++++++++---------- .../java/com/akto/util/enums/GlobalEnums.java | 2 +- 9 files changed, 46 insertions(+), 72 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java index 5b82b64f9d..18f4c2a8aa 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java @@ -249,7 +249,7 @@ public String fetchAllCollectionsBasic() { Integer accountId = Context.accountId.get(); GlobalEnums.CONTEXT_SOURCE contextSource = Context.contextSource.get(); if (accountId != null) { - UsersCollectionsList.deleteContextCollectionsForUser(accountId, contextSource); + UsersCollectionsList.deleteContextCollectionsForUser(accountId, contextSource, Context.subCategory.get()); } } catch (Exception e) { // Log the error but don't fail the entire request @@ -334,7 +334,7 @@ public String createCollection() { UsersCollectionsList.deleteCollectionIdsFromCache(userId, accountId); // remove the cache of context collections for account - UsersCollectionsList.deleteContextCollectionsForUser(Context.accountId.get(), Context.contextSource.get()); + UsersCollectionsList.deleteContextCollectionsForUser(Context.accountId.get(), Context.contextSource.get(), Context.subCategory.get()); } catch(Exception e){ } @@ -403,7 +403,7 @@ public String deleteMultipleCollections() { UsersCollectionsList.deleteCollectionIdsFromCache(userId, accountId); // remove the cache of context collections for account - UsersCollectionsList.deleteContextCollectionsForUser(Context.accountId.get(), Context.contextSource.get()); + UsersCollectionsList.deleteContextCollectionsForUser(Context.accountId.get(), Context.contextSource.get(), Context.subCategory.get()); } catch (Exception e) { } diff --git a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java index 00239d803f..942c3e5c45 100644 --- a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java +++ b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java @@ -110,21 +110,14 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } if(!StringUtils.isEmpty(subCategoryFromRequest)) { - try { - // Convert from frontend string format to enum format - GlobalEnums.SUB_CATEGORY_SOURCE subCategoryEnum; - if ("Cloud Security".equalsIgnoreCase(subCategoryFromRequest)) { - subCategoryEnum = GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY; - } else if ("Endpoint Security".equalsIgnoreCase(subCategoryFromRequest)) { - subCategoryEnum = GlobalEnums.SUB_CATEGORY_SOURCE.ENDPOINT_SECURITY; - } else { - // Default fallback - subCategoryEnum = GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY; + if(StringUtils.isEmpty(subCategoryFromRequest)){ + Context.subCategory.set(GlobalEnums.SUB_CATEGORY_SOURCE.DEFAULT); + } else { + try { + Context.subCategory.set(GlobalEnums.SUB_CATEGORY_SOURCE.valueOf(subCategoryFromRequest.toUpperCase())); + } catch (Exception e) { + Context.subCategory.set(GlobalEnums.SUB_CATEGORY_SOURCE.DEFAULT); } - Context.subCategory.set(subCategoryEnum); - } catch (Exception e) { - // Fallback to default if conversion fails - Context.subCategory.set(GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY); } } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx index edae9327a8..a710efb5da 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx @@ -215,6 +215,7 @@ const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, cov c.disableClick = true } const tagsList = JSON.stringify(c?.tagsList || "") + const currentSubCategory = PersistStore.getState().subCategory return{ ...c, icon: CircleTickMajor, @@ -222,7 +223,7 @@ const convertToNewData = (collectionsArr, sensitiveInfoMap, severityInfoMap, cov envTypeOriginal: c?.envType, envType: c?.envType?.map(func.formatCollectionType), ...(shouldShowLeftNavSwitch() ? { - iconComp: (icon) + iconComp: (icon) } : {}), ...((isGenAISecurityCategory() || isAgenticSecurityCategory()) && func.isDemoAccount() && tagsList.includes("gen-ai") ? { iconComp: () diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js index cfb831fdad..aa1109819b 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js @@ -859,24 +859,6 @@ const safariExtensionObj = { key: "SAFARI_BROWSER_EXTENSION", } -const cloudflareWarpObj = { - icon: '/public/cloudflare.svg', - label: "Cloudflare WARP", - text: "Cloudflare WARP setup is recommended, if you use Cloudflare WARP for secure, accelerated device-to-internet connectivity.", - docsUrl: 'https://docs.akto.io/traffic-connector', - component: , - key: "CLOUDFLARE_WARP", -} - -const zscalerObj = { - icon: '/public/zscaler_logo.svg', - label: "Zscaler", - text: "Zscaler setup is recommended,if your organization uses Zscaler for secure, zero-trust internet and app access.", - docsUrl: 'https://docs.akto.io/traffic-connector', - component: , - key: "ZSCALER", -} - const quick_start_policy_lines= [ `{`, diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js b/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js index 60b1742c0b..c8a4b4a97b 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/PersistStore.js @@ -61,7 +61,7 @@ const initialState = { sendEventOnLogin: false, tableSelectedTab: {}, dashboardCategory: 'API Security', - subCategory: 'Cloud Security', + subCategory: 'Default', }; let persistStore = (set, get) => ({ diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js b/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js index 51e71b97e0..dfe35a11ec 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/labelHelper.js @@ -60,14 +60,6 @@ export function isSubCategory(category) { return getSubCategory() === category; } -export function isCloudSecurityLeftNav() { - return isLeftNavCategory(SUB_CATEGORY_CLOUD_SECURITY); -} - -export function isEndpointSecurityLeftNav() { - return isLeftNavCategory(SUB_CATEGORY_ENDPOINT_SECURITY); -} - export function shouldShowLeftNavSwitch() { return isMCPSecurityCategory() || isAgenticSecurityCategory(); } diff --git a/apps/dashboard/web/polaris_web/web/src/util/request.js b/apps/dashboard/web/polaris_web/web/src/util/request.js index 13101a1c7f..894ff6721e 100644 --- a/apps/dashboard/web/polaris_web/web/src/util/request.js +++ b/apps/dashboard/web/polaris_web/web/src/util/request.js @@ -101,7 +101,7 @@ service.interceptors.request.use((config) => { config.headers['Content-Type'] = 'application/json' config.headers["access-token"] = SessionStore.getState().accessToken const currentCategory = PersistStore.getState().dashboardCategory || "API Security"; - const subCategory = PersistStore.getState().subCategory || "Cloud Security"; + let subCategory = PersistStore.getState().subCategory || "Default"; let contextSource = "API"; if (currentCategory === "API Security") { contextSource = "API"; @@ -110,8 +110,15 @@ service.interceptors.request.use((config) => { } else if (currentCategory === "Agentic Security") { contextSource = "AGENTIC"; } + + if(subCategory === "Cloud Security"){ + subCategory = "CLOUD_SECURITY"; + } else if(subCategory === "Endpoint Security"){ + subCategory = "ENDPOINT_SECURITY"; + } + config.headers['x-context-source'] = contextSource; - config.headers['x-sub-category'] = subCategory; + config.headers['x-sub-category'] = subCategory; if (window.ACTIVE_ACCOUNT) { diff --git a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java index dda062d900..82e31e641a 100644 --- a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java +++ b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java @@ -41,6 +41,8 @@ public static void deleteCollectionIdsFromCache(int userId, int accountId) { public static final String RBAC_FEATURE = "RBAC_FEATURE"; + private static final Bson tagFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.SOURCE, CollectionTags.TagSource.ENDPOINT)); + /* * Cases: * 1. For admin we save the list as null and @@ -97,25 +99,32 @@ public static List getCollectionsIdForUser(int userId, int accountId) { return collectionList; } - public static void deleteContextCollectionsForUser(int accountId, CONTEXT_SOURCE source) { + public static void deleteContextCollectionsForUser(int accountId, CONTEXT_SOURCE source, GlobalEnums.SUB_CATEGORY_SOURCE subCategory) { if(source == null) { source = CONTEXT_SOURCE.API; } + + if(subCategory == null) { + subCategory = GlobalEnums.SUB_CATEGORY_SOURCE.DEFAULT; + } if(contextCollectionsMap.isEmpty()) { return; } // Remove all cache entries for this accountId and source (regardless of leftNavCategory) - final String keyPrefix = accountId + "_" + source + "_"; - contextCollectionsMap.entrySet().removeIf(entry -> - entry.getKey().startsWith(keyPrefix)); + String keyPrefix = accountId + "_" + source.name() + "_" + subCategory.name() ; + contextCollectionsMap.remove(keyPrefix); } public static Set getContextCollectionsForUser(int accountId, CONTEXT_SOURCE source, GlobalEnums.SUB_CATEGORY_SOURCE subCategory) { if(source == null) { source = CONTEXT_SOURCE.API; } + + if(subCategory == null) { + subCategory = GlobalEnums.SUB_CATEGORY_SOURCE.DEFAULT; + } // Create cache key that includes leftNavCategory to ensure proper filtering - String cacheKey = accountId + "_" + source + "_" + (subCategory != null ? subCategory : "null"); + String cacheKey = accountId + "_" + source.name() + "_" + subCategory.name() ; Pair, Integer> collectionIdEntry = contextCollectionsMap.get(cacheKey); Set collectionList; @@ -140,25 +149,13 @@ public static Set getContextCollections(CONTEXT_SOURCE source, GlobalEn ) ); - if (subCategory != null) { - + Bson subCategoryFilter = Filters.empty(); + if (!subCategory.equals(GlobalEnums.SUB_CATEGORY_SOURCE.DEFAULT)){ if ((subCategory.equals(GlobalEnums.SUB_CATEGORY_SOURCE.ENDPOINT_SECURITY))) { - // For Endpoint Security: filter collections where tags_string has source = "Endpoint" - finalFilter = Filters.elemMatch(ApiCollection.TAGS_STRING, - Filters.and( - Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), - Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) - ) - ); + subCategoryFilter = tagFilter; } else if (subCategory.equals(GlobalEnums.SUB_CATEGORY_SOURCE.CLOUD_SECURITY)) { - // For Cloud Security: exclude collections that have source = "ENDPOINT" tag - finalFilter = Filters.not( - Filters.elemMatch(ApiCollection.TAGS_STRING, - Filters.and( - Filters.eq(CollectionTags.KEY_NAME, CollectionTags.SOURCE), - Filters.eq(CollectionTags.VALUE, CollectionTags.TagSource.ENDPOINT) - ) - ) + subCategoryFilter = Filters.not( + tagFilter ); } } @@ -167,7 +164,8 @@ public static Set getContextCollections(CONTEXT_SOURCE source, GlobalEn case MCP: finalFilter = Filters.and( Filters.exists(ApiCollection.TAGS_STRING), - Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_MCP_SERVER_TAG)) + Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_MCP_SERVER_TAG)), + subCategoryFilter ); break; case GEN_AI: @@ -183,7 +181,8 @@ public static Set getContextCollections(CONTEXT_SOURCE source, GlobalEn Filters.exists(ApiCollection.TAGS_STRING), Filters.or( Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_MCP_SERVER_TAG)), - Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_GEN_AI_TAG)) + Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_GEN_AI_TAG)), + subCategoryFilter ) ); break; diff --git a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java index 88f2b16fe3..3eab02ddcc 100644 --- a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java +++ b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java @@ -160,7 +160,7 @@ public enum CONTEXT_SOURCE { } public enum SUB_CATEGORY_SOURCE { - CLOUD_SECURITY, ENDPOINT_SECURITY; + DEFAULT, CLOUD_SECURITY, ENDPOINT_SECURITY; } /* ********************************************************************** */ From e93cfe5cf1d0078c3a166585c641942da1cc55d6 Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 19:49:53 +0530 Subject: [PATCH 07/18] subcategory in dashboard changes --- .../components/layouts/header/Headers.js | 13 +++++++++---- .../components/layouts/leftnav/LeftNav.js | 18 ++++++++++++++++-- .../observe/api_collections/ApiCollections.jsx | 3 ++- .../web/polaris_web/web/src/util/request.js | 5 ++++- .../akto/dto/rbac/UsersCollectionsList.java | 6 +++--- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js index ca91959d77..477aa13d81 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js @@ -130,12 +130,17 @@ export default function Header() { LocalStore.getState().setSubCategoryMap({}); SessionStore.getState().setThreatFiltersMap({}); - // Only set subcategory for dashboard categories that support subcategories + // Set appropriate default subcategory for each dashboard category if (value === "MCP Security" || value === "Agentic Security") { - PersistStore.getState().setSubCategory(currentSubCategory); + // For MCP/Agentic Security, use Cloud Security as default on first load + const defaultSubCategory = currentSubCategory === 'Default' ? 'Cloud Security' : currentSubCategory; + PersistStore.getState().setSubCategory(defaultSubCategory); + } else if (value === "API Security") { + // For API Security, use Default subcategory (no filtering) + PersistStore.getState().setSubCategory('Default'); } else { - // For API Security and other categories, reset to default - PersistStore.getState().setSubCategory('Cloud Security'); + // For other categories, use Default subcategory + PersistStore.getState().setSubCategory('Default'); } setDashboardCategory(value); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 7b79c63bab..8b76a4c861 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -39,7 +39,14 @@ export default function LeftNav() { const resetStore = LocalStore(state => state.resetStore); const resetSession = SessionStore(state => state.resetStore); const resetFields = IssuesStore(state => state.resetStore); - const subCategory = PersistStore((state) => state.subCategory) || SUB_CATEGORY_CLOUD_SECURITY; + const dashboardCategory = PersistStore((state) => state.dashboardCategory) || "API Security"; + const getDefaultSubCategory = () => { + if (dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security") { + return SUB_CATEGORY_CLOUD_SECURITY; + } + return "Default"; + }; + const subCategory = PersistStore((state) => state.subCategory) || getDefaultSubCategory(); const setSubCategory = PersistStore((state) => state.setSubCategory); const handleSelect = (selectedId) => { @@ -103,7 +110,6 @@ export default function LeftNav() { }) } - const dashboardCategory = PersistStore((state) => state.dashboardCategory) || "API Security"; // Helper function to duplicate navigation items with different prefix and independent selection const duplicateNavItems = (items, prefix, startKey = 100) => { @@ -120,8 +126,12 @@ export default function LeftNav() { // Set the left nav category based on section if (prefix === "cloud") { setSubCategory("Cloud Security"); + // Clear collections cache to trigger refresh + PersistStore.getState().setAllCollections([]); } else if (prefix === "endpoint") { setSubCategory("Endpoint Security"); + // Clear collections cache to trigger refresh + PersistStore.getState().setAllCollections([]); } const originalHandler = item.onClick; @@ -147,8 +157,12 @@ export default function LeftNav() { // Set the left nav category based on section if (prefix === "cloud") { setSubCategory("Cloud Security"); + // Clear collections cache to trigger refresh + PersistStore.getState().setAllCollections([]); } else if (prefix === "endpoint") { setSubCategory("Endpoint Security"); + // Clear collections cache to trigger refresh + PersistStore.getState().setAllCollections([]); } const originalHandler = subItem.onClick; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx index a710efb5da..81d2f0ecc3 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/ApiCollections.jsx @@ -349,6 +349,7 @@ function ApiCollections(props) { } const allCollections = PersistStore(state => state.allCollections) + const subCategory = PersistStore(state => state.subCategory) // const allCollections = dummyData.allCollections; const setAllCollections = PersistStore(state => state.setAllCollections) const setCollectionsMap = PersistStore(state => state.setCollectionsMap) @@ -649,7 +650,7 @@ function ApiCollections(props) { useEffect(() => { fetchData() resetFunc() - }, []) + }, [subCategory]) const createCollectionModalActivatorRef = useRef(); const resetResourcesSelected = () => { TableStore.getState().setSelectedItems([]) diff --git a/apps/dashboard/web/polaris_web/web/src/util/request.js b/apps/dashboard/web/polaris_web/web/src/util/request.js index 894ff6721e..650bc64bd3 100644 --- a/apps/dashboard/web/polaris_web/web/src/util/request.js +++ b/apps/dashboard/web/polaris_web/web/src/util/request.js @@ -101,14 +101,17 @@ service.interceptors.request.use((config) => { config.headers['Content-Type'] = 'application/json' config.headers["access-token"] = SessionStore.getState().accessToken const currentCategory = PersistStore.getState().dashboardCategory || "API Security"; - let subCategory = PersistStore.getState().subCategory || "Default"; + let subCategory = PersistStore.getState().subCategory; let contextSource = "API"; if (currentCategory === "API Security") { contextSource = "API"; + subCategory = subCategory || "Default"; } else if (currentCategory === "MCP Security") { contextSource = "MCP"; + subCategory = subCategory || "Cloud Security"; } else if (currentCategory === "Agentic Security") { contextSource = "AGENTIC"; + subCategory = subCategory || "Cloud Security"; } if(subCategory === "Cloud Security"){ diff --git a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java index 82e31e641a..49b8293129 100644 --- a/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java +++ b/libs/dao/src/main/java/com/akto/dto/rbac/UsersCollectionsList.java @@ -181,9 +181,9 @@ public static Set getContextCollections(CONTEXT_SOURCE source, GlobalEn Filters.exists(ApiCollection.TAGS_STRING), Filters.or( Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_MCP_SERVER_TAG)), - Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_GEN_AI_TAG)), - subCategoryFilter - ) + Filters.elemMatch(ApiCollection.TAGS_STRING, Filters.eq(CollectionTags.KEY_NAME, Constants.AKTO_GEN_AI_TAG)) + ), + subCategoryFilter ); break; default: From e373cd6a8f494449cebea851b2c7d94fc3bd57eb Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 20:29:22 +0530 Subject: [PATCH 08/18] subcategory in dashboard changes --- .../web/src/apps/dashboard/pages/quick_start/transform.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js index aa1109819b..2f1143e2c6 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js @@ -1518,8 +1518,7 @@ const quickStartFunc = { "Manual": manual, "Akto SDK": aktoSdk, "Virtual Machines": vm, - "Source Code": sourceCode, - "Secure Web Networks": secureWebNetworks + "Source Code": sourceCode } return connectors; From 486e69598c160bbc28a14ab60bbe2874aaeddceb Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 20:37:18 +0530 Subject: [PATCH 09/18] subcategory in dashboard changes --- .../web/src/apps/dashboard/pages/quick_start/transform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js index 2f1143e2c6..8d680d3f39 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/quick_start/transform.js @@ -1518,7 +1518,7 @@ const quickStartFunc = { "Manual": manual, "Akto SDK": aktoSdk, "Virtual Machines": vm, - "Source Code": sourceCode + "Source Code": sourceCode, } return connectors; From 934ce03364eee67871b4709eb9dcc59a1244f57e Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 20:45:24 +0530 Subject: [PATCH 10/18] reverted back --- .../src/main/java/com/akto/action/IngestionAction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java b/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java index 2394d1c63a..a589eadf03 100644 --- a/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java +++ b/apps/data-ingestion-service/src/main/java/com/akto/action/IngestionAction.java @@ -52,6 +52,7 @@ private boolean sendLogsToCustomAccount(List batchData){ public String ingestData() { try { if(sendLogsToCustomAccount(batchData)){ + System.setProperty("DATABASE_ABSTRACTOR_SERVICE_TOKEN", "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoiaW52aXRlX3VzZXIiLCJhY2NvdW50SWQiOjE2NjI2ODA0NjMsImlhdCI6MTc2MDU5NzM0OCwiZXhwIjoxNzc2MzIyMTQ4fQ.b-aqZEiTinzE1tavKDe6t7Ec7TsnsGoVRdxCiMmeOM20JcJ7aEgOZaJxD7O9zyoD6AEXmpEghd04wGhGCECBOKWivDS8Y_fdatLw8R7hH0Y-pu8QEMC1whbXXJrNhsRGXihLIiQ80nDKbrv6ObbyDwy4NPYoCFK8Mpu2i4W8qZHBJXnxmVkCp8Cp_LyeDLotXvc8DAp9huHASil0BSOxiUwHsw3Efk4BkRlHADfAwGFz4j-ozdbiK0SHHvOZNicl1wgpvDk0nHRLhIg3Ynx-Fk4Pp0agb0MCpS55-CRMBbx3zy9xRdkhIGdOydEzZKK5p311hwPnxxeL6Dp1C2f89g"); } From c911c444d429684518bb72a285f7ac88d1361289 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 27 Oct 2025 22:29:23 +0530 Subject: [PATCH 11/18] Modifying left nav --- .../components/layouts/leftnav/LeftNav.js | 500 +++++------------- 1 file changed, 130 insertions(+), 370 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 8b76a4c861..a92bf7ac7e 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -5,7 +5,6 @@ import { MarketingFilledMinor, ReportFilledMinor, DiamondAlertMinor, - StarFilledMinor, FinancesMinor, LockMajor, AutomationFilledMajor @@ -22,7 +21,7 @@ import func from "@/util/func"; import Dropdown from "../Dropdown"; import SessionStore from "../../../../main/SessionStore"; import IssuesStore from "../../../pages/issues/issuesStore"; -import { CATEGORY_API_SECURITY, mapLabel, shouldShowLeftNavSwitch, SUB_CATEGORY_CLOUD_SECURITY, SUB_CATEGORY_ENDPOINT_SECURITY, getSubCategory } from "../../../../main/labelHelper"; +import { CATEGORY_API_SECURITY, mapLabel } from "../../../../main/labelHelper"; export default function LeftNav() { const navigate = useNavigate(); @@ -40,36 +39,19 @@ export default function LeftNav() { const resetSession = SessionStore(state => state.resetStore); const resetFields = IssuesStore(state => state.resetStore); const dashboardCategory = PersistStore((state) => state.dashboardCategory) || "API Security"; - const getDefaultSubCategory = () => { - if (dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security") { - return SUB_CATEGORY_CLOUD_SECURITY; - } - return "Default"; - }; - const subCategory = PersistStore((state) => state.subCategory) || getDefaultSubCategory(); + const subcategory = PersistStore((state) => state.subCategory); const setSubCategory = PersistStore((state) => state.setSubCategory); - const handleSelect = (selectedId) => { - setLeftNavSelected(selectedId); - // Store navigation state in sessionStorage for other components to access - sessionStorage.setItem('leftNavSelected', selectedId); - - // Dispatch custom event to notify other components of navigation change - const navigationChangeEvent = new CustomEvent('navigationChanged', { - detail: { - selectedId: selectedId, - timestamp: Date.now() - } - }); - window.dispatchEvent(navigationChangeEvent); - }; - - const handleSubCategoryChange = (selected) => { - setSubCategory(selected); - // Force refresh of current page to reload data with new left nav category - window.location.reload(); - }; + const handleClick = (key, path, activeState, subcategory = "Default") => { + const finalKey = subcategory === "Endpoint Security" ? key + "_1" : key + "_0"; + setLeftNavSelected(finalKey); + navigate(path); + setActive(activeState); + if (subcategory) { + setSubCategory(subcategory); + } + } const handleAccountChange = async (selected) => { resetAll(); @@ -79,133 +61,45 @@ export default function LeftNav() { await api.goToAccount(selected); func.setToast(true, false, `Switched to account ${accounts[selected]}`); window.location.href = '/dashboard/observe/inventory'; - }; + } const accountOptions = Object.keys(accounts).map(accountId => ({ label: accounts[accountId], value: accountId })); - let reportsSubNavigationItems = [ - { - label: "Issues", - onClick: () => { - navigate("/dashboard/reports/issues"); - handleSelect("dashboard_reports_issues"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_reports_issues", - } - ] + - if (window.USER_NAME.indexOf("@akto.io")) { - reportsSubNavigationItems.push({ - label: "Compliance", - onClick: () => { - navigate("/dashboard/reports/compliance"); - handleSelect("dashboard_reports_compliance"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_reports_compliance", - }) - } - - - // Helper function to duplicate navigation items with different prefix and independent selection - const duplicateNavItems = (items, prefix, startKey = 100) => { - return items.map((item, index) => { - const newKey = item.key ? `${prefix}_${item.key}` : `${prefix}_${startKey + index}`; - const originalSelected = item.selected; - - return { - ...item, - key: newKey, - // Each section has independent selection state - selected: originalSelected !== undefined ? leftNavSelected === newKey : undefined, - onClick: item.onClick ? () => { - // Set the left nav category based on section - if (prefix === "cloud") { - setSubCategory("Cloud Security"); - // Clear collections cache to trigger refresh - PersistStore.getState().setAllCollections([]); - } else if (prefix === "endpoint") { - setSubCategory("Endpoint Security"); - // Clear collections cache to trigger refresh - PersistStore.getState().setAllCollections([]); - } - - const originalHandler = item.onClick; - originalHandler(); - // Set selection specific to this section - handleSelect(newKey); - } : undefined, - subNavigationItems: item.subNavigationItems ? item.subNavigationItems.map((subItem, subIndex) => { - // Create unique keys for sub-navigation items - const originalSubSelected = subItem.selected; - const subNavKey = originalSubSelected ? - leftNavSelected.replace('dashboard_', `${prefix}_dashboard_`) : - `${prefix}_sub_${subIndex}`; - - return { - ...subItem, - // Sub-items are selected only if they match this section's prefix - selected: originalSubSelected !== undefined ? - leftNavSelected.startsWith(prefix) && - leftNavSelected === subNavKey - : undefined, - onClick: subItem.onClick ? () => { - // Set the left nav category based on section - if (prefix === "cloud") { - setSubCategory("Cloud Security"); - // Clear collections cache to trigger refresh - PersistStore.getState().setAllCollections([]); - } else if (prefix === "endpoint") { - setSubCategory("Endpoint Security"); - // Clear collections cache to trigger refresh - PersistStore.getState().setAllCollections([]); - } - - const originalHandler = subItem.onClick; - originalHandler(); - // Extract the base selection pattern and add section prefix - const basePattern = leftNavSelected.replace(/^(cloud_|endpoint_)/, ''); - handleSelect(`${prefix}_${basePattern}`); - } : undefined, - }; - }) : undefined - }; - }); - }; - - const navItems = useMemo(() => { - // Account dropdown (stays at the top) - const accountSection = [ + const getMainNavItems = (subCategoryValue) => { + const subCategoryIndex = subCategoryValue === "Endpoint Security" ? "1" : "0"; + let reportsSubNavigationItems = [ { - label: (!func.checkLocal()) ? ( - - accounts[activeAccount]} - selected={(type) => handleAccountChange(type)} - /> - - - ) : null + label: "Issues", + onClick: () => handleClick("dashboard_reports_issues", "/dashboard/reports/issues", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_reports_issues", + } + ] + + if (window.USER_NAME.indexOf("@akto.io")) { + reportsSubNavigationItems.push({ + label: "Compliance", + onClick: () => handleClick("dashboard_reports_compliance", "/dashboard/reports/compliance", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_reports_compliance", + }) + } + return [ + { + label: "Quick Start", + icon: AppsFilledMajor, + onClick: () => handleClick("dashboard_quick_start", "/dashboard/quick-start", "normal", subCategoryValue), + selected: leftNavSelected === "dashboard_quick_start" + subCategoryIndex, + key: "quick_start", }, - ]; - - // Main navigation items (to be duplicated) - const mainNavItems = [ { label: mapLabel("API Security Posture", dashboardCategory), icon: ReportFilledMinor, - onClick: () => { - handleSelect("dashboard_home"); - navigate("/dashboard/home"); - setActive("normal"); - }, - selected: leftNavSelected === "dashboard_home", + onClick: () => handleClick("dashboard_home", "/dashboard/home", "normal", subCategoryValue), + selected: leftNavSelected === "dashboard_home" + subCategoryIndex, key: "2", }, { @@ -215,7 +109,7 @@ export default function LeftNav() { variant="bodyMd" fontWeight="medium" color={ - leftNavSelected.includes("observe") + leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("observe") ? active === "active" ? "subdued" : "" @@ -226,66 +120,38 @@ export default function LeftNav() { ), icon: InventoryFilledMajor, - onClick: () => { - handleSelect("dashboard_observe_inventory"); - navigate("/dashboard/observe/inventory"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_observe"), + onClick: () => handleClick("dashboard_observe_inventory", "/dashboard/observe/inventory", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_observe"), subNavigationItems: [ { label: "Collections", - onClick: () => { - navigate("/dashboard/observe/inventory"); - handleSelect("dashboard_observe_inventory"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_observe_inventory", + onClick: () => handleClick("dashboard_observe_inventory", "/dashboard/observe/inventory", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_observe_inventory" + subCategoryIndex, }, { label: "Recent Changes", - onClick: () => { - navigate("/dashboard/observe/changes"); - handleSelect("dashboard_observe_changes"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_observe_changes", + onClick: () => handleClick("dashboard_observe_changes", "/dashboard/observe/changes", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_observe_changes" + subCategoryIndex, }, { label: "Sensitive Data", - onClick: () => { - navigate("/dashboard/observe/sensitive"); - handleSelect("dashboard_observe_sensitive"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_observe_sensitive", + onClick: () => handleClick("dashboard_observe_sensitive", "/dashboard/observe/sensitive", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_observe_sensitive" + subCategoryIndex, }, - ...(window?.STIGG_FEATURE_WISE_ALLOWED?.AKTO_DAST?.isGranted && dashboardCategory == CATEGORY_API_SECURITY ? [{ + ...(window?.STIGG_FEATURE_WISE_ALLOWED?.AKTO_DAST?.isGranted && dashboardCategory === CATEGORY_API_SECURITY ? [{ label: "DAST scans", - onClick: () => { - navigate("/dashboard/observe/dast-progress"); - handleSelect("dashboard_observe_dast_progress"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_observe_dast_progress" + onClick: () => handleClick("dashboard_observe_dast_progress", "/dashboard/observe/dast-progress", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_observe_dast_progress" + subCategoryIndex, }] : []), ...((dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security") ? [{ label: "Audit Data", - onClick: () => { - navigate("/dashboard/observe/audit"); - handleSelect("dashboard_observe_audit"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_observe_audit", + onClick: () => handleClick("dashboard_observe_audit", "/dashboard/observe/audit", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_observe_audit" + subCategoryIndex, }] : []), ...((dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security") ? [{ label: "Endpoint Shield", - onClick: () => { - navigate("/dashboard/observe/endpoint-shield"); - handleSelect("dashboard_observe_endpoint_shield"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_observe_endpoint_shield", + onClick: () => handleClick("dashboard_observe_endpoint_shield", "/dashboard/observe/endpoint-shield", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_observe_endpoint_shield" + subCategoryIndex, }] : []), ], key: "3", @@ -297,7 +163,7 @@ export default function LeftNav() { variant="bodyMd" fontWeight="medium" color={ - leftNavSelected.includes("testing") + leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("testing") ? active === "active" ? "subdued" : "" @@ -308,48 +174,28 @@ export default function LeftNav() { ), icon: MarketingFilledMinor, - onClick: () => { - navigate("/dashboard/testing/"); - handleSelect("dashboard_testing"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_testing"), + onClick: () => handleClick("dashboard_testing", "/dashboard/testing/", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_testing"), subNavigationItems: [ { label: "Results", - onClick: () => { - navigate("/dashboard/testing/"); - handleSelect("dashboard_testing"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_testing", + onClick: () => handleClick("dashboard_testing", "/dashboard/testing/", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_testing" + subCategoryIndex, }, { label: mapLabel("Test", dashboardCategory) + " Roles", - onClick: () => { - navigate("/dashboard/testing/roles"); - handleSelect("dashboard_testing_roles"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_testing_roles", + onClick: () => handleClick("dashboard_testing_roles", "/dashboard/testing/roles", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_testing_roles" + subCategoryIndex, }, { label: "User Config", - onClick: () => { - navigate("/dashboard/testing/user-config"); - handleSelect("dashboard_testing_user_config"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_testing_user_config", + onClick: () => handleClick("dashboard_testing_user_config", "/dashboard/testing/user-config", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_testing_user_config" + subCategoryIndex, }, { label:mapLabel("Test", dashboardCategory) + " Suite", - onClick:()=>{ - navigate("/dashboard/testing/test-suite"); - handleSelect("dashboard_testing_test_suite"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_testing_test_suite", + onClick: () => handleClick("dashboard_testing_test_suite", "/dashboard/testing/test-suite", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_testing_test_suite" + subCategoryIndex, } ], key: "4", @@ -362,30 +208,18 @@ export default function LeftNav() { ), icon: FinancesMinor, - onClick: () => { - handleSelect("dashboard_test_library_tests"); - navigate("/dashboard/test-library/tests"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_test_library"), + onClick: () => handleClick("dashboard_test_library_tests", "/dashboard/test-library/tests", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_test_library"), subNavigationItems: [ { label: mapLabel("More Tests", dashboardCategory), - onClick: () => { - navigate("/dashboard/test-library/tests"); - handleSelect("dashboard_test_library_tests"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_test_library_tests", + onClick: () => handleClick("dashboard_test_library_tests", "/dashboard/test-library/tests", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_test_library_tests" + subCategoryIndex, }, { label: "Editor", - onClick: () => { - navigate("/dashboard/test-editor"); - handleSelect("dashboard_test_library_test_editor"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_test_library_test_editor", + onClick: () => handleClick("dashboard_test_library_test_editor", "/dashboard/test-editor", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_test_library_test_editor" + subCategoryIndex, }, ], key: "5", @@ -397,12 +231,8 @@ export default function LeftNav() { ), icon: AutomationFilledMajor, - onClick: () => { - handleSelect("dashboard_prompt_hardening"); - navigate("/dashboard/prompt-hardening"); - setActive("normal"); - }, - selected: leftNavSelected === "dashboard_prompt_hardening", + onClick: () => handleClick("dashboard_prompt_hardening", "/dashboard/prompt-hardening", "normal", subCategoryValue), + selected: leftNavSelected === "dashboard_prompt_hardening" + subCategoryIndex, key: "prompt_hardening", }] : []), { @@ -412,7 +242,7 @@ export default function LeftNav() { variant="bodyMd" fontWeight="medium" color={ - leftNavSelected.includes("reports") + leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("reports") ? active === "active" ? "subdued" : "" @@ -423,12 +253,8 @@ export default function LeftNav() { ), icon: ReportFilledMinor, - onClick: () => { - navigate("/dashboard/reports/issues"); - handleSelect("dashboard_reports_issues"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_reports"), + onClick: () => handleClick("dashboard_reports_issues", "/dashboard/reports/issues", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_reports"), subNavigationItems: reportsSubNavigationItems, key: "6", }, @@ -439,62 +265,38 @@ export default function LeftNav() { ), icon: DiamondAlertMinor, - onClick: () => { - handleSelect("dashboard_threat_actor"); - navigate("/dashboard/protection/threat-actor"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_threat"), + onClick: () => handleClick("dashboard_threat_actor", "/dashboard/protection/threat-actor", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_threat"), url: "#", key: "7", subNavigationItems: [ ...(dashboardCategory === "API Security" && func.isDemoAccount() ? [{ label: "Dashboard", - onClick: () => { - navigate("/dashboard/protection/threat-dashboard"); - handleSelect("dashboard_threat_dashboard"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_threat_dashboard", + onClick: () => handleClick("dashboard_threat_dashboard", "/dashboard/protection/threat-dashboard", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_threat_dashboard" + subCategoryIndex, }] : []), { label: "Threat Actors", - onClick: () => { - navigate("/dashboard/protection/threat-actor"); - handleSelect("dashboard_threat_actor"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_threat_actor", + onClick: () => handleClick("dashboard_threat_actor", "/dashboard/protection/threat-actor", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_threat_actor" + subCategoryIndex, }, { label: "Threat Activity", - onClick: () => { - navigate("/dashboard/protection/threat-activity"); - handleSelect("dashboard_threat_activity"); - setActive("active"); - }, + onClick: () => handleClick("dashboard_threat_activity", "/dashboard/protection/threat-activity", "active", subCategoryValue), selected: - leftNavSelected === "dashboard_threat_activity", + leftNavSelected === "dashboard_threat_activity" + subCategoryIndex, }, { label: `${mapLabel("APIs", dashboardCategory)} Under Threat`, - onClick: () => { - navigate("/dashboard/protection/threat-api"); - handleSelect("dashboard_threat_api"); - setActive("active"); - }, + onClick: () => handleClick("dashboard_threat_api", "/dashboard/protection/threat-api", "active", subCategoryValue), selected: - leftNavSelected === "dashboard_threat_api", + leftNavSelected === "dashboard_threat_api" + subCategoryIndex, }, { label: "Threat Policy", - onClick: () => { - navigate("/dashboard/protection/threat-policy"); - handleSelect("dashboard_threat_policy"); - setActive("active"); - }, + onClick: () => handleClick("dashboard_threat_policy", "/dashboard/protection/threat-policy", "active", subCategoryValue), selected: - leftNavSelected === "dashboard_threat_policy", + leftNavSelected === "dashboard_threat_policy" + subCategoryIndex, }, ], }] : []), @@ -508,9 +310,9 @@ export default function LeftNav() { // onClick: () => { // handleSelect("agent_team_members"); // navigate("/dashboard/agent-team/members"); - // setActive("normal"); + // setActive("normal", subCategoryValue); // }, - // selected: leftNavSelected.includes("agent_team"), + // selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("agent_team"), // url: "#", // key: "8", // }] : []), @@ -521,31 +323,19 @@ export default function LeftNav() { ), icon: LockMajor, - onClick: () => { - handleSelect("dashboard_mcp_guardrails"); - navigate("/dashboard/guardrails/activity"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_guardrails"), + onClick: () => handleClick("dashboard_mcp_guardrails", "/dashboard/guardrails/activity", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_guardrails"), url: "#", key: "9", subNavigationItems: [ { label: "Guardrails Activity", - onClick: () => { - navigate("/dashboard/guardrails/activity"); - handleSelect("dashboard_guardrails_activity"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_guardrails_activity", + onClick: () => handleClick("dashboard_guardrails_activity", "/dashboard/guardrails/activity", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_guardrails_activity" + subCategoryIndex, }, { label: "Guardrails Policies", - onClick: () => { - navigate("/dashboard/guardrails/policies"); - handleSelect("dashboard_guardrails_policies"); - setActive("active"); - }, + onClick: () => handleClick("dashboard_guardrails_policies", "/dashboard/guardrails/policies", "active", subCategoryValue), selected: leftNavSelected === "dashboard_guardrails_policies", } @@ -559,33 +349,21 @@ export default function LeftNav() { ), icon: LockMajor, - onClick: () => { - handleSelect("dashboard_ai_agent_guardrails"); - navigate("/dashboard/guardrails/activity"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_guardrails"), + onClick: () => handleClick("dashboard_ai_agent_guardrails", "/dashboard/guardrails/activity", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_guardrails"), url: "#", key: "10", subNavigationItems: [ { label: "Guardrails Activity", - onClick: () => { - navigate("/dashboard/guardrails/activity"); - handleSelect("dashboard_guardrails_activity"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_guardrails_activity", + onClick: () => handleClick("dashboard_guardrails_activity", "/dashboard/guardrails/activity", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_guardrails_activity" + subCategoryIndex, }, { label: "Guardrails Policies", - onClick: () => { - navigate("/dashboard/guardrails/policies"); - handleSelect("dashboard_guardrails_policies"); - setActive("active"); - }, + onClick: () => handleClick("dashboard_guardrails_policies", "/dashboard/guardrails/policies", "active", subCategoryValue), selected: - leftNavSelected === "dashboard_guardrails_policies", + leftNavSelected === "dashboard_guardrails_policies" + subCategoryIndex, } ] }] : []), @@ -596,69 +374,53 @@ export default function LeftNav() { ), icon: LockMajor, - onClick: () => { - handleSelect("dashboard_agentic_guardrails"); - navigate("/dashboard/guardrails/activity"); - setActive("normal"); - }, - selected: leftNavSelected.includes("_guardrails"), + onClick: () => handleClick("dashboard_agentic_guardrails", "/dashboard/guardrails/activity", "normal", subCategoryValue), + selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_guardrails"), url: "#", key: "11", subNavigationItems: [ { label: "Guardrails Activity", - onClick: () => { - navigate("/dashboard/guardrails/activity"); - handleSelect("dashboard_guardrails_activity"); - setActive("active"); - }, - selected: leftNavSelected === "dashboard_guardrails_activity", + onClick: () => handleClick("dashboard_guardrails_activity", "/dashboard/guardrails/activity", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_guardrails_activity" + subCategoryIndex, }, { label: "Guardrails Policies", - onClick: () => { - navigate("/dashboard/guardrails/policies"); - handleSelect("dashboard_guardrails_policies"); - setActive("active"); - }, + onClick: () => handleClick("dashboard_guardrails_policies", "/dashboard/guardrails/policies", "active", subCategoryValue), selected: - leftNavSelected === "dashboard_guardrails_policies", + leftNavSelected === "dashboard_guardrails_policies" + subCategoryIndex, } ] }] : []) ]; + } - // Add Quick Start if it doesn't exist - const quickStartItem = { - label: "Quick Start", - icon: AppsFilledMajor, - onClick: () => { - handleSelect("dashboard_quick_start") - navigate("/dashboard/quick-start") - setActive("normal") - }, - selected: leftNavSelected === "dashboard_quick_start", - key: "quick_start", - }; + const navItems = useMemo(() => { - const exists = mainNavItems.find(item => item.key === "quick_start"); - if (!exists) { - mainNavItems.splice(0, 0, quickStartItem); - } + const accountSection = [ + { + label: (!func.checkLocal()) ? ( + + accounts[activeAccount]} + selected={(type) => handleAccountChange(type)} + /> + - // Conditionally create Cloud Security and Endpoint Security sections - // Only show divisions for MCP Security and Agentic Security + ) : null + } + ] + + const mainNavItems = getMainNavItems("Cloud Security"); const shouldShowDivisions = dashboardCategory === "MCP Security" || dashboardCategory === "Agentic Security"; let allItems; if (shouldShowDivisions) { - // Create Cloud Security and Endpoint Security sections - const cloudSecurityItems = duplicateNavItems(mainNavItems, "cloud", 200); - const endpointSecurityItems = duplicateNavItems(mainNavItems, "endpoint", 300); - + const apiSecurityItems = getMainNavItems("Endpoint Security"); allItems = [ ...accountSection, - // Cloud Security Section Header { label: ( @@ -670,8 +432,7 @@ export default function LeftNav() { key: "cloud_header", disabled: true, }, - ...cloudSecurityItems, - // Endpoint Security Section Header + ...mainNavItems, { label: ( @@ -683,10 +444,9 @@ export default function LeftNav() { key: "endpoint_header", disabled: true, }, - ...endpointSecurityItems + ...apiSecurityItems ]; - } else { - // For API Security and other categories, show normal navigation without divisions + }else{ allItems = [ ...accountSection, ...mainNavItems @@ -694,7 +454,7 @@ export default function LeftNav() { } return allItems - }, [dashboardCategory, leftNavSelected, subCategory]) + }, [dashboardCategory, leftNavSelected, subcategory]) const navigationMarkup = (
From 3ba9b5ad73cbe22507c1d1561ff25b41bcc3857d Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Mon, 27 Oct 2025 22:50:02 +0530 Subject: [PATCH 12/18] left navigation changes --- .../components/layouts/leftnav/LeftNav.js | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index a92bf7ac7e..d4d6e3375d 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -16,7 +16,7 @@ import PersistStore from "../../../../main/PersistStore"; import LocalStore from "../../../../main/LocalStorageStore"; import Store from "../../../store"; import api from "../../../../signup/api"; -import { useMemo, useState} from "react"; +import { useMemo, useState, useEffect} from "react"; import func from "@/util/func"; import Dropdown from "../Dropdown"; import SessionStore from "../../../../main/SessionStore"; @@ -42,14 +42,27 @@ export default function LeftNav() { const subcategory = PersistStore((state) => state.subCategory); const setSubCategory = PersistStore((state) => state.setSubCategory); + // Set initial selection based on current path and subcategory + useEffect(() => { + const pathString = func.transformString(location.pathname); + const subCategoryIndex = subcategory === "Endpoint Security" ? "1" : "0"; + const keyWithIndex = pathString + subCategoryIndex; + + // Only update if the current selection doesn't match + if (leftNavSelected !== keyWithIndex && leftNavSelected !== pathString) { + setLeftNavSelected(keyWithIndex); + } + }, [subcategory, location.pathname]); - const handleClick = (key, path, activeState, subcategory = "Default") => { - const finalKey = subcategory === "Endpoint Security" ? key + "_1" : key + "_0"; + const handleClick = (key, path, activeState, subcategoryValue = "Default") => { + const finalKey = subcategoryValue === "Endpoint Security" ? key + "_1" : key + "_0"; setLeftNavSelected(finalKey); navigate(path); setActive(activeState); - if (subcategory) { - setSubCategory(subcategory); + if (subcategoryValue && subcategoryValue !== subcategory) { + setSubCategory(subcategoryValue); + // Clear collections cache to trigger refresh when subcategory changes + PersistStore.getState().setAllCollections([]); } } @@ -72,11 +85,13 @@ export default function LeftNav() { const getMainNavItems = (subCategoryValue) => { const subCategoryIndex = subCategoryValue === "Endpoint Security" ? "1" : "0"; + const isCurrentSubCategory = subcategory === subCategoryValue; + let reportsSubNavigationItems = [ { label: "Issues", onClick: () => handleClick("dashboard_reports_issues", "/dashboard/reports/issues", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_reports_issues", + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_reports_issues" + subCategoryIndex || leftNavSelected === "dashboard_reports_issues"), } ] @@ -84,7 +99,7 @@ export default function LeftNav() { reportsSubNavigationItems.push({ label: "Compliance", onClick: () => handleClick("dashboard_reports_compliance", "/dashboard/reports/compliance", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_reports_compliance", + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_reports_compliance" + subCategoryIndex || leftNavSelected === "dashboard_reports_compliance"), }) } return [ @@ -92,14 +107,14 @@ export default function LeftNav() { label: "Quick Start", icon: AppsFilledMajor, onClick: () => handleClick("dashboard_quick_start", "/dashboard/quick-start", "normal", subCategoryValue), - selected: leftNavSelected === "dashboard_quick_start" + subCategoryIndex, + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_quick_start" + subCategoryIndex || leftNavSelected === "dashboard_quick_start"), key: "quick_start", }, { label: mapLabel("API Security Posture", dashboardCategory), icon: ReportFilledMinor, onClick: () => handleClick("dashboard_home", "/dashboard/home", "normal", subCategoryValue), - selected: leftNavSelected === "dashboard_home" + subCategoryIndex, + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_home" + subCategoryIndex || leftNavSelected === "dashboard_home"), key: "2", }, { @@ -121,22 +136,22 @@ export default function LeftNav() { ), icon: InventoryFilledMajor, onClick: () => handleClick("dashboard_observe_inventory", "/dashboard/observe/inventory", "normal", subCategoryValue), - selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_observe"), + selected: isCurrentSubCategory && leftNavSelected.includes("_observe"), subNavigationItems: [ { label: "Collections", onClick: () => handleClick("dashboard_observe_inventory", "/dashboard/observe/inventory", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_observe_inventory" + subCategoryIndex, + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_observe_inventory" + subCategoryIndex || leftNavSelected === "dashboard_observe_inventory"), }, { label: "Recent Changes", onClick: () => handleClick("dashboard_observe_changes", "/dashboard/observe/changes", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_observe_changes" + subCategoryIndex, + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_observe_changes" + subCategoryIndex || leftNavSelected === "dashboard_observe_changes"), }, { label: "Sensitive Data", onClick: () => handleClick("dashboard_observe_sensitive", "/dashboard/observe/sensitive", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_observe_sensitive" + subCategoryIndex, + selected: isCurrentSubCategory && (leftNavSelected === "dashboard_observe_sensitive" + subCategoryIndex || leftNavSelected === "dashboard_observe_sensitive"), }, ...(window?.STIGG_FEATURE_WISE_ALLOWED?.AKTO_DAST?.isGranted && dashboardCategory === CATEGORY_API_SECURITY ? [{ label: "DAST scans", From 9da21c3d704758731c3fcfb0b22bb9e766b6afa3 Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Tue, 28 Oct 2025 16:47:21 +0530 Subject: [PATCH 13/18] left navigation changes --- .../src/apps/dashboard/pages/dashboard/HomeDashboard.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx index fc9c6712a6..44a6f07526 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx @@ -71,6 +71,7 @@ function HomeDashboard() { const allCollections = PersistStore(state => state.allCollections) const hostNameMap = PersistStore(state => state.hostNameMap) const coverageMap = PersistStore(state => state.coverageMap) + const subCategory = PersistStore(state => state.subCategory) const [authMap, setAuthMap] = useState({}) const [apiTypesData, setApiTypesData] = useState([{ "data": [], "color": "#D6BBFB" }]) const [riskScoreData, setRiskScoreData] = useState([]) @@ -448,21 +449,21 @@ function HomeDashboard() { useEffect(() => { fetchData() - }, [startTimestamp, endTimestamp]) + }, [startTimestamp, endTimestamp, subCategory]) // Fetch MCP API call stats when time range changes useEffect(() => { if (allCollections && allCollections.length > 0) { fetchMcpApiCallStats(mcpStatsTimeRange, func.timeNow()); } - }, [mcpStatsTimeRange, allCollections]) + }, [mcpStatsTimeRange, allCollections, subCategory]) // Fetch Policy Guardrail stats when time range changes useEffect(() => { if (allCollections && allCollections.length > 0) { fetchPolicyGuardrailStats(policyGuardrailStatsTimeRange, func.timeNow()); } - }, [policyGuardrailStatsTimeRange, allCollections]) + }, [policyGuardrailStatsTimeRange, allCollections, subCategory]) async function getActionItemsDataAndCount() { const data = await fetchActionItemsData(); From 33cf8523c39c8e5cbb79376cf5c8577619146a8e Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Tue, 28 Oct 2025 16:54:44 +0530 Subject: [PATCH 14/18] Threat merged with Guardrail in left navigation --- .../components/layouts/leftnav/LeftNav.js | 29 ++++++++++++++++--- .../threat_detection/ThreatActorPage.jsx | 3 +- .../pages/threat_detection/ThreatApiPage.jsx | 2 +- .../components/ThreatActivityTimeline.jsx | 4 +-- .../components/ThreatWorldMap.jsx | 5 ++-- .../components/TopThreatTypeChart.jsx | 4 +-- .../web/src/apps/main/labelHelperMap.js | 6 ++-- 7 files changed, 39 insertions(+), 14 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index d4d6e3375d..43104f26e6 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -273,7 +273,7 @@ export default function LeftNav() { subNavigationItems: reportsSubNavigationItems, key: "6", }, - ...(window?.STIGG_FEATURE_WISE_ALLOWED?.THREAT_DETECTION?.isGranted ? [{ + ...(window?.STIGG_FEATURE_WISE_ALLOWED?.THREAT_DETECTION?.isGranted && dashboardCategory === "API Security" ? [{ label: ( {mapLabel("Threat Detection", dashboardCategory)} @@ -339,7 +339,7 @@ export default function LeftNav() { ), icon: LockMajor, onClick: () => handleClick("dashboard_mcp_guardrails", "/dashboard/guardrails/activity", "normal", subCategoryValue), - selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_guardrails"), + selected: leftNavSelected.includes(subCategoryIndex) && (leftNavSelected.includes("_guardrails") || leftNavSelected.includes("_threat")), url: "#", key: "9", subNavigationItems: [ @@ -352,7 +352,17 @@ export default function LeftNav() { label: "Guardrails Policies", onClick: () => handleClick("dashboard_guardrails_policies", "/dashboard/guardrails/policies", "active", subCategoryValue), selected: - leftNavSelected === "dashboard_guardrails_policies", + leftNavSelected === "dashboard_guardrails_policies" + subCategoryIndex, + }, + { + label: "Guardrail Actors", + onClick: () => handleClick("dashboard_threat_actor", "/dashboard/protection/threat-actor", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_threat_actor" + subCategoryIndex, + }, + { + label: `${mapLabel("APIs", dashboardCategory)} Under Guardrail`, + onClick: () => handleClick("dashboard_threat_api", "/dashboard/protection/threat-api", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_threat_api" + subCategoryIndex, } ] @@ -390,7 +400,7 @@ export default function LeftNav() { ), icon: LockMajor, onClick: () => handleClick("dashboard_agentic_guardrails", "/dashboard/guardrails/activity", "normal", subCategoryValue), - selected: leftNavSelected.includes(subCategoryIndex) && leftNavSelected.includes("_guardrails"), + selected: leftNavSelected.includes(subCategoryIndex) && (leftNavSelected.includes("_guardrails") || leftNavSelected.includes("_threat")), url: "#", key: "11", subNavigationItems: [ @@ -404,7 +414,18 @@ export default function LeftNav() { onClick: () => handleClick("dashboard_guardrails_policies", "/dashboard/guardrails/policies", "active", subCategoryValue), selected: leftNavSelected === "dashboard_guardrails_policies" + subCategoryIndex, + }, + { + label: "Guardrail Actors", + onClick: () => handleClick("dashboard_threat_actor", "/dashboard/protection/threat-actor", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_threat_actor" + subCategoryIndex, + }, + { + label: `${mapLabel("APIs", dashboardCategory)} Under Guardrail`, + onClick: () => handleClick("dashboard_threat_api", "/dashboard/protection/threat-api", "active", subCategoryValue), + selected: leftNavSelected === "dashboard_threat_api" + subCategoryIndex, } + ] }] : []) ]; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx index 67d056dd53..4c5aa813d6 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx @@ -14,6 +14,7 @@ import { HorizontalGrid, VerticalStack } from "@shopify/polaris"; import { ThreatSummary } from "./components/ThreatSummary"; import ThreatActivityTimeline from "./components/ThreatActivityTimeline"; import React from "react"; +import { getDashboardCategory, mapLabel } from "../../../main/labelHelper"; const ChartComponent = ({ onSubCategoryClick, currDateRange }) => { return ( @@ -79,7 +80,7 @@ function ThreatActorPage() { return ( } + title={} isFirstPage={true} primaryAction={ } + title={} isFirstPage={true} primaryAction={ } /> ) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx index cdd06790b6..ff01d42097 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx @@ -6,6 +6,7 @@ import FullScreen from "highcharts/modules/full-screen"; import InfoCard from "../../dashboard/new_components/InfoCard"; import { Spinner } from "@shopify/polaris"; import api from "../api"; +import { getDashboardCategory, mapLabel } from "../../../../main/labelHelper"; // Initialize modules Exporting(Highcharts); @@ -144,8 +145,8 @@ function ThreatWorldMap({ startTimestamp, endTimestamp, style}) { return (
} /> ); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx index 865094c582..7841217fd9 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx @@ -17,8 +17,8 @@ const TopThreatTypeChart = ({ data }) => { }, [data]); return ( Date: Wed, 29 Oct 2025 14:53:20 +0530 Subject: [PATCH 15/18] Threat merged with Guardrail in left navigation and mcp security check removed --- .../components/layouts/header/Headers.js | 5 ++--- .../components/layouts/leftnav/LeftNav.js | 14 +++++++----- .../threat_detection/ThreatDashboardPage.jsx | 8 +++---- .../threat_detection/ThreatDetectionPage.jsx | 22 +++++++++---------- .../threat_detection/ThreatPolicyPage.jsx | 4 ++-- .../components/ActorDetails.jsx | 2 +- .../web/src/apps/main/labelHelperMap.js | 4 ++-- 7 files changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js index 477aa13d81..1a66c20b73 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js @@ -131,8 +131,8 @@ export default function Header() { SessionStore.getState().setThreatFiltersMap({}); // Set appropriate default subcategory for each dashboard category - if (value === "MCP Security" || value === "Agentic Security") { - // For MCP/Agentic Security, use Cloud Security as default on first load + if (value === "Agentic Security") { + // For Agentic Security, use Cloud Security as default on first load const defaultSubCategory = currentSubCategory === 'Default' ? 'Cloud Security' : currentSubCategory; PersistStore.getState().setSubCategory(defaultSubCategory); } else if (value === "API Security") { @@ -261,7 +261,6 @@ export default function Header() { handleClick("dashboard_guardrails_activity", "/dashboard/guardrails/activity", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_guardrails_activity" + subCategoryIndex, + label: "Guardrail Activity", + onClick: () => handleClick("dashboard_threat_activity", "/dashboard/protection/threat-activity", "active", subCategoryValue), + selected: + leftNavSelected === "dashboard_threat_activity" + subCategoryIndex, }, { label: "Guardrails Policies", @@ -405,9 +406,10 @@ export default function LeftNav() { key: "11", subNavigationItems: [ { - label: "Guardrails Activity", - onClick: () => handleClick("dashboard_guardrails_activity", "/dashboard/guardrails/activity", "active", subCategoryValue), - selected: leftNavSelected === "dashboard_guardrails_activity" + subCategoryIndex, + label: "Guardrail Activity", + onClick: () => handleClick("dashboard_threat_activity", "/dashboard/protection/threat-activity", "active", subCategoryValue), + selected: + leftNavSelected === "dashboard_threat_activity" + subCategoryIndex, }, { label: "Guardrails Policies", diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx index b759fbf6cb..e06baa4d7d 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx @@ -316,8 +316,8 @@ function ThreatDashboardPage() { pieInnerSize="50%" /> } - title="Threat Status" - titleToolTip="Distribution of threats by their current status" + title={`${mapLabel("Threat", getDashboardCategory())} Status`} + titleToolTip={`Distribution of ${mapLabel("Threat", getDashboardCategory())} by their current status`} /> ) @@ -337,8 +337,8 @@ function ThreatDashboardPage() { pieInnerSize="50%" /> } - title="Threat Actors by Severity" - titleToolTip="Distribution of threat actors categorized by severity level" + title={`${mapLabel("Threat", getDashboardCategory())} Actors by Severity`} + titleToolTip={`Distribution of ${mapLabel("Threat", getDashboardCategory())} actors categorized by severity level`} /> ) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx index 7508277051..d403f42719 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx @@ -140,7 +140,7 @@ const ChartComponent = ({ subCategoryCount, severityCountMap }) => { data={subCategoryCount} /> { /> { - func.isDemoAccount() && !isApiSecurityCategory() ? + !isApiSecurityCategory() ? { /> , // Add P95 latency graphs for MCP and AI Agent security in demo mode - ...(isDemoMode && !isApiSecurityCategory() ? [ + ...(!isApiSecurityCategory() ? [ } isFirstPage={true} diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx index c8cb1c2425..45c738a886 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx @@ -24,8 +24,8 @@ function ThreatPolicyPage() { return } isFirstPage={true} diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ActorDetails.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ActorDetails.jsx index ded131c735..5d23641d16 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ActorDetails.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ActorDetails.jsx @@ -73,7 +73,7 @@ export const ActorDetails = ({ actorDetails, setShowActorDetails }) => { ] return ( Date: Wed, 29 Oct 2025 18:38:05 +0530 Subject: [PATCH 16/18] left nav changes --- .../src/apps/dashboard/components/layouts/leftnav/LeftNav.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 08791a8f64..5db90d91b9 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -400,7 +400,7 @@ export default function LeftNav() { ), icon: LockMajor, - onClick: () => handleClick("dashboard_agentic_guardrails", "/dashboard/guardrails/activity", "normal", subCategoryValue), + onClick: () => handleClick("dashboard_threat_activity", "/dashboard/protection/threat-activity", "normal", subCategoryValue), selected: leftNavSelected.includes(subCategoryIndex) && (leftNavSelected.includes("_guardrails") || leftNavSelected.includes("_threat")), url: "#", key: "11", From 4b62cc91f792f4859467cfc7dd7fcccc6e152afc Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Thu, 30 Oct 2025 10:05:47 +0530 Subject: [PATCH 17/18] ui changes --- .../dashboard/pages/threat_detection/ThreatDashboardPage.jsx | 1 + .../apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx index e06baa4d7d..03fdfc9581 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx @@ -18,6 +18,7 @@ import ThreatSankeyChart from './components/ThreatSankeyChart'; import ThreatCategoryStackedChart from './components/ThreatCategoryStackedChart'; import GetPrettifyEndpoint from '../observe/GetPrettifyEndpoint'; import api from './api'; +import { mapLabel, getDashboardCategory } from '../../../main/labelHelper'; function ThreatDashboardPage() { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx index 45c738a886..03921310db 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx @@ -7,6 +7,7 @@ import values from "@/util/values"; import {produce} from "immer" import func from "@/util/func"; import {HorizontalGrid} from "@shopify/polaris"; +import { mapLabel, getDashboardCategory } from '../../../main/labelHelper'; function ThreatPolicyPage() { From 6b9c38529dcf624820f57824a0a161ff7e4afcb2 Mon Sep 17 00:00:00 2001 From: aanchalakto Date: Tue, 4 Nov 2025 10:54:42 +0530 Subject: [PATCH 18/18] dummy data for endpoint in threat --- .../threat_detection/utils/ThreatsUtils.java | 48 +++- .../components/layouts/leftnav/LeftNav.js | 2 +- .../threat_detection/ThreatDetectionPage.jsx | 229 +++++++++++++++--- .../components/ThreatApiSubcategoryCount.jsx | 45 +++- .../components/TopThreatTypeChart.jsx | 56 ++++- 5 files changed, 333 insertions(+), 47 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/utils/ThreatsUtils.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/utils/ThreatsUtils.java index 3f2f86ad1d..23a813f6da 100644 --- a/apps/dashboard/src/main/java/com/akto/action/threat_detection/utils/ThreatsUtils.java +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/utils/ThreatsUtils.java @@ -2,6 +2,8 @@ import com.akto.dao.context.Context; import com.akto.dao.monitoring.FilterYamlTemplateDao; +import com.akto.util.enums.GlobalEnums.CONTEXT_SOURCE; +import com.akto.util.enums.GlobalEnums.SUB_CATEGORY_SOURCE; import java.util.ArrayList; import java.util.List; @@ -12,12 +14,56 @@ public class ThreatsUtils { public static List getTemplates(List latestAttack) { Set contextTemplatesForAccount = FilterYamlTemplateDao.getContextTemplatesForAccount(Context.accountId.get(), Context.contextSource.get()); +// Set contextTemplatesForAccount = getContextTemplatesWithSubCategoryFilter( +// Context.accountId.get(), +// Context.contextSource.get(), +// Context.getSubCategory() +// ); +// +// System.out.println("ThreatsUtils.getTemplates: Context=" + Context.contextSource.get() + +// ", SubCategory=" + Context.getSubCategory() + +// ", Templates count=" + contextTemplatesForAccount.size()); if(latestAttack == null || latestAttack.isEmpty()) { return new ArrayList<>(contextTemplatesForAccount); } - return latestAttack.stream().filter(contextTemplatesForAccount::contains).collect(Collectors.toList()); } + + /** + * Get templates filtered by context and subcategory. + * Implements the same filtering logic as UsersCollectionsList.getContextCollections() + */ +// private static Set getContextTemplatesWithSubCategoryFilter(int accountId, CONTEXT_SOURCE contextSource, SUB_CATEGORY_SOURCE subCategory) { +// // For non-AGENTIC contexts, use existing logic without subcategory filtering +// if (contextSource != CONTEXT_SOURCE.AGENTIC) { +// System.out.println("ThreatsUtils: Using standard context filtering for " + contextSource); +// return FilterYamlTemplateDao.getContextTemplatesForAccount(accountId, contextSource,subCategory); +// } +// +// // Handle null subcategory +// if (subCategory == null) { +// subCategory = SUB_CATEGORY_SOURCE.DEFAULT; +// } +// +// System.out.println("ThreatsUtils: Processing AGENTIC context with SubCategory: " + subCategory); +// +// // Apply subcategory-based filtering similar to UsersCollectionsList.getContextCollections() +// if (subCategory == SUB_CATEGORY_SOURCE.ENDPOINT_SECURITY) { +// // For endpoint security, use only MCP templates +// System.out.println("ThreatsUtils: Using MCP templates for Endpoint Security"); +// return FilterYamlTemplateDao.getContextTemplatesForAccount(accountId, CONTEXT_SOURCE.MCP,subCategory); +// +// } else if (subCategory == SUB_CATEGORY_SOURCE.CLOUD_SECURITY) { +// // For cloud security, use only GenAI templates +// System.out.println("ThreatsUtils: Using GenAI templates for Cloud Security"); +// return FilterYamlTemplateDao.getContextTemplatesForAccount(accountId, CONTEXT_SOURCE.GEN_AI,subCategory); +// +// } else { +// // For DEFAULT subcategory or other cases, use full AGENTIC templates (both MCP and GenAI) +// System.out.println("ThreatsUtils: Using all AGENTIC templates (DEFAULT subcategory)"); +// return FilterYamlTemplateDao.getContextTemplatesForAccount(accountId, CONTEXT_SOURCE.AGENTIC,subCategory); +// } +// } } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index 5db90d91b9..18400c0830 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -273,7 +273,7 @@ export default function LeftNav() { subNavigationItems: reportsSubNavigationItems, key: "6", }, - ...(window?.STIGG_FEATURE_WISE_ALLOWED?.THREAT_DETECTION?.isGranted && dashboardCategory === "API Security" ? [{ + ...( dashboardCategory === "API Security" ? [{ label: ( {mapLabel("Threat Detection", dashboardCategory)} diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx index d403f42719..8aeac7eb79 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx @@ -18,7 +18,8 @@ import threatDetectionFunc from "./transform"; import InfoCard from "../dashboard/new_components/InfoCard"; import BarGraph from "../../components/charts/BarGraph"; import SessionStore from "../../../main/SessionStore"; -import { getDashboardCategory, isApiSecurityCategory, mapLabel } from "../../../main/labelHelper"; +import { getDashboardCategory, isApiSecurityCategory, mapLabel, getSubCategory, SUB_CATEGORY_ENDPOINT_SECURITY } from "../../../main/labelHelper"; +import PersistStore from "../../../main/PersistStore"; import { useNavigate } from "react-router-dom"; import LineChart from "../../components/charts/LineChart"; import P95LatencyGraph from "../../components/charts/P95LatencyGraph"; @@ -84,6 +85,40 @@ const directionData = [ } ] +const directionData2 = [ + { + name: 'Request', + data: [ + [1722556800000, 9], // Aug 1 + [1725235200000, 16], // Sept 1 + [1725580800000, 13], // Sept 5 + [1725926400000, 6], // Sept 9 + [1726272000000, 19], // Sept 13 + [1726617600000, 10], // Sept 17 + [1726963200000, 17], // Sept 21 + [1727308800000, 8], // Sept 25 + [1727654400000, 15], // Sept 29 + ], + color: '#6200EA' + }, + { + name: 'Response', + data: [ + [1722556800000, 9], // Aug 1 + [1725235200000, 8], // Sept 1 + [1725580800000, 17], // Sept 5 + [1725926400000, 12], // Sept 9 + [1726272000000, 5], // Sept 13 + [1726617600000, 15], // Sept 17 + [1726963200000, 9], // Sept 21 + [1727308800000, 19], // Sept 25 + [1727654400000, 10], // Sept 29 + ], + color: '#AF6CF6' + } +] + + const flaggedData = [ { name: 'Safe', @@ -131,7 +166,68 @@ const flaggedData = [ } ] +const flaggedData2 = [ + { + name: 'Safe', + data: [ + [1722556800000, 53], // Aug 1 + [1725235200000, 72], // Sept 1 + [1725580800000, 70], // Sept 5 + [1725926400000, 74], // Sept 9 + [1726272000000, 32], // Sept 13 + [1726617600000, 85], // Sept 17 + [1726963200000, 88], // Sept 21 + [1727308800000, 81], // Sept 25 + [1727654400000, 95], // Sept 29 + ], + color: '#AEE9D1' + }, + { + name: 'Flagged', + data: [ + [1722556800000, 21], // Aug 1 (12+9 from directionData) + [1725235200000, 24], // Sept 1 (16+8) + [1725580800000, 30], // Sept 5 (13+17) + [1725926400000, 18], // Sept 9 (6+12) + [1726272000000, 24], // Sept 13 (19+5) + [1726617600000, 25], // Sept 17 (10+15) + [1726963200000, 26], // Sept 21 (17+9) + [1727308800000, 27], // Sept 25 (8+19) + [1727654400000, 25], // Sept 29 (15+10) + ], + color: '#E45357' + } +] + const ChartComponent = ({ subCategoryCount, severityCountMap }) => { + // Get current subcategory to determine which dataset to use (reactive) + const currentSubCategory = PersistStore(state => state.subCategory); + const isEndpointSecurity = currentSubCategory === SUB_CATEGORY_ENDPOINT_SECURITY; + + // Add useEffect to track when component updates + useEffect(() => { + }, [currentSubCategory]); + + // Temporary alert to ensure we see the values + if (typeof window !== 'undefined') { + console.warn('CHART COMPONENT RENDER - SubCategory:', currentSubCategory, 'IsEndpoint:', isEndpointSecurity); + } + + // Select appropriate data based on subcategory + const selectedDirectionData = isEndpointSecurity ? directionData2 : directionData; + const selectedFlaggedData = isEndpointSecurity ? flaggedData2 : flaggedData; + + // Debug logging for development + if (process.env.NODE_ENV === 'development') { + console.log('ThreatDetectionPage chart data selection:', { + currentSubCategory, + isEndpointSecurity, + usingAlternateData: isEndpointSecurity, + directionDataFirstPoint: selectedDirectionData[0]?.data[0], + flaggedDataFirstPoint: selectedFlaggedData[0]?.data[0] + }); + } + return ( @@ -140,10 +236,11 @@ const ChartComponent = ({ subCategoryCount, severityCountMap }) => { data={subCategoryCount} /> { { !isApiSecurityCategory() ? { } /> state.threatFiltersMap); + const currentSubCategory = PersistStore((state) => state.subCategory); + + // Add useEffect to track subcategory changes in main component + useEffect(() => { + console.log('Main component useEffect - subcategory changed to:', currentSubCategory); + }, [currentSubCategory]); const startTimestamp = parseInt(currDateRange.period.since.getTime()/1000) const endTimestamp = parseInt(currDateRange.period.until.getTime()/1000) @@ -238,6 +343,9 @@ function ThreatDetectionPage() { const intervalDays = 3; // Every 3 days const dataPoints = Math.floor(totalDays / intervalDays) + 1; // +1 to include today + // Use the reactive subcategory from main component + const isEndpointSecurity = currentSubCategory === SUB_CATEGORY_ENDPOINT_SECURITY; + // Predefined latency values for consistent demo data (total 20-30ms) const latencyValues = [ { incoming: 12.5, output: 8.2, total: 21.4 }, @@ -261,14 +369,33 @@ function ThreatDetectionPage() { { incoming: 12.1, output: 14.8, total: 28.1 }, { incoming: 14.9, output: 8.7, total: 24.9 } ]; + + const latencyValues2 = [ + { incoming: 12.5, output: 8.2, total: 21.4 }, + { incoming: 15.3, output: 9.8, total: 25.7 }, + { incoming: 11.7, output: 7.4, total: 20.3 }, + { incoming: 10.3, output: 16.1, total: 27.9 }, + { incoming: 13.8, output: 8.6, total: 23.1 }, + { incoming: 14.6, output: 9.9, total: 25.5 }, + { incoming: 12.2, output: 7.5, total: 20.8 }, + { incoming: 11.4, output: 13.7, total: 26.4 }, + { incoming: 16.1, output: 9.5, total: 27.2 }, + { incoming: 13.5, output: 11.2, total: 25.8 }, + { incoming: 15.8, output: 9.6, total: 26.7 }, + { incoming: 12.1, output: 14.8, total: 28.1 }, + { incoming: 14.9, output: 8.7, total: 24.9 } + ]; + + // Select appropriate latency values based on subcategory + const selectedLatencyValues = isEndpointSecurity ? latencyValues2 : latencyValues; for (let i = 0; i < dataPoints; i++) { const daysAgo = i * intervalDays; const timestamp = now - (daysAgo * 24 * 60 * 60 * 1000); // Use deterministic values based on data point index - const latencyIndex = i % latencyValues.length; - const latency = latencyValues[latencyIndex]; + const latencyIndex = i % selectedLatencyValues.length; + const latency = selectedLatencyValues[latencyIndex]; data.push({ timestamp: Math.floor(timestamp / 1000), // Convert to seconds @@ -283,7 +410,7 @@ function ThreatDetectionPage() { console.error('Error generating latency data:', error); return []; } - }, []); + }, [currentSubCategory]); /** * Handle latency click events from the P95LatencyGraph component @@ -343,26 +470,70 @@ function ThreatDetectionPage() { useEffect(() => { const fetchThreatCategoryCount = async () => { setLoading(true); - const res = await api.fetchThreatCategoryCount(startTimestamp, endTimestamp); - const finalObj = threatDetectionFunc.getGraphsData(res); - setSubCategoryCount(finalObj.subCategoryCount); - setLoading(false); - }; + + if (getDashboardCategory() === "Agentic Security") { + // Provide dummy data for subCategoryCount (TopThreatTypeChart will use its own dummy data for Agentic Security) + const dummySubCategoryCount = [ + { text: "Dummy Category", value: 1, color: "#ccc" } + ]; + setSubCategoryCount(dummySubCategoryCount); + setLoading(false); + } else { + // Use real API data for API Security and other dashboard categories + const res = await api.fetchThreatCategoryCount(startTimestamp, endTimestamp); + const finalObj = threatDetectionFunc.getGraphsData(res); + setSubCategoryCount(finalObj.subCategoryCount); + setLoading(false); + } + }; const fetchCountBySeverity = async () => { setLoading(true); - let severityMap = { - CRITICAL: 0, - HIGH: 0, - MEDIUM: 0, - LOW: 0, + + // Check if we're in Agentic Security to use dummy data + if (getDashboardCategory() === "Agentic Security") { + console.log("Using dummy severity data for Agentic Security"); + + // Dummy severity data for Cloud Security (AI/ML threats tend to have more critical issues) + // Pattern: Higher critical/high counts due to AI model vulnerabilities + const cloudSeverityMap = { + CRITICAL: 8, // Model poisoning, prompt injection attacks + HIGH: 15, // Data exposure, adversarial examples + MEDIUM: 12, // Model extraction, inference manipulation + LOW: 5, // Minor configuration issues + }; + + // Dummy severity data for Endpoint Security (More distributed across severities) + // Pattern: More medium/low threats due to system-level monitoring + const endpointSeverityMap = { + CRITICAL: 4, // System compromises, privilege escalation + HIGH: 9, // Unauthorized access, command injection + MEDIUM: 18, // Resource exhaustion, file tampering + LOW: 13, // Configuration drift, minor violations + }; + + const isEndpointSecurity = currentSubCategory === SUB_CATEGORY_ENDPOINT_SECURITY; + const selectedSeverityMap = isEndpointSecurity ? endpointSeverityMap : cloudSeverityMap; + + console.log(`Using ${isEndpointSecurity ? 'Endpoint' : 'Cloud'} Security severity data:`, selectedSeverityMap); + + setSeverityCountMap(convertToGraphData(selectedSeverityMap)); + setLoading(false); + } else { + // Use real API data for non-Agentic categories + let severityMap = { + CRITICAL: 0, + HIGH: 0, + MEDIUM: 0, + LOW: 0, + } + const res = await api.fetchCountBySeverity(startTimestamp, endTimestamp); + res.categoryCounts.forEach(({ subCategory, count }) => { + severityMap[subCategory] = count; + }); + setSeverityCountMap(convertToGraphData(severityMap)); + setLoading(false); } - const res = await api.fetchCountBySeverity(startTimestamp, endTimestamp); - res.categoryCounts.forEach(({ subCategory, count }) => { - severityMap[subCategory] = count; - }); - setSeverityCountMap(convertToGraphData(severityMap)); - setLoading(false); }; fetchThreatCategoryCount(); @@ -378,15 +549,15 @@ function ThreatDetectionPage() { setLatencyData([]); } } - }, [startTimestamp, endTimestamp]); + }, [startTimestamp, endTimestamp, currentSubCategory]); const components = [ - , + , // Add P95 latency graphs for MCP and AI Agent security in demo mode ...(!isApiSecurityCategory() ? [ { + const [categoryData, setCategoryData] = useState([]); + const currentSubCategory = PersistStore(state => state.subCategory); + const isEndpointSecurity = currentSubCategory === SUB_CATEGORY_ENDPOINT_SECURITY; + + useEffect(() => { + // Use dummy data based on subcategory for Agentic Security + if (getDashboardCategory() === "Agentic Security") { + const selectedDummyData = isEndpointSecurity ? endpointSecurityDummyData : cloudSecurityDummyData; + console.log(`ThreatApiSubcategoryCount: Using ${isEndpointSecurity ? 'Endpoint' : 'Cloud'} Security category dummy data`); + setCategoryData(selectedDummyData); + } else { + // Use real data for other categories + setCategoryData(data || []); + } + }, [data, currentSubCategory, isEndpointSecurity]); + const Data = () => ( [x.text, x.value]) .sort((a, b) => b[0].localeCompare(a[0]))} /> @@ -15,8 +52,8 @@ const ThreatApiSubcategoryCount = ({ data }) => { return ( } /> ); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx index 7841217fd9..646bb328fd 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx @@ -1,26 +1,58 @@ import React, { useEffect, useState } from "react"; import InfoCard from "../../dashboard/new_components/InfoCard"; import BarGraph from "../../../components/charts/BarGraph"; -import { getDashboardCategory, mapLabel } from "../../../../main/labelHelper"; +import { getDashboardCategory, mapLabel, SUB_CATEGORY_ENDPOINT_SECURITY } from "../../../../main/labelHelper"; +import PersistStore from "../../../../main/PersistStore"; + +// Dummy data for Cloud Security (GenAI threats) +const cloudSecurityDummyData = [ + { text: "Prompt Injection", value: 15, color: "#4e76cd" }, + { text: "Data Poisoning", value: 8, color: "#4e76cd" }, + { text: "Model Extraction", value: 12, color: "#4e76cd" }, + { text: "Adversarial Examples", value: 6, color: "#4e76cd" }, + { text: "Training Data Exposure", value: 10, color: "#4e76cd" }, + { text: "Inference Manipulation", value: 4, color: "#4e76cd" }, + { text: "Model Inversion", value: 7, color: "#4e76cd" }, +]; + +// Dummy data for Endpoint Security (MCP threats) +const endpointSecurityDummyData = [ + { text: "File System Tampering", value: 13, color: "#4e76cd" }, + { text: "Memory Corruption", value: 5, color: "#4e76cd" }, + { text: "Network Intrusion", value: 16, color: "#4e76cd" }, + { text: "Service Hijacking", value: 7, color: "#4e76cd" }, +]; const TopThreatTypeChart = ({ data }) => { const [chartData, setChartData] = useState([]); + const currentSubCategory = PersistStore(state => state.subCategory); + const isEndpointSecurity = currentSubCategory === SUB_CATEGORY_ENDPOINT_SECURITY; + useEffect(() => { - const chartData = data - .sort((a, b) => a.text.localeCompare(b.text)) - .map((x) => ({ - text: x.text.replaceAll("_", " "), - value: x.value, - color: x.color, - })); - setChartData(chartData); - }, [data]); + // Use dummy data based on subcategory for Agentic Security + if (getDashboardCategory() === "Agentic Security") { + const selectedDummyData = isEndpointSecurity ? endpointSecurityDummyData : cloudSecurityDummyData; + console.log(`TopThreatTypeChart: Using ${isEndpointSecurity ? 'Endpoint' : 'Cloud'} Security dummy data`); + setChartData(selectedDummyData); + } else { + // Use real data for other categories + const processedData = data + .sort((a, b) => a.text.localeCompare(b.text)) + .map((x) => ({ + text: x.text.replaceAll("_", " "), + value: x.value, + color: x.color, + })); + setChartData(processedData); + } + }, [data, currentSubCategory, isEndpointSecurity]); return ( { }} showYAxis={true} yAxisTitle={`# of ${mapLabel("APIs", getDashboardCategory())}`} - barWidth={100 - (data.length * 6)} + barWidth={100 - (chartData.length * 6)} barGap={12} showGridLines={true} />