Programming

basic programming

Generate an HTML Trivy Report Ordered by Severity

Trivy is an Open Source tools for scanning software artifacts, and image vulnerabilities, which is maintained by Aqua Security. We can also generate Trivy reports and displaying the list of vulnerabilities as an HTML report. We can also create our own custom HTML template that would suitable for our needs.

Below is a sample HTML report that we use for sorting vulnerabilities based on its severity level,

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
{{- if . }}
    <style>
      * {
        font-family: Arial, Helvetica, sans-serif;
      }
      h1 {
        text-align: center;
      }
      .group-header th {
        font-size: 200%;
      }
      .sub-header th {
        font-size: 150%;
      }
      table, th, td {
        border: 1px solid black;
        border-collapse: collapse;
        white-space: nowrap;
        padding: .3em;
      }
      table {
        margin: 0 auto;
      }
      .severity {
        text-align: center;
        font-weight: bold;
        color: #fafafa;
      }
      .severity-LOW .severity { background-color: #5fbb31; }
      .severity-MEDIUM .severity { background-color: #e9c600; }
      .severity-HIGH .severity { background-color: #ff8800; }
      .severity-CRITICAL .severity { background-color: #e40000; }
      .severity-UNKNOWN .severity { background-color: #747474; }
      .severity-LOW { background-color: #5fbb3160; }
      .severity-MEDIUM { background-color: #e9c60060; }
      .severity-HIGH { background-color: #ff880060; }
      .severity-CRITICAL { background-color: #e4000060; }
      .severity-UNKNOWN { background-color: #74747460; }
      table tr td:first-of-type {
        font-weight: bold;
      }
      .links a,
      .links[data-more-links=on] a {
        display: block;
      }
      .links[data-more-links=off] a:nth-of-type(1n+5) {
        display: none;
      }
      a.toggle-more-links { cursor: pointer; }
    </style>
    <title>{{- escapeXML ( index . 0 ).Target }} - Trivy Report - {{ now }} </title>
    <script>
      window.onload = function() {
        document.querySelectorAll('td.links').forEach(function(linkCell) {
          var links = [].concat.apply([], linkCell.querySelectorAll('a'));
          [].sort.apply(links, function(a, b) {
            return a.href > b.href ? 1 : -1;
          });
          links.forEach(function(link, idx) {
            if (links.length > 3 && 3 === idx) {
              var toggleLink = document.createElement('a');
              toggleLink.innerText = "Toggle more links";
              toggleLink.href = "#toggleMore";
              toggleLink.setAttribute("class", "toggle-more-links");
              linkCell.appendChild(toggleLink);
            }
            linkCell.appendChild(link);
          });
        });
        document.querySelectorAll('a.toggle-more-links').forEach(function(toggleLink) {
          toggleLink.onclick = function() {
            var expanded = toggleLink.parentElement.getAttribute("data-more-links");
            toggleLink.parentElement.setAttribute("data-more-links", "on" === expanded ? "off" : "on");
            return false;
          };
        });
      };
	  
	  window.addEventListener('DOMContentLoaded', () => {	  
			const severityOrder = {
			  "CRITICAL": 1,
			  "HIGH": 2,
			  "MEDIUM": 3,
			  "LOW": 4
			};

			const table = document.getElementById("myTable");
			const tbody = table.tBodies[0];
			const rows = Array.from(tbody.rows);
			
			const columnIndex = 2; 
			
			rows.sort((a, b) => {
				  const cellA = a.cells[columnIndex];
				  const cellB = b.cells[columnIndex];

				  if (!cellA || !cellB) {					
					return 0; // Skip sort if data is malformed
				  }
				  
				  if (cellA.textContent.trim().toUpperCase()=='SEVERITY' || cellB.textContent.trim().toUpperCase()=='SEVERITY') {		
					return 0; // Skip sort if data is malformed
				  }

				  const sevA = cellA.textContent.trim().toUpperCase();
				  const sevB = cellB.textContent.trim().toUpperCase();

				  const orderA = severityOrder[sevA] ?? 999;
				  const orderB = severityOrder[sevB] ?? 999;

				  return orderA - orderB;
			});

			rows.forEach(row => tbody.appendChild(row));
	  });
	
    </script>
  </head>
  <body>
    <h1>{{- escapeXML ( index . 0 ).Target }} - Trivy Report - {{ now }}</h1>
    <table id="myTable">
    {{- range . }}
      <tr class="group-header"><th colspan="6">{{ .Type | toString | escapeXML }}</th></tr>
      {{- if (eq (len .Vulnerabilities) 0) }}
      <tr><th colspan="6">No Vulnerabilities found</th></tr>
      {{- else }}
      <tr class="sub-header">
        <th>Package</th>
        <th>Vulnerability ID</th>
        <th>Severity</th>
        <th>Installed Version</th>
        <th>Fixed Version</th>
        <th>Links</th>
      </tr>
        {{- range .Vulnerabilities }}
      <tr class="severity-{{ escapeXML .Vulnerability.Severity }}">
        <td class="pkg-name">{{ escapeXML .PkgName }}</td>
        <td>{{ escapeXML .VulnerabilityID }}</td>
        <td class="severity">{{ escapeXML .Vulnerability.Severity }}</td>
        <td class="pkg-version">{{ escapeXML .InstalledVersion }}</td>
        <td>{{ escapeXML .FixedVersion }}</td>
        <td class="links" data-more-links="off">
          {{- range .Vulnerability.References }}
          <a href={{ escapeXML . | printf "%q" }}>{{ escapeXML . }}</a>
          {{- end }}
        </td>
      </tr>
        {{- end }}
      {{- end }}
      {{- if (eq (len .Misconfigurations ) 0) }}
      <tr><th colspan="6">No Misconfigurations found</th></tr>
      {{- else }}
      <tr class="sub-header">
        <th>Type</th>
        <th>Misconf ID</th>
        <th>Check</th>
        <th>Severity</th>
        <th>Message</th>
      </tr>
        {{- range .Misconfigurations }}
      <tr class="severity-{{ escapeXML .Severity }}">
        <td class="misconf-type">{{ escapeXML .Type }}</td>
        <td>{{ escapeXML .ID }}</td>
        <td class="misconf-check">{{ escapeXML .Title }}</td>
        <td class="severity">{{ escapeXML .Severity }}</td>
        <td class="link" data-more-links="off"  style="white-space:normal;">
          {{ escapeXML .Message }}
          <br>
            <a href={{ escapeXML .PrimaryURL | printf "%q" }}>{{ escapeXML .PrimaryURL }}</a>
          </br>
        </td>
      </tr>
        {{- end }}
      {{- end }}
    {{- end }}
    </table>
{{- else }}
  </head>
  <body>
    <h1>Trivy Returned Empty Report</h1>
{{- end }}
  </body>
</html>

Save it as “html.tpl”, and run the below command

$ trivy image --scanners vuln  my-image:latest --format template \
       --template "@/tmp/html.tpl" -o /tmp/my-image-vulnerabilities.html

It shall generate a report like below,

Java 21 and Maven Error PKIX when Connecting to Self Signed Nexus Registry

Had this error while doing a maven build

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

With a complete error log,

$ mvn clean package -s settings.xml

.......

[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Unresolveable build extension: Plugin com.redhat.quarkus.platform:quarkus-maven-plugin:3.15.3.SP1-redhat-00002 or one of its dependencies could not be resolved: Failed to collect dependencies at com.redhat.quarkus.platform:quarkus-maven-plugin:jar:3.15.3.SP1-redhat-00002 -> io.quarkus:quarkus-bootstrap-maven-resolver:jar:3.15.3.redhat-00004 -> io.smallrye.beanbag:smallrye-beanbag-maven:jar:1.5.2.redhat-00001 -> io.smallrye.beanbag:smallrye-beanbag-sisu:jar:1.5.2.redhat-00001 -> javax.inject:javax.inject:jar:1.0.0.redhat-00014 @
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.redhat.quarkus.platform:quarkus-camel-bom:pom:3.15.3.SP1-redhat-00002 (present, but unavailable): Could not transfer artifact com.redhat.quarkus.platform:quarkus-camel-bom:pom:3.15.3.SP1-redhat-00002 from/to mvn-repository (https://nexus/maven-group/): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target @ line 32, column 25

But somehow the previous solution on my previous post is not working. Maybe because of a different Java version or Maven version. So I need to find another solution, and this is what was working on my end.

First we need to take the self-signed certificate that belongs to the remote Nexus instance

$ echo "" | openssl s_client -connect nexus:8443  -showcerts 2>/dev/null | openssl x509 -out nexus.crt

Import it into our key

$ keytool -import -alias mycert -keystore /tmp/customcacerts -file nexus.crt -storepass changeit -noprompt

And use it on our Maven build

$ ./mvnw -Djavax.net.ssl.trustStore=/tmp/customcacerts \
        -Djavax.net.ssl.trustStorePassword=changeit clean install \ 
		-s settings.xml 

Everything is working well after that.

Maven Error PKIX When Connecting to Self Signed Nexus Repository

Had this error while doing a maven build

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

With a complete error log,

$ mvn clean package -s settings.xml

.......

[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Unresolveable build extension: Plugin com.redhat.quarkus.platform:quarkus-maven-plugin:3.15.3.SP1-redhat-00002 or one of its dependencies could not be resolved: Failed to collect dependencies at com.redhat.quarkus.platform:quarkus-maven-plugin:jar:3.15.3.SP1-redhat-00002 -> io.quarkus:quarkus-bootstrap-maven-resolver:jar:3.15.3.redhat-00004 -> io.smallrye.beanbag:smallrye-beanbag-maven:jar:1.5.2.redhat-00001 -> io.smallrye.beanbag:smallrye-beanbag-sisu:jar:1.5.2.redhat-00001 -> javax.inject:javax.inject:jar:1.0.0.redhat-00014 @
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: com.redhat.quarkus.platform:quarkus-camel-bom:pom:3.15.3.SP1-redhat-00002 (present, but unavailable): Could not transfer artifact com.redhat.quarkus.platform:quarkus-camel-bom:pom:3.15.3.SP1-redhat-00002 from/to mvn-repository (https://nexus/maven-group/): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target @ line 32, column 25
[ERROR] 'dependencies.dependency.version' for org.apache.camel.quarkus:camel-quarkus-direct:jar is missing. @ line 45, column 21
[ERROR] 'dependencies.dependency.version' for org.apache.camel.quarkus:camel-quarkus-jackson:jar is missing. @ line 49, column 21
[ERROR] 'dependencies.dependency.version' for org.apache.camel.quarkus:camel-quarkus-rest-openapi:jar is missing. @ line 53, column 21
[ERROR] 'dependencies.dependency.version' for org.apache.camel.quarkus:camel-quarkus-rest:jar is missing. @ line 57, column 21
 @
[ERROR] The build could not read 1 project -> [Help 1]

Workaround is quite simple,

$ mvn -Dmaven.wagon.http.ssl.insecure=true clean package -s settings.xml

Script to Generate Series of Thread Dump

There are times when we want to see which threads are blocking our requests, and generating a thread dump is one way to find it out. The thing is sometimes we need to create a series of thread dumps, that’s why i have this script to do it for me. Create a file with the name of “jstack.sh”, with below script as its content

# number of cycles.
LOOP=3
# seconds between cycles.
INTERVAL=10

for ((i=1; i <= $LOOP; i++))
do
   _now=$(date)
   echo "\n \n ${_now}" >> cpu.out
   top -l 1 -o cpu -pid $1 >> cpu.out
   echo "\n \n ${_now}" >> tdump.out
   jstack -l $1 >> tdump.out
   echo "thread dump #" $i
   if [ $i -lt $LOOP ]; then
      echo "Sleeping..."
      sleep $INTERVAL
   fi
done

Repository for above script can be found below,

https://github.com/edwin/java-thread-dump

Deploying Python App using S2I to Openshift Container Platform

For this sample, we are trying to deploy a Flask application to Openshift without any CI/CD tools involved. Means we are deploying directly by using S2I functionality thru OCP Dashboard.

Lets start with a simple hello world Python application, it is basically one simple py file

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "<h1>Hello, World!</h1>"

app.run(host="0.0.0.0", port=8080)

and one requirements.txt file,

flask

And after that, we can push our complete Python code to a Git repository. For example, im using below Git repo,

https://github.com/edwin/hello-world-flask

We can deploy to Openshift from the Developer dashboard,

Put the Git repo URL there

It will trigger a build process

Would took a while

But it will deployed successfully after a while

We can access the deployed apps directly after we expose a Route to our app.