Skip to content

Commit

Permalink
feat: Generate PDF report for single scan mode (#637)
Browse files Browse the repository at this point in the history
Signed-off-by: Oleg Kopysov <[email protected]>
  • Loading branch information
o-kopysov authored Oct 10, 2024
1 parent a31ae67 commit cf0e1d2
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 49 deletions.
11 changes: 8 additions & 3 deletions doc/docs/user-guide/cli/cli_file.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ Example:
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=</path/to/file/or/folder>
```

* Optionally, generate an HTML report and save it in a specified folder using flag `--build.html.report`.
Replace `path/to/your/folder` with the full path to the folder where you want to save the HTML report,
and `your_report_filename.html` with the desired filename for the report.
* Optionally, generate an HTML report and save it in a specified folder using flag `--build.html.report`,
or PDF report using flag `--build.pdf.report`.
Replace `path/to/your/folder` with the full path to the folder where you want to save the HTML/PDF report,
and `your_report_filename.html` or `your_report_filename.pdf` with the desired filename for the report.

```bash
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=</path/to/file/or/folder> --build.html.report=<your_report_filename.html>
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=</path/to/file/or/folder> --build.pdf.report=<your_report_filename.pdf>
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=</path/to/file/or/folder> --build.html.report=<your_report_filename.html> --build.pdf.report=<your_report_filename.pdf>
```

!!! warning
Expand All @@ -61,4 +64,6 @@ Examples of the command:
```bash
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=test.c
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=test_folder --build.html.report=test/report.html
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=test_folder --build.pdf.report=test/report.pdf
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=test_folder --build.html.report=test/report.html --build.pdf.report=test/report.pdf
```
11 changes: 8 additions & 3 deletions doc/docs/user-guide/cli/cli_pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ Example:
java -jar -Dspring.profiles.active=singlescan -Dgithub.token=<my-token> lpvs-*.jar --github.pull.request=<PR URL>
```

* Optionally, generate an HTML report and save it in a specified folder using flag `--build.html.report`.
Replace `path/to/your/folder` with the full path to the folder where you want to save the HTML report,
and `your_report_filename.html` with the desired filename for the report.
* Optionally, generate an HTML report and save it in a specified folder using flag `--build.html.report`,
or PDF report using flag `--build.pdf.report`.
Replace `path/to/your/folder` with the full path to the folder where you want to save the HTML/PDF report,
and `your_report_filename.html` or `your_report_filename.pdf` with the desired filename for the report.

```bash
java -jar -Dspring.profiles.active=singlescan -Dgithub.token=<my-token> lpvs-*.jar --github.pull.request=<PR URL> --build.html.report=</path/to/your/folder/your_report_filename.html>
java -jar -Dspring.profiles.active=singlescan -Dgithub.token=<my-token> lpvs-*.jar --github.pull.request=<PR URL> --build.pdf.report=</path/to/your/folder/your_report_filename.pdf>
java -jar -Dspring.profiles.active=singlescan -Dgithub.token=<my-token> lpvs-*.jar --github.pull.request=<PR URL> --build.html.report=</path/to/your/folder/your_report_filename.html> --build.pdf.report=</path/to/your/folder/your_report_filename.pdf>
```

!!! warning
Expand All @@ -61,4 +64,6 @@ Examples of the command:
```bash
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --github.pull.request=https://github.com/Samsung/LPVS/pull/2
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --github.pull.request=https://github.com/Samsung/LPVS/pull/2 --build.html.report=report.html
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --github.pull.request=https://github.com/Samsung/LPVS/pull/2 --build.pdf.report=report.pdf
java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --github.pull.request=https://github.com/Samsung/LPVS/pull/2 --build.html.report=report.html --build.pdf.report=report.pdf
```
3 changes: 3 additions & 0 deletions doc/docs/user-guide/config/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ The following command line options are available:
- `--build.html.report`: This setting specifies the path to the HTML report file which will be generated after the scan.
If it is not specified, no HTML report will be generated and result of the scan will be displayed in the console.

- `--build.pdf.report`: This setting specifies the path to the PDF report file which will be generated after the scan.
If it is not specified, no PDF report will be generated and result of the scan will be displayed in the console.

- `--github.pull.request`: This setting specifies the pull request URL which should be scanned by the LPVS application.

- `--local.path`: This setting specifies the path to the local file or folder which should be scanned by the LPVS application.
Expand Down
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@
</properties>

<dependencies>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-core</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand Down
51 changes: 41 additions & 10 deletions src/main/java/com/lpvs/entity/report/LPVSReportBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import com.lpvs.entity.enums.LPVSVcs;
import com.lpvs.entity.LPVSConflict;
import com.lpvs.util.LPVSCommentUtil;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import io.micrometer.common.util.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -22,10 +24,7 @@
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
Expand Down Expand Up @@ -245,6 +244,32 @@ public static void saveHTMLToFile(String htmlContent, String filePath) {
}
}

/**
* Saves PDF report to given location.
*
* @param htmlContent The string, containing report in HTML format.
* @param filePath The path to expected pdf report file.
*/
public static void generatePdfFromHtml(String htmlContent, String filePath) {
String report =
htmlContent
.replaceAll(" class=\"panel\"", "")
.replaceAll("<script>[\\s\\S]*?</script>", "")
.replaceAll("<button.*</button>", "")
.replaceAll("body [\\s\\S]*?}", "");
try (FileOutputStream os = new FileOutputStream(filePath)) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.withHtmlContent(report, new File(filePath).toURI().toString())
// Size for the standard A4 landscape page
.useDefaultPageSize(11.7f, 8.3f, BaseRendererBuilder.PageSizeUnits.INCHES)
.toStream(os)
.run();
log.info("LPVS report saved to: " + filePath);
} catch (IOException ex) {
log.error("Error during saving PDF report: " + ex.getMessage());
}
}

/**
* Generates a comment to the pull request for publication to the VCS.
*
Expand Down Expand Up @@ -508,12 +533,14 @@ private void addBlockOfTableForLicenseTypeMD(
*/
private String generateLicenseConflictsTableHTML(List<LPVSConflict<String, String>> conflicts) {
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.append("<table>");
htmlBuilder.append("<table>").append("<thead>");
htmlBuilder
.append("<tr>")
.append("<th>Conflict</th>")
.append("<th>Explanation</th>")
.append("<tr>");
.append("</tr>")
.append("</thead>")
.append("<tbody>");

for (LPVSConflict<String, String> conflict : conflicts) {
htmlBuilder
Expand All @@ -528,7 +555,7 @@ private String generateLicenseConflictsTableHTML(List<LPVSConflict<String, Strin
.append("</td>")
.append("</tr>");
}
htmlBuilder.append("</table>");
htmlBuilder.append("</tbody>").append("</table>");
return htmlBuilder.toString();
}

Expand Down Expand Up @@ -559,8 +586,9 @@ private String getExplanationForLicenseConflict(String lic1, String lic2) {
private String generateLicenseTableHTML(
Map<String, GroupInfo<?>> detectedLicenseInfo, LPVSQueue webhookConfig, LPVSVcs vcs) {
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.append("<table>");
htmlBuilder
.append("<table>")
.append("<thead>")
.append("<tr>")
.append("<th>License Type / Explanation</th>")
.append("<th>License SPDX ID</th>")
Expand All @@ -570,7 +598,10 @@ private String generateLicenseTableHTML(
.append("<th>Component File Path</th>")
.append("<th>Matched Lines</th>")
.append("<th>Match Value</th>")
.append("<tr>");
.append("</tr>")
.append("</thead>")
.append("<tbody>");

// Prohibited licenses
addBlockOfTableForLicenseTypeHTML(
htmlBuilder, detectedLicenseInfo, prohibited, webhookConfig, vcs);
Expand All @@ -584,7 +615,7 @@ private String generateLicenseTableHTML(
addBlockOfTableForLicenseTypeHTML(
htmlBuilder, detectedLicenseInfo, permitted, webhookConfig, vcs);

htmlBuilder.append("</table>");
htmlBuilder.append("</tbody>").append("</table>");
return htmlBuilder.toString();
}

Expand Down
51 changes: 38 additions & 13 deletions src/main/java/com/lpvs/service/scan/LPVSDetectService.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import lombok.extern.slf4j.Slf4j;

import static com.lpvs.entity.report.LPVSReportBuilder.generatePdfFromHtml;
import static com.lpvs.entity.report.LPVSReportBuilder.saveHTMLToFile;

/**
Expand Down Expand Up @@ -85,6 +86,12 @@ public class LPVSDetectService {
@Value("${build.html.report:}")
private String htmlReport;

/**
* Optional parameter to save pdf report to specified location.
*/
@Value("${build.pdf.report:}")
private String pdfReport;

/**
* Spring application context.
*/
Expand Down Expand Up @@ -192,20 +199,19 @@ public void runSingleScan() {

// Report generation
// 1. HTML format
if (generateReport && !StringUtils.isBlank(htmlReport)) {
File report = new File(HtmlUtils.htmlEscape(htmlReport));
String folderPath = report.getParent();
if (folderPath == null) {
folderPath = ".";
if (generateReport
&& (!StringUtils.isBlank(htmlReport) || !StringUtils.isBlank(pdfReport))) {
String reportContent =
reportBuilder.generateHtmlReportSingleScan(
path, scanResult, detectedConflicts, null, null);
if (!StringUtils.isBlank(htmlReport) && checkFolderForReport(htmlReport)) {
saveHTMLToFile(
reportContent,
new File(HtmlUtils.htmlEscape(htmlReport)).getAbsolutePath());
}
File folder = new File(folderPath);
if (folder.exists() && folder.isDirectory()) {
String reportFile =
reportBuilder.generateHtmlReportSingleScan(
path, scanResult, detectedConflicts, null, null);
saveHTMLToFile(reportFile, report.getAbsolutePath());
} else {
log.error("Error: The parent directory '" + folder.getPath() + "' does not exist.");
if (!StringUtils.isBlank(pdfReport) && checkFolderForReport(pdfReport)) {
generatePdfFromHtml(
reportContent, new File(HtmlUtils.htmlEscape(pdfReport)).getAbsolutePath());
}
SpringApplication.exit(ctx, () -> 0);
} else if (generateReport) {
Expand All @@ -219,6 +225,25 @@ public void runSingleScan() {
}
}

/**
* Checks if the folder exists where the report will be saved.
* @param reportPath the path to the report file
* @return {@code true} if the folder exists and is a directory, otherwise {@code false}
*/
private boolean checkFolderForReport(String reportPath) {
File report = new File(HtmlUtils.htmlEscape(reportPath));
String folderPath = report.getParent();
if (folderPath == null) {
folderPath = ".";
}
File folder = new File(folderPath);
if (folder.exists() && folder.isDirectory()) {
return true;
}
log.error("Error: The parent directory '" + folder.getPath() + "' does not exist.");
return false;
}

/**
* Creates a new LPVSQueue object with default values for a local scan.
*
Expand Down
20 changes: 14 additions & 6 deletions src/main/resources/templates/report_single_scan.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<meta charset="UTF-8" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />

<title th:text="${title}">title</title>

Expand Down Expand Up @@ -55,15 +55,23 @@
transition: max-height 0.2s ease-out;
}
table {
border-collapse: separate;
border-collapse: collapse;
width: 100%;
padding: 20px;
table-layout: fixed;
}
table td, table th {
padding: 8px;
text-align: left;
border: 1px solid black;
width: auto;
word-wrap: break-words;
}
table thead {
display: table-header-group;
}
table tbody {
display: table-row-group;
}
.mt-30 {
margin-top: 30px;
Expand All @@ -87,7 +95,7 @@ <h1><b>Report</b> - <b>L</b>icense <b>P</b>re-<b>V</b>alidation <b>S</b>ervice (
<b>Used scanner:</b> <span th:text=${usedScanner}>usedScanner</span><br/>
<b>Version of LPVS:</b> <span th:text=${lpvsVersion}>lpvsVersion</span><br/>
</div>
<hr>
<hr />
<h3 class="ml-30">Detected Licenses</h3>
<div class="items" th:if="${licenseDetected} > 0">
<b>Potential license issues detected:</b> <span th:text=${licenseDetected}>licenseDetected</span>
Expand All @@ -107,7 +115,7 @@ <h3 class="ml-30">Detected Licenses</h3>
<p th:utext=${licenseTable}></p>
</div>
</div>
<hr>
<hr />
<h3 class="ml-30">Detected License Conflicts</h3>
<div class="items" th:if="${licenseConflicts} > 0">
<b>Potential license conflict(s) detected:</b> <span th:text=${licenseConflicts}>licenseConflicts</span>
Expand Down
19 changes: 18 additions & 1 deletion src/test/java/com/lpvs/entity/report/LPVSReportBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.text.SimpleDateFormat;
import java.util.*;

import static com.lpvs.entity.report.LPVSReportBuilder.generatePdfFromHtml;
import static com.lpvs.entity.report.LPVSReportBuilder.saveHTMLToFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -297,11 +298,27 @@ void testSaveHTMLToFile() throws IOException {
}

@Test
void saveHTMLToFile_CatchBlock_N() {
void testSavePDFToFile() throws IOException {
String htmlContent = "<html><body><p class=\"panel\">Test HTML</p></body></html>";
String filePath = "test-output.pdf";

generatePdfFromHtml(htmlContent, filePath);

Path path = Paths.get(filePath);
assertTrue(Files.exists(path));

// Clean up: delete the created file
Files.deleteIfExists(path);
}

@Test
void saveHTMLandPDFtoFile_CatchBlock_N() {
String htmlContent = "<html><body></body></html>";
Path invalidPath = tempDir.resolve("invalid/path/with/special/characters");
saveHTMLToFile(htmlContent, invalidPath.toString());
assertFalse(Files.exists(invalidPath));
generatePdfFromHtml(htmlContent, invalidPath.toString());
assertFalse(Files.exists(invalidPath));
}

private LPVSFile createSampleFile(
Expand Down
Loading

0 comments on commit cf0e1d2

Please sign in to comment.