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..609ed8a46a 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,6 +11,11 @@
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+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;
import org.bson.types.ObjectId;
import org.json.JSONObject;
@@ -32,10 +38,45 @@ 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);
-
+
public String downloadReportPDF() {
+ if(reportUrl == null || reportUrl.isEmpty()) {
+ status = "ERROR";
+ addActionError("Report URL cannot be empty");
+ return ERROR.toUpperCase();
+ }
+
+ String reportUrlId;
+ try {
+ String path = new URL(reportUrl).getPath();
+ String[] segments = path.split("/");
+ reportUrlId = segments[segments.length - 1];
+ } catch (Exception e) {
+ 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", reportUrlIdObj));
+ if(testReport != null && (testReport.getPdfReportString() != null && !testReport.getPdfReportString().isEmpty())) {
+ status = "COMPLETED";
+ pdf = testReport.getPdfReportString();
+ return SUCCESS.toUpperCase();
+ }
+ }
+
if (reportId == null) {
// Initiate PDF generation
@@ -89,6 +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();
+ 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);
@@ -146,4 +203,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 13b04d2dfc..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
@@ -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,20 +242,32 @@ 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)
+ let generationStarted = false
+ setTimeout(() => {
+ clearInterval(reportToastInterval)
+ generationStarted = true
+ if(status === "IN_PROGRESS") {
+ 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 startDownloadReponse = await api.downloadReportPDF(null, organizationName, currentDate, reportUrl, true)
const reportId = startDownloadReponse?.reportId
status = startDownloadReponse?.status
+ pdf = startDownloadReponse?.pdf
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") {
@@ -268,16 +280,22 @@ const VulnerabilityReport = () => {
await func.sleep(WAIT_DURATION)
- func.setToast(true, false, "Report PDF generation in progress. Please wait...")
+ 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 {
- pdfError = "Failed to start PDF download"
+ if(status !== "COMPLETED") {
+ pdfError = "Failed to start PDF download"
+ }
}
} catch (err) {
- pdfError = err.message
+ pdfError = err?.response?.data?.actionErrors?.[0] || err.message
}
- clearInterval(progressToastInterval)
+ clearInterval(reportToastInterval)
if (status === "COMPLETED") {
if (pdf === undefined) {
@@ -300,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)
@@ -321,7 +339,9 @@ const VulnerabilityReport = () => {