Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Generate PDF report for single scan mode #637

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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