From af07292b72e81e88b67218f49c84893d9201d33f Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Thu, 7 Oct 2021 03:13:38 -0300 Subject: [PATCH 1/4] Creates `infer` deployment files --- deployments/dockerfiles/infer/Dockerfile | 49 +++++++++++++++++++ .../dockerfiles/infer/mvn-entrypoint.sh | 43 ++++++++++++++++ deployments/scripts/build-containers.sh | 3 +- .../scripts/check-containers-version.sh | 4 +- deployments/scripts/generate-local-token.sh | 0 deployments/scripts/push-containers.sh | 3 ++ 6 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 deployments/dockerfiles/infer/Dockerfile create mode 100644 deployments/dockerfiles/infer/mvn-entrypoint.sh mode change 100644 => 100755 deployments/scripts/generate-local-token.sh diff --git a/deployments/dockerfiles/infer/Dockerfile b/deployments/dockerfiles/infer/Dockerfile new file mode 100644 index 00000000..87b611f0 --- /dev/null +++ b/deployments/dockerfiles/infer/Dockerfile @@ -0,0 +1,49 @@ +# Dockerfile used to create "huskyci/npmaudit" image +# https://hub.docker.com/r/huskyci/infer/ +# +# Dockerfile highly inspired by: +# - https://github.com/facebook/infer/tree/main/docker +# - https://github.com/globocom/huskyCI/tree/master/deployments/dockerfiles/spotbugs +FROM debian:bullseye-slim + +ARG MAVEN_VERSION=3.8.3 +ARG GRADLE_VERSION=5.6.2 + +# mkdir the man/man1 directory due to Debian bug #863199 +RUN apt-get update \ + && mkdir -p /usr/share/man/man1 \ + && apt-get install --yes --no-install-recommends \ + curl \ + wget \ + unzip \ + libc6-dev \ + openjdk-11-jdk-headless \ + sqlite3 \ + xz-utils \ + zlib1g-dev \ + && mkdir -p /usr/share/maven /usr/share/maven/ref \ + && curl -fsSL -o /tmp/apache-maven.tar.gz https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ + && rm -f /tmp/apache-maven.tar.gz \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn \ + && mkdir -p /opt \ + && cd /opt \ + && wget -nc -O gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip \ + && unzip gradle.zip \ + && rm -f gradle.zip \ + && mv gradle-${GRADLE_VERSION} gradle \ + && rm -rf /var/lib/apt/lists/* + +COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh + +# Download the Infer release +RUN INFER_VERSION=v1.1.0; \ + cd /opt \ + && curl -sL \ + https://github.com/facebook/infer/releases/download/${INFER_VERSION}/infer-linux64-${INFER_VERSION}.tar.xz | \ + tar xJ \ + && rm -f /infer \ + && ln -s ${PWD}/infer-linux64-$INFER_VERSION /infer + +# Install infer +ENV PATH /infer/bin:${PATH} diff --git a/deployments/dockerfiles/infer/mvn-entrypoint.sh b/deployments/dockerfiles/infer/mvn-entrypoint.sh new file mode 100644 index 00000000..dd7090a0 --- /dev/null +++ b/deployments/dockerfiles/infer/mvn-entrypoint.sh @@ -0,0 +1,43 @@ +#! /bin/bash -eu +# +# Copyright 2021 Globo.com authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. +# +# This script will Copy files from /usr/share/maven/ref into ${MAVEN_CONFIG} +# So the initial ~/.m2 is set with expected content. +# +set -o pipefail + +copy_reference_file() { + local root="${1}" + local f="${2%/}" + local logfile="${3}" + local rel="${f/${root}/}" # path relative to /usr/share/maven/ref/ + echo "$f" >> "$logfile" + echo " $f -> $rel" >> "$logfile" + if [[ ! -e ${MAVEN_CONFIG}/${rel} || $f = *.override ]] + then + echo "copy $rel to ${MAVEN_CONFIG}" >> "$logfile" + mkdir -p "${MAVEN_CONFIG}/$(dirname "${rel}")" + cp -r "${f}" "${MAVEN_CONFIG}/${rel}"; + fi; +} + +copy_reference_files() { + local log="$MAVEN_CONFIG/copy_reference_file.log" + + if (touch "${log}" > /dev/null 2>&1) + then + echo "--- Copying files at $(date)" >> "$log" + find /usr/share/maven/ref/ -type f -exec bash -eu -c 'copy_reference_file /usr/share/maven/ref/ "$1" "$2"' _ {} "$log" \; + else + echo "Can not write to ${log}. Wrong volume permissions? Carrying on ..." + fi +} + +export -f copy_reference_file +copy_reference_files +unset MAVEN_CONFIG + +/usr/bin/mvn install -Dmaven.test.skip=true \ No newline at end of file diff --git a/deployments/scripts/build-containers.sh b/deployments/scripts/build-containers.sh index 11756cac..0dd42226 100755 --- a/deployments/scripts/build-containers.sh +++ b/deployments/scripts/build-containers.sh @@ -17,4 +17,5 @@ docker build deployments/dockerfiles/npmaudit/ -t huskyci/yarnaudit:latest docker build deployments/dockerfiles/safety/ -t huskyci/safety:latest docker build deployments/dockerfiles/gitleaks/ -t huskyci/gitleaks:latest docker build deployments/dockerfiles/spotbugs/ -t huskyci/spotbugs:latest -docker build deployments/dockerfiles/tfsec/ -t huskyci/tfsec:latest \ No newline at end of file +docker build deployments/dockerfiles/tfsec/ -t huskyci/tfsec:latest +docker build deployments/dockerfiles/infer/ -t huskyci/infer:latest \ No newline at end of file diff --git a/deployments/scripts/check-containers-version.sh b/deployments/scripts/check-containers-version.sh index fc43fdf5..bb6c3652 100755 --- a/deployments/scripts/check-containers-version.sh +++ b/deployments/scripts/check-containers-version.sh @@ -19,6 +19,7 @@ safetyVersion=$(docker run --rm huskyci/safety:latest safety --version | awk -F gitleaksVersion=$(docker run --rm huskyci/gitleaks:latest gitleaks --version) spotbugsVersion=$(docker run --rm huskyci/spotbugs:latest cat /opt/spotbugs/version) tfsecVersion=$(docker run --rm huskyci/tfsec:latest ./tfsec -v) +inferVersion=$(docker run --rm huskyci/infer:latest infer --version | grep Infer | awk -F " " '{print $3}') echo "bandit: $banditVersion" echo "brakeman: $brakemanVersion" @@ -30,4 +31,5 @@ echo "yarnauditVersion: $yarnAuditVersion" echo "safetyVersion: $safetyVersion" echo "gitleaksVersion: $gitleaksVersion" echo "spotbugsVersion: $spotbugsVersion" -echo "tfsecVersion: $tfsecVersion" \ No newline at end of file +echo "tfsecVersion: $tfsecVersion" +echo "inferVersion: $inferVersion" \ No newline at end of file diff --git a/deployments/scripts/generate-local-token.sh b/deployments/scripts/generate-local-token.sh old mode 100644 new mode 100755 diff --git a/deployments/scripts/push-containers.sh b/deployments/scripts/push-containers.sh index 91469707..e30ae505 100755 --- a/deployments/scripts/push-containers.sh +++ b/deployments/scripts/push-containers.sh @@ -18,6 +18,7 @@ safetyVersion=$(docker run --rm huskyci/safety:latest safety --version | awk -F gitleaksVersion=$(docker run --rm huskyci/gitleaks:latest gitleaks --version) spotbugsVersion=$(docker run --rm huskyci/spotbugs:latest cat /opt/spotbugs/version) tfsecVersion=$(docker run --rm huskyci/tfsec:latest ./tfsec -v) +inferVersion=$(docker run --rm huskyci/infer:latest infer --version | grep Infer | awk -F " " '{print $3}') docker tag "huskyci/bandit:latest" "huskyci/bandit:$banditVersion" docker tag "huskyci/brakeman:latest" "huskyci/brakeman:$brakemanVersion" @@ -30,6 +31,7 @@ docker tag "huskyci/safety:latest" "huskyci/safety:$safetyVersion" docker tag "huskyci/gitleaks:latest" "huskyci/gitleaks:$gitleaksVersion" docker tag "huskyci/spotbugs:latest" "huskyci/spotbugs:$spotbugsVersion" docker tag "huskyci/tfsec:latest" "huskyci/tfsec:$tfsecVersion" +docker tag "huskyci/infer:latest" "huskyci/infer:$inferVersion" docker push "huskyci/bandit:latest" && docker push "huskyci/bandit:$banditVersion" docker push "huskyci/brakeman:latest" && docker push "huskyci/brakeman:$brakemanVersion" @@ -42,3 +44,4 @@ docker push "huskyci/safety:latest" && docker push "huskyci/safety:$safetyVersio docker push "huskyci/gitleaks:latest" && docker push "huskyci/gitleaks:$gitleaksVersion" docker push "huskyci/spotbugs:latest" && docker push "huskyci/spotbugs:$spotbugsVersion" docker push "huskyci/tfsec:latest" && docker push "huskyci/tfsec:$tfsecVersion" +docker push "huskyci/infer:latest" && docker push "huskyci/infer:$inferVersion" From c66a339d89006a74bc85a7c136e58d79524f445d Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Thu, 7 Oct 2021 03:15:21 -0300 Subject: [PATCH 2/4] Modifies `client` to print out the Infer output --- client/analysis/output.go | 54 ++++++++++++++++++++++++++++++++++----- client/types/infer.go | 18 +++++++++++++ client/types/types.go | 6 +++-- 3 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 client/types/infer.go diff --git a/client/analysis/output.go b/client/analysis/output.go index ca2a6e2f..a3b644bc 100644 --- a/client/analysis/output.go +++ b/client/analysis/output.go @@ -72,6 +72,11 @@ func printSTDOUTOutput(analysis types.Analysis) { printSTDOUTOutputTFSec(outputJSON.HclResults.HuskyCITFSecOutput.MediumVulns) printSTDOUTOutputTFSec(outputJSON.HclResults.HuskyCITFSecOutput.HighVulns) + // infer + printSTDOUTOutputInfer(outputJSON.JavaResults.HuskyCIInferOutput.LowVulns) + printSTDOUTOutputInfer(outputJSON.JavaResults.HuskyCIInferOutput.MediumVulns) + printSTDOUTOutputInfer(outputJSON.JavaResults.HuskyCIInferOutput.HighVulns) + printAllSummary(analysis) } @@ -190,22 +195,33 @@ func prepareAllSummary(analysis types.Analysis) { outputJSON.Summary.TFSecSummary.FoundVuln = true } + // Infer summary + outputJSON.Summary.InferSummary.LowVuln = len(outputJSON.JavaResults.HuskyCIInferOutput.LowVulns) + outputJSON.Summary.InferSummary.MediumVuln = len(outputJSON.JavaResults.HuskyCIInferOutput.MediumVulns) + outputJSON.Summary.InferSummary.HighVuln = len(outputJSON.JavaResults.HuskyCIInferOutput.HighVulns) + if len(outputJSON.JavaResults.HuskyCIInferOutput.LowVulns) > 0 || len(outputJSON.JavaResults.HuskyCIInferOutput.NoSecVulns) > 0 { + outputJSON.Summary.InferSummary.FoundInfo = true + } + if len(outputJSON.JavaResults.HuskyCIInferOutput.MediumVulns) > 0 || len(outputJSON.JavaResults.HuskyCIInferOutput.HighVulns) > 0 { + outputJSON.Summary.InferSummary.FoundVuln = true + } + // Total summary - if outputJSON.Summary.GosecSummary.FoundVuln || outputJSON.Summary.BanditSummary.FoundVuln || outputJSON.Summary.SafetySummary.FoundVuln || outputJSON.Summary.BrakemanSummary.FoundVuln || outputJSON.Summary.NpmAuditSummary.FoundVuln || outputJSON.Summary.YarnAuditSummary.FoundVuln || outputJSON.Summary.GitleaksSummary.FoundVuln || outputJSON.Summary.SpotBugsSummary.FoundVuln || outputJSON.Summary.TFSecSummary.FoundVuln { + if outputJSON.Summary.GosecSummary.FoundVuln || outputJSON.Summary.BanditSummary.FoundVuln || outputJSON.Summary.SafetySummary.FoundVuln || outputJSON.Summary.BrakemanSummary.FoundVuln || outputJSON.Summary.NpmAuditSummary.FoundVuln || outputJSON.Summary.YarnAuditSummary.FoundVuln || outputJSON.Summary.GitleaksSummary.FoundVuln || outputJSON.Summary.SpotBugsSummary.FoundVuln || outputJSON.Summary.TFSecSummary.FoundVuln || outputJSON.Summary.InferSummary.FoundVuln { outputJSON.Summary.TotalSummary.FoundVuln = true types.FoundVuln = true - } else if outputJSON.Summary.GosecSummary.FoundInfo || outputJSON.Summary.BanditSummary.FoundInfo || outputJSON.Summary.SafetySummary.FoundInfo || outputJSON.Summary.BrakemanSummary.FoundInfo || outputJSON.Summary.NpmAuditSummary.FoundInfo || outputJSON.Summary.YarnAuditSummary.FoundInfo || outputJSON.Summary.GitleaksSummary.FoundInfo || outputJSON.Summary.SpotBugsSummary.FoundInfo || outputJSON.Summary.TFSecSummary.FoundInfo { + } else if outputJSON.Summary.GosecSummary.FoundInfo || outputJSON.Summary.BanditSummary.FoundInfo || outputJSON.Summary.SafetySummary.FoundInfo || outputJSON.Summary.BrakemanSummary.FoundInfo || outputJSON.Summary.NpmAuditSummary.FoundInfo || outputJSON.Summary.YarnAuditSummary.FoundInfo || outputJSON.Summary.GitleaksSummary.FoundInfo || outputJSON.Summary.SpotBugsSummary.FoundInfo || outputJSON.Summary.TFSecSummary.FoundInfo || outputJSON.Summary.InferSummary.FoundInfo { outputJSON.Summary.TotalSummary.FoundInfo = true types.FoundInfo = true } totalNoSec = outputJSON.Summary.BrakemanSummary.NoSecVuln + outputJSON.Summary.BanditSummary.NoSecVuln + outputJSON.Summary.GosecSummary.NoSecVuln + outputJSON.Summary.GitleaksSummary.NoSecVuln - totalLow = outputJSON.Summary.BrakemanSummary.LowVuln + outputJSON.Summary.SafetySummary.LowVuln + outputJSON.Summary.BanditSummary.LowVuln + outputJSON.Summary.GosecSummary.LowVuln + outputJSON.Summary.NpmAuditSummary.LowVuln + outputJSON.Summary.YarnAuditSummary.LowVuln + outputJSON.Summary.GitleaksSummary.LowVuln + outputJSON.Summary.SpotBugsSummary.LowVuln + outputJSON.Summary.TFSecSummary.LowVuln + totalLow = outputJSON.Summary.BrakemanSummary.LowVuln + outputJSON.Summary.SafetySummary.LowVuln + outputJSON.Summary.BanditSummary.LowVuln + outputJSON.Summary.GosecSummary.LowVuln + outputJSON.Summary.NpmAuditSummary.LowVuln + outputJSON.Summary.YarnAuditSummary.LowVuln + outputJSON.Summary.GitleaksSummary.LowVuln + outputJSON.Summary.SpotBugsSummary.LowVuln + outputJSON.Summary.TFSecSummary.LowVuln + outputJSON.Summary.InferSummary.LowVuln - totalMedium = outputJSON.Summary.BrakemanSummary.MediumVuln + outputJSON.Summary.SafetySummary.MediumVuln + outputJSON.Summary.BanditSummary.MediumVuln + outputJSON.Summary.GosecSummary.MediumVuln + outputJSON.Summary.NpmAuditSummary.MediumVuln + outputJSON.Summary.YarnAuditSummary.MediumVuln + outputJSON.Summary.GitleaksSummary.MediumVuln + outputJSON.Summary.SpotBugsSummary.MediumVuln + outputJSON.Summary.TFSecSummary.MediumVuln + totalMedium = outputJSON.Summary.BrakemanSummary.MediumVuln + outputJSON.Summary.SafetySummary.MediumVuln + outputJSON.Summary.BanditSummary.MediumVuln + outputJSON.Summary.GosecSummary.MediumVuln + outputJSON.Summary.NpmAuditSummary.MediumVuln + outputJSON.Summary.YarnAuditSummary.MediumVuln + outputJSON.Summary.GitleaksSummary.MediumVuln + outputJSON.Summary.SpotBugsSummary.MediumVuln + outputJSON.Summary.TFSecSummary.MediumVuln + outputJSON.Summary.InferSummary.MediumVuln - totalHigh = outputJSON.Summary.BrakemanSummary.HighVuln + outputJSON.Summary.SafetySummary.HighVuln + outputJSON.Summary.BanditSummary.HighVuln + outputJSON.Summary.GosecSummary.HighVuln + outputJSON.Summary.NpmAuditSummary.HighVuln + outputJSON.Summary.YarnAuditSummary.HighVuln + outputJSON.Summary.GitleaksSummary.HighVuln + outputJSON.Summary.SpotBugsSummary.HighVuln + outputJSON.Summary.TFSecSummary.HighVuln + totalHigh = outputJSON.Summary.BrakemanSummary.HighVuln + outputJSON.Summary.SafetySummary.HighVuln + outputJSON.Summary.BanditSummary.HighVuln + outputJSON.Summary.GosecSummary.HighVuln + outputJSON.Summary.NpmAuditSummary.HighVuln + outputJSON.Summary.YarnAuditSummary.HighVuln + outputJSON.Summary.GitleaksSummary.HighVuln + outputJSON.Summary.SpotBugsSummary.HighVuln + outputJSON.Summary.TFSecSummary.HighVuln + outputJSON.Summary.InferSummary.HighVuln outputJSON.Summary.TotalSummary.HighVuln = totalHigh outputJSON.Summary.TotalSummary.MediumVuln = totalMedium @@ -216,7 +232,7 @@ func prepareAllSummary(analysis types.Analysis) { func printAllSummary(analysis types.Analysis) { - var gosecVersion, banditVersion, safetyVersion, brakemanVersion, npmauditVersion, yarnauditVersion, gitleaksVersion, spotbugsVersion, tfsecVersion string + var gosecVersion, banditVersion, safetyVersion, brakemanVersion, npmauditVersion, yarnauditVersion, gitleaksVersion, spotbugsVersion, tfsecVersion, inferVersion string for _, container := range analysis.Containers { switch container.SecurityTest.Name { @@ -238,6 +254,8 @@ func printAllSummary(analysis types.Analysis) { gitleaksVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag) case "tfsec": tfsecVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag) + case "infer": + inferVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag) } } @@ -322,6 +340,15 @@ func printAllSummary(analysis types.Analysis) { fmt.Printf("[HUSKYCI][SUMMARY] NoSecHusky: %d\n", outputJSON.Summary.GitleaksSummary.NoSecVuln) } + if outputJSON.Summary.InferSummary.FoundVuln || outputJSON.Summary.InferSummary.FoundInfo { + fmt.Println() + fmt.Printf("[HUSKYCI][SUMMARY] Generic -> %s\n", inferVersion) + fmt.Printf("[HUSKYCI][SUMMARY] High: %d\n", outputJSON.Summary.InferSummary.HighVuln) + fmt.Printf("[HUSKYCI][SUMMARY] Medium: %d\n", outputJSON.Summary.InferSummary.MediumVuln) + fmt.Printf("[HUSKYCI][SUMMARY] Low: %d\n", outputJSON.Summary.InferSummary.LowVuln) + fmt.Printf("[HUSKYCI][SUMMARY] NoSecHusky: %d\n", outputJSON.Summary.InferSummary.NoSecVuln) + } + if outputJSON.Summary.TotalSummary.FoundVuln || outputJSON.Summary.TotalSummary.FoundInfo { fmt.Println() fmt.Printf("[HUSKYCI][SUMMARY] Total\n") @@ -442,6 +469,7 @@ func printSTDOUTOutputSpotBugs(issues []types.HuskyCIVulnerability) { fmt.Printf("[HUSKYCI][!] Type: %s\n", issue.Type) } } + func printSTDOUTOutputTFSec(issues []types.HuskyCIVulnerability) { for _, issue := range issues { fmt.Println() @@ -467,3 +495,17 @@ func printSTDOUTOutputGitleaks(issues []types.HuskyCIVulnerability) { fmt.Printf("[HUSKYCI][!] Code: %s\n", issue.Code) } } + +func printSTDOUTOutputInfer(issues []types.HuskyCIVulnerability) { + for _, issue := range issues { + fmt.Println() + fmt.Printf("[HUSKYCI][!] Title: %s\n", issue.Title) + fmt.Printf("[HUSKYCI][!] Language: %s\n", issue.Language) + fmt.Printf("[HUSKYCI][!] Tool: %s\n", issue.SecurityTool) + fmt.Printf("[HUSKYCI][!] Severity: %s\n", issue.Severity) + fmt.Printf("[HUSKYCI][!] Details: %s\n", issue.Details) + fmt.Printf("[HUSKYCI][!] File: %s\n", issue.File) + fmt.Printf("[HUSKYCI][!] Line: %s\n", issue.Line) + fmt.Printf("[HUSKYCI][!] Type: %s\n", issue.Type) + } +} diff --git a/client/types/infer.go b/client/types/infer.go new file mode 100644 index 00000000..0c3d93e1 --- /dev/null +++ b/client/types/infer.go @@ -0,0 +1,18 @@ +// Copyright 2021 Globo.com authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +// InferOutput holds all data from Infer output. +type InferOutput []InferResult + +// InferResult holds the detailed information from Infer output. +type InferResult struct { + Type string `json:"bug_type"` + Message string `json:"qualifier"` + File string `json:"file"` + Line string `json:"line"` + Severity string `json:"severity"` + Title string `json:"bug_type_hum"` +} diff --git a/client/types/types.go b/client/types/types.go index a2ec2b62..b99c4eaa 100644 --- a/client/types/types.go +++ b/client/types/types.go @@ -21,8 +21,8 @@ var IsJSONoutput bool // JSONPayload is a struct that represents the JSON payload needed to make a HuskyCI API request. type JSONPayload struct { - RepositoryURL string `json:"repositoryURL"` - RepositoryBranch string `json:"repositoryBranch"` + RepositoryURL string `json:"repositoryURL"` + RepositoryBranch string `json:"repositoryBranch"` LanguageExclusions map[string]bool `json:"languageExclusions"` } @@ -140,6 +140,7 @@ type JavaScriptResults struct { // JavaResults represents all Java security tests results. type JavaResults struct { HuskyCISpotBugsOutput HuskyCISecurityTestOutput `bson:"spotbugsoutput,omitempty" json:"spotbugsoutput,omitempty"` + HuskyCIInferOutput HuskyCISecurityTestOutput `bson:"inferoutput,omitempty" json:"inferoutput,omitempty"` } // RubyResults represents all Ruby security tests results. @@ -179,6 +180,7 @@ type Summary struct { SpotBugsSummary HuskyCISummary `json:"spotbugssummary,omitempty"` GitleaksSummary HuskyCISummary `json:"gitleakssummary,omitempty"` TFSecSummary HuskyCISummary `json:"tfsecsummary,omitempty"` + InferSummary HuskyCISummary `json:"infersummary,omitempty"` TotalSummary HuskyCISummary `json:"totalsummary,omitempty"` } From 4f5730e82533714eb3e4f73f1880f4dbd3bb92c3 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Thu, 7 Oct 2021 03:16:41 -0300 Subject: [PATCH 3/4] Modifies `cli` to use `huskyci/infer` for Java projects --- cli/analysis/analysis.go | 2 +- cli/cmd/root.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/analysis/analysis.go b/cli/analysis/analysis.go index 21bf3468..bcc0f415 100644 --- a/cli/analysis/analysis.go +++ b/cli/analysis/analysis.go @@ -174,7 +174,7 @@ func (a *Analysis) getAvailableSecurityTests(languages []string) map[string][]st case "JavaScript": list[language] = []string{"huskyci/npmaudit", "huskyci/yarnaudit"} case "Java": - list[language] = []string{"huskyci/spotbugs"} + list[language] = []string{"huskyci/spotbugs", "huskyci/infer"} case "HCL": list[language] = []string{"huskyci/tfsec"} } diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 6e2c2df7..c26368e0 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -25,7 +25,7 @@ huskyCI is an open source tool that orchestrates security tests and centralizes all results into a database for further analysis and metrics. It can perform static security analysis in Python (Bandit and Safety), Ruby (Brakeman), JavaScript (Npm Audit and Yarn Audit), Golang (Gosec), -Java (SpotBugs plus Find Sec Bugs) and HCL (TFSec). It can also audit repositories +Java (SpotBugs plus Find Sec Bugs and Infer) and HCL (TFSec). It can also audit repositories for secrets like AWS Secret Keys, Private SSH Keys, and many others using GitLeaks. `, From b7e585ff93c86869cec20f422f891482cb00cfe1 Mon Sep 17 00:00:00 2001 From: Pablo Aguilar Date: Thu, 7 Oct 2021 03:19:18 -0300 Subject: [PATCH 4/4] Modifies `api` to run Infer tests --- api/config.yaml | 46 ++++++++++++++++++++ api/context/context.go | 2 + api/context/context_test.go | 10 +++++ api/log/messagecodes.go | 1 + api/securitytest/infer.go | 73 ++++++++++++++++++++++++++++++++ api/securitytest/run.go | 13 +++++- api/securitytest/securitytest.go | 1 + api/types/types.go | 1 + api/util/api/api.go | 17 +++++++- 9 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 api/securitytest/infer.go diff --git a/api/config.yaml b/api/config.yaml index 6ad37e38..2b35bbba 100644 --- a/api/config.yaml +++ b/api/config.yaml @@ -321,6 +321,52 @@ spotbugs: default: false timeOutInSeconds: 3600 +infer: + name: infer + image: huskyci/infer + imageTag: "4.0.0-beta4" + cmd: |+ + mkdir -p ~/.ssh && + echo '%GIT_PRIVATE_SSH_KEY%' > ~/.ssh/huskyci_id_rsa && + chmod 600 ~/.ssh/huskyci_id_rsa && + echo "IdentityFile ~/.ssh/huskyci_id_rsa" >> /etc/ssh/ssh_config && + echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && + GIT_TERMINAL_PROMPT=0 git clone -b %GIT_BRANCH% --single-branch %GIT_REPO% code --quiet 2> /tmp/errorGitCloneInfer + if [ $? -eq 0 ]; then + cd code + if [ -f "pom.xml" ]; then + mv ../code /tmp/code + cd /tmp/code + project_type=$(cat pom.xml|grep packaging|cut -d'<' -f2|cut -d'>' -f2) + infer run -- bash /usr/local/bin/mvn-entrypoint.sh 2> /tmp/errorMavenBuild 1> /dev/null + if [ $? -eq 0 ]; then + cat infer-out/report.json + else + echo "ERROR_RUNNING_MAVEN_BUILD" + cat /tmp/errorMavenBuild + fi + elif [ -f "build.gradle" ]; then + mv ../code /tmp/code + cd /tmp/code + infer run -- /opt/gradle/bin/gradle -p /tmp/code build 2> /tmp/errorGradleBuild 1> /dev/null + if [ $? -eq 0 ]; then + cat infer-out/report.json + else + echo "ERROR_RUNNING_GRADLE_BUILD" + cat /tmp/errorGradleBuild + fi + else + echo "ERROR_UNSUPPORTED_JAVA_PROJECT" + fi + else + echo "ERROR_CLONING" + cat /tmp/errorGitCloneInfer + fi + type: Language + language: Java + default: false + timeOutInSeconds: 3600 + gitleaks: name: gitleaks image: huskyci/gitleaks diff --git a/api/context/context.go b/api/context/context.go index d9e1cb8d..a5bd38b7 100644 --- a/api/context/context.go +++ b/api/context/context.go @@ -84,6 +84,7 @@ type APIConfig struct { GitleaksSecurityTest *types.SecurityTest SafetySecurityTest *types.SecurityTest TFSecSecurityTest *types.SecurityTest + InferSecurityTest *types.SecurityTest DBInstance db.Requests Cache *cache.Cache } @@ -129,6 +130,7 @@ func (dF DefaultConfig) SetOnceConfig() { GitleaksSecurityTest: dF.getSecurityTestConfig("gitleaks"), SafetySecurityTest: dF.getSecurityTestConfig("safety"), TFSecSecurityTest: dF.getSecurityTestConfig("tfsec"), + InferSecurityTest: dF.getSecurityTestConfig("infer"), DBInstance: dF.GetDB(), Cache: dF.GetCache(), } diff --git a/api/context/context_test.go b/api/context/context_test.go index 7d78064b..9d1d48c8 100644 --- a/api/context/context_test.go +++ b/api/context/context_test.go @@ -437,6 +437,16 @@ var _ = Describe("Context", func() { Default: fakeCaller.expectedBoolFromConfig, TimeOutInSeconds: fakeCaller.expectedIntFromConfig, }, + InferSecurityTest: &types.SecurityTest{ + Name: fakeCaller.expectedStringFromConfig, + Image: fakeCaller.expectedStringFromConfig, + ImageTag: fakeCaller.expectedStringFromConfig, + Cmd: fakeCaller.expectedStringFromConfig, + Type: fakeCaller.expectedStringFromConfig, + Language: fakeCaller.expectedStringFromConfig, + Default: fakeCaller.expectedBoolFromConfig, + TimeOutInSeconds: fakeCaller.expectedIntFromConfig, + }, DBInstance: &db.MongoRequests{}, Cache: apiConfig.Cache, // cannot be compared due to channels inside the structure } diff --git a/api/log/messagecodes.go b/api/log/messagecodes.go index deb39097..f5a6fff8 100644 --- a/api/log/messagecodes.go +++ b/api/log/messagecodes.go @@ -74,6 +74,7 @@ var MsgCode = map[int]string{ 1038: "Could not Unmarshall the following gitleaksOutput: ", 1039: "Could not Unmarshall the following spotbugsOutput: ", 1040: "Could not Unmarshall the following tfsecOutput: ", + 1041: "Could not Unmarshall the following inferOutput: ", // MongoDB infos 21: "Connecting to MongoDB.", diff --git a/api/securitytest/infer.go b/api/securitytest/infer.go new file mode 100644 index 00000000..816f73c0 --- /dev/null +++ b/api/securitytest/infer.go @@ -0,0 +1,73 @@ +// Copyright 2021 Globo.com authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securitytest + +import ( + "encoding/json" + + "github.com/globocom/huskyCI/api/log" + "github.com/globocom/huskyCI/api/types" +) + +// InferOutput holds all data from Infer output. +type InferOutput []InferResult + +// InferResult holds the detailed information from Infer output. +type InferResult struct { + Type string `json:"bug_type"` + Message string `json:"qualifier"` + File string `json:"file"` + Line string `json:"line"` + Severity string `json:"severity"` + Title string `json:"bug_type_hum"` +} + +func analyzeInfer(scanInfo *SecTestScanInfo) error { + var inferOutput InferOutput + + if err := json.Unmarshal([]byte(scanInfo.Container.COutput), &inferOutput); err != nil { + log.Error("analyzeInfer", "INFER", 1041, scanInfo.Container.COutput, err) + scanInfo.ErrorFound = err + return err + } + scanInfo.FinalOutput = inferOutput + + // if len is equal to zero no issues were found + if len(inferOutput) == 0 { + scanInfo.prepareContainerAfterScan() + return nil + } + + scanInfo.prepareInferVulns() + scanInfo.prepareContainerAfterScan() + return nil +} + +func (inferScan *SecTestScanInfo) prepareInferVulns() { + huskyCIInferResults := types.HuskyCISecurityTestOutput{} + inferOutput := inferScan.FinalOutput.(InferOutput) + + for _, result := range inferOutput { + inferVuln := types.HuskyCIVulnerability{ + Language: "Java", + SecurityTool: "Infer", + Severity: result.Severity, + File: result.File, + Line: result.Line, + Details: result.Message, + Type: result.Type, + Title: result.Title, + } + + switch inferVuln.Severity { + case "INFO": + huskyCIInferResults.LowVulns = append(huskyCIInferResults.LowVulns, inferVuln) + case "WARNING": + huskyCIInferResults.MediumVulns = append(huskyCIInferResults.MediumVulns, inferVuln) + case "ERROR": + huskyCIInferResults.HighVulns = append(huskyCIInferResults.HighVulns, inferVuln) + } + } +} diff --git a/api/securitytest/run.go b/api/securitytest/run.go index da874527..e288145d 100644 --- a/api/securitytest/run.go +++ b/api/securitytest/run.go @@ -29,6 +29,7 @@ const yarnaudit = "yarnaudit" const spotbugs = "spotbugs" const gitleaks = "gitleaks" const tfsec = "tfsec" +const infer = "infer" // Start runs both generic and language security func (results *RunAllInfo) Start(enryScan SecTestScanInfo) error { @@ -103,7 +104,7 @@ func (results *RunAllInfo) runGenericScans(enryScan SecTestScanInfo) error { go func(genericTest *types.SecurityTest) { defer wg.Done() newGenericScan := SecTestScanInfo{} - // LanguageExclusions is only utilized on first scan (enryScan) therefore set as nil + // LanguageExclusions is only utilized on first scan (enryScan) therefore set as nil enryScan.LanguageExclusions = nil if err := newGenericScan.New(enryScan.RID, enryScan.URL, enryScan.Branch, genericTest.Name, enryScan.LanguageExclusions); err != nil { select { @@ -168,7 +169,7 @@ func (results *RunAllInfo) runLanguageScans(enryScan SecTestScanInfo) error { go func(languageTest *types.SecurityTest) { defer wg.Done() newLanguageScan := SecTestScanInfo{} - // LanguageExclusions is only utilized on first scan (enryScan) therefore set as nil + // LanguageExclusions is only utilized on first scan (enryScan) therefore set as nil enryScan.LanguageExclusions = nil if err := newLanguageScan.New(enryScan.RID, enryScan.URL, enryScan.Branch, languageTest.Name, enryScan.LanguageExclusions); err != nil { select { @@ -228,6 +229,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) { results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.HighVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.HighVulns, highVuln) case tfsec: results.HuskyCIResults.HclResults.HuskyCITFSecOutput.HighVulns = append(results.HuskyCIResults.HclResults.HuskyCITFSecOutput.HighVulns, highVuln) + case infer: + results.HuskyCIResults.JavaResults.HuskyCIInferOutput.HighVulns = append(results.HuskyCIResults.JavaResults.HuskyCIInferOutput.HighVulns, highVuln) } } @@ -251,6 +254,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) { results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.MediumVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.MediumVulns, mediumVuln) case tfsec: results.HuskyCIResults.HclResults.HuskyCITFSecOutput.MediumVulns = append(results.HuskyCIResults.HclResults.HuskyCITFSecOutput.MediumVulns, mediumVuln) + case infer: + results.HuskyCIResults.JavaResults.HuskyCIInferOutput.MediumVulns = append(results.HuskyCIResults.JavaResults.HuskyCIInferOutput.MediumVulns, mediumVuln) } } @@ -274,6 +279,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) { results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.LowVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.LowVulns, lowVuln) case tfsec: results.HuskyCIResults.HclResults.HuskyCITFSecOutput.LowVulns = append(results.HuskyCIResults.HclResults.HuskyCITFSecOutput.LowVulns, lowVuln) + case infer: + results.HuskyCIResults.JavaResults.HuskyCIInferOutput.LowVulns = append(results.HuskyCIResults.JavaResults.HuskyCIInferOutput.LowVulns, lowVuln) } } @@ -297,6 +304,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) { results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.NoSecVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.NoSecVulns, noSec) case tfsec: results.HuskyCIResults.HclResults.HuskyCITFSecOutput.NoSecVulns = append(results.HuskyCIResults.HclResults.HuskyCITFSecOutput.NoSecVulns, noSec) + case infer: + results.HuskyCIResults.JavaResults.HuskyCIInferOutput.NoSecVulns = append(results.HuskyCIResults.JavaResults.HuskyCIInferOutput.NoSecVulns, noSec) } } } diff --git a/api/securitytest/securitytest.go b/api/securitytest/securitytest.go index a159dc23..94f68f96 100644 --- a/api/securitytest/securitytest.go +++ b/api/securitytest/securitytest.go @@ -24,6 +24,7 @@ var securityTestAnalyze = map[string]func(scanInfo *SecTestScanInfo) error{ "gitleaks": analyseGitleaks, "safety": analyzeSafety, "tfsec": analyzeTFSec, + "infer": analyzeInfer, } // SecTestScanInfo holds all information of securityTest scan. diff --git a/api/types/types.go b/api/types/types.go index a18c87e6..87195be5 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -122,6 +122,7 @@ type JavaScriptResults struct { // JavaResults represents all Java security tests results. type JavaResults struct { HuskyCISpotBugsOutput HuskyCISecurityTestOutput `bson:"spotbugsoutput,omitempty" json:"spotbugsoutput,omitempty"` + HuskyCIInferOutput HuskyCISecurityTestOutput `bson:"inferoutput,omitempty" json:"inferoutput,omitempty"` } // RubyResults represents all Ruby security tests results. diff --git a/api/util/api/api.go b/api/util/api/api.go index 4fc4036a..6492f8d9 100644 --- a/api/util/api/api.go +++ b/api/util/api/api.go @@ -117,7 +117,20 @@ func (cH *CheckUtils) checkDB(configAPI *apiContext.APIConfig) error { } func (cH *CheckUtils) checkEachSecurityTest(configAPI *apiContext.APIConfig) error { - securityTests := []string{"enry", "gitauthors", "gosec", "brakeman", "bandit", "npmaudit", "yarnaudit", "spotbugs", "gitleaks", "safety", "tfsec"} + securityTests := []string{ + "enry", + "gitauthors", + "gosec", + "brakeman", + "bandit", + "npmaudit", + "yarnaudit", + "spotbugs", + "gitleaks", + "safety", + "tfsec", + "infer", + } for _, securityTest := range securityTests { if err := checkSecurityTest(securityTest, configAPI); err != nil { errMsg := fmt.Sprintf("%s %s", securityTest, err) @@ -173,6 +186,8 @@ func checkSecurityTest(securityTestName string, configAPI *apiContext.APIConfig) securityTestConfig = *configAPI.SafetySecurityTest case "tfsec": securityTestConfig = *configAPI.TFSecSecurityTest + case "infer": + securityTestConfig = *configAPI.InferSecurityTest default: return errors.New("securityTest name not defined") }