From 8cb24effd95bd56c1a9c2b96ffb499784e5a769e Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Thu, 9 Jan 2025 11:09:41 +0530 Subject: [PATCH 01/39] Seperating collection for vul results --- .../java/com/akto/testing/TestExecutor.java | 14 +++++ libs/dao/src/main/java/com/akto/DaoInit.java | 2 + .../VulnerableTestingRunResultDao.java | 55 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java diff --git a/apps/testing/src/main/java/com/akto/testing/TestExecutor.java b/apps/testing/src/main/java/com/akto/testing/TestExecutor.java index 447f85399d..753ded420f 100644 --- a/apps/testing/src/main/java/com/akto/testing/TestExecutor.java +++ b/apps/testing/src/main/java/com/akto/testing/TestExecutor.java @@ -11,6 +11,7 @@ import com.akto.dao.testing.TestRolesDao; import com.akto.dao.testing.TestingRunResultDao; import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.VulnerableTestingRunResultDao; import com.akto.dao.testing.WorkflowTestResultsDao; import com.akto.dao.testing.WorkflowTestsDao; import com.akto.dto.ApiInfo; @@ -573,6 +574,19 @@ public void insertResultsAndMakeIssues(List testingRunResults, TestingRunResultDao.instance.insertMany(testingRunResults); loggerMaker.infoAndAddToDb("Inserted testing results", LogDb.TESTING); + // insert vulnerable testing run results here + List vulTestResults = new ArrayList<>(); + for(TestingRunResult runResult: testingRunResults){ + if(runResult != null && runResult.isVulnerable()){ + vulTestResults.add(runResult); + } + } + + if(!vulTestResults.isEmpty()){ + loggerMaker.infoAndAddToDb("Inserted vul testing results.", LogDb.TESTING); + VulnerableTestingRunResultDao.instance.insertMany(vulTestResults); + } + TestingRunResultSummariesDao.instance.getMCollection().withWriteConcern(WriteConcern.W1).findOneAndUpdate( Filters.eq(Constants.ID, testRunResultSummaryId), Updates.inc(TestingRunResultSummary.TEST_RESULTS_COUNT, resultSize) diff --git a/libs/dao/src/main/java/com/akto/DaoInit.java b/libs/dao/src/main/java/com/akto/DaoInit.java index 7e4ea5a2fe..a4ed5c4b39 100644 --- a/libs/dao/src/main/java/com/akto/DaoInit.java +++ b/libs/dao/src/main/java/com/akto/DaoInit.java @@ -8,6 +8,7 @@ import com.akto.dao.testing.TestingRunDao; import com.akto.dao.testing.TestingRunResultDao; import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.VulnerableTestingRunResultDao; import com.akto.dao.testing_run_findings.TestingRunIssuesDao; import com.akto.dao.traffic_metrics.TrafficAlertsDao; import com.akto.dao.traffic_metrics.RuntimeMetricsDao; @@ -409,6 +410,7 @@ public static void createIndices() { TrafficAlertsDao.instance.createIndicesIfAbsent(); RuntimeMetricsDao.instance.createIndicesIfAbsent(); ApiAuditLogsDao.instance.createIndicesIfAbsent(); + VulnerableTestingRunResultDao.instance.createIndicesIfAbsent(); } } diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java new file mode 100644 index 0000000000..834973cfa3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -0,0 +1,55 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDaoWithRbac; +import com.akto.dao.MCollection; +import com.akto.dao.context.Context; +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.testing.GenericTestResult; +import com.akto.dto.testing.TestingRunResult; +import com.mongodb.client.model.CreateCollectionOptions; + +public class VulnerableTestingRunResultDao extends AccountsContextDaoWithRbac { + + public static final VulnerableTestingRunResultDao instance = new VulnerableTestingRunResultDao(); + + public void createIndicesIfAbsent() { + + String dbName = Context.accountId.get()+""; + + CreateCollectionOptions createCollectionOptions = new CreateCollectionOptions(); + createCollectionIfAbsent(dbName, getCollName(), createCollectionOptions); + + + MCollection.createIndexIfAbsent(getDBName(), getCollName(), + new String[] { TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.VULNERABLE }, false); + + MCollection.createIndexIfAbsent(getDBName(), getCollName(), new String[]{TestingRunResult.END_TIMESTAMP}, false); + + String[] fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.API_INFO_KEY+"."+ApiInfoKey.API_COLLECTION_ID}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); + + fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_RESULTS+"."+GenericTestResult._CONFIDENCE}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); + + fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_SUPER_TYPE}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); + + fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.VULNERABLE, TestingRunResult.API_INFO_KEY, TestingRunResult.TEST_SUB_TYPE}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); + } + + @Override + public String getCollName() { + return "vulnerable_testing_run_results"; + } + + @Override + public Class getClassT() { + return TestingRunResult.class; + } + + @Override + public String getFilterKeyString() { + return TestingRunResult.API_INFO_KEY + "." + ApiInfoKey.API_COLLECTION_ID; + } +} From c451f1db62c43429e10af76deec2950640d20aa4 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Thu, 9 Jan 2025 11:20:25 +0530 Subject: [PATCH 02/39] Adding a field for new testing run --- .../main/java/com/akto/dto/testing/TestingRun.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java index 26ba54ae83..add535eb0a 100644 --- a/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java @@ -28,6 +28,8 @@ public class TestingRun { public static final String _API_COLLECTION_ID = "testingEndpoints.apiCollectionId"; public static final String _API_COLLECTION_ID_IN_LIST = "testingEndpoints.apisList.apiCollectionId"; + private boolean isNewTestRun = true; + @BsonIgnore private String hexId; @BsonIgnore @@ -59,6 +61,7 @@ public TestingRun(int scheduleTimestamp, String userEmail, TestingEndpoints test this.name = name; this.triggeredBy = triggeredBy; this.sendSlackAlert = sendSlackAlert; + this.isNewTestRun = true; } public TestingRun(int scheduleTimestamp, String userEmail, TestingEndpoints testingEndpoints, int testIdConfig, State state, int periodInSeconds, String name, int testRunTime, int maxConcurrentRequests, boolean sendSlackAlert) { this.scheduleTimestamp = scheduleTimestamp; @@ -73,6 +76,7 @@ public TestingRun(int scheduleTimestamp, String userEmail, TestingEndpoints test this.periodInSeconds = periodInSeconds; this.name = name; this.sendSlackAlert = sendSlackAlert; + this.isNewTestRun = true; } public TestingRunConfig getTestingRunConfig() { @@ -224,4 +228,12 @@ public String toString() { "}"; } + public boolean isNewTestRun() { + return isNewTestRun; + } + + public void setNewTestRun(boolean isNewTestRun) { + this.isNewTestRun = isNewTestRun; + } + } From 997e05503869ccd8953bde6532516777000dd555 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Fri, 10 Jan 2025 11:32:44 +0530 Subject: [PATCH 03/39] fixing start.sh file for all cases --- apps/testing/start.sh | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/apps/testing/start.sh b/apps/testing/start.sh index 1f0118fc8e..e8e5e79e37 100644 --- a/apps/testing/start.sh +++ b/apps/testing/start.sh @@ -1,16 +1,37 @@ #!/bin/bash -# Get total memory in MB -TOTAL_MEM=$(free -m | awk '/Mem:/ {print $2}') +# 1. Detect and read cgroup memory limits +if [ -f /sys/fs/cgroup/memory.max ]; then + # cgroup v2 + MEM_LIMIT_BYTES=$(cat /sys/fs/cgroup/memory.max) +elif [ -f /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then + # cgroup v1 + MEM_LIMIT_BYTES=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes) +else + # Fallback to free -b (bytes) if cgroup file not found + echo "Neither cgroup v2 nor v1 memory file found, defaulting to free -m" + # Convert from kB to bytes + MEM_LIMIT_BYTES=$(free -b | awk '/Mem:/ {print $2}') +fi -# Print the total memory for debugging -echo "Total memory available: ${TOTAL_MEM} MB" +# 2. Handle edge cases: "max" means no strict limit or a very large limit +if [ "$MEM_LIMIT_BYTES" = "max" ]; then + # Arbitrary fallback (1 GiB in bytes here, but adjust as needed) + echo "Cgroup memory limit set to 'max', defaulting to 1GiB" + MEM_LIMIT_BYTES=1073741824 +fi -# Calculate 80% of total memory -XMX_MEM=$((TOTAL_MEM * 80 / 100)) +# 3. Convert the memory limit from bytes to MB (integer division) +MEM_LIMIT_MB=$((MEM_LIMIT_BYTES / 1024 / 1024)) +echo "Detected container memory limit: ${MEM_LIMIT_MB} MB" -# Print the calculated XMX value for debugging +# 4. Calculate 80% of that limit for Xmx +XMX_MEM=$((MEM_LIMIT_MB * 80 / 100)) echo "Calculated -Xmx value: ${XMX_MEM} MB" -# Run Java with dynamically calculated -Xmx value -exec java -XX:+ExitOnOutOfMemoryError -Xmx${XMX_MEM}m -jar /app/testing-1.0-SNAPSHOT-jar-with-dependencies.jar +# 5. Run Java with the dynamically calculated -Xmx +exec java \ + -XX:+ExitOnOutOfMemoryError \ + -Xmx${XMX_MEM}m \ + -jar /app/testing-1.0-SNAPSHOT-jar-with-dependencies.jar + From 8575ceea9d7c89af28da8118feff1ca13f658450 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Fri, 10 Jan 2025 17:48:01 +0530 Subject: [PATCH 04/39] Fixing all dashboard related queries for vulnerable results collection --- .../akto/action/testing/StartTestAction.java | 46 +++++++--- .../action/testing_issues/IssuesAction.java | 89 +++++++++++++++---- .../src/main/java/com/akto/testing/Main.java | 2 - .../com/akto/dao/testing/TestingRunDao.java | 17 +++- .../testing/TestingRunResultSummariesDao.java | 3 + .../VulnerableTestingRunResultDao.java | 81 ++++++++++++++--- .../java/com/akto/dto/testing/TestingRun.java | 1 + .../dto/testing/TestingRunResultSummary.java | 12 +++ .../java/com/akto/github/GithubUtils.java | 28 ++++-- .../src/main/java/com/akto/testing/Utils.java | 12 ++- .../com/akto/usage/UsageMetricCalculator.java | 3 + 11 files changed, 243 insertions(+), 51 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 1271981c48..3ab78c8945 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -614,7 +614,7 @@ private Map getCountMapForQueryMode(ObjectId testingRunResultSu Map resultantMap = new HashMap<>(); List filterList = prepareTestRunResultsFilters(testingRunResultSummaryId, queryMode); - int count = (int) TestingRunResultDao.instance.count(Filters.and(filterList)); + int count = VulnerableTestingRunResultDao.instance.countFromDb(Filters.and(filterList), queryMode.equals(QueryMode.VULNERABLE)); resultantMap.put(queryMode.toString(), count); return resultantMap; @@ -720,8 +720,8 @@ public String fetchTestingRunResults() { timeNow = Context.now(); Bson filters = testingRunResultFilters.isEmpty() ? Filters.empty() : Filters.and(testingRunResultFilters); - this.testingRunResults = TestingRunResultDao.instance - .fetchLatestTestingRunResultWithCustomAggregations(filters, pageLimit, skip, sortStage); + this.testingRunResults = VulnerableTestingRunResultDao.instance + .fetchLatestTestingRunResultWithCustomAggregations(filters, pageLimit, skip, sortStage, testingRunResultSummaryId, queryMode.equals(QueryMode.VULNERABLE)); loggerMaker.infoAndAddToDb("[" + (Context.now() - timeNow) + "] Fetched testing run results of size: " + testingRunResults.size(), LogDb.DASHBOARD); timeNow = Context.now(); @@ -778,13 +778,25 @@ public String fetchVulnerableTestRunResults() { try { testingRunResultSummaryId = new ObjectId(testingRunResultSummaryHexId); Bson filterForReport = com.akto.action.testing.Utils.createFiltersForTestingReport(reportFilterList); + boolean isStoredInVulnerableCollection = VulnerableTestingRunResultDao.instance.isStoredInVulnerableCollection(testingRunResultSummaryId, true); Bson filters = Filters.and( Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummaryId), Filters.eq(TestingRunResult.VULNERABLE, true), filterForReport ); + List testingRunResultList = new ArrayList<>(); + if(isStoredInVulnerableCollection){ + filters = Filters.and( + Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummaryId), + filterForReport + ); + testingRunResultList = VulnerableTestingRunResultDao.instance.findAll(filters, skip, 50, null); + }else{ + testingRunResultList = TestingRunResultDao.instance.findAll(filters, skip, 50, null); + } + - List testingRunResultList = TestingRunResultDao.instance.findAll(filters, skip, 50, null); + // Map sampleDataVsCurlMap = new HashMap<>(); // for (TestingRunResult runResult: testingRunResultList) { // WorkflowTest workflowTest = runResult.getWorkflowTest(); @@ -848,7 +860,7 @@ public static String getNodeResultLastMessage(String message) { public String fetchTestRunResultDetails() { ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); - this.testingRunResult = TestingRunResultDao.instance.findOne("_id", testingRunResultId); + this.testingRunResult = VulnerableTestingRunResultDao.instance.findOne("_id", testingRunResultId); List runResults = new ArrayList<>(); for (GenericTestResult testResult: this.testingRunResult.getTestResults()) { @@ -868,7 +880,7 @@ public String fetchTestRunResultDetails() { public String fetchIssueFromTestRunResultDetails() { ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); - TestingRunResult result = TestingRunResultDao.instance.findOne(Constants.ID, testingRunResultId); + TestingRunResult result = VulnerableTestingRunResultDao.instance.findOne(Constants.ID, testingRunResultId); try { if (result.isVulnerable()) { // name = category @@ -1132,12 +1144,14 @@ public void run() { Context.accountId.set(accountId); try { ObjectId summaryObjectId = new ObjectId(testingRunResultSummaryHexId); - List testingRunResults = TestingRunResultDao.instance.findAll( + boolean isStoredInVulnerableCollection = VulnerableTestingRunResultDao.instance.isStoredInVulnerableCollection(summaryObjectId, true); + List testingRunResults = VulnerableTestingRunResultDao.instance.findAll( Filters.and( Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, summaryObjectId), vulnerableFilter ), - Projections.include(TestingRunResult.API_INFO_KEY, TestingRunResult.TEST_SUB_TYPE) + Projections.include(TestingRunResult.API_INFO_KEY, TestingRunResult.TEST_SUB_TYPE), + isStoredInVulnerableCollection ); if(testingRunResults.isEmpty()){ @@ -1182,10 +1196,18 @@ public void run() { ); // update testing run results, by setting them isIgnored true - TestingRunResultDao.instance.updateMany( - Filters.in(Constants.ID, ignoredResults), - Updates.set(TestingRunResult.IS_IGNORED_RESULT, true) - ); + if(isStoredInVulnerableCollection){ + VulnerableTestingRunResultDao.instance.updateMany( + Filters.in(Constants.ID, ignoredResults), + Updates.set(TestingRunResult.IS_IGNORED_RESULT, true) + ); + }else{ + TestingRunResultDao.instance.updateMany( + Filters.in(Constants.ID, ignoredResults), + Updates.set(TestingRunResult.IS_IGNORED_RESULT, true) + ); + } + } catch (Exception e) { e.printStackTrace(); } diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index cf951c1bb5..3fccd5b7d0 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -1,6 +1,5 @@ package com.akto.action.testing_issues; -import com.akto.action.ExportSampleDataAction; import com.akto.action.UserAction; import com.akto.action.testing.Utils; import com.akto.dao.HistoricalDataDao; @@ -11,6 +10,7 @@ import com.akto.dao.test_editor.YamlTemplateDao; import com.akto.dao.testing.TestingRunResultDao; import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.VulnerableTestingRunResultDao; import com.akto.dao.testing.sources.TestReportsDao; import com.akto.dao.testing.sources.TestSourceConfigsDao; import com.akto.dao.testing_run_findings.TestingRunIssuesDao; @@ -328,13 +328,33 @@ public String fetchVulnerableTestingRunResultsFromIssues() { issues = TestingRunIssuesDao.instance.findAll(filters, skip, 50, null); } List andFilters = new ArrayList<>(); + List filtersForNewCollection = new ArrayList<>(); + + Map summaryIdVsIsNew = new HashMap<>(); + for (TestingRunIssues issue : issues) { - andFilters.add(Filters.and( - Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, issue.getLatestTestingRunSummaryId()), - Filters.eq(TestingRunResult.TEST_SUB_TYPE, issue.getId().getTestSubCategory()), - Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()), - Filters.eq(TestingRunResult.VULNERABLE, true) - )); + + ObjectId currentSummaryId = issue.getLatestTestingRunSummaryId(); + + Bson baseFilter = Filters.and( + Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, currentSummaryId), + Filters.eq(TestingRunResult.TEST_SUB_TYPE, issue.getId().getTestSubCategory()), + Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()) + ); + + Boolean val = summaryIdVsIsNew.get(currentSummaryId.toHexString()); + if(val == null){ + val = VulnerableTestingRunResultDao.instance.isStoredInVulnerableCollection(currentSummaryId, true); + summaryIdVsIsNew.put(currentSummaryId.toHexString(), val); + } + + if(!val){ + andFilters.add( + Filters.and(baseFilter,Filters.eq(TestingRunResult.VULNERABLE, true)) + ); + }else{ + filtersForNewCollection.add(baseFilter); + } } if (issues.isEmpty()) { this.testingRunResults = new ArrayList<>(); @@ -348,9 +368,17 @@ public String fetchVulnerableTestingRunResultsFromIssues() { String key = issue.getId().getApiInfoKey().toString() + "_" + testSubCategory; testingRunIssuesMap.put(key, issue); } + if(!andFilters.isEmpty()){ + Bson orFilters = Filters.or(andFilters); + this.testingRunResults = TestingRunResultDao.instance.findAll(orFilters); + } + + if (!filtersForNewCollection.isEmpty()) { + this.testingRunResults.addAll( + VulnerableTestingRunResultDao.instance.findAll(Filters.or(filtersForNewCollection)) + ); + } - Bson orFilters = Filters.or(andFilters); - this.testingRunResults = TestingRunResultDao.instance.findAll(orFilters); Map sampleDataVsCurlMap = new HashMap<>(); // todo: fix for (TestingRunResult runResult: this.testingRunResults) { @@ -426,7 +454,10 @@ public String fetchTestingRunResult() { Filters.eq(TestingRunResult.TEST_SUB_TYPE, testSubType), Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()) ); - testingRunResult = TestingRunResultDao.instance.findOne(filterForRunResult); + testingRunResult = VulnerableTestingRunResultDao.instance.findOne(filterForRunResult); + if(testingRunResult == null){ + testingRunResult = TestingRunResultDao.instance.findOne(filterForRunResult); + } if (issue.isUnread() && (currentUserRole.equals(Role.ADMIN) || currentUserRole.equals(Role.MEMBER))) { logger.info("Issue id from db to be marked as read " + issueId); Bson update = Updates.combine(Updates.set(TestingRunIssues.UNREAD, false), @@ -575,6 +606,7 @@ public void run() { // update summaries accordingly with issues ignored + // TODO: fix this smartly Map mapSummaryToResultId = TestingRunResultDao.instance.mapSummaryIdToTestingResultHexId(testingRunResultHexIdsMap.keySet()); Map> summaryWiseCountMap = new HashMap<>(); @@ -604,21 +636,44 @@ public void run() { List issueStatusQuery; List testingRunResultList; private Map> filters; + public String fetchIssuesByStatusAndSummaryId() { + if(latestTestingRunSummaryId == null || latestTestingRunSummaryId.isEmpty()){ + addActionError("SummaryId is a required field and cannot be empty."); + return ERROR.toUpperCase(); + } + if(!ObjectId.isValid(latestTestingRunSummaryId)){ + addActionError("SummaryId is not valid"); + return ERROR.toUpperCase(); + } + + ObjectId objectId = new ObjectId(latestTestingRunSummaryId); + Bson triFilters = Filters.and( Filters.in(TestingRunIssues.TEST_RUN_ISSUES_STATUS, issueStatusQuery), - Filters.in(TestingRunIssues.LATEST_TESTING_RUN_SUMMARY_ID, new ObjectId(latestTestingRunSummaryId)) + Filters.in(TestingRunIssues.LATEST_TESTING_RUN_SUMMARY_ID, objectId) ); issues = TestingRunIssuesDao.instance.findAll(triFilters, Projections.include("_id")); - List testingRunResultsFilterList = new ArrayList<>(); + boolean isStoredInVulnerableCollection = VulnerableTestingRunResultDao.instance.isStoredInVulnerableCollection(objectId, true); for(TestingRunIssues issue: issues) { - testingRunResultsFilterList.add(Filters.and( + Bson filter = Filters.empty(); + if(isStoredInVulnerableCollection){ + filter = Filters.and( + Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, new ObjectId(latestTestingRunSummaryId)), + Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()), + Filters.eq(TestingRunResult.TEST_SUB_TYPE, issue.getId().getTestSubCategory()) + ); + + }else{ + filter = Filters.and( Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, new ObjectId(latestTestingRunSummaryId)), Filters.eq(TestingRunResult.VULNERABLE, true), Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()), Filters.eq(TestingRunResult.TEST_SUB_TYPE, issue.getId().getTestSubCategory()) - )); + ); + } + testingRunResultsFilterList.add(filter); } List filtersList = new ArrayList<>(); @@ -632,7 +687,11 @@ public String fetchIssuesByStatusAndSummaryId() { return SUCCESS.toUpperCase(); } - testingRunResultList = TestingRunResultDao.instance.fetchLatestTestingRunResultWithCustomAggregations(Filters.and(filtersList), limit, skip, sortStage); + if(isStoredInVulnerableCollection){ + testingRunResultList = VulnerableTestingRunResultDao.instance.fetchLatestTestingRunResultWithCustomAggregations(Filters.and(filtersList), limit, skip, sortStage); + }else{ + testingRunResultList = TestingRunResultDao.instance.fetchLatestTestingRunResultWithCustomAggregations(Filters.and(filtersList), limit, skip, sortStage); + } return SUCCESS.toUpperCase(); } diff --git a/apps/testing/src/main/java/com/akto/testing/Main.java b/apps/testing/src/main/java/com/akto/testing/Main.java index b0fc059786..7921c7cf1e 100644 --- a/apps/testing/src/main/java/com/akto/testing/Main.java +++ b/apps/testing/src/main/java/com/akto/testing/Main.java @@ -59,10 +59,8 @@ import org.slf4j.LoggerFactory; import java.util.*; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java index d5f824ebd0..1346882054 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java @@ -12,8 +12,8 @@ import com.akto.dto.testing.TestingRun; import com.mongodb.client.model.CreateCollectionOptions; import com.akto.dto.testing.TestingRunResultSummary; +import com.akto.util.Constants; import com.mongodb.client.MongoCursor; -import com.mongodb.client.model.CreateCollectionOptions; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Projections; @@ -36,6 +36,9 @@ public void createIndicesIfAbsent() { fieldNames = new String[]{TestingRun.NAME}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); + + fieldNames = new String[]{Constants.ID, TestingRun.IS_NEW_TESTING_RUN}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); } public List getTestConfigIdsToDelete(List testingRunIds){ @@ -78,6 +81,18 @@ public List getSummaryIdsFromRunIds(List testRunIds){ return testingSummaryIds; } + public boolean isStoredInVulnerableCollection(ObjectId testingRunId){ + if(testingRunId == null){ + return false; + } + return instance.count( + Filters.and( + Filters.eq(Constants.ID, testingRunId), + Filters.eq(TestingRun.IS_NEW_TESTING_RUN, true) + ) + ) > 0; + } + @Override public String getCollName() { return "testing_run"; diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java index 514c705abb..b82572ac91 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java @@ -140,6 +140,9 @@ public void createIndicesIfAbsent() { fieldNames = new String[]{TestingRunResultSummary.END_TIMESTAMP}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); + fieldNames = new String[]{Constants.ID, TestingRunResultSummary.IS_NEW_TESTING_RUN_RESULT_SUMMARY}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); + IndexOptions sparseIndex = new IndexOptions().sparse(true); diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java index 834973cfa3..14ed2cbc65 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -1,17 +1,26 @@ package com.akto.dao.testing; -import com.akto.dao.AccountsContextDaoWithRbac; +import java.util.List; + +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + import com.akto.dao.MCollection; import com.akto.dao.context.Context; import com.akto.dto.ApiInfo.ApiInfoKey; import com.akto.dto.testing.GenericTestResult; import com.akto.dto.testing.TestingRunResult; +import com.akto.dto.testing.TestingRunResultSummary; +import com.akto.util.Constants; import com.mongodb.client.model.CreateCollectionOptions; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; -public class VulnerableTestingRunResultDao extends AccountsContextDaoWithRbac { +public class VulnerableTestingRunResultDao extends TestingRunResultDao { public static final VulnerableTestingRunResultDao instance = new VulnerableTestingRunResultDao(); + @Override public void createIndicesIfAbsent() { String dbName = Context.accountId.get()+""; @@ -21,7 +30,7 @@ public void createIndicesIfAbsent() { MCollection.createIndexIfAbsent(getDBName(), getCollName(), - new String[] { TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.VULNERABLE }, false); + new String[] { TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID }, false); MCollection.createIndexIfAbsent(getDBName(), getCollName(), new String[]{TestingRunResult.END_TIMESTAMP}, false); @@ -34,22 +43,72 @@ public void createIndicesIfAbsent() { fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_SUPER_TYPE}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); - fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.VULNERABLE, TestingRunResult.API_INFO_KEY, TestingRunResult.TEST_SUB_TYPE}; + fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.API_INFO_KEY, TestingRunResult.TEST_SUB_TYPE}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); } - @Override - public String getCollName() { - return "vulnerable_testing_run_results"; + public boolean isStoredInVulnerableCollection(ObjectId objectId, boolean isSummary){ + if(!isSummary){ + return TestingRunDao.instance.isStoredInVulnerableCollection(objectId); + }else { + try { + Bson filter = Filters.and( + Filters.eq(Constants.ID, objectId), + Filters.eq(TestingRunResultSummary.IS_NEW_TESTING_RUN_RESULT_SUMMARY, true) + ); + boolean isNew = TestingRunResultSummariesDao.instance.count(filter) > 0; + if(!isNew){ + TestingRunResultSummary trrs = TestingRunResultSummariesDao.instance.findOne( + Filters.eq(Constants.ID, objectId), + Projections.include(TestingRunResultSummary.TESTING_RUN_ID) + ); + return TestingRunDao.instance.isStoredInVulnerableCollection(trrs.getTestingRunId()); + }else{ + return isNew; + } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + } + + public int countFromDb(Bson filter, boolean isVulnerable){ + if(isVulnerable){ + int count = (int) instance.count(filter); + if(count != 0){ + return count; + } + } + return (int) super.count(filter); + } + + public List fetchLatestTestingRunResultWithCustomAggregations(Bson filters, int limit, int skip, Bson customSort, ObjectId summaryId, boolean isVulnerable) { + if(isVulnerable && instance.isStoredInVulnerableCollection(summaryId, true)){ + return instance.fetchLatestTestingRunResultWithCustomAggregations(filters, limit, skip, customSort); + }else{ + return super.fetchLatestTestingRunResultWithCustomAggregations(filters, limit, skip, customSort); + } } @Override - public Class getClassT() { - return TestingRunResult.class; + public TestingRunResult findOne(Bson q) { + TestingRunResult tr = super.findOne(q); + if(tr == null){ + tr = instance.findOne(q); + } + return tr; + } + + public List findAll(Bson q, Bson projection, boolean isStoredInVulnerableCollection) { + if(isStoredInVulnerableCollection){ + return instance.findAll(q,projection); + } + return super.findAll(q, projection); } @Override - public String getFilterKeyString() { - return TestingRunResult.API_INFO_KEY + "." + ApiInfoKey.API_COLLECTION_ID; + public String getCollName() { + return "vulnerable_testing_run_results"; } } diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java index add535eb0a..8c3ffc9806 100644 --- a/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java @@ -28,6 +28,7 @@ public class TestingRun { public static final String _API_COLLECTION_ID = "testingEndpoints.apiCollectionId"; public static final String _API_COLLECTION_ID_IN_LIST = "testingEndpoints.apisList.apiCollectionId"; + public static final String IS_NEW_TESTING_RUN = "isNewTestRun"; private boolean isNewTestRun = true; @BsonIgnore diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java index 1cb5a55958..f6db94cf06 100644 --- a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java @@ -33,6 +33,9 @@ public class TestingRunResultSummary { private int testIdConfig; + public static final String IS_NEW_TESTING_RUN_RESULT_SUMMARY = "isNewTestingSummary"; + private boolean isNewTestingSummary = true; + @BsonIgnore private String hexId; @@ -53,6 +56,7 @@ public TestingRunResultSummary(int startTimestamp, int endTimestamp, Map affectedEndpoints = new HashSet<>(); Map testSuperTypeCount = new HashMap<>(); + boolean isNewTestingSummary = VulnerableTestingRunResultDao.instance.isStoredInVulnerableCollection(testingRunResultSummary.getId(), true); + boolean fetchMore = true; int skip = 0; int limit = 1000; + List testingRunResultList; while (fetchMore) { - List testingRunResultList = TestingRunResultDao.instance.findAll(Filters.and(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummary.getId()), + if(isNewTestingSummary){ + testingRunResultList = VulnerableTestingRunResultDao.instance.findAll(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummary.getId()), skip, limit, null); + } else{ + testingRunResultList = TestingRunResultDao.instance.findAll(Filters.and(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummary.getId()), Filters.eq(TestingRunResult.VULNERABLE, true)), skip, limit, null); + } - if (testingRunResultList.isEmpty() || testingRunResultList.size() < 1000) { + if (testingRunResultList == null || testingRunResultList.isEmpty() || testingRunResultList.size() < 1000) { fetchMore = false; } else { skip = skip + limit; } - - for (TestingRunResult testingRunResult : testingRunResultList) { - String superType = testingRunResult.getTestSuperType(); - testSuperTypeCount.merge(superType, 1, Integer::sum); - ApiInfo.ApiInfoKey infoKey = testingRunResult.getApiInfoKey(); - String url = infoKey.method.name() + " " + infoKey.getUrl(); - affectedEndpoints.add(url); + if(testingRunResultList != null){ + for (TestingRunResult testingRunResult : testingRunResultList) { + String superType = testingRunResult.getTestSuperType(); + testSuperTypeCount.merge(superType, 1, Integer::sum); + ApiInfo.ApiInfoKey infoKey = testingRunResult.getApiInfoKey(); + String url = infoKey.method.name() + " " + infoKey.getUrl(); + affectedEndpoints.add(url); + } + testingRunResultList.clear(); } } diff --git a/libs/utils/src/main/java/com/akto/testing/Utils.java b/libs/utils/src/main/java/com/akto/testing/Utils.java index 77913061df..89b548d72c 100644 --- a/libs/utils/src/main/java/com/akto/testing/Utils.java +++ b/libs/utils/src/main/java/com/akto/testing/Utils.java @@ -14,6 +14,7 @@ import org.bson.conversions.Bson; import org.bson.types.ObjectId; import com.akto.dao.testing.TestingRunResultDao; +import com.akto.dao.testing.VulnerableTestingRunResultDao; import com.akto.dao.testing_run_findings.TestingRunIssuesDao; import com.akto.dto.ApiInfo.ApiInfoKey; import com.akto.dto.CollectionConditions.ConditionsType; @@ -441,7 +442,16 @@ public static Map finalCountIssuesMap(ObjectId testingRunResult Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummaryId), Filters.eq(TestingRunResult.VULNERABLE, true) ); - List allVulResults = TestingRunResultDao.instance.findAll(filterQ, projection); + boolean isNewTestingSummary = VulnerableTestingRunResultDao.instance.isStoredInVulnerableCollection(testingRunResultSummaryId, true); + List allVulResults = new ArrayList<>(); + if(!isNewTestingSummary){ + allVulResults = TestingRunResultDao.instance.findAll(filterQ, projection); + }else{ + allVulResults = VulnerableTestingRunResultDao.instance.findAll( + Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummaryId) + ); + } + Map testingIssuesIdsMap = TestingUtils. listOfIssuesIdsFromTestingRunResults(allVulResults, true, false); 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 b561a2637d..b97a4cf470 100644 --- a/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java +++ b/libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java @@ -150,6 +150,9 @@ public static int calculateTestRuns(UsageMetric usageMetric) { add(Filters.gt(TestingRunResult.END_TIMESTAMP, measureEpoch)); add(demoAndDeactivatedCollFilter); }}; + + // TODO: When we shift vulnerable test results into new collection completely {without making copy}, fix count here then. + int testRuns = (int) TestingRunResultDao.instance.count(Filters.and(filters)); /* From 1b0f507cef1efb950c02b1f975bb329884a9d69a Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Sat, 11 Jan 2025 20:12:43 +0530 Subject: [PATCH 05/39] Changing role based access on features --- apps/dashboard/src/main/resources/struts.xml | 6 +++--- libs/dao/src/main/java/com/akto/dto/rbac/RbacEnums.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index 21609dc00b..eab02875a9 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -330,9 +330,9 @@ - USER_ACTIONS + ADMIN_ACTIONS READ_WRITE - User made another user admin in Akto + Admin changed the role of the user in Akto dashboard 403 @@ -1163,7 +1163,7 @@ - USER_ACTIONS + INVITE_MEMBERS READ_WRITE User invited another user to Akto diff --git a/libs/dao/src/main/java/com/akto/dto/rbac/RbacEnums.java b/libs/dao/src/main/java/com/akto/dto/rbac/RbacEnums.java index 21396d64ad..f97ca62b60 100644 --- a/libs/dao/src/main/java/com/akto/dto/rbac/RbacEnums.java +++ b/libs/dao/src/main/java/com/akto/dto/rbac/RbacEnums.java @@ -41,7 +41,7 @@ public enum Feature { METRICS(AccessGroups.DEBUG_INFO), LOGS(AccessGroups.DEBUG_INFO), BILLING(AccessGroups.SETTINGS), - INVITE_MEMBERS(AccessGroups.SETTINGS), + INVITE_MEMBERS(AccessGroups.DEBUG_INFO), ADMIN_ACTIONS(AccessGroups.ADMIN), USER_ACTIONS(AccessGroups.USER); private final AccessGroups accessGroup; From 9f33a7b6ff46ad1f728f9c13028e03cfd13b0cd0 Mon Sep 17 00:00:00 2001 From: Fenil Shah Date: Sat, 11 Jan 2025 20:18:07 +0530 Subject: [PATCH 06/39] added trial msg --- .../src/main/java/com/akto/action/ProfileAction.java | 1 + .../java/com/akto/listener/InitializerListener.java | 5 +++++ .../com/akto/utils/billing/OrganizationUtils.java | 4 ++++ apps/dashboard/web/pages/login.jsp | 2 ++ .../web/src/apps/dashboard/pages/Dashboard.jsx | 7 ++++++- .../main/java/com/akto/dto/billing/Organization.java | 11 +++++++++++ 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java b/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java index e294f9e393..6aa4368501 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java @@ -230,6 +230,7 @@ public static void executeMeta1(Utility utility, User user, HttpServletRequest r userDetails.append("expired", organization.checkExpirationWithAktoSync()); userDetails.append("hotjarSiteId", organization.getHotjarSiteId()); userDetails.append("planType", organization.getplanType()); + userDetails.append("trialMsg", organization.gettrialMsg()); } if (versions.length > 2) { diff --git a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java index a15c7fb389..87d69c935a 100644 --- a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java +++ b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java @@ -2480,6 +2480,7 @@ public static Organization fetchAndSaveFeatureWiseAllowed(Organization organizat int gracePeriod = organization.getGracePeriod(); String hotjarSiteId = organization.getHotjarSiteId(); String planType = organization.getplanType(); + String trialMsg = organization.gettrialMsg(); String organizationId = organization.getId(); /* * This ensures, we don't fetch feature wise allowed from akto too often. @@ -2545,6 +2546,7 @@ public static Organization fetchAndSaveFeatureWiseAllowed(Organization organizat gracePeriod = OrganizationUtils.fetchOrgGracePeriodFromMetaData(metaData); hotjarSiteId = OrganizationUtils.fetchHotjarSiteId(metaData); planType = OrganizationUtils.fetchplanType(metaData); + trialMsg = OrganizationUtils.fetchtrialMsg(metaData); boolean expired = OrganizationUtils.fetchExpired(metaData); boolean telemetryEnabled = OrganizationUtils.fetchTelemetryEnabled(metaData); // setTelemetrySettings(organization, telemetryEnabled); @@ -2557,6 +2559,8 @@ public static Organization fetchAndSaveFeatureWiseAllowed(Organization organizat organization.setplanType(planType); + organization.settrialMsg(trialMsg); + organization.setGracePeriod(gracePeriod); organization.setFeatureWiseAllowed(featureWiseAllowed); organization.setExpired(expired); @@ -2579,6 +2583,7 @@ public static Organization fetchAndSaveFeatureWiseAllowed(Organization organizat Updates.set(Organization._EXPIRED, expired), Updates.set(Organization.HOTJAR_SITE_ID, hotjarSiteId), Updates.set(Organization.PLAN_TYPE, planType), + Updates.set(Organization.TRIAL_MSG, trialMsg), Updates.set(Organization.TEST_TELEMETRY_ENABLED, testTelemetryEnabled), Updates.set(Organization.LAST_FEATURE_MAP_UPDATE, lastFeatureMapUpdate))); diff --git a/apps/dashboard/src/main/java/com/akto/utils/billing/OrganizationUtils.java b/apps/dashboard/src/main/java/com/akto/utils/billing/OrganizationUtils.java index b264693e8f..7a48af000c 100644 --- a/apps/dashboard/src/main/java/com/akto/utils/billing/OrganizationUtils.java +++ b/apps/dashboard/src/main/java/com/akto/utils/billing/OrganizationUtils.java @@ -291,6 +291,10 @@ public static String fetchplanType(BasicDBObject additionalMetaData) { return additionalMetaData.getString("PLAN_TYPE", ""); } + public static String fetchtrialMsg(BasicDBObject additionalMetaData) { + return additionalMetaData.getString("TRIAL_MSG", ""); + } + public static boolean fetchTelemetryEnabled(BasicDBObject additionalMetaData) { return additionalMetaData.getString("ENABLE_TELEMETRY", "NA").equalsIgnoreCase("ENABLED"); } diff --git a/apps/dashboard/web/pages/login.jsp b/apps/dashboard/web/pages/login.jsp index 6a221a096b..817493ed68 100644 --- a/apps/dashboard/web/pages/login.jsp +++ b/apps/dashboard/web/pages/login.jsp @@ -98,6 +98,8 @@ window.PLAN_TYPE = '${requestScope.planType}' + window.TRIAL_MSG = '${requestScope.trialMsg}' + // Enabling the debug mode flag is useful during implementation, // but it's recommended you remove it for production diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx index ea0ece7f05..241abd41d7 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx @@ -197,13 +197,18 @@ function Dashboard() { : null} {func.checkLocal() && !(location.pathname.includes("test-editor") || location.pathname.includes("settings") || location.pathname.includes("onboarding") || location.pathname.includes("summary")) ?
- + Need a 1:1 experience?
: null} + {window.TRIAL_MSG && !(location.pathname.includes("test-editor") || location.pathname.includes("settings") || location.pathname.includes("onboarding") || location.pathname.includes("summary")) ?
+ + {window.TRIAL_MSG} + +
: null} ) diff --git a/libs/dao/src/main/java/com/akto/dto/billing/Organization.java b/libs/dao/src/main/java/com/akto/dto/billing/Organization.java index 0e6f906cbd..59e2403799 100644 --- a/libs/dao/src/main/java/com/akto/dto/billing/Organization.java +++ b/libs/dao/src/main/java/com/akto/dto/billing/Organization.java @@ -39,6 +39,9 @@ public class Organization { public static final String PLAN_TYPE = "planType"; public String planType = "planType"; + public static final String TRIAL_MSG = "trialMsg"; + public String trialMsg = "trialMsg"; + public static final String TEST_TELEMETRY_ENABLED = "testTelemetryEnabled"; private boolean testTelemetryEnabled; private int gracePeriod; @@ -142,6 +145,14 @@ public void setplanType(String planType) { this.planType = planType; } + public String gettrialMsg() { + return trialMsg; + } + + public void settrialMsg(String trialMsg) { + this.trialMsg = trialMsg; + } + public int getLastFeatureMapUpdate() { return lastFeatureMapUpdate; } From e8518d89614f9257e1f53fd48ae090e230af998c Mon Sep 17 00:00:00 2001 From: Ankush Jain Date: Sat, 11 Jan 2025 10:09:51 -0800 Subject: [PATCH 07/39] Add remediation to all tests --- .../action/testing/TestTemplatesAction.java | 54 + .../action/testing_issues/IssuesAction.java | 6 +- .../akto/listener/InitializerListener.java | 89 + apps/dashboard/src/main/resources/struts.xml | 20 + .../web/polaris_web/package-lock.json | 1447 ++++++++++++++++- apps/dashboard/web/polaris_web/package.json | 1 + .../components/shared/MarkdownViewer.jsx | 15 + .../components/editor_config/keywords.js | 2 +- .../TestRunResultPage/TestRunResultFlyout.jsx | 29 +- .../TestRunResultPage/TestRunResultPage.jsx | 5 +- .../pages/testing/TestRunResultPage/style.css | 92 ++ .../src/apps/dashboard/pages/testing/api.js | 8 + libs/dao/src/main/java/com/akto/DaoInit.java | 3 +- .../com/akto/dao/testing/RemediationsDao.java | 22 + .../com/akto/dto/testing/Remediation.java | 78 + 15 files changed, 1789 insertions(+), 82 deletions(-) create mode 100644 apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/shared/MarkdownViewer.jsx create mode 100644 libs/dao/src/main/java/com/akto/dao/testing/RemediationsDao.java create mode 100644 libs/dao/src/main/java/com/akto/dto/testing/Remediation.java diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java new file mode 100644 index 0000000000..c56efdb597 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java @@ -0,0 +1,54 @@ +package com.akto.action.testing; + +import org.apache.commons.lang3.StringUtils; + +import com.akto.action.UserAction; +import com.akto.dao.testing.RemediationsDao; +import com.akto.dto.testing.Remediation; +import com.akto.util.Constants; + +public class TestTemplatesAction extends UserAction { + + public String execute(){ + throw new IllegalStateException("Not implemented"); + } + + String testId; + String remediation; + public String fetchRemediationInfo() { + + if (StringUtils.isEmpty(testId)) { + addActionError("testId is empty"); + return ERROR.toUpperCase(); + } + + Remediation remediationObj = RemediationsDao.instance.findOne(Constants.ID, testId); + + if (remediationObj == null) { + addActionError("remediation missing from db for testId: " + testId); + return ERROR.toUpperCase(); + } + + this.remediation = remediationObj.getRemediationText(); + + return SUCCESS.toUpperCase(); + } + + public String getTestId() { + return this.testId; + } + + public void setTestId(String testId) { + this.testId = testId; + } + + public String getRemediation() { + return this.remediation; + } + + public void setRemediation(String remediation) { + this.remediation = remediation; + } + + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index cf951c1bb5..86e18b5820 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -462,6 +462,11 @@ public static BasicDBObject createSubcategoriesInfoObj(TestConfig testConfig) { infoObj.put("_name", testConfig.getId()); infoObj.put("content", testConfig.getContent()); infoObj.put("templateSource", testConfig.getTemplateSource()); + + + infoObj.put("remediation", testConfig.getAuthor().equalsIgnoreCase(Constants._AKTO)); + + infoObj.put("updatedTs", testConfig.getUpdateTs()); infoObj.put("author", testConfig.getAuthor()); @@ -518,7 +523,6 @@ public String fetchAllSubCategories() { return SUCCESS.toUpperCase(); } - public String updateIssueStatus () { if (issueId == null || statusToBeUpdated == null || ignoreReason == null) { throw new IllegalStateException(); diff --git a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java index 3bfc3eb7b3..fd96532743 100644 --- a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java +++ b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java @@ -3142,6 +3142,13 @@ public void run() { return; } + try { + processRemedationFilesZip(testingTemplates); + } catch (Exception e) { + e.printStackTrace(); + loggerMaker.infoAndAddToDb("Unable to import remediations", LogDb.DASHBOARD); + } + Map allYamlTemplates = TestTemplateUtils.getZipFromMultipleRepoAndBranch(getAktoDefaultTestLibs()); AccountTask.instance.executeTask((account) -> { try { @@ -3174,6 +3181,88 @@ public void run() { }, 0, 4, TimeUnit.HOURS); } + public static void processRemedationFilesZip(byte[] zipFile) { + if (zipFile != null) { + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(zipFile); + ZipInputStream zipInputStream = new ZipInputStream(inputStream)) { + + ZipEntry entry; + List remediations = RemediationsDao.instance.findAll(Filters.empty(), Projections.include(Remediation.TEST_ID, Remediation.HASH)); + Map> mapRemediationIdToHash = remediations.stream().collect(Collectors.groupingBy(Remediation::getid)); + + int countUnchangedRemediations = 0; + int countTotalRemediations = 0; + + while ((entry = zipInputStream.getNextEntry()) != null) { + if (!entry.isDirectory()) { + String entryName = entry.getName(); + + boolean isRemediation = entryName.contains("remediation"); + if (!isRemediation) { + loggerMaker.infoAndAddToDb( + String.format("%s not a remediation file, skipping", entryName), + LogDb.DASHBOARD); + continue; + } + + if (!entryName.endsWith(".md")) { + loggerMaker.infoAndAddToDb( + String.format("%s not a md file, skipping", entryName), + LogDb.DASHBOARD); + continue; + } + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = zipInputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + String templateContent = new String(outputStream.toByteArray(), "UTF-8"); + + List remediationsInDb = mapRemediationIdToHash.get(entryName); + int remediationHashFromFile = templateContent.hashCode(); + + countTotalRemediations++; + if (remediationsInDb != null && remediationsInDb.size() >= 1) { + int remediationHashInDb = remediationsInDb.get(0).getHash(); + + if (remediationHashFromFile == remediationHashInDb) { + countUnchangedRemediations++; + } else { + loggerMaker.infoAndAddToDb("Updating remediation content: " + entryName, LogDb.DASHBOARD); + Bson updates = + Updates.combine( + Updates.set(Remediation.REMEDIATION_TEXT, templateContent), + Updates.set(Remediation.HASH, remediationHashFromFile) + ); + + RemediationsDao.instance.updateOne(Remediation.TEST_ID, Remediation.REMEDIATION_TEXT, updates); + } + } else { + loggerMaker.infoAndAddToDb("Inserting remediation content: " + entryName, LogDb.DASHBOARD); + RemediationsDao.instance.insertOne(new Remediation(entryName, templateContent, remediationHashFromFile)); + } + } + + zipInputStream.closeEntry(); + } + + if (countTotalRemediations != countUnchangedRemediations) { + loggerMaker.infoAndAddToDb(countUnchangedRemediations + "/" + countTotalRemediations + "remediations unchanged", LogDb.DASHBOARD); + } + + } catch (Exception ex) { + cacheLoggerMaker.errorAndAddToDb(ex, + String.format("Error while processing Test template files zip. Error %s", ex.getMessage()), + LogDb.DASHBOARD); + } + } else { + loggerMaker.infoAndAddToDb("Received null zip file"); + } + } + public static void processTemplateFilesZip(byte[] zipFile, String author, String source, String repositoryUrl) { if (zipFile != null) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(zipFile); diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index 21609dc00b..1e90045d1f 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -4026,6 +4026,26 @@
+ + + + + + + 403 + false + ^actionErrors.* + + + remediation + + + 422 + false + ^actionErrors.* + + + diff --git a/apps/dashboard/web/polaris_web/package-lock.json b/apps/dashboard/web/polaris_web/package-lock.json index 1da9833510..b6a7e720ad 100644 --- a/apps/dashboard/web/polaris_web/package-lock.json +++ b/apps/dashboard/web/polaris_web/package-lock.json @@ -37,6 +37,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-flow-renderer": "^10.3.12", + "react-markdown": "^9.0.3", "react-router-dom": "^6.14.0", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.6.1", @@ -5738,6 +5739,15 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/eslint": { "version": "8.44.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", @@ -5761,6 +5771,15 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/express": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", @@ -5896,11 +5915,26 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.5.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", @@ -6387,6 +6421,12 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "license": "ISC" + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -8287,6 +8327,16 @@ "babylon": "bin/babylon.js" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -8627,6 +8677,16 @@ "node": ">=4" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -8666,6 +8726,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", @@ -9856,6 +9926,29 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decode-named-character-reference/node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -10016,6 +10109,19 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -11127,6 +11233,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -11265,6 +11381,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extract-files": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", @@ -12272,6 +12394,115 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/hastscript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", @@ -12464,6 +12695,16 @@ "react": "0.14 || 15 || 16 || 17 || 18" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-webpack-plugin": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", @@ -17399,6 +17640,16 @@ "node": ">=8" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -17513,100 +17764,838 @@ "tmpl": "1.0.5" } }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", "dependencies": { - "fs-monkey": "^1.0.4" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "engines": { - "node": ">= 4.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "node_modules/mdast-util-from-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" + "node_modules/mdast-util-mdx-expression/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, - "engines": { - "node": ">=8.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" + "node_modules/mdast-util-mdx-jsx/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } + "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" + "node_modules/mdast-util-mdx-jsx/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", + "node_modules/mdast-util-mdx-jsx/node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "engines": { @@ -20238,6 +21227,41 @@ "react": ">=16.8.0" } }, + "node_modules/react-markdown": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.3.tgz", + "integrity": "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-markdown/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/react-property": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz", @@ -21117,6 +22141,48 @@ "node": ">= 0.10" } }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -22227,6 +23293,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -22941,6 +24031,26 @@ "node": ">=8" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -23190,6 +24300,43 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -23201,6 +24348,104 @@ "node": ">=8" } }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -23366,6 +24611,46 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/vfile/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -24402,6 +25687,16 @@ "optional": true } } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/apps/dashboard/web/polaris_web/package.json b/apps/dashboard/web/polaris_web/package.json index f1ed97eca9..10b9f7bcd5 100644 --- a/apps/dashboard/web/polaris_web/package.json +++ b/apps/dashboard/web/polaris_web/package.json @@ -32,6 +32,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-flow-renderer": "^10.3.12", + "react-markdown": "^9.0.3", "react-router-dom": "^6.14.0", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.6.1", diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/shared/MarkdownViewer.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/shared/MarkdownViewer.jsx new file mode 100644 index 0000000000..20d2b7679f --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/shared/MarkdownViewer.jsx @@ -0,0 +1,15 @@ +import Markdown from 'react-markdown' +import {Box} from '@shopify/polaris' + + +const MarkdownViewer = ({markdown}) => { + + return ( + + {markdown} + + ) +} + + +export default MarkdownViewer \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/test_editor/components/editor_config/keywords.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/test_editor/components/editor_config/keywords.js index e81a894445..e226070b68 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/test_editor/components/editor_config/keywords.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/test_editor/components/editor_config/keywords.js @@ -1,7 +1,7 @@ const keywords = [ "id", "info", - "name", "description", "details", "impact", "category", "shortName", "displayName", "subCategory", "severity", "tags", "references", + "name", "description", "details", "impact", "remediation", "attributes", "category", "shortName", "displayName", "subCategory", "severity", "tags", "references", "response_code", "method", "url", "request_payload", "response_payload", "request_headers", "response_headers", "query_param", "api_collection_id", "regex", "eq", "neq", "gt", "gte", "lt", "lte", "key", "value", "requests", "req", "res", diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx index 891c962c7f..485e78f430 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx @@ -8,6 +8,7 @@ import LayoutWithTabs from '../../../components/layouts/LayoutWithTabs' import { Badge, Box, Button, Divider, HorizontalStack, Icon, Popover, Text, VerticalStack, Link, Modal } from '@shopify/polaris' import api from '../../observe/api' import issuesApi from "../../issues/api" +import testingApi from "../api" import GridRows from '../../../components/shared/GridRows' import { useNavigate } from 'react-router-dom' import TitleWithInfo from '@/apps/dashboard/components/shared/TitleWithInfo' @@ -16,11 +17,13 @@ import ActivityTracker from '../../dashboard/components/ActivityTracker' import observeFunc from "../../observe/transform.js" import settingFunctions from '../../settings/module.js' import JiraTicketCreationModal from '../../../components/shared/JiraTicketCreationModal.jsx' +import MarkdownViewer from '../../../components/shared/MarkdownViewer.jsx' function TestRunResultFlyout(props) { - const { selectedTestRunResult, loading, issueDetails ,getDescriptionText, infoState, createJiraTicket, jiraIssueUrl, showDetails, setShowDetails, isIssuePage} = props + const { selectedTestRunResult, loading, issueDetails ,getDescriptionText, infoState, createJiraTicket, jiraIssueUrl, showDetails, setShowDetails, isIssuePage, remediationSrc} = props + const [remediationText, setRemediationText] = useState("") const [fullDescription, setFullDescription] = useState(false) const [rowItems, setRowItems] = useState([]) const [popoverActive, setPopoverActive] = useState(false) @@ -30,6 +33,16 @@ function TestRunResultFlyout(props) { const [projId, setProjId] = useState('') // modify testing run result and headers const infoStateFlyout = infoState && infoState.length > 0 ? infoState.filter((item) => item.title !== 'Jira') : [] + const fetchRemediationInfo = useCallback (async (testId) => { + if (testId && testId.length > 0) { + await testingApi.fetchRemediationInfo(testId).then((resp) => { + setRemediationText(resp) + }).catch((err) => { + setRemediationText("Remediations not configured for this test.") + }) + } + }) + const fetchApiInfo = useCallback( async(apiInfoKey) => { let apiInfo = {} if(apiInfoKey !== null){ @@ -64,6 +77,12 @@ function TestRunResultFlyout(props) { } },[issueDetails?.id?.apiInfoKey]) + useEffect(() => { + if (remediationSrc) { + fetchRemediationInfo("tests-library-master/remediation/"+selectedTestRunResult.testCategoryId+".md") + } + }, [selectedTestRunResult.testCategoryId, remediationSrc]) + function ignoreAction(ignoreReason){ const severity = (selectedTestRunResult && selectedTestRunResult.vulnerable) ? issueDetails.severity : ""; let obj = {} @@ -343,6 +362,12 @@ function TestRunResultFlyout(props) { component: } + const remediationTab = (selectedTestRunResult && selectedTestRunResult.vulnerable) && { + id: "remediation", + content: "Remediation", + component: () + } + const errorTab = { id: "error", content: "Attempt", @@ -368,7 +393,7 @@ function TestRunResultFlyout(props) { const tabsComponent = ( {}} /> ) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultPage.jsx index 0c45df452b..a4a2dee3d0 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultPage.jsx @@ -71,6 +71,7 @@ function TestRunResultPage(props) { const [infoState, setInfoState] = useState([]) const [loading, setLoading] = useState(true); const [showDetails, setShowDetails] = useState(true) + const [remediation, setRemediation] = useState("") const hostNameMap = PersistStore(state => state.hostNameMap) const useFlyout = location.pathname.includes("test-editor") ? false : true @@ -209,6 +210,7 @@ function TestRunResultPage(props) { const moreInfoSections = transform.getInfoSectionsHeaders() setJiraIssueUrl(jiraIssueCopy) setInfoState(transform.fillMoreInformation(subCategoryMap[runIssues?.id?.testSubCategory],moreInfoSections, runIssuesArr, jiraIssueCopy, onClickButton)) + setRemediation(subCategoryMap[runIssues?.id?.testSubCategory]?.remediation) // setJiraIssueUrl(jiraIssueUrl) // setInfoState(transform.fillMoreInformation(subCategoryMap[runIssues?.id?.testSubCategory],moreInfoSections, runIssuesArr)) } else { @@ -224,7 +226,8 @@ function TestRunResultPage(props) { useFlyout ? <> sensitiveDataEndpointsClassModel = ClassModel.builder(SensitiveDataEndpoints.class).enableDiscriminator(true).build(); ClassModel allApisGroupClassModel = ClassModel.builder(AllAPIsGroup.class).enableDiscriminator(true).build(); + ClassModel remediationClassModel = ClassModel.builder(Remediation.class).enableDiscriminator(true).build(); ClassModel RuntimeMetricsClassModel = ClassModel.builder(RuntimeMetrics.class).enableDiscriminator(true).build(); ClassModel codeAnalysisApiModel = ClassModel.builder(CodeAnalysisApi.class).enableDiscriminator(true).build(); ClassModel codeAnalysisRepoModel = ClassModel.builder(CodeAnalysisRepo.class).enableDiscriminator(true).build(); @@ -299,7 +300,7 @@ public static CodecRegistry createCodecRegistry(){ nodeClassModel, connectionClassModel, edgeClassModel, replaceDetailClassModel, modifyHostDetailClassModel, fileUploadClassModel ,fileUploadLogClassModel, codeAnalysisCollectionClassModel, codeAnalysisApiLocationClassModel, codeAnalysisApiInfoClassModel, codeAnalysisApiInfoKeyClassModel, riskScoreTestingEndpointsClassModel, OrganizationFlagsClassModel, sensitiveDataEndpointsClassModel, unauthenticatedEndpointsClassModel, allApisGroupClassModel, - eventsExampleClassModel, RuntimeMetricsClassModel, codeAnalysisRepoModel, codeAnalysisApiModel, historicalDataClassModel, configSettingClassModel, configSettingsConditionTypeClassModel).automatic(true).build()); + eventsExampleClassModel, remediationClassModel, RuntimeMetricsClassModel, codeAnalysisRepoModel, codeAnalysisApiModel, historicalDataClassModel, configSettingClassModel, configSettingsConditionTypeClassModel).automatic(true).build()); final CodecRegistry customEnumCodecs = CodecRegistries.fromCodecs( new EnumCodec<>(Conditions.Operator.class), diff --git a/libs/dao/src/main/java/com/akto/dao/testing/RemediationsDao.java b/libs/dao/src/main/java/com/akto/dao/testing/RemediationsDao.java new file mode 100644 index 0000000000..b37adb2360 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/RemediationsDao.java @@ -0,0 +1,22 @@ +package com.akto.dao.testing; + +import com.akto.dao.CommonContextDao; +import com.akto.dto.testing.Remediation; + +public class RemediationsDao extends CommonContextDao { + + public static final RemediationsDao instance = new RemediationsDao(); + + private RemediationsDao() {} + + @Override + public String getCollName() { + return "remediations"; + } + + @Override + public Class getClassT() { + return Remediation.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/Remediation.java b/libs/dao/src/main/java/com/akto/dto/testing/Remediation.java new file mode 100644 index 0000000000..29a10c962b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/Remediation.java @@ -0,0 +1,78 @@ +package com.akto.dto.testing; + +import org.bson.codecs.pojo.annotations.BsonId; +import java.util.Objects; + +public class Remediation { + + public static final String TEST_ID = "id"; + @BsonId + public String id; + + public static final String REMEDIATION_TEXT = "remediationText"; + public String remediationText; + + public static final String HASH = "hash"; + public int hash; + + + public Remediation() { + } + + public Remediation(String id, String remediationText, int hash) { + this.id = id; + this.remediationText = remediationText; + this.hash = hash; + } + + public String getid() { + return this.id; + } + + public void setid(String id) { + this.id = id; + } + + public String getRemediationText() { + return this.remediationText; + } + + public void setRemediationText(String remediationText) { + this.remediationText = remediationText; + } + + public int getHash() { + return this.hash; + } + + public void setHash(int hash) { + this.hash = hash; + } + + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Remediation)) { + return false; + } + Remediation remediation = (Remediation) o; + return Objects.equals(id, remediation.id) && Objects.equals(remediationText, remediation.remediationText) && hash == remediation.hash; + } + + @Override + public int hashCode() { + return Objects.hash(id, remediationText, hash); + } + + @Override + public String toString() { + return "{" + + " id='" + getid() + "'" + + ", remediationText='" + getRemediationText() + "'" + + ", hash='" + getHash() + "'" + + "}"; + } + +} From 617868418238ab1a64bd5de3f461f90cc0e08f4b Mon Sep 17 00:00:00 2001 From: Ankush Jain Date: Sat, 11 Jan 2025 10:55:27 -0800 Subject: [PATCH 08/39] read remediation from template --- .../java/com/akto/action/testing/TestTemplatesAction.java | 7 +++---- .../java/com/akto/action/testing_issues/IssuesAction.java | 8 ++++++-- .../testing/TestRunResultPage/TestRunResultFlyout.jsx | 4 +++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java index c56efdb597..abd0cec280 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/TestTemplatesAction.java @@ -25,11 +25,10 @@ public String fetchRemediationInfo() { Remediation remediationObj = RemediationsDao.instance.findOne(Constants.ID, testId); if (remediationObj == null) { - addActionError("remediation missing from db for testId: " + testId); - return ERROR.toUpperCase(); + this.remediation = ""; + } else { + this.remediation = remediationObj.getRemediationText(); } - - this.remediation = remediationObj.getRemediationText(); return SUCCESS.toUpperCase(); } diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index 86e18b5820..2bbb845192 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -43,6 +43,7 @@ import com.mongodb.client.result.InsertOneResult; import com.opensymphony.xwork2.Action; +import org.apache.commons.lang3.StringUtils; import org.bson.Document; import org.bson.conversions.Bson; import org.bson.types.ObjectId; @@ -463,8 +464,11 @@ public static BasicDBObject createSubcategoriesInfoObj(TestConfig testConfig) { infoObj.put("content", testConfig.getContent()); infoObj.put("templateSource", testConfig.getTemplateSource()); - - infoObj.put("remediation", testConfig.getAuthor().equalsIgnoreCase(Constants._AKTO)); + String remediationContent = info.getRemediation(); + + if (!StringUtils.isEmpty(remediationContent)) { + infoObj.put("remediation", remediationContent); + } infoObj.put("updatedTs", testConfig.getUpdateTs()); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx index 485e78f430..e39f1701ed 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/TestRunResultPage/TestRunResultFlyout.jsx @@ -78,8 +78,10 @@ function TestRunResultFlyout(props) { },[issueDetails?.id?.apiInfoKey]) useEffect(() => { - if (remediationSrc) { + if (!remediationSrc) { fetchRemediationInfo("tests-library-master/remediation/"+selectedTestRunResult.testCategoryId+".md") + } else { + setRemediationText(remediationSrc) } }, [selectedTestRunResult.testCategoryId, remediationSrc]) From b3d703d033ec5b3418945f3b512231649bbd8b6c Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 11:23:31 +0530 Subject: [PATCH 09/39] Fixing null pointer exception --- apps/dashboard/src/main/java/com/akto/action/TeamAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/TeamAction.java b/apps/dashboard/src/main/java/com/akto/action/TeamAction.java index 11d2e93d1f..b57826b7f1 100644 --- a/apps/dashboard/src/main/java/com/akto/action/TeamAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/TeamAction.java @@ -63,7 +63,7 @@ public String fetchTeamData() { for(Object obj: users) { BasicDBObject userObj = (BasicDBObject) obj; RBAC rbac = userToRBAC.get(userObj.getInt("id")); - String status = rbac == null ? Role.MEMBER.getName() : rbac.getRole().getName(); + String status = (rbac == null || rbac.getRole() == null) ? Role.MEMBER.getName() : rbac.getRole().getName(); userObj.append("role", status); try { String login = userObj.getString(User.LOGIN); From be7154c51d22f201fa5e56822ee701e568abc3d0 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 11:34:50 +0530 Subject: [PATCH 10/39] Added check in ui --- .../dashboard/pages/settings/users/Users.jsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx index adc3d73268..1ff480eac8 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx @@ -160,11 +160,16 @@ const Users = () => { } // Call Update Role API - setUsers(users.map(user => user.login === login ? { ...user, role: newRole } : user)) - setRoleSelectionPopup(prevState => ({ ...prevState, [login]: false })) - await updateUserRole(login, newRole) - - toggleRoleSelectionPopup(id) + await updateUserRole(login, newRole).then((res) => { + try { + setUsers(users.map(user => user.login === login ? { ...user, role: newRole } : user)) + setRoleSelectionPopup(prevState => ({ ...prevState, [login]: false })) + toggleRoleSelectionPopup(id) + func.setToast(true, false, "Updated user role successfully") + } catch (error) { + } + }) + } const toggleRoleSelectionPopup = (id) => { @@ -238,7 +243,7 @@ const Users = () => { primaryAction={{ content: 'Invite user', onAction: () => toggleInviteUserModal(), - 'disabled': (isLocalDeploy || userRole === 'GUEST') + // 'disabled': (isLocalDeploy || userRole === 'GUEST') }} divider > From 0190492b57b34918734a025abda1811d7c9cd8ae Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 11:39:41 +0530 Subject: [PATCH 11/39] Changing invite user button for roles --- .../web/src/apps/dashboard/pages/settings/users/Users.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx index 1ff480eac8..8cfb0b4d5f 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/users/Users.jsx @@ -243,7 +243,7 @@ const Users = () => { primaryAction={{ content: 'Invite user', onAction: () => toggleInviteUserModal(), - // 'disabled': (isLocalDeploy || userRole === 'GUEST') + 'disabled': (isLocalDeploy || userRole === 'GUEST' || userRole === 'DEVELOPER') }} divider > From c32e4896960bd7bdb02a38c680faa5aca4e2c212 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:02:45 +0530 Subject: [PATCH 12/39] feat: storing vul report pdf in db --- .../java/com/akto/action/ReportAction.java | 21 +++++++++ .../VulnerabilityReport.jsx | 26 ++++++++--- libs/dao/src/main/java/com/akto/DaoInit.java | 1 + .../akto/dao/VulnerabilityReportPDFDao.java | 42 +++++++++++++++++ .../com/akto/dto/VulnerabilityReportPDF.java | 45 +++++++++++++++++++ 5 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java create mode 100644 libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java diff --git a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java index 87617004fb..a97748c500 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java @@ -10,6 +10,9 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import com.akto.dao.VulnerabilityReportPDFDao; +import com.akto.dto.VulnerabilityReportPDF; +import com.mongodb.client.model.Filters; import org.apache.struts2.ServletActionContext; import org.bson.types.ObjectId; import org.json.JSONObject; @@ -36,6 +39,19 @@ public class ReportAction extends UserAction { private static final LoggerMaker loggerMaker = new LoggerMaker(ReportAction.class); public String downloadReportPDF() { + if(reportUrl == null || reportUrl.isEmpty()) { + status = "ERROR"; + addActionError("Report URL cannot be empty"); + return ERROR.toUpperCase(); + } + + VulnerabilityReportPDF vulnerabilityReportPDF = VulnerabilityReportPDFDao.instance.findOne(Filters.eq(VulnerabilityReportPDF.VULNERABILITY_REPORT_URL, reportUrl)); + if(vulnerabilityReportPDF != null && (vulnerabilityReportPDF.getVulnerabilityReportPDFBinary() != null || !vulnerabilityReportPDF.getVulnerabilityReportPDFBinary().isEmpty())) { + status = "COMPLETED"; + pdf = vulnerabilityReportPDF.getVulnerabilityReportPDFBinary(); + return SUCCESS.toUpperCase(); + } + if (reportId == null) { // Initiate PDF generation @@ -89,6 +105,11 @@ public String downloadReportPDF() { if (status.equals("COMPLETED")) { loggerMaker.infoAndAddToDb("Pdf download status for report id - " + reportId + " completed. Attaching pdf in response ", LogDb.DASHBOARD); pdf = node.get("base64PDF").textValue(); + VulnerabilityReportPDFDao.instance.insertOne(new VulnerabilityReportPDF( + reportUrl, + pdf, + Context.now() + )); } } catch (Exception e) { loggerMaker.errorAndAddToDb(e, "Error while polling pdf download for report id - " + reportId, LogDb.DASHBOARD); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index 13b04d2dfc..b9eac54183 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -1,5 +1,5 @@ /* eslint-disable no-loop-func */ -import { Box, Divider, Frame, HorizontalStack, Text, TopBar, VerticalStack } from '@shopify/polaris' +import { Box, Button, Divider, Frame, HorizontalStack, Text, TopBar, VerticalStack } from '@shopify/polaris' import React, { useEffect, useRef } from 'react' import api from '../api' import issuesApi from '@/apps/dashboard/pages/issues/api' @@ -233,7 +233,7 @@ const VulnerabilityReport = () => { const handleDownloadPF = async () => { - const WAIT_DURATION = 2500, MAX_RETRIES = 15 + const WAIT_DURATION = 5000, MAX_RETRIES = 60 const reportUrl = window.location.href let pdfError = "" @@ -242,15 +242,23 @@ const VulnerabilityReport = () => { setPdfDownloadEnabled(false) - const progressToastInterval = setInterval(() => { - func.setToast(true, false, "Report PDF generation in progress. Please wait...") + let reportToastInterval = setInterval(() => { + func.setToast(true, false, "Preparing your report. This might take a moment...") }, 1000) + setTimeout(() => { + clearInterval(reportToastInterval) + reportToastInterval = setInterval(() => { + func.setToast(true, false, "Report PDF generation in progress. Please wait...") + }, 1000) + }, 6000) + try { // Trigger pdf download const startDownloadReponse = await api.downloadReportPDF(null, organizationName, currentDate, reportUrl) const reportId = startDownloadReponse?.reportId status = startDownloadReponse?.status + pdf = startDownloadReponse?.pdf if (reportId !== null && status === "IN_PROGRESS") { // Poll for PDF completion @@ -271,13 +279,15 @@ const VulnerabilityReport = () => { func.setToast(true, false, "Report PDF generation in progress. Please wait...") } } else { - pdfError = "Failed to start PDF download" + if(status !== "COMPLETED") { + pdfError = "Failed to start PDF download" + } } } catch (err) { pdfError = err.message } - clearInterval(progressToastInterval) + clearInterval(reportToastInterval) if (status === "COMPLETED") { if (pdf === undefined) { @@ -321,7 +331,9 @@ const VulnerabilityReport = () => { {currentDate}
- {/* */} + { + window.USER_NAME?.toLowerCase()?.includes("ababank") ? : <> + } Logo
diff --git a/libs/dao/src/main/java/com/akto/DaoInit.java b/libs/dao/src/main/java/com/akto/DaoInit.java index 7e4ea5a2fe..08d97119ce 100644 --- a/libs/dao/src/main/java/com/akto/DaoInit.java +++ b/libs/dao/src/main/java/com/akto/DaoInit.java @@ -409,6 +409,7 @@ public static void createIndices() { TrafficAlertsDao.instance.createIndicesIfAbsent(); RuntimeMetricsDao.instance.createIndicesIfAbsent(); ApiAuditLogsDao.instance.createIndicesIfAbsent(); + VulnerabilityReportPDFDao.instance.createIndicesIfAbsent(); } } diff --git a/libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java b/libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java new file mode 100644 index 0000000000..25558d10e5 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java @@ -0,0 +1,42 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.VulnerabilityReportPDF; +import com.mongodb.client.MongoDatabase; + +public class VulnerabilityReportPDFDao extends AccountsContextDao { + + public static final VulnerabilityReportPDFDao instance = new VulnerabilityReportPDFDao(); + + public VulnerabilityReportPDFDao() {} + + @Override + public String getCollName() { + return "vulnerability_report_pdf"; + } + + @Override + public Class getClassT() { + return VulnerabilityReportPDF.class; + } + + + public void createIndicesIfAbsent() { + boolean exists = false; + String dbName = Context.accountId.get()+""; + MongoDatabase db = clients[0].getDatabase(dbName); + for (String col: db.listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + } + + if (!exists) { + db.createCollection(getCollName()); + } + + MCollection.createIndexIfAbsent(getDBName(), getCollName(), new String[] { VulnerabilityReportPDF.VULNERABILITY_REPORT_URL }, false); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java b/libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java new file mode 100644 index 0000000000..b638beb6f0 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java @@ -0,0 +1,45 @@ +package com.akto.dto; + +public class VulnerabilityReportPDF { + + public static final String VULNERABILITY_REPORT_URL = "vulnerabilityReportUrl"; + private String vulnerabilityReportUrl; + + public static final String VULNERABILITY_REPORT_PDF_BINARY = "vulnerabilityReportPDFBinary"; + private String vulnerabilityReportPDFBinary; + + public static final String LAST_UPDATE_TIME_STAMP = "lastUpdateTimeStamp"; + private int lastUpdateTimestamp; + + public VulnerabilityReportPDF() {} + + public VulnerabilityReportPDF(String vulnerabilityReportUrl, String vulnerabilityReportPDFBinary, int lastUpdateTimestamp) { + this.vulnerabilityReportUrl = vulnerabilityReportUrl; + this.vulnerabilityReportPDFBinary = vulnerabilityReportPDFBinary; + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public String getVulnerabilityReportUrl() { + return vulnerabilityReportUrl; + } + + public void setVulnerabilityReportUrl(String vulnerabilityReportUrl) { + this.vulnerabilityReportUrl = vulnerabilityReportUrl; + } + + public String getVulnerabilityReportPDFBinary() { + return vulnerabilityReportPDFBinary; + } + + public void setVulnerabilityReportPDFBinary(String vulnerabilityReportPDFBinary) { + this.vulnerabilityReportPDFBinary = vulnerabilityReportPDFBinary; + } + + public int getLastUpdateTimestamp() { + return lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(int lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } +} From 84dbc353b375602f7f793c8c9facbdebfa341023 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:14:13 +0530 Subject: [PATCH 13/39] fix: update environment variable for report PDF download URL --- .../dashboard/src/main/java/com/akto/action/ReportAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java index a97748c500..a894486ec3 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java @@ -73,7 +73,7 @@ public String downloadReportPDF() { session.setAttribute("login", Context.now()); } - String url = System.getenv("PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; + String url = System.getenv("REPORT_PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; JSONObject requestBody = new JSONObject(); requestBody.put("reportId", reportId); requestBody.put("username", user.getName()); @@ -94,7 +94,7 @@ public String downloadReportPDF() { loggerMaker.infoAndAddToDb("Polling pdf download status for report id - " + reportId, LogDb.DASHBOARD); try { - String url = System.getenv("PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; + String url = System.getenv("REPORT_PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; JSONObject requestBody = new JSONObject(); requestBody.put("reportId", reportId); String reqData = requestBody.toString(); From 20897dbdbd297d61c685b9f3e184eec59371494f Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:17:59 +0530 Subject: [PATCH 14/39] feat: allow download button for additional user account --- .../pages/testing/vulnerability_report/VulnerabilityReport.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index b9eac54183..ddb9e7c758 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -332,7 +332,7 @@ const VulnerabilityReport = () => {
{ - window.USER_NAME?.toLowerCase()?.includes("ababank") ? : <> + (window.USER_NAME?.toLowerCase()?.includes("ababank") || window.ACCOUNT_NAME?.toLowerCase()?.includes("advanced bank")) ? : <> } Logo
From 71ca510a014786a638313494f597e47ecaa41e98 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:48:35 +0530 Subject: [PATCH 15/39] feat: add firstPollRequest parameter for report PDF download --- .../main/java/com/akto/action/ReportAction.java | 17 ++++++++++++----- .../web/src/apps/dashboard/pages/testing/api.js | 4 ++-- .../VulnerabilityReport.jsx | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java index a894486ec3..d097268fb7 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java @@ -35,6 +35,7 @@ public class ReportAction extends UserAction { private String reportUrl; private String pdf; private String status; + private boolean firstPollRequest; private static final LoggerMaker loggerMaker = new LoggerMaker(ReportAction.class); @@ -45,11 +46,13 @@ public String downloadReportPDF() { return ERROR.toUpperCase(); } - VulnerabilityReportPDF vulnerabilityReportPDF = VulnerabilityReportPDFDao.instance.findOne(Filters.eq(VulnerabilityReportPDF.VULNERABILITY_REPORT_URL, reportUrl)); - if(vulnerabilityReportPDF != null && (vulnerabilityReportPDF.getVulnerabilityReportPDFBinary() != null || !vulnerabilityReportPDF.getVulnerabilityReportPDFBinary().isEmpty())) { - status = "COMPLETED"; - pdf = vulnerabilityReportPDF.getVulnerabilityReportPDFBinary(); - return SUCCESS.toUpperCase(); + if(firstPollRequest) { + VulnerabilityReportPDF vulnerabilityReportPDF = VulnerabilityReportPDFDao.instance.findOne(Filters.eq(VulnerabilityReportPDF.VULNERABILITY_REPORT_URL, reportUrl)); + if(vulnerabilityReportPDF != null && (vulnerabilityReportPDF.getVulnerabilityReportPDFBinary() != null || !vulnerabilityReportPDF.getVulnerabilityReportPDFBinary().isEmpty())) { + status = "COMPLETED"; + pdf = vulnerabilityReportPDF.getVulnerabilityReportPDFBinary(); + return SUCCESS.toUpperCase(); + } } if (reportId == null) { @@ -167,4 +170,8 @@ public String getStatus() { public void setStatus(String status) { this.status = status; } + + public void setFirstPollRequest(boolean firstPollRequest) { + this.firstPollRequest = firstPollRequest; + } } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js index 5d9c8b82f4..4e9053a3a4 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js @@ -425,11 +425,11 @@ export default { data: {} }) }, - downloadReportPDF(reportId, organizationName, reportDate, reportUrl) { + downloadReportPDF(reportId, organizationName, reportDate, reportUrl, firstPollRequest) { return request({ url: '/api/downloadReportPDF', method: 'post', - data: {reportId, organizationName, reportDate, reportUrl} + data: {reportId, organizationName, reportDate, reportUrl, firstPollRequest} }) }, fetchScript() { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index ddb9e7c758..35fed5f482 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -255,7 +255,7 @@ const VulnerabilityReport = () => { try { // Trigger pdf download - const startDownloadReponse = await api.downloadReportPDF(null, organizationName, currentDate, reportUrl) + const startDownloadReponse = await api.downloadReportPDF(null, organizationName, currentDate, reportUrl, true) const reportId = startDownloadReponse?.reportId status = startDownloadReponse?.status pdf = startDownloadReponse?.pdf @@ -263,7 +263,7 @@ const VulnerabilityReport = () => { if (reportId !== null && status === "IN_PROGRESS") { // Poll for PDF completion for(let i = 0; i < MAX_RETRIES; i++) { - const pdfPollResponse = await api.downloadReportPDF(reportId, organizationName, currentDate, reportUrl) + const pdfPollResponse = await api.downloadReportPDF(reportId, organizationName, currentDate, reportUrl, false) status = pdfPollResponse?.status if (status === "COMPLETED") { From 53b04344812a8d7875f865439d7d1adc3e35a7e6 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:03:22 +0530 Subject: [PATCH 16/39] refactor: replace VulnerabilityReportPDF with TestReports for PDF handling and remove unused DAO --- .../java/com/akto/action/ReportAction.java | 27 ++++++----- libs/dao/src/main/java/com/akto/DaoInit.java | 1 - .../akto/dao/VulnerabilityReportPDFDao.java | 42 ----------------- .../com/akto/dto/VulnerabilityReportPDF.java | 45 ------------------- 4 files changed, 17 insertions(+), 98 deletions(-) delete mode 100644 libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java delete mode 100644 libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java diff --git a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java index d097268fb7..bda2c52dca 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java @@ -1,5 +1,6 @@ package com.akto.action; +import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; @@ -10,9 +11,10 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import com.akto.dao.VulnerabilityReportPDFDao; -import com.akto.dto.VulnerabilityReportPDF; +import com.akto.dao.testing.sources.TestReportsDao; +import com.akto.dto.testing.sources.TestReports; import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; import org.apache.struts2.ServletActionContext; import org.bson.types.ObjectId; import org.json.JSONObject; @@ -46,11 +48,20 @@ public String downloadReportPDF() { return ERROR.toUpperCase(); } + String reportUrlId; + try { + String path = new URL(reportUrl).getPath(); + String[] segments = path.split("/"); + reportUrlId = segments[segments.length - 1]; + } catch (Exception e) { + reportUrlId = ""; + } + if(firstPollRequest) { - VulnerabilityReportPDF vulnerabilityReportPDF = VulnerabilityReportPDFDao.instance.findOne(Filters.eq(VulnerabilityReportPDF.VULNERABILITY_REPORT_URL, reportUrl)); - if(vulnerabilityReportPDF != null && (vulnerabilityReportPDF.getVulnerabilityReportPDFBinary() != null || !vulnerabilityReportPDF.getVulnerabilityReportPDFBinary().isEmpty())) { + TestReports testReport = TestReportsDao.instance.findOne(Filters.eq("_id", new ObjectId(reportUrlId))); + if(testReport != null && (testReport.getPdfReportString() != null && !testReport.getPdfReportString().isEmpty())) { status = "COMPLETED"; - pdf = vulnerabilityReportPDF.getVulnerabilityReportPDFBinary(); + pdf = testReport.getPdfReportString(); return SUCCESS.toUpperCase(); } } @@ -108,11 +119,7 @@ public String downloadReportPDF() { if (status.equals("COMPLETED")) { loggerMaker.infoAndAddToDb("Pdf download status for report id - " + reportId + " completed. Attaching pdf in response ", LogDb.DASHBOARD); pdf = node.get("base64PDF").textValue(); - VulnerabilityReportPDFDao.instance.insertOne(new VulnerabilityReportPDF( - reportUrl, - pdf, - Context.now() - )); + TestReportsDao.instance.updateOne(Filters.eq("_id", new ObjectId(reportUrlId)), Updates.set(TestReports.PDF_REPORT_STRING, pdf)); } } catch (Exception e) { loggerMaker.errorAndAddToDb(e, "Error while polling pdf download for report id - " + reportId, LogDb.DASHBOARD); diff --git a/libs/dao/src/main/java/com/akto/DaoInit.java b/libs/dao/src/main/java/com/akto/DaoInit.java index 08d97119ce..7e4ea5a2fe 100644 --- a/libs/dao/src/main/java/com/akto/DaoInit.java +++ b/libs/dao/src/main/java/com/akto/DaoInit.java @@ -409,7 +409,6 @@ public static void createIndices() { TrafficAlertsDao.instance.createIndicesIfAbsent(); RuntimeMetricsDao.instance.createIndicesIfAbsent(); ApiAuditLogsDao.instance.createIndicesIfAbsent(); - VulnerabilityReportPDFDao.instance.createIndicesIfAbsent(); } } diff --git a/libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java b/libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java deleted file mode 100644 index 25558d10e5..0000000000 --- a/libs/dao/src/main/java/com/akto/dao/VulnerabilityReportPDFDao.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.akto.dao; - -import com.akto.dao.context.Context; -import com.akto.dto.VulnerabilityReportPDF; -import com.mongodb.client.MongoDatabase; - -public class VulnerabilityReportPDFDao extends AccountsContextDao { - - public static final VulnerabilityReportPDFDao instance = new VulnerabilityReportPDFDao(); - - public VulnerabilityReportPDFDao() {} - - @Override - public String getCollName() { - return "vulnerability_report_pdf"; - } - - @Override - public Class getClassT() { - return VulnerabilityReportPDF.class; - } - - - public void createIndicesIfAbsent() { - boolean exists = false; - String dbName = Context.accountId.get()+""; - MongoDatabase db = clients[0].getDatabase(dbName); - for (String col: db.listCollectionNames()){ - if (getCollName().equalsIgnoreCase(col)){ - exists = true; - break; - } - } - - if (!exists) { - db.createCollection(getCollName()); - } - - MCollection.createIndexIfAbsent(getDBName(), getCollName(), new String[] { VulnerabilityReportPDF.VULNERABILITY_REPORT_URL }, false); - } - -} diff --git a/libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java b/libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java deleted file mode 100644 index b638beb6f0..0000000000 --- a/libs/dao/src/main/java/com/akto/dto/VulnerabilityReportPDF.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.akto.dto; - -public class VulnerabilityReportPDF { - - public static final String VULNERABILITY_REPORT_URL = "vulnerabilityReportUrl"; - private String vulnerabilityReportUrl; - - public static final String VULNERABILITY_REPORT_PDF_BINARY = "vulnerabilityReportPDFBinary"; - private String vulnerabilityReportPDFBinary; - - public static final String LAST_UPDATE_TIME_STAMP = "lastUpdateTimeStamp"; - private int lastUpdateTimestamp; - - public VulnerabilityReportPDF() {} - - public VulnerabilityReportPDF(String vulnerabilityReportUrl, String vulnerabilityReportPDFBinary, int lastUpdateTimestamp) { - this.vulnerabilityReportUrl = vulnerabilityReportUrl; - this.vulnerabilityReportPDFBinary = vulnerabilityReportPDFBinary; - this.lastUpdateTimestamp = lastUpdateTimestamp; - } - - public String getVulnerabilityReportUrl() { - return vulnerabilityReportUrl; - } - - public void setVulnerabilityReportUrl(String vulnerabilityReportUrl) { - this.vulnerabilityReportUrl = vulnerabilityReportUrl; - } - - public String getVulnerabilityReportPDFBinary() { - return vulnerabilityReportPDFBinary; - } - - public void setVulnerabilityReportPDFBinary(String vulnerabilityReportPDFBinary) { - this.vulnerabilityReportPDFBinary = vulnerabilityReportPDFBinary; - } - - public int getLastUpdateTimestamp() { - return lastUpdateTimestamp; - } - - public void setLastUpdateTimestamp(int lastUpdateTimestamp) { - this.lastUpdateTimestamp = lastUpdateTimestamp; - } -} From 015cffb97fbf84260b657aad3d0216dcc04a225f Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:32:11 +0530 Subject: [PATCH 17/39] fix: validate report URL ID and update environment variable for PDF download service --- .../java/com/akto/action/ReportAction.java | 20 ++++++++++++++----- .../VulnerabilityReport.jsx | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java index bda2c52dca..5bbd28825e 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java @@ -54,11 +54,21 @@ public String downloadReportPDF() { String[] segments = path.split("/"); reportUrlId = segments[segments.length - 1]; } catch (Exception e) { - reportUrlId = ""; + status = "ERROR"; + addActionError("Report URL cannot be empty"); + return ERROR.toUpperCase(); + } + + if(!ObjectId.isValid(reportUrlId)) { + status = "ERROR"; + addActionError("Report URL is invalid"); + return ERROR.toUpperCase(); } + ObjectId reportUrlIdObj = new ObjectId(reportUrlId); + if(firstPollRequest) { - TestReports testReport = TestReportsDao.instance.findOne(Filters.eq("_id", new ObjectId(reportUrlId))); + TestReports testReport = TestReportsDao.instance.findOne(Filters.eq("_id", reportUrlIdObj)); if(testReport != null && (testReport.getPdfReportString() != null && !testReport.getPdfReportString().isEmpty())) { status = "COMPLETED"; pdf = testReport.getPdfReportString(); @@ -87,7 +97,7 @@ public String downloadReportPDF() { session.setAttribute("login", Context.now()); } - String url = System.getenv("REPORT_PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; + String url = System.getenv("PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; JSONObject requestBody = new JSONObject(); requestBody.put("reportId", reportId); requestBody.put("username", user.getName()); @@ -108,7 +118,7 @@ public String downloadReportPDF() { loggerMaker.infoAndAddToDb("Polling pdf download status for report id - " + reportId, LogDb.DASHBOARD); try { - String url = System.getenv("REPORT_PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; + String url = System.getenv("PUPPETEER_REPLAY_SERVICE_URL") + "/downloadReportPDF"; JSONObject requestBody = new JSONObject(); requestBody.put("reportId", reportId); String reqData = requestBody.toString(); @@ -119,7 +129,7 @@ public String downloadReportPDF() { if (status.equals("COMPLETED")) { loggerMaker.infoAndAddToDb("Pdf download status for report id - " + reportId + " completed. Attaching pdf in response ", LogDb.DASHBOARD); pdf = node.get("base64PDF").textValue(); - TestReportsDao.instance.updateOne(Filters.eq("_id", new ObjectId(reportUrlId)), Updates.set(TestReports.PDF_REPORT_STRING, pdf)); + TestReportsDao.instance.updateOne(Filters.eq("_id", reportUrlIdObj), Updates.set(TestReports.PDF_REPORT_STRING, pdf)); } } catch (Exception e) { loggerMaker.errorAndAddToDb(e, "Error while polling pdf download for report id - " + reportId, LogDb.DASHBOARD); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index 35fed5f482..775094ef5c 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -332,7 +332,7 @@ const VulnerabilityReport = () => {
{ - (window.USER_NAME?.toLowerCase()?.includes("ababank") || window.ACCOUNT_NAME?.toLowerCase()?.includes("advanced bank")) ? : <> + (window.USER_NAME?.toLowerCase()?.includes("@akto.io") || window.ACCOUNT_NAME?.toLowerCase()?.includes("advanced bank")) ? : <> } Logo
From 052e4ca3677ccbfe4c81ea63909bdef5be044ef4 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:13:00 +0530 Subject: [PATCH 18/39] fix: improve report PDF generation toast handling and prevent duplicate messages --- .../vulnerability_report/VulnerabilityReport.jsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index 775094ef5c..b3d20ba660 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -246,11 +246,15 @@ const VulnerabilityReport = () => { func.setToast(true, false, "Preparing your report. This might take a moment...") }, 1000) + let generationStarted = false setTimeout(() => { clearInterval(reportToastInterval) - reportToastInterval = setInterval(() => { - func.setToast(true, false, "Report PDF generation in progress. Please wait...") - }, 1000) + generationStarted = true + if(status === "IN_PROGRESS") { + reportToastInterval = setInterval(() => { + func.setToast(true, false, "Report PDF generation in progress. Please wait...") + }, 1000) + } }, 6000) try { @@ -276,7 +280,9 @@ const VulnerabilityReport = () => { await func.sleep(WAIT_DURATION) - func.setToast(true, false, "Report PDF generation in progress. Please wait...") + if(generationStarted) { + func.setToast(generationStarted, false, "Report PDF generation in progress. Please wait...") + } } } else { if(status !== "COMPLETED") { From cb428aca242ce09065034335f669f7136b5ac2eb Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:18:49 +0530 Subject: [PATCH 19/39] fix: toast message handling --- .../testing/vulnerability_report/VulnerabilityReport.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index b3d20ba660..07082a8c4b 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -280,9 +280,7 @@ const VulnerabilityReport = () => { await func.sleep(WAIT_DURATION) - if(generationStarted) { - func.setToast(generationStarted, false, "Report PDF generation in progress. Please wait...") - } + func.setToast(generationStarted, false, "Report PDF generation in progress. Please wait...") } } else { if(status !== "COMPLETED") { From 16f07a132a800d50cb4e2e41a529a9d117848d5c Mon Sep 17 00:00:00 2001 From: Fenil Shah Date: Mon, 13 Jan 2025 13:00:44 +0530 Subject: [PATCH 20/39] changed the font size --- .../web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx index 241abd41d7..89d9e36ab4 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/Dashboard.jsx @@ -206,7 +206,7 @@ function Dashboard() { : null} {window.TRIAL_MSG && !(location.pathname.includes("test-editor") || location.pathname.includes("settings") || location.pathname.includes("onboarding") || location.pathname.includes("summary")) ?
- {window.TRIAL_MSG} + {window.TRIAL_MSG}
: null} From 9f450fb3480d0b9e8d205f39d15635e2adbacb3f Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 13:56:21 +0530 Subject: [PATCH 21/39] Fixing queries in testing containers --- .../action/testing_issues/IssuesAction.java | 2 +- .../src/main/java/com/akto/testing/Main.java | 6 +++-- .../akto/dao/testing/TestingRunResultDao.java | 2 +- .../VulnerableTestingRunResultDao.java | 6 ++--- .../src/main/java/com/akto/testing/Utils.java | 22 +++++++++++++++++++ 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index 3fccd5b7d0..a0f3fff9f9 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -454,7 +454,7 @@ public String fetchTestingRunResult() { Filters.eq(TestingRunResult.TEST_SUB_TYPE, testSubType), Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()) ); - testingRunResult = VulnerableTestingRunResultDao.instance.findOne(filterForRunResult); + testingRunResult = VulnerableTestingRunResultDao.instance.findOne(filterForRunResult, null); if(testingRunResult == null){ testingRunResult = TestingRunResultDao.instance.findOne(filterForRunResult); } diff --git a/apps/testing/src/main/java/com/akto/testing/Main.java b/apps/testing/src/main/java/com/akto/testing/Main.java index 7921c7cf1e..5578664122 100644 --- a/apps/testing/src/main/java/com/akto/testing/Main.java +++ b/apps/testing/src/main/java/com/akto/testing/Main.java @@ -10,6 +10,7 @@ import com.akto.dao.testing.TestingRunDao; import com.akto.dao.testing.TestingRunResultDao; import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.VulnerableTestingRunResultDao; import com.akto.dao.testing_run_findings.TestingRunIssuesDao; import com.akto.dto.billing.FeatureAccess; import com.akto.dto.billing.SyncLimit; @@ -437,7 +438,7 @@ public void run() { Filters.eq(TestingRunResultSummary.TESTING_RUN_ID, testingRun.getId()), Filters.eq(TestingRunResultSummary.STATE, State.FAILED) ); - List testingRunResults = TestingRunResultDao.instance.fetchLatestTestingRunResult(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummary.getId()), 1); + List testingRunResults = Utils.fetchLatestTestingRunResult(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummary.getId())); if (testingRunResults != null && !testingRunResults.isEmpty()) { TestingRunResult testingRunResult = testingRunResults.get(0); if (Context.now() - testingRunResult.getEndTimestamp() < LAST_TEST_RUN_EXECUTION_DELTA) { @@ -745,10 +746,11 @@ private static void raiseMixpanelEvent(ObjectId summaryId, TestingRun testingRun if(newIssuesModelList.size() <= 5) { Bson filterForRunResult = Filters.and( Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunIssues.getLatestTestingRunSummaryId()), + Filters.eq(TestingRunResult.VULNERABLE, true), Filters.eq(TestingRunResult.TEST_SUB_TYPE, testingRunIssues.getId().getTestSubCategory()), Filters.eq(TestingRunResult.API_INFO_KEY, testingRunIssues.getId().getApiInfoKey()) ); - TestingRunResult testingRunResult = TestingRunResultDao.instance.findOne(filterForRunResult, Projections.include("_id")); + TestingRunResult testingRunResult = VulnerableTestingRunResultDao.instance.findOne(filterForRunResult, Projections.include("_id")); testRunResultId = testingRunResult.getHexId(); } else testRunResultId = ""; diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java index eed942bfea..9f028d1d6c 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java @@ -134,7 +134,7 @@ public List fetchLatestTestingRunResult(Bson filters, int limi pipeline.add(Aggregates.skip(skip)); pipeline.add(Aggregates.limit(limit)); pipeline.addAll(customAggregation); - MongoCursor cursor = instance.getMCollection() + MongoCursor cursor = this.getMCollection() .aggregate(pipeline, BasicDBObject.class).cursor(); List testingRunResults = new ArrayList<>(); while (cursor.hasNext()) { diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java index 14ed2cbc65..edca45725b 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -92,10 +92,10 @@ public List fetchLatestTestingRunResultWithCustomAggregations( } @Override - public TestingRunResult findOne(Bson q) { - TestingRunResult tr = super.findOne(q); + public TestingRunResult findOne(Bson q, Bson projection) { + TestingRunResult tr = super.findOne(q, projection); if(tr == null){ - tr = instance.findOne(q); + tr = instance.findOne(q, projection); } return tr; } diff --git a/libs/utils/src/main/java/com/akto/testing/Utils.java b/libs/utils/src/main/java/com/akto/testing/Utils.java index 89b548d72c..71ce70c3e1 100644 --- a/libs/utils/src/main/java/com/akto/testing/Utils.java +++ b/libs/utils/src/main/java/com/akto/testing/Utils.java @@ -39,6 +39,7 @@ import com.akto.util.enums.GlobalEnums.Severity; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; import com.mongodb.client.MongoCursor; import com.mongodb.client.model.Accumulators; import com.mongodb.client.model.Aggregates; @@ -475,5 +476,26 @@ public static Map finalCountIssuesMap(ObjectId testingRunResult return countIssuesMap; } + + public static List fetchLatestTestingRunResult(Bson filter){ + List resultsFromNonVulCollection = TestingRunResultDao.instance.fetchLatestTestingRunResult(filter, 1); + List resultsFromVulCollection = VulnerableTestingRunResultDao.instance.fetchLatestTestingRunResult(filter, 1); + + if(resultsFromVulCollection != null && !resultsFromVulCollection.isEmpty()){ + if(resultsFromNonVulCollection != null && !resultsFromNonVulCollection.isEmpty()){ + TestingRunResult tr1 = resultsFromVulCollection.get(0); + TestingRunResult tr2 = resultsFromNonVulCollection.get(0); + if(tr1.getEndTimestamp() >= tr2.getEndTimestamp()){ + return resultsFromVulCollection; + }else{ + return resultsFromVulCollection; + } + }else{ + return resultsFromVulCollection; + } + } + + return resultsFromNonVulCollection; + } } From e3225b68865e1f1966673146b9feeffb1972569e Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 14:33:29 +0530 Subject: [PATCH 22/39] Fixing logical errors --- .../dao/testing/VulnerableTestingRunResultDao.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java index edca45725b..f5024b33fd 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -80,22 +80,21 @@ public int countFromDb(Bson filter, boolean isVulnerable){ return count; } } - return (int) super.count(filter); + return (int) TestingRunResultDao.instance.count(filter); } public List fetchLatestTestingRunResultWithCustomAggregations(Bson filters, int limit, int skip, Bson customSort, ObjectId summaryId, boolean isVulnerable) { if(isVulnerable && instance.isStoredInVulnerableCollection(summaryId, true)){ return instance.fetchLatestTestingRunResultWithCustomAggregations(filters, limit, skip, customSort); }else{ - return super.fetchLatestTestingRunResultWithCustomAggregations(filters, limit, skip, customSort); + return TestingRunResultDao.instance.fetchLatestTestingRunResultWithCustomAggregations(filters, limit, skip, customSort); } } - @Override public TestingRunResult findOne(Bson q, Bson projection) { - TestingRunResult tr = super.findOne(q, projection); + TestingRunResult tr = TestingRunResultDao.instance.findOne(q, projection); if(tr == null){ - tr = instance.findOne(q, projection); + return super.findOne(q, projection); } return tr; } @@ -104,7 +103,7 @@ public List findAll(Bson q, Bson projection, boolean isStoredI if(isStoredInVulnerableCollection){ return instance.findAll(q,projection); } - return super.findAll(q, projection); + return TestingRunResultDao.instance.findAll(q, projection); } @Override From 2d127f2f17ccb72562a640c30cb2157b8784e862 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 16:10:01 +0530 Subject: [PATCH 23/39] Fixing default role for sso users --- .../java/com/akto/action/SignupAction.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/SignupAction.java b/apps/dashboard/src/main/java/com/akto/action/SignupAction.java index 1e15177698..dcfb32c302 100644 --- a/apps/dashboard/src/main/java/com/akto/action/SignupAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/SignupAction.java @@ -13,6 +13,7 @@ import com.akto.notifications.slack.NewUserJoiningAlert; import com.akto.notifications.slack.SlackAlerts; import com.akto.notifications.slack.SlackSender; +import com.akto.usage.UsageMetricCalculator; import com.akto.util.http_request.CustomHttpRequest; import com.akto.utils.Auth0; import com.akto.utils.GithubLogin; @@ -548,9 +549,14 @@ public String registerViaOkta() throws IOException{ String username = userInfo.get("preferred_username").toString(); SignupInfo.OktaSignupInfo oktaSignupInfo= new SignupInfo.OktaSignupInfo(accessToken, username); + + RBAC.Role defaultRole = RBAC.Role.MEMBER; + if(UsageMetricCalculator.isRbacFeatureAvailable(accountId)){ + defaultRole = RBAC.Role.GUEST; + } shouldLogin = "true"; - createUserAndRedirect(email, username, oktaSignupInfo, accountId, Config.ConfigType.OKTA.toString(), RBAC.Role.MEMBER); + createUserAndRedirect(email, username, oktaSignupInfo, accountId, Config.ConfigType.OKTA.toString(), defaultRole); code = ""; } catch (Exception e) { loggerMaker.errorAndAddToDb("Error while signing in via okta sso \n" + e.getMessage(), LogDb.DASHBOARD); @@ -659,7 +665,13 @@ public String registerViaAzure() throws Exception{ shouldLogin = "true"; logger.info("Successful signing with Azure Idp for: "+ useremail); SignupInfo.SamlSsoSignupInfo signUpInfo = new SignupInfo.SamlSsoSignupInfo(username, useremail, Config.ConfigType.AZURE); - createUserAndRedirect(useremail, username, signUpInfo, this.accountId, Config.ConfigType.AZURE.toString(), RBAC.Role.MEMBER); + + RBAC.Role defaultRole = RBAC.Role.MEMBER; + if(UsageMetricCalculator.isRbacFeatureAvailable(this.accountId)){ + defaultRole = RBAC.Role.GUEST; + } + + createUserAndRedirect(useremail, username, signUpInfo, this.accountId, Config.ConfigType.AZURE.toString(), defaultRole); } catch (Exception e1) { loggerMaker.errorAndAddToDb("Error while signing in via azure sso \n" + e1.getMessage(), LogDb.DASHBOARD); servletResponse.sendRedirect("/login"); @@ -708,7 +720,13 @@ public String registerViaGoogleSamlSso() throws IOException{ shouldLogin = "true"; SignupInfo.SamlSsoSignupInfo signUpInfo = new SignupInfo.SamlSsoSignupInfo(username, userEmail, Config.ConfigType.GOOGLE_SAML); - createUserAndRedirect(userEmail, username, signUpInfo, this.accountId, Config.ConfigType.GOOGLE_SAML.toString(), RBAC.Role.MEMBER); + + RBAC.Role defaultRole = RBAC.Role.MEMBER; + if(UsageMetricCalculator.isRbacFeatureAvailable(this.accountId)){ + defaultRole = RBAC.Role.GUEST; + } + + createUserAndRedirect(userEmail, username, signUpInfo, this.accountId, Config.ConfigType.GOOGLE_SAML.toString(), defaultRole); } catch (Exception e1) { loggerMaker.errorAndAddToDb("Error while signing in via google workspace sso \n" + e1.getMessage(), LogDb.DASHBOARD); servletResponse.sendRedirect("/login"); From b13baaf9ccf399791b7881de7fab9cd704bccda4 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:13:11 +0530 Subject: [PATCH 24/39] fix: add error message for PDF download failure due to large size --- .../testing/vulnerability_report/VulnerabilityReport.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index 07082a8c4b..320f9c7cb4 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -281,6 +281,10 @@ const VulnerabilityReport = () => { await func.sleep(WAIT_DURATION) func.setToast(generationStarted, false, "Report PDF generation in progress. Please wait...") + + if(i === MAX_RETRIES - 1) { + } + pdfError = "Failed to download PDF. The size might be too large. Filter out unnecessary issues and try again." } } else { if(status !== "COMPLETED") { From 70e68f03c09f389452275824f31abda0fe1e555d Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 17:17:27 +0530 Subject: [PATCH 25/39] inserting flag in summaries --- apps/testing/src/main/java/com/akto/testing/Main.java | 4 +++- .../java/com/akto/dto/testing/TestingRunResultSummary.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/testing/Main.java b/apps/testing/src/main/java/com/akto/testing/Main.java index 5578664122..84276402a9 100644 --- a/apps/testing/src/main/java/com/akto/testing/Main.java +++ b/apps/testing/src/main/java/com/akto/testing/Main.java @@ -101,7 +101,8 @@ private static TestingRunResultSummary createTRRSummaryIfAbsent(TestingRun testi Updates.set(TestingRunResultSummary.STATE, TestingRun.State.RUNNING), Updates.setOnInsert(TestingRunResultSummary.START_TIMESTAMP, start), Updates.set(TestingRunResultSummary.TEST_RESULTS_COUNT, 0), - Updates.set(TestingRunResultSummary.COUNT_ISSUES, emptyCountIssuesMap) + Updates.set(TestingRunResultSummary.COUNT_ISSUES, emptyCountIssuesMap), + Updates.set(TestingRunResultSummary.IS_NEW_TESTING_RUN_RESULT_SUMMARY, true) ), new FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.AFTER) ); @@ -517,6 +518,7 @@ public void run() { trrs.setState(State.RUNNING); trrs.setTestResultsCount(0); trrs.setCountIssues(emptyCountIssuesMap); + trrs.setNewTestingSummary(true); TestingRunResultSummariesDao.instance.insertOne(trrs); summaryId = trrs.getId(); } else { diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java index f6db94cf06..297775e993 100644 --- a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java @@ -176,7 +176,7 @@ public String toString() { "}"; } - public boolean isNewTestingSummary() { + public boolean getNewTestingSummary() { return isNewTestingSummary; } From 321a3255b36504c440b0a1784d1e03912a887450 Mon Sep 17 00:00:00 2001 From: notshivansh Date: Mon, 13 Jan 2025 17:48:23 +0530 Subject: [PATCH 26/39] fix prod.yml --- .github/workflows/prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index cb9c03fd61..a6b8934bc3 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -134,7 +134,7 @@ jobs: fi if [[ "${{ github.event.inputs.threat_detection_backend}}" == "true" ]]; then - cd apps/threat-detection-backend + cd ../threat-detection-backend docker buildx build --platform linux/arm64/v8,linux/amd64 -t $ECR_REGISTRY/akto-threat-detection-backend:$IMAGE_TAG . --push fi - name: Configure AWS Credentials for ECR From e38f3625d3776549ab39089683612081ce29c35c Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 18:06:32 +0530 Subject: [PATCH 27/39] Resolving comments pt1 --- .../main/java/com/akto/action/testing/StartTestAction.java | 4 ++-- .../dao/src/main/java/com/akto/dao/testing/TestingRunDao.java | 3 --- .../com/akto/dao/testing/TestingRunResultSummariesDao.java | 4 ---- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 3ab78c8945..068ef9497c 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -860,7 +860,7 @@ public static String getNodeResultLastMessage(String message) { public String fetchTestRunResultDetails() { ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); - this.testingRunResult = VulnerableTestingRunResultDao.instance.findOne("_id", testingRunResultId); + this.testingRunResult = VulnerableTestingRunResultDao.instance.findOne(Filters.eq(Constants.ID, testingRunResultId), null); List runResults = new ArrayList<>(); for (GenericTestResult testResult: this.testingRunResult.getTestResults()) { @@ -880,7 +880,7 @@ public String fetchTestRunResultDetails() { public String fetchIssueFromTestRunResultDetails() { ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); - TestingRunResult result = VulnerableTestingRunResultDao.instance.findOne(Constants.ID, testingRunResultId); + TestingRunResult result = VulnerableTestingRunResultDao.instance.findOne(Filters.eq(Constants.ID, testingRunResultId), null); try { if (result.isVulnerable()) { // name = category diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java index 1346882054..66904ff68c 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java @@ -36,9 +36,6 @@ public void createIndicesIfAbsent() { fieldNames = new String[]{TestingRun.NAME}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); - - fieldNames = new String[]{Constants.ID, TestingRun.IS_NEW_TESTING_RUN}; - MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); } public List getTestConfigIdsToDelete(List testingRunIds){ diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java index b82572ac91..ebb084f8a5 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java @@ -140,10 +140,6 @@ public void createIndicesIfAbsent() { fieldNames = new String[]{TestingRunResultSummary.END_TIMESTAMP}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); - fieldNames = new String[]{Constants.ID, TestingRunResultSummary.IS_NEW_TESTING_RUN_RESULT_SUMMARY}; - MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames,false); - - IndexOptions sparseIndex = new IndexOptions().sparse(true); Bson branchIndex = Indexes.ascending("metadata.branch"); From 74cd2a498cc220533ce665400093fca81c9f3ff4 Mon Sep 17 00:00:00 2001 From: notshivansh Date: Mon, 13 Jan 2025 18:19:43 +0530 Subject: [PATCH 28/39] fix image tags --- .github/workflows/prod.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index a6b8934bc3..164d6534c5 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -98,7 +98,6 @@ jobs: ECR_REPOSITORY: akto-api-security IMAGE_TAG_1: local IMAGE_TAG_2: ${{ github.event.inputs.release_version }}_local - IMAGE_TAG_3: latest run: | docker buildx create --use # Build a docker container and push it to DockerHub @@ -135,7 +134,7 @@ jobs: if [[ "${{ github.event.inputs.threat_detection_backend}}" == "true" ]]; then cd ../threat-detection-backend - docker buildx build --platform linux/arm64/v8,linux/amd64 -t $ECR_REGISTRY/akto-threat-detection-backend:$IMAGE_TAG . --push + docker buildx build --platform linux/arm64/v8,linux/amd64 -t $ECR_REGISTRY/akto-threat-detection-backend:$IMAGE_TAG_1 -t $ECR_REGISTRY/akto-threat-detection-backend:$IMAGE_TAG_2 . --push fi - name: Configure AWS Credentials for ECR uses: aws-actions/configure-aws-credentials@v1 From 87bc3bba4d1e8d268f98d0519896992f9b82ba19 Mon Sep 17 00:00:00 2001 From: Umesh Kumar <166806589+TangoBeeAkto@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:24:55 +0530 Subject: [PATCH 29/39] fix: enhance error handling for PDF download and update toast messages --- .../java/com/akto/action/ReportAction.java | 20 +++++++++++++++++-- .../VulnerabilityReport.jsx | 8 ++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java index 5bbd28825e..609ed8a46a 100644 --- a/apps/dashboard/src/main/java/com/akto/action/ReportAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/ReportAction.java @@ -13,6 +13,7 @@ import com.akto.dao.testing.sources.TestReportsDao; import com.akto.dto.testing.sources.TestReports; +import com.mongodb.MongoCommandException; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Updates; import org.apache.struts2.ServletActionContext; @@ -40,7 +41,7 @@ public class ReportAction extends UserAction { private boolean firstPollRequest; private static final LoggerMaker loggerMaker = new LoggerMaker(ReportAction.class); - + public String downloadReportPDF() { if(reportUrl == null || reportUrl.isEmpty()) { status = "ERROR"; @@ -129,7 +130,22 @@ public String downloadReportPDF() { if (status.equals("COMPLETED")) { loggerMaker.infoAndAddToDb("Pdf download status for report id - " + reportId + " completed. Attaching pdf in response ", LogDb.DASHBOARD); pdf = node.get("base64PDF").textValue(); - TestReportsDao.instance.updateOne(Filters.eq("_id", reportUrlIdObj), Updates.set(TestReports.PDF_REPORT_STRING, pdf)); + try { + TestReportsDao.instance.updateOne(Filters.eq("_id", reportUrlIdObj), Updates.set(TestReports.PDF_REPORT_STRING, pdf)); + } catch(Exception e) { + loggerMaker.errorAndAddToDb("Error: " + e.getMessage() + ", while updating report binary for reportId: " + reportId, LogDb.DASHBOARD); + if (e instanceof MongoCommandException) { + MongoCommandException mongoException = (MongoCommandException) e; + if (mongoException.getCode() == 17420) { + addActionError("The report is too large to save. Please reduce its size and try again."); + } else { + addActionError("A database error occurred while saving the report. Try again later."); + } + } else { + addActionError("An error occurred while updating the report in DB. Please try again."); + } + status = "ERROR"; + } } } catch (Exception e) { loggerMaker.errorAndAddToDb(e, "Error while polling pdf download for report id - " + reportId, LogDb.DASHBOARD); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index 320f9c7cb4..fb68a80bfe 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -283,8 +283,8 @@ const VulnerabilityReport = () => { func.setToast(generationStarted, false, "Report PDF generation in progress. Please wait...") if(i === MAX_RETRIES - 1) { + pdfError = "Failed to download PDF. The size might be too large. Filter out unnecessary issues and try again." } - pdfError = "Failed to download PDF. The size might be too large. Filter out unnecessary issues and try again." } } else { if(status !== "COMPLETED") { @@ -292,7 +292,7 @@ const VulnerabilityReport = () => { } } } catch (err) { - pdfError = err.message + pdfError = err?.response?.data?.actionErrors?.[0] || err.message } clearInterval(reportToastInterval) @@ -318,13 +318,13 @@ const VulnerabilityReport = () => { link.click(); func.setToast(true, false, "Report PDF downloaded.") } catch (err) { - pdfError = err.message + pdfError = err?.response?.data?.actionErrors?.[0] || err.message } } } if (pdfError !== "") { - func.setToast(true, true, `Error while downloading PDF. Please try again. \nError: ${pdfError}`) + func.setToast(true, true, `Error: ${pdfError}`) } setPdfDownloadEnabled(true) From 2164d3199b40bbabd97bb6bab86f5e58ad4537a2 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 13 Jan 2025 20:30:27 +0530 Subject: [PATCH 30/39] removing redundant indexes --- .../akto/dao/testing/VulnerableTestingRunResultDao.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java index f5024b33fd..d945e8cd76 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -31,13 +31,8 @@ public void createIndicesIfAbsent() { MCollection.createIndexIfAbsent(getDBName(), getCollName(), new String[] { TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID }, false); - - MCollection.createIndexIfAbsent(getDBName(), getCollName(), new String[]{TestingRunResult.END_TIMESTAMP}, false); - - String[] fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.API_INFO_KEY+"."+ApiInfoKey.API_COLLECTION_ID}; - MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); - - fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_RESULTS+"."+GenericTestResult._CONFIDENCE}; + + String[] fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_RESULTS+"."+GenericTestResult._CONFIDENCE}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_SUPER_TYPE}; From 422e22e6b58f920a3b44a46fb99de9738c0be016 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Tue, 14 Jan 2025 09:54:56 +0530 Subject: [PATCH 31/39] renaming the function --- .../main/java/com/akto/action/testing/StartTestAction.java | 4 ++-- .../java/com/akto/action/testing_issues/IssuesAction.java | 5 +---- apps/testing/src/main/java/com/akto/testing/Main.java | 2 +- .../akto/dao/testing/VulnerableTestingRunResultDao.java | 7 +++---- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 068ef9497c..44315873a4 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -860,7 +860,7 @@ public static String getNodeResultLastMessage(String message) { public String fetchTestRunResultDetails() { ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); - this.testingRunResult = VulnerableTestingRunResultDao.instance.findOne(Filters.eq(Constants.ID, testingRunResultId), null); + this.testingRunResult = VulnerableTestingRunResultDao.instance.findOneWithComparison(Filters.eq(Constants.ID, testingRunResultId), null); List runResults = new ArrayList<>(); for (GenericTestResult testResult: this.testingRunResult.getTestResults()) { @@ -880,7 +880,7 @@ public String fetchTestRunResultDetails() { public String fetchIssueFromTestRunResultDetails() { ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); - TestingRunResult result = VulnerableTestingRunResultDao.instance.findOne(Filters.eq(Constants.ID, testingRunResultId), null); + TestingRunResult result = VulnerableTestingRunResultDao.instance.findOneWithComparison(Filters.eq(Constants.ID, testingRunResultId), null); try { if (result.isVulnerable()) { // name = category diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index a0f3fff9f9..5f741f56d3 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -454,10 +454,7 @@ public String fetchTestingRunResult() { Filters.eq(TestingRunResult.TEST_SUB_TYPE, testSubType), Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()) ); - testingRunResult = VulnerableTestingRunResultDao.instance.findOne(filterForRunResult, null); - if(testingRunResult == null){ - testingRunResult = TestingRunResultDao.instance.findOne(filterForRunResult); - } + testingRunResult = VulnerableTestingRunResultDao.instance.findOneWithComparison(filterForRunResult, null); if (issue.isUnread() && (currentUserRole.equals(Role.ADMIN) || currentUserRole.equals(Role.MEMBER))) { logger.info("Issue id from db to be marked as read " + issueId); Bson update = Updates.combine(Updates.set(TestingRunIssues.UNREAD, false), diff --git a/apps/testing/src/main/java/com/akto/testing/Main.java b/apps/testing/src/main/java/com/akto/testing/Main.java index 84276402a9..c24cf75539 100644 --- a/apps/testing/src/main/java/com/akto/testing/Main.java +++ b/apps/testing/src/main/java/com/akto/testing/Main.java @@ -752,7 +752,7 @@ private static void raiseMixpanelEvent(ObjectId summaryId, TestingRun testingRun Filters.eq(TestingRunResult.TEST_SUB_TYPE, testingRunIssues.getId().getTestSubCategory()), Filters.eq(TestingRunResult.API_INFO_KEY, testingRunIssues.getId().getApiInfoKey()) ); - TestingRunResult testingRunResult = VulnerableTestingRunResultDao.instance.findOne(filterForRunResult, Projections.include("_id")); + TestingRunResult testingRunResult = VulnerableTestingRunResultDao.instance.findOneWithComparison(filterForRunResult, Projections.include("_id")); testRunResultId = testingRunResult.getHexId(); } else testRunResultId = ""; diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java index d945e8cd76..7aaad2657a 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -7,7 +7,6 @@ import com.akto.dao.MCollection; import com.akto.dao.context.Context; -import com.akto.dto.ApiInfo.ApiInfoKey; import com.akto.dto.testing.GenericTestResult; import com.akto.dto.testing.TestingRunResult; import com.akto.dto.testing.TestingRunResultSummary; @@ -86,10 +85,10 @@ public List fetchLatestTestingRunResultWithCustomAggregations( } } - public TestingRunResult findOne(Bson q, Bson projection) { - TestingRunResult tr = TestingRunResultDao.instance.findOne(q, projection); + public TestingRunResult findOneWithComparison(Bson q, Bson projection) { + TestingRunResult tr = super.findOne(q, projection); if(tr == null){ - return super.findOne(q, projection); + return TestingRunResultDao.instance.findOne(q, projection); } return tr; } From 21a1ab2920b1007c578bb976a282ed3186cf7fa6 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Tue, 14 Jan 2025 10:13:21 +0530 Subject: [PATCH 32/39] Added backward compatibilty to mark summaries as new --- .../akto/listener/InitializerListener.java | 21 +++++++++++ .../VulnerableTestingRunResultDao.java | 36 ++++++++++++++----- .../com/akto/dto/BackwardCompatibility.java | 14 +++++++- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java index 3bfc3eb7b3..01da99bffb 100644 --- a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java +++ b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java @@ -2970,11 +2970,32 @@ private static void moveOktaOidcSSO(BackwardCompatibility backwardCompatibility) } } + private static void markSummariesAsVulnerable(BackwardCompatibility backwardCompatibility){ + // case for the customers where vulnerable are stored in new collection and only testing runs are marked as new. + + if(backwardCompatibility.getMarkSummariesVulnerable() == 0){ + + List summaryIds = VulnerableTestingRunResultDao.instance.summaryIdsStoredForVulnerableTests(); + if(!summaryIds.isEmpty()){ + TestingRunResultSummariesDao.instance.updateMany( + Filters.in(Constants.ID, summaryIds), + Updates.set(TestingRunResultSummary.IS_NEW_TESTING_RUN_RESULT_SUMMARY, true) + ); + } + + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.MARK_SUMMARIES_NEW_FOR_VULNERABLE, Context.now()) + ); + } + } + public static void setBackwardCompatibilities(BackwardCompatibility backwardCompatibility){ if (DashboardMode.isMetered()) { initializeOrganizationAccountBelongsTo(backwardCompatibility); setOrganizationsInBilling(backwardCompatibility); } + markSummariesAsVulnerable(backwardCompatibility); setAktoDefaultNewUI(backwardCompatibility); dropLastCronRunInfoField(backwardCompatibility); fetchIntegratedConnections(backwardCompatibility); diff --git a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java index 7aaad2657a..8fcfd00f04 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/VulnerableTestingRunResultDao.java @@ -1,5 +1,6 @@ package com.akto.dao.testing; +import java.util.ArrayList; import java.util.List; import org.bson.conversions.Bson; @@ -11,6 +12,10 @@ import com.akto.dto.testing.TestingRunResult; import com.akto.dto.testing.TestingRunResultSummary; import com.akto.util.Constants; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Accumulators; +import com.mongodb.client.model.Aggregates; import com.mongodb.client.model.CreateCollectionOptions; import com.mongodb.client.model.Filters; import com.mongodb.client.model.Projections; @@ -51,15 +56,7 @@ public boolean isStoredInVulnerableCollection(ObjectId objectId, boolean isSumma Filters.eq(TestingRunResultSummary.IS_NEW_TESTING_RUN_RESULT_SUMMARY, true) ); boolean isNew = TestingRunResultSummariesDao.instance.count(filter) > 0; - if(!isNew){ - TestingRunResultSummary trrs = TestingRunResultSummariesDao.instance.findOne( - Filters.eq(Constants.ID, objectId), - Projections.include(TestingRunResultSummary.TESTING_RUN_ID) - ); - return TestingRunDao.instance.isStoredInVulnerableCollection(trrs.getTestingRunId()); - }else{ - return isNew; - } + return isNew; } catch (Exception e) { e.printStackTrace(); return false; @@ -100,6 +97,27 @@ public List findAll(Bson q, Bson projection, boolean isStoredI return TestingRunResultDao.instance.findAll(q, projection); } + public List summaryIdsStoredForVulnerableTests(){ + String groupedId = "summaries"; + List pipeLine = new ArrayList<>(); + pipeLine.add( + Aggregates.group(groupedId, Accumulators.addToSet("summaryIds", "$" + TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID)) + ); + try { + MongoCursor cursor = instance.getMCollection().aggregate(pipeLine, BasicDBObject.class).cursor(); + List uniqueSummaries = new ArrayList<>(); + while (cursor.hasNext()) { + BasicDBObject dbObject = cursor.next(); + uniqueSummaries = (List) dbObject.get("summaryIds"); + } + return uniqueSummaries; + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } + + } + @Override public String getCollName() { return "vulnerable_testing_run_results"; diff --git a/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java b/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java index b4b3a83f9a..59c95fe474 100644 --- a/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java +++ b/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java @@ -103,6 +103,9 @@ public class BackwardCompatibility { public static final String MOVE_OKTA_OIDC_SSO = "moveOktaOidcSSO"; private int moveOktaOidcSSO; + public static final String MARK_SUMMARIES_NEW_FOR_VULNERABLE = "markSummariesVulnerable"; + private int markSummariesVulnerable; + public BackwardCompatibility(int id, int dropFilterSampleData, int resetSingleTypeInfoCount, int dropWorkflowTestResult, int readyForNewTestingFramework,int addAktoDataTypes, boolean deploymentStatusUpdated, int authMechanismData, boolean mirroringLambdaTriggered, int deleteAccessListFromApiToken, @@ -112,7 +115,7 @@ public BackwardCompatibility(int id, int dropFilterSampleData, int resetSingleTy int loginSignupGroups, int vulnerableApiUpdationVersionV1, int riskScoreGroups, int deactivateCollections, int disableAwsSecretPii, int apiCollectionAutomatedField, int automatedApiGroups, int addAdminRoleIfAbsent, int dropSpecialCharacterApiCollections, int fixApiAccessType, - int addDefaultFilters, int moveAzureSamlToNormalSaml, int deleteOptionsAPIs, int moveOktaOidcSSO) { + int addDefaultFilters, int moveAzureSamlToNormalSaml, int deleteOptionsAPIs, int moveOktaOidcSSO, int markSummariesVulnerable) { this.id = id; this.dropFilterSampleData = dropFilterSampleData; this.resetSingleTypeInfoCount = resetSingleTypeInfoCount; @@ -145,6 +148,7 @@ public BackwardCompatibility(int id, int dropFilterSampleData, int resetSingleTy this.moveAzureSamlToNormalSaml = moveAzureSamlToNormalSaml; this.deleteOptionsAPIs = deleteOptionsAPIs; this.moveOktaOidcSSO = moveOktaOidcSSO; + this.markSummariesVulnerable = markSummariesVulnerable; } public BackwardCompatibility() { @@ -437,4 +441,12 @@ public int getMoveOktaOidcSSO() { public void setMoveOktaOidcSSO(int moveOktaOidcSSO) { this.moveOktaOidcSSO = moveOktaOidcSSO; } + + public int getMarkSummariesVulnerable() { + return markSummariesVulnerable; + } + + public void setMarkSummariesVulnerable(int markSummariesVulnerable) { + this.markSummariesVulnerable = markSummariesVulnerable; + } } From a1c77a8eec9946e838fb9ccef6b9e78df2e7ed7f Mon Sep 17 00:00:00 2001 From: ag060 Date: Tue, 14 Jan 2025 10:54:16 +0530 Subject: [PATCH 33/39] removed concurrency control from staging worflow --- .github/workflows/staging.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 5d9ee4f432..8598288499 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -7,10 +7,6 @@ on: pull_request: workflow_dispatch: -concurrency: - group: "staging${{ github.head_ref }}" - cancel-in-progress: true - jobs: build: runs-on: ubuntu-22.04 From 02958a7c9ef76870515b30c98d623c1cf311400f Mon Sep 17 00:00:00 2001 From: ag060 Date: Tue, 14 Jan 2025 11:01:54 +0530 Subject: [PATCH 34/39] running staging build only if manually triggerd or pr against master branch is approved --- .github/workflows/staging.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 8598288499..bc7d728299 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -5,10 +5,12 @@ on: push: branches: [master] pull_request: + types: [ submitted ] workflow_dispatch: jobs: build: + if: (github.event.pull_request.base.ref == 'master' && github.event.review.state == 'approved') || (github.event_name == 'workflow_dispatch') runs-on: ubuntu-22.04 outputs: IMAGE_TAG: ${{ steps.docker_tag.outputs.IMAGE_TAG }} From b23ebd66c2563a73aed9bba4a036c6d147dcfbef Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Tue, 14 Jan 2025 13:33:16 +0530 Subject: [PATCH 35/39] Changing the summary with regard to new result collection --- .../com/akto/action/testing_issues/IssuesAction.java | 11 ++++++++--- .../com/akto/dao/testing/TestingRunResultDao.java | 5 +---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index 5f741f56d3..5f76b8195a 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -602,9 +602,14 @@ public void run() { countIssuesMap.put(Severity.LOW.toString(), 0); // update summaries accordingly with issues ignored - - // TODO: fix this smartly - Map mapSummaryToResultId = TestingRunResultDao.instance.mapSummaryIdToTestingResultHexId(testingRunResultHexIdsMap.keySet()); + // currently we change the summaries from result page only + // so only 1 result comes at a time + // Map testingRunResultHexIdsMap has only 1 result. + + Map mapSummaryToResultId = VulnerableTestingRunResultDao.instance.mapSummaryIdToTestingResultHexId(testingRunResultHexIdsMap.keySet()); + if(mapSummaryToResultId.isEmpty()){ + mapSummaryToResultId = TestingRunResultDao.instance.mapSummaryIdToTestingResultHexId(testingRunResultHexIdsMap.keySet()); + } Map> summaryWiseCountMap = new HashMap<>(); for(ObjectId summaryId: mapSummaryToResultId.keySet()){ diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java index 9f028d1d6c..1eea09acb0 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java @@ -198,10 +198,7 @@ public Map mapSummaryIdToTestingResultHexId(Set testing .map(ObjectId::new) .collect(Collectors.toList()); - // doing only for 1000 results at a time - objectIdList = objectIdList.subList(0, 1000); - - List runResults = instance.findAll(Filters.in(Constants.ID, objectIdList), Projections.include(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID)); + List runResults = this.findAll(Filters.in(Constants.ID, objectIdList), Projections.include(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID)); for(TestingRunResult runResult: runResults){ finalMap.put(runResult.getTestRunResultSummaryId(), runResult.getHexId()); } From 5fdcd3e373911d8e85bd96775735e8465f63329c Mon Sep 17 00:00:00 2001 From: ag060 Date: Tue, 14 Jan 2025 14:03:25 +0530 Subject: [PATCH 36/39] dummy commit --- .github/workflows/staging.yml | 2 +- README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index bc7d728299..4f6945e08b 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -4,7 +4,7 @@ name: Staging on: push: branches: [master] - pull_request: + pull_request_review: types: [ submitted ] workflow_dispatch: diff --git a/README.md b/README.md index 7325989549..f10d330c31 100644 --- a/README.md +++ b/README.md @@ -238,3 +238,5 @@ We welcome contributions to this project. Please read our [CONTRIBUTING.md](CONT ## License This project is licensed under the [MIT License](LICENSE.md). + +### Testing \ No newline at end of file From b115362e42a2fe73fca633fcd268b38baf1f83b1 Mon Sep 17 00:00:00 2001 From: ag060 Date: Tue, 14 Jan 2025 14:05:31 +0530 Subject: [PATCH 37/39] fixed staging workflow not getting triggered on PR approval --- .github/workflows/staging.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index bc7d728299..4f6945e08b 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -4,7 +4,7 @@ name: Staging on: push: branches: [master] - pull_request: + pull_request_review: types: [ submitted ] workflow_dispatch: From 18e0658d81eadee3821c62acb7fb702813072d3c Mon Sep 17 00:00:00 2001 From: ag060 Date: Tue, 14 Jan 2025 15:20:49 +0530 Subject: [PATCH 38/39] Revert "dummy commit" This reverts commit 5fdcd3e373911d8e85bd96775735e8465f63329c. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index f10d330c31..7325989549 100644 --- a/README.md +++ b/README.md @@ -238,5 +238,3 @@ We welcome contributions to this project. Please read our [CONTRIBUTING.md](CONT ## License This project is licensed under the [MIT License](LICENSE.md). - -### Testing \ No newline at end of file From e195b92d80cdd62ed55fe9e6a0fd95645d1a46be Mon Sep 17 00:00:00 2001 From: Ankush Jain <91221068+ankush-jain-akto@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:39:34 -0800 Subject: [PATCH 39/39] use isMetered instead of localdeploy --- .../src/main/java/com/akto/listener/InitializerListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java index 62d8b49dee..c0345cbcaf 100644 --- a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java +++ b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java @@ -3357,7 +3357,7 @@ public static void processTemplateFilesZip(byte[] zipFile, String author, String if (testConfig != null) { boolean hasSettings = testConfig.getAttributes() != null; - if (hasSettings && !testConfig.getAttributes().getPlan().equals(TemplatePlan.FREE) && DashboardMode.isLocalDeployment()) { + if (hasSettings && !testConfig.getAttributes().getPlan().equals(TemplatePlan.FREE) && !DashboardMode.isMetered()) { skipped++; continue; }