Repository: globocom/huskyCI
Pull request #413 (2019-10-18 13:28:45)

Remove unused HUSKYCI_DOCKERAPI_CERT_CA env var
This PR will guarantee that this environment variable is not required anymore. A new wiki is being created to update all huskyCI env vars. 🙃


Patch:
diff --git a/README.md b/README.md
index 14cf643..ad8d290 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ huskyCI is an open-source tool that performs security tests inside CI pipelines
 
 The main goal of this project is to help development teams improve the quality of their code by finding vulnerabilities as soon as possible.
 
-huskyCI can perform static security analysis in Python ([Bandit][Bandit] and [Safety][Safety]), Ruby ([Brakeman][Brakeman]), JavaScript ([Npm Audit][NpmAudit] and [Yarn Audit][YarnAudit]) and Golang ([Gosec][Gosec]). It can also audit repositories for secrets like AWS Secret Keys, Private SSH Keys, and many others using ([GitLeaks][Gitleaks]). You should check our [wiki](https://github.com/globocom/huskyCI/wiki/How-does-huskyCI-work%3F) to better understand how this tool could help securing your organization projects!
+huskyCI can perform static security analysis in Python ([Bandit][Bandit] and [Safety][Safety]), Ruby ([Brakeman][Brakeman]), JavaScript ([Npm Audit][NpmAudit] and [Yarn Audit][YarnAudit]), Golang ([Gosec][Gosec]), and Java([SpotBugs][SpotBugs] plus [Find Sec Bugs][FindSec]). It can also audit repositories for secrets like AWS Secret Keys, Private SSH Keys, and many others using [GitLeaks][Gitleaks]. You should check our [wiki](https://github.com/globocom/huskyCI/wiki/How-does-huskyCI-work%3F) to better understand how this tool could help securing your organization projects!
 
 <p align="center">
   <img src="huskyCI.gif" />
@@ -113,3 +113,5 @@ This project is licensed under the BSD 3-Clause "New" or "Revised" License - rea
 [NpmAudit]: https://docs.npmjs.com/cli/audit
 [YarnAudit]: https://yarnpkg.com/lang/en/docs/cli/audit/
 [Gitleaks]: https://github.com/zricethezav/gitleaks
+[SpotBugs]: https://spotbugs.github.io
+[FindSec]: https://find-sec-bugs.github.io
diff --git a/api/config.yaml b/api/config.yaml
index f007f71..31dbe54 100644
--- a/api/config.yaml
+++ b/api/config.yaml
@@ -233,6 +233,62 @@ yarnaudit:
   default: true
   timeOutInSeconds: 360
 
+spotbugs:
+  name: spotbugs
+  image: huskyci/spotbugs
+  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 clone -b %GIT_BRANCH% --single-branch %GIT_REPO% code --quiet 2> /tmp/errorGitCloneSpotBugs
+    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)
+           bash /usr/local/bin/mvn-entrypoint.sh 2> /tmp/errorMavenBuild 1> /dev/null
+           if [ $? -eq 0 ]; then
+               if [[ "$project_type" = "war" ]]; then
+                  # WAR
+                   war_file=$(ls -lS target/*.war|head -n1|cut -d'/' -f2|sed -e "s/.war//")
+                   mv target/$war_file /tmp/needToBeScanned
+               else
+                  # JAR
+                   mkdir /tmp/needToBeScanned
+                   cp target/*.jar /tmp/needToBeScanned/
+               fi
+               java -jar /opt/spotbugs/lib/spotbugs.jar -textui -quiet -xml -bugCategories SECURITY -exclude /opt/spotbugs/exclude.xml -pluginList /opt/findsecbugs-plugin-1.9.0.jar /tmp/needToBeScanned
+           else
+               echo "ERROR_RUNNING_MAVEN_BUILD"
+               cat /tmp/errorMavenBuild
+           fi
+       elif [ -f "build.gradle" ]; then
+           mv ../code /tmp/code
+           cd /tmp/code
+           /opt/gradle/bin/gradle -p /tmp/code build 2> /tmp/errorGradleBuild 1> /dev/null
+            if [ $? -eq 0 ]; then
+               mv build /tmp/needToBeScanned
+               java -jar /opt/spotbugs/lib/spotbugs.jar -textui -quiet -xml -bugCategories SECURITY -exclude /opt/spotbugs/exclude.xml -pluginList /opt/findsecbugs-plugin-1.9.0.jar /tmp/needToBeScanned
+            else
+               echo "ERROR_RUNNING_GRADLE_BUILD"
+               cat /tmp/errorGradleBuild
+            fi
+       else
+           echo "ERROR_UNSUPPORTED_JAVA_PROJECT"
+       fi
+    else
+        echo "ERROR_CLONING"
+        cat /tmp/errorGitCloneSpotBugs
+    fi
+  type: Language
+  language: Java
+  default: true
+  timeOutInSeconds: 3600
+
 gitleaks:
   name: gitleaks
   image: huskyci/gitleaks
@@ -259,4 +315,4 @@ gitleaks:
     fi
   type: Generic
   default: true
-  timeOutInSeconds: 360
+  timeOutInSeconds: 360
\ No newline at end of file
diff --git a/api/context/context.go b/api/context/context.go
index d8c73f6..51831a8 100644
--- a/api/context/context.go
+++ b/api/context/context.go
@@ -80,6 +80,7 @@ type APIConfig struct {
 	BrakemanSecurityTest   *types.SecurityTest
 	NpmAuditSecurityTest   *types.SecurityTest
 	YarnAuditSecurityTest  *types.SecurityTest
+	SpotBugsSecurityTest   *types.SecurityTest
 	GitleaksSecurityTest   *types.SecurityTest
 	SafetySecurityTest     *types.SecurityTest
 	DBInstance             db.Requests
@@ -122,8 +123,9 @@ func (dF DefaultConfig) SetOnceConfig() {
 			BrakemanSecurityTest:   dF.getSecurityTestConfig("brakeman"),
 			NpmAuditSecurityTest:   dF.getSecurityTestConfig("npmaudit"),
 			YarnAuditSecurityTest:  dF.getSecurityTestConfig("yarnaudit"),
-			SafetySecurityTest:     dF.getSecurityTestConfig("safety"),
+			SpotBugsSecurityTest:   dF.getSecurityTestConfig("spotbugs"),
 			GitleaksSecurityTest:   dF.getSecurityTestConfig("gitleaks"),
+			SafetySecurityTest:     dF.getSecurityTestConfig("safety"),
 			DBInstance:             dF.GetDB(),
 		}
 	})
diff --git a/api/log/messagecodes.go b/api/log/messagecodes.go
index 6311ab1..75b2fab 100644
--- a/api/log/messagecodes.go
+++ b/api/log/messagecodes.go
@@ -72,6 +72,7 @@ var MsgCode = map[int]string{
 	1036: "Could not Unmarshal the following yarnauditOutput: ",
 	1037: "Internal error running Yarnaudit: ",
 	1038: "Could not Unmarshall the following gitleaksOutput: ",
+	1039: "Could not Unmarshall the following spotbugsOutput: ",
 
 	// MongoDB infos
 	21: "Connecting to MongoDB.",
diff --git a/api/types/types.go b/api/types/types.go
index 3dd080f..01540c4 100644
--- a/api/types/types.go
+++ b/api/types/types.go
@@ -101,6 +101,7 @@ type HuskyCIResults struct {
 	PythonResults     PythonResults     `bson:"pythonresults,omitempty" json:"pythonresults,omitempty"`
 	JavaScriptResults JavaScriptResults `bson:"javascriptresults,omitempty" json:"javascriptresults,omitempty"`
 	RubyResults       RubyResults       `bson:"rubyresults,omitempty" json:"rubyresults,omitempty"`
+	JavaResults       JavaResults       `bson:"javaresults,omitempty" json:"javaresults,omitempty"`
 	GenericResults    GenericResults    `bson:"genericresults,omitempty" json:"genericresults,omitempty"`
 }
 
@@ -121,6 +122,11 @@ type JavaScriptResults struct {
 	HuskyCIYarnAuditOutput HuskyCISecurityTestOutput `bson:"yarnauditoutput,omitempty" json:"yarnauditoutput,omitempty"`
 }
 
+// JavaResults represents all Java security tests results.
+type JavaResults struct {
+	HuskyCISpotBugsOutput HuskyCISecurityTestOutput `bson:"spotbugsoutput,omitempty" json:"spotbugsoutput,omitempty"`
+}
+
 // RubyResults represents all Ruby security tests results.
 type RubyResults struct {
 	HuskyCIBrakemanOutput HuskyCISecurityTestOutput `bson:"brakemanoutput,omitempty" json:"brakemanoutput,omitempty"`
diff --git a/api/util/api/api.go b/api/util/api/api.go
index b17292e..5e1d6a5 100644
--- a/api/util/api/api.go
+++ b/api/util/api/api.go
@@ -83,7 +83,6 @@ func (cH *CheckUtils) checkEnvVars() error {
 		// "HUSKYCI_DOCKERAPI_CERT_KEY_VALUE", (optional)
 		// "HUSKYCI_DOCKERAPI_API_TLS_CERT_VALUE", (optional)
 		// "HUSKYCI_DOCKERAPI_API_TLS_KEY_VALUE", (optional)
-		// "HUSKYCI_DOCKERAPI_CERT_CA", (optional)
 		// "HUSKYCI_DOCKERAPI_CERT_CA_VALUE", (optional)
 		// "HUSKYCI_DOCKERAPI_PORT", (optional)
 		// "HUSKYCI_DOCKERAPI_TLS_VERIFY", (optional)
@@ -145,7 +144,7 @@ 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", "safety", "gitleaks"}
+	securityTests := []string{"enry", "gitauthors", "gosec", "brakeman", "bandit", "npmaudit", "yarnaudit", "spotbugs", "gitleaks", "safety"}
 	for _, securityTest := range securityTests {
 		if err := checkSecurityTest(securityTest, configAPI); err != nil {
 			errMsg := fmt.Sprintf("%s %s", securityTest, err)
@@ -193,10 +192,12 @@ func checkSecurityTest(securityTestName string, configAPI *apiContext.APIConfig)
 		securityTestConfig = *configAPI.NpmAuditSecurityTest
 	case "yarnaudit":
 		securityTestConfig = *configAPI.YarnAuditSecurityTest
-	case "safety":
-		securityTestConfig = *configAPI.SafetySecurityTest
+	case "spotbugs":
+		securityTestConfig = *configAPI.SpotBugsSecurityTest
 	case "gitleaks":
 		securityTestConfig = *configAPI.GitleaksSecurityTest
+	case "safety":
+		securityTestConfig = *configAPI.SafetySecurityTest
 	default:
 		return errors.New("securityTest name not defined")
 	}
diff --git a/client/analysis/output.go b/client/analysis/output.go
index 756c404..3a762a3 100644
--- a/client/analysis/output.go
+++ b/client/analysis/output.go
@@ -62,6 +62,11 @@ func printSTDOUTOutput(analysis types.Analysis) {
 	printSTDOUTOutputGitleaks(outputJSON.GenericResults.HuskyCIGitleaksOutput.MediumVulns)
 	printSTDOUTOutputGitleaks(outputJSON.GenericResults.HuskyCIGitleaksOutput.HighVulns)
 
+	// spotbugs
+	printSTDOUTOutputSpotBugs(outputJSON.JavaResults.HuskyCISpotBugsOutput.LowVulns)
+	printSTDOUTOutputSpotBugs(outputJSON.JavaResults.HuskyCISpotBugsOutput.MediumVulns)
+	printSTDOUTOutputSpotBugs(outputJSON.JavaResults.HuskyCISpotBugsOutput.HighVulns)
+
 	printAllSummary(analysis)
 }
 
@@ -73,6 +78,7 @@ func prepareAllSummary(analysis types.Analysis) {
 	outputJSON.JavaScriptResults = analysis.HuskyCIResults.JavaScriptResults
 	outputJSON.PythonResults = analysis.HuskyCIResults.PythonResults
 	outputJSON.RubyResults = analysis.HuskyCIResults.RubyResults
+	outputJSON.JavaResults = analysis.HuskyCIResults.JavaResults
 	outputJSON.GenericResults = analysis.HuskyCIResults.GenericResults
 
 	// GoSec summary
@@ -142,6 +148,17 @@ func prepareAllSummary(analysis types.Analysis) {
 		outputJSON.Summary.YarnAuditSummary.FoundVuln = true
 	}
 
+	// SpotBugs summary
+	outputJSON.Summary.SpotBugsSummary.LowVuln = len(outputJSON.JavaResults.HuskyCISpotBugsOutput.LowVulns)
+	outputJSON.Summary.SpotBugsSummary.MediumVuln = len(outputJSON.JavaResults.HuskyCISpotBugsOutput.MediumVulns)
+	outputJSON.Summary.SpotBugsSummary.HighVuln = len(outputJSON.JavaResults.HuskyCISpotBugsOutput.HighVulns)
+	if len(outputJSON.JavaResults.HuskyCISpotBugsOutput.LowVulns) > 0 {
+		outputJSON.Summary.SpotBugsSummary.FoundInfo = true
+	}
+	if len(outputJSON.JavaResults.HuskyCISpotBugsOutput.MediumVulns) > 0 || len(outputJSON.JavaResults.HuskyCISpotBugsOutput.HighVulns) > 0 {
+		outputJSON.Summary.SpotBugsSummary.FoundVuln = true
+	}
+
 	// GitLeaks summary
 	outputJSON.Summary.GitleaksSummary.LowVuln = len(outputJSON.GenericResults.HuskyCIGitleaksOutput.LowVulns)
 	outputJSON.Summary.GitleaksSummary.MediumVuln = len(outputJSON.GenericResults.HuskyCIGitleaksOutput.MediumVulns)
@@ -154,18 +171,18 @@ func prepareAllSummary(analysis types.Analysis) {
 	}
 
 	// 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 {
+	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.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 {
+	} 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.TotalSummary.FoundInfo = true
 		types.FoundInfo = true
 	}
 
 	totalNoSec = outputJSON.Summary.BanditSummary.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
-	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
-	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
+	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
+	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
+	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.TotalSummary.HighVuln = totalHigh
 	outputJSON.Summary.TotalSummary.MediumVuln = totalMedium
@@ -176,7 +193,7 @@ func prepareAllSummary(analysis types.Analysis) {
 
 func printAllSummary(analysis types.Analysis) {
 
-	var gosecVersion, banditVersion, safetyVersion, brakemanVersion, npmauditVersion, yarnauditVersion, gitleaksVersion string
+	var gosecVersion, banditVersion, safetyVersion, brakemanVersion, npmauditVersion, yarnauditVersion, gitleaksVersion, spotbugsVersion string
 
 	for _, container := range analysis.Containers {
 		switch container.SecurityTest.Name {
@@ -192,6 +209,8 @@ func printAllSummary(analysis types.Analysis) {
 			npmauditVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag)
 		case "yarnaudit":
 			yarnauditVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag)
+		case "spotbugs":
+			spotbugsVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag)
 		case "gitleaks":
 			gitleaksVersion = fmt.Sprintf("%s:%s", container.SecurityTest.Image, container.SecurityTest.ImageTag)
 		}
@@ -246,6 +265,14 @@ func printAllSummary(analysis types.Analysis) {
 		fmt.Printf("[HUSKYCI][SUMMARY] Low: %d\n", outputJSON.Summary.YarnAuditSummary.LowVuln)
 	}
 
+	if outputJSON.Summary.SpotBugsSummary.FoundVuln || outputJSON.Summary.SpotBugsSummary.FoundInfo {
+		fmt.Println()
+		fmt.Printf("[HUSKYCI][SUMMARY] Java -> %s\n", spotbugsVersion)
+		fmt.Printf("[HUSKYCI][SUMMARY] High: %d\n", outputJSON.Summary.SpotBugsSummary.HighVuln)
+		fmt.Printf("[HUSKYCI][SUMMARY] Medium: %d\n", outputJSON.Summary.SpotBugsSummary.MediumVuln)
+		fmt.Printf("[HUSKYCI][SUMMARY] Low: %d\n", outputJSON.Summary.SpotBugsSummary.LowVuln)
+	}
+
 	if outputJSON.Summary.GitleaksSummary.FoundVuln || outputJSON.Summary.GitleaksSummary.FoundInfo {
 		fmt.Println()
 		fmt.Printf("[HUSKYCI][SUMMARY] Generic -> %s\n", gitleaksVersion)
@@ -353,6 +380,21 @@ func printSTDOUTOutputYarnAudit(issues []types.HuskyCIVulnerability) {
 	}
 }
 
+func printSTDOUTOutputSpotBugs(issues []types.HuskyCIVulnerability) {
+	for _, issue := range issues {
+		fmt.Println()
+		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][!] Confidence: %s\n", issue.Confidence)
+		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][!] Code: %s\n", issue.Code)
+		fmt.Printf("[HUSKYCI][!] Type: %s\n", issue.Type)
+	}
+}
+
 func printSTDOUTOutputGitleaks(issues []types.HuskyCIVulnerability) {
 	for _, issue := range issues {
 		fmt.Println()
diff --git a/client/types/spotbugs.go b/client/types/spotbugs.go
new file mode 100644
index 0000000..48cd1d3
--- /dev/null
+++ b/client/types/spotbugs.go
@@ -0,0 +1,53 @@
+// Copyright 2019 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
+
+import "encoding/xml"
+
+// SpotBugsOutput is the struct that holds all data from SpotBugs output.
+type SpotBugsOutput struct {
+	XMLName       xml.Name        `xml:"BugCollection"`
+	Project       Project         `xml:"Project"`
+	SpotBugsIssue []SpotBugsIssue `xml:"BugInstance"`
+	Errors        Error           `xml:"Errors"`
+}
+
+// Project is the struct that holds data about project
+type Project struct {
+	XMLName xml.Name `xml:"Project"`
+	Name    string   `xml:"projectName,attr"`
+	Jar     string   `xml:"Jar"`
+	Plugin  string   `xml:"Plugin"`
+}
+
+// SpotBugsIssue is the struct that holds all issues from SpotBugs output.
+type SpotBugsIssue struct {
+	XMLName      xml.Name     `xml:"BugInstance"`
+	Type         string       `xml:"type,attr"`
+	Priority     string       `xml:"priority,attr"`
+	Rank         string       `xml:"rank,attr"`
+	Abbreviation string       `xml:"abbrev,attr"`
+	Category     string       `xml:"category,attr"`
+	SourceLine   []SourceLine `xml:"SourceLine"`
+}
+
+// Error is the struct that holds errors that happened in analysis
+type Error struct {
+	XMLName        xml.Name `xml:"Errors"`
+	Errors         string   `xml:"errors,attr"`
+	MissingClasses string   `xml:"missingClasses,attr"`
+}
+
+// SourceLine is the struct that holds details about issue location
+type SourceLine struct {
+	XMLName       xml.Name `xml:"SourceLine"`
+	ClassName     string   `xml:"classname,attr"`
+	Start         string   `xml:"start,attr"`
+	End           string   `xml:"end,attr"`
+	StartByteCode string   `xml:"startBytecode,attr"`
+	EndByteCode   string   `xml:"endBytecode,attr"`
+	SourceFile    string   `xml:"sourcefile,attr"`
+	SourcePath    string   `xml:"sourcepath,attr"`
+}
diff --git a/client/types/types.go b/client/types/types.go
index a7bae89..b5c0f2e 100644
--- a/client/types/types.go
+++ b/client/types/types.go
@@ -61,6 +61,7 @@ type HuskyCIResults struct {
 	PythonResults     PythonResults     `bson:"pythonresults,omitempty" json:"pythonresults,omitempty"`
 	JavaScriptResults JavaScriptResults `bson:"javascriptresults,omitempty" json:"javascriptresults,omitempty"`
 	RubyResults       RubyResults       `bson:"rubyresults,omitempty" json:"rubyresults,omitempty"`
+	JavaResults       JavaResults       `bson:"javaresults,omitempty" json:"javaresults,omitempty"`
 	GenericResults    GenericResults    `bson:"genericresults,omitempty" json:"genericresults,omitempty"`
 }
 
@@ -110,6 +111,7 @@ type JSONOutput struct {
 	PythonResults     PythonResults     `json:"pythonresults,omitempty"`
 	JavaScriptResults JavaScriptResults `json:"javascriptresults,omitempty"`
 	RubyResults       RubyResults       `json:"rubyresults,omitempty"`
+	JavaResults       JavaResults       `json:"javaresults,omitempty"`
 	GenericResults    GenericResults    `json:"genericresults,omitempty"`
 	Summary           Summary           `json:"summary,omitempty"`
 }
@@ -131,6 +133,11 @@ type JavaScriptResults struct {
 	HuskyCIYarnAuditOutput HuskyCISecurityTestOutput `bson:"yarnauditoutput,omitempty" json:"yarnauditoutput,omitempty"`
 }
 
+// JavaResults represents all Java security tests results.
+type JavaResults struct {
+	HuskyCISpotBugsOutput HuskyCISecurityTestOutput `bson:"spotbugsoutput,omitempty" json:"spotbugsoutput,omitempty"`
+}
+
 // RubyResults represents all Ruby security tests results.
 type RubyResults struct {
 	HuskyCIBrakemanOutput HuskyCISecurityTestOutput `bson:"brakemanoutput,omitempty" json:"brakemanoutput,omitempty"`
@@ -160,6 +167,7 @@ type Summary struct {
 	NpmAuditSummary  HuskyCISummary `json:"npmauditsummary,omitempty"`
 	YarnAuditSummary HuskyCISummary `json:"yarnauditsummary,omitempty"`
 	BrakemanSummary  HuskyCISummary `json:"brakemansummary,omitempty"`
+	SpotBugsSummary  HuskyCISummary `json:"spotbugssummary,omitempty"`
 	GitleaksSummary  HuskyCISummary `json:"gitleakssummary,omitempty"`
 	TotalSummary     HuskyCISummary `json:"totalsummary,omitempty"`
 }
diff --git a/deployments/dockerfiles/spotbugs/Dockerfile b/deployments/dockerfiles/spotbugs/Dockerfile
new file mode 100644
index 0000000..ede5db0
--- /dev/null
+++ b/deployments/dockerfiles/spotbugs/Dockerfile
@@ -0,0 +1,40 @@
+FROM openjdk:8-jdk-alpine
+
+RUN apk update && apk upgrade \
+	&& apk add --no-cache curl tar bash wget unzip openssh-client \
+	&& apk add git
+
+ARG SPOTBUGS_VERSION=4.0.0-beta4
+ARG FINDSECBUGS_VERSION=1.9.0
+ARG MAVEN_VERSION=3.6.2
+ARG GRADLE_VERSION=5.6.2
+ARG USER_HOME_DIR="/root"
+
+RUN 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 \
+  && wget -nc -O spotbugs.zip http://repo.maven.apache.org/maven2/com/github/spotbugs/spotbugs/${SPOTBUGS_VERSION}/spotbugs-${SPOTBUGS_VERSION}.zip \
+  && unzip spotbugs.zip \
+  && rm -f spotbugs.zip \
+  && mv spotbugs-${SPOTBUGS_VERSION} spotbugs \
+  && wget -nc -O findsecbugs-plugin-${FINDSECBUGS_VERSION}.jar https://repo1.maven.org/maven2/com/h3xstream/findsecbugs/findsecbugs-plugin/${FINDSECBUGS_VERSION}/findsecbugs-plugin-${FINDSECBUGS_VERSION}.jar \
+  && echo -n $SPOTBUGS_VERSION > /opt/spotbugs/version
+
+ENV MAVEN_HOME /usr/share/maven
+ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
+ENV GRADLE_USER_HOME "$USER_HOME_DIR/.gradle"
+
+COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh
+COPY settings-docker.xml /usr/share/maven/ref/
+COPY exclude.xml /opt/spotbugs/exclude.xml
+
+VOLUME "$USER_HOME_DIR/.m2"
+VOLUME "$USER_HOME_DIR/.gradle"
diff --git a/deployments/dockerfiles/spotbugs/exclude.xml b/deployments/dockerfiles/spotbugs/exclude.xml
new file mode 100644
index 0000000..8af1370
--- /dev/null
+++ b/deployments/dockerfiles/spotbugs/exclude.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<FindBugsFilter xmlns="https://github.com/spotbugs/filter/3.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">
+    <Match>
+        <Package name="~java\..*" />
+    </Match>
+    <Match>
+        <Package name="~javax\..*" />
+    </Match>
+    <Match>
+        <Package name="~org\.apache\..*" />
+    </Match>
+    <Match>
+        <Package name="~org\.eclipse\..*" />
+    </Match>
+    <Match>
+        <Package name="~org\.slf4j\..*" />
+    </Match>
+    <Match>
+        <Package name="~org\.hibernate\..*" />
+    </Match>
+    <Match>
+        <Package name="~org\.springframework\..*" />
+    </Match>
+    <Match>
+        <Package name="~com\.mysql\..*" />
+    </Match>
+    <Match>
+        <Package name="~spark\..*" />
+    </Match>
+    <Match>
+        <Class name="~.*\.*Test" />
+    </Match>
+</FindBugsFilter>
diff --git a/deployments/dockerfiles/spotbugs/mvn-entrypoint.sh b/deployments/dockerfiles/spotbugs/mvn-entrypoint.sh
new file mode 100644
index 0000000..d57dfb0
--- /dev/null
+++ b/deployments/dockerfiles/spotbugs/mvn-entrypoint.sh
@@ -0,0 +1,43 @@
+#! /bin/bash -eu
+#
+# Copyright 2019 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/dockerfiles/spotbugs/settings-docker.xml b/deployments/dockerfiles/spotbugs/settings-docker.xml
new file mode 100644
index 0000000..b65947c
--- /dev/null
+++ b/deployments/dockerfiles/spotbugs/settings-docker.xml
@@ -0,0 +1,5 @@
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
+    <localRepository>/usr/share/maven/ref/repository</localRepository>
+</settings>
\ No newline at end of file
diff --git a/deployments/scripts/build-containers.sh b/deployments/scripts/build-containers.sh
index 0d3beaa..b034277 100755
--- a/deployments/scripts/build-containers.sh
+++ b/deployments/scripts/build-containers.sh
@@ -15,4 +15,5 @@ docker build deployments/dockerfiles/gosec/ -t huskyci/gosec:latest
 docker build deployments/dockerfiles/npmaudit/ -t huskyci/npmaudit:latest
 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
\ No newline at end of file
+docker build deployments/dockerfiles/gitleaks/ -t huskyci/gitleaks:latest
+docker build deployments/dockerfiles/spotbugs/ -t huskyci/spotbugs:latest
\ No newline at end of file
diff --git a/deployments/scripts/check-containers-version.sh b/deployments/scripts/check-containers-version.sh
index ccc46f9..317e6d4 100755
--- a/deployments/scripts/check-containers-version.sh
+++ b/deployments/scripts/check-containers-version.sh
@@ -17,6 +17,7 @@ npmAuditVersion=$(docker run --rm huskyci/npmaudit:latest npm audit --version)
 yarnAuditVersion=$(docker run --rm huskyci/yarnaudit:latest yarn audit --version )
 safetyVersion=$(docker run --rm huskyci/safety:latest safety --version | awk -F " " '{print $3}')
 gitleaksVersion=$(docker run --rm huskyci/gitleaks:latest gitleaks --version)
+spotbugsVersion=$(docker run --rm huskyci/spotbugs:latest cat /opt/spotbugs/version)
 
 echo "bandit: $banditVersion"
 echo "brakeman: $brakemanVersion"
@@ -26,4 +27,5 @@ echo "gosecVersion: $gosecVersion"
 echo "npmauditVersion: $npmAuditVersion"
 echo "yarnauditVersion: $yarnAuditVersion"
 echo "safetyVersion: $safetyVersion"
-echo "gitleaksVersion: $gitleaksVersion"
\ No newline at end of file
+echo "gitleaksVersion: $gitleaksVersion"
+echo "spotbugsVersion: $spotbugsVersion"
\ No newline at end of file
diff --git a/deployments/scripts/push-containers.sh b/deployments/scripts/push-containers.sh
index bcede9c..c270304 100755
--- a/deployments/scripts/push-containers.sh
+++ b/deployments/scripts/push-containers.sh
@@ -16,6 +16,7 @@ npmAuditVersion=$(docker run --rm huskyci/npmaudit:latest npm audit --version)
 yarnAuditVersion=$(docker run --rm huskyci/yarnaudit:latest yarn audit --version )
 safetyVersion=$(docker run --rm huskyci/safety:latest safety --version | awk -F " " '{print $3}')
 gitleaksVersion=$(docker run --rm huskyci/gitleaks:latest gitleaks --version)
+spotbugsVersion=$(docker run --rm huskyci/spotbugs:latest cat /opt/spotbugs/version)
 
 docker tag "huskyci/bandit:latest" "huskyci/bandit:$banditVersion"
 docker tag "huskyci/brakeman:latest" "huskyci/brakeman:$brakemanVersion"
@@ -26,6 +27,7 @@ docker tag "huskyci/npmaudit:latest" "huskyci/npmaudit:$npmAuditVersion"
 docker tag "huskyci/yarnaudit:latest" "huskyci/yarnaudit:$yarnAuditVersion"
 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 push "huskyci/bandit:latest" && docker push "huskyci/bandit:$banditVersion"
 docker push "huskyci/brakeman:latest" && docker push "huskyci/brakeman:$brakemanVersion"
@@ -36,3 +38,4 @@ docker push "huskyci/npmaudit:latest" && docker push "huskyci/npmaudit:$npmAudit
 docker push "huskyci/yarnaudit:latest" && docker push "huskyci/yarnaudit:$yarnAuditVersion"
 docker push "huskyci/safety:latest" && docker push "huskyci/safety:$safetyVersion"
 docker push "huskyci/gitleaks:latest" && docker push "huskyci/gitleaks:$gitleaksVersion"
+docker push "huskyci/spotbugs:latest" && docker push "huskyci/spotbugs:$spotbugsVersion"


Test patch:
diff --git a/api/context/context_test.go b/api/context/context_test.go
index 6e7d422..17c8f12 100644
--- a/api/context/context_test.go
+++ b/api/context/context_test.go
@@ -446,6 +446,16 @@ var _ = Describe("Context", func() {
 						Default:          fakeCaller.expectedBoolFromConfig,
 						TimeOutInSeconds: fakeCaller.expectedIntFromConfig,
 					},
+					SpotBugsSecurityTest: &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{},
 				}
 				Expect(apiConfig).To(Equal(expectedConfig))
diff --git a/api/securitytest/run.go b/api/securitytest/run.go
index 18c1866..ddba332 100644
--- a/api/securitytest/run.go
+++ b/api/securitytest/run.go
@@ -208,6 +208,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) {
 			results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.HighVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.HighVulns, highVuln)
 		case "yarnaudit":
 			results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.HighVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.HighVulns, highVuln)
+		case "spotbugs":
+			results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.HighVulns = append(results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.HighVulns, highVuln)
 		case "gitleaks":
 			results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.HighVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.HighVulns, highVuln)
 		}
@@ -227,6 +229,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) {
 			results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.MediumVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.MediumVulns, mediumVuln)
 		case "yarnaudit":
 			results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.MediumVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.MediumVulns, mediumVuln)
+		case "spotbugs":
+			results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.MediumVulns = append(results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.MediumVulns, mediumVuln)
 		case "gitleaks":
 			results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.MediumVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.MediumVulns, mediumVuln)
 		}
@@ -246,6 +250,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) {
 			results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.LowVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.LowVulns, lowVuln)
 		case "yarnaudit":
 			results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.LowVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.LowVulns, lowVuln)
+		case "spotbugs":
+			results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.LowVulns = append(results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.LowVulns, lowVuln)
 		case "gitleaks":
 			results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.LowVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.LowVulns, lowVuln)
 		}
@@ -265,6 +271,8 @@ func (results *RunAllInfo) setVulns(securityTestScan SecTestScanInfo) {
 			results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.NoSecVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCINpmAuditOutput.NoSecVulns, noSec)
 		case "yarnaudit":
 			results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.NoSecVulns = append(results.HuskyCIResults.JavaScriptResults.HuskyCIYarnAuditOutput.NoSecVulns, noSec)
+		case "spotbugs":
+			results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.NoSecVulns = append(results.HuskyCIResults.JavaResults.HuskyCISpotBugsOutput.NoSecVulns, noSec)
 		case "gitleaks":
 			results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.NoSecVulns = append(results.HuskyCIResults.GenericResults.HuskyCIGitleaksOutput.LowVulns, noSec)
 		}
diff --git a/api/securitytest/securitytest.go b/api/securitytest/securitytest.go
index 9db5e44..f046428 100644
--- a/api/securitytest/securitytest.go
+++ b/api/securitytest/securitytest.go
@@ -21,8 +21,9 @@ var securityTestAnalyze = map[string]func(scanInfo *SecTestScanInfo) error{
 	"gosec":      analyzeGosec,
 	"npmaudit":   analyzeNpmaudit,
 	"yarnaudit":  analyzeYarnaudit,
-	"safety":     analyzeSafety,
+	"spotbugs":   analyzeSpotBugs,
 	"gitleaks":   analyseGitleaks,
+	"safety":     analyzeSafety,
 }
 
 // SecTestScanInfo holds all information of securityTest scan.
diff --git a/api/securitytest/spotbugs.go b/api/securitytest/spotbugs.go
new file mode 100644
index 0000000..d7197fb
--- /dev/null
+++ b/api/securitytest/spotbugs.go
@@ -0,0 +1,204 @@
+// Copyright 2019 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/xml"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/globocom/huskyCI/api/log"
+	"github.com/globocom/huskyCI/api/types"
+)
+
+const (
+	mediumSeverityValue = 15
+	highSeverityValue   = 10
+)
+
+// SpotBugsOutput is the struct that holds all data from SpotBugs output.
+type SpotBugsOutput struct {
+	XMLName       xml.Name        `xml:"BugCollection"`
+	Project       Project         `xml:"Project"`
+	SpotBugsIssue []SpotBugsIssue `xml:"BugInstance"`
+	Errors        Error           `xml:"Errors"`
+}
+
+// Project is the struct that holds data about project
+type Project struct {
+	XMLName xml.Name `xml:"Project"`
+	Name    string   `xml:"projectName,attr"`
+	Jar     string   `xml:"Jar"`
+	Plugin  string   `xml:"Plugin"`
+}
+
+// SpotBugsIssue is the struct that holds all issues from SpotBugs output.
+type SpotBugsIssue struct {
+	XMLName      xml.Name     `xml:"BugInstance"`
+	Type         string       `xml:"type,attr"`
+	Priority     string       `xml:"priority,attr"`
+	Rank         string       `xml:"rank,attr"`
+	Abbreviation string       `xml:"abbrev,attr"`
+	Category     string       `xml:"category,attr"`
+	SourceLine   []SourceLine `xml:"SourceLine"`
+}
+
+// Error is the struct that holds errors that happened in analysis
+type Error struct {
+	XMLName        xml.Name `xml:"Errors"`
+	Errors         string   `xml:"errors,attr"`
+	MissingClasses string   `xml:"missingClasses,attr"`
+}
+
+// SourceLine is the struct that holds details about issue location
+type SourceLine struct {
+	XMLName       xml.Name `xml:"SourceLine"`
+	ClassName     string   `xml:"classname,attr"`
+	Start         string   `xml:"start,attr"`
+	End           string   `xml:"end,attr"`
+	StartByteCode string   `xml:"startBytecode,attr"`
+	EndByteCode   string   `xml:"endBytecode,attr"`
+	SourceFile    string   `xml:"sourcefile,attr"`
+	SourcePath    string   `xml:"sourcepath,attr"`
+}
+
+func analyzeSpotBugs(spotbugsScan *SecTestScanInfo) error {
+
+	spotBugsOutput := SpotBugsOutput{}
+	spotbugsScan.FinalOutput = spotBugsOutput
+
+	// check if GRADLE failed running
+	errorGradleFound := strings.Contains(spotbugsScan.Container.COutput, "ERROR_RUNNING_GRADLE_BUILD")
+	if errorGradleFound {
+		spotbugsScan.ErrorFound = errors.New("error running gradle")
+		spotbugsScan.prepareSpotBugsVulns()
+		spotbugsScan.prepareContainerAfterScan()
+		return nil
+	}
+
+	// check if Maven failed running
+	errorMavenFound := strings.Contains(spotbugsScan.Container.COutput, "ERROR_RUNNING_MAVEN_BUILD")
+	if errorMavenFound {
+		spotbugsScan.ErrorFound = errors.New("error running maven")
+		spotbugsScan.prepareSpotBugsVulns()
+		spotbugsScan.prepareContainerAfterScan()
+		return nil
+	}
+
+	// check if unsuported java project was found
+	errorUnsuported := strings.Contains(spotbugsScan.Container.COutput, "ERROR_UNSUPPORTED_JAVA_PROJECT")
+	if errorUnsuported {
+		spotbugsScan.ErrorFound = errors.New("error unsuported java project")
+		spotbugsScan.prepareSpotBugsVulns()
+		spotbugsScan.prepareContainerAfterScan()
+		return nil
+	}
+
+	// nil cOutput states that no Issues were found.
+	if spotbugsScan.Container.COutput == "" {
+		spotbugsScan.prepareContainerAfterScan()
+		return nil
+	}
+
+	// Unmarshall rawOutput into finalOutput, that is a SpotBugsOutput struct.
+	spotBugsOutput, err := parseXMLtoJSON([]byte(spotbugsScan.Container.COutput))
+	if err != nil {
+		log.Error("analyzeSpotBugs", "SPOTBUGS", 1039, spotbugsScan.Container.COutput, err)
+		spotbugsScan.ErrorFound = err
+		spotbugsScan.prepareContainerAfterScan()
+		return err
+	}
+
+	spotbugsScan.FinalOutput = spotBugsOutput
+
+	// check results and prepare all vulnerabilities found
+	spotbugsScan.prepareSpotBugsVulns()
+	spotbugsScan.prepareContainerAfterScan()
+	return nil
+}
+
+func parseXMLtoJSON(byteValue []byte) (SpotBugsOutput, error) {
+	var bugs SpotBugsOutput
+	if err := xml.Unmarshal(byteValue, &bugs); err != nil {
+		return bugs, err
+	}
+	numOfErrors, err := strconv.Atoi(bugs.Errors.Errors)
+	if err != nil {
+		return bugs, err
+	}
+	numOfMissingClasses, err := strconv.Atoi(bugs.Errors.MissingClasses)
+	if err != nil {
+		return bugs, err
+	}
+	if (len(bugs.SpotBugsIssue) == 0) && (numOfErrors > 0 || numOfMissingClasses > 0) {
+		return bugs, fmt.Errorf("spotbugs has risen errors because of [%d] missing classes and [%d] errors while analysing", numOfMissingClasses, numOfErrors)
+	}
+	return bugs, nil
+}
+
+func (spotbugsScan *SecTestScanInfo) prepareSpotBugsVulns() {
+	var spotbugsOutput SpotBugsOutput
+
+	huskyCIspotbugsResults := types.HuskyCISecurityTestOutput{}
+	spotbugsOutput = spotbugsScan.FinalOutput.(SpotBugsOutput)
+
+	if spotbugsScan.ErrorFound != nil {
+		spotbugsVuln := types.HuskyCIVulnerability{}
+		spotbugsVuln.Language = "Java"
+		spotbugsVuln.SecurityTool = "SpotBugs"
+		spotbugsVuln.Details = fmt.Sprintf("An error occured running huskyCI scan on your Java project: %s", spotbugsScan.ErrorFound.Error())
+		spotbugsVuln.Severity = "LOW"
+		spotbugsVuln.Confidence = "HIGH"
+
+		spotbugsScan.Vulnerabilities.LowVulns = append(huskyCIspotbugsResults.LowVulns, spotbugsVuln)
+		return
+	}
+
+	for i := 0; i < len(spotbugsOutput.SpotBugsIssue); i++ {
+		for j := 0; j < len(spotbugsOutput.SpotBugsIssue[i].SourceLine); j++ {
+			spotbugsVuln := types.HuskyCIVulnerability{}
+			spotbugsVuln.Language = "Java"
+			spotbugsVuln.SecurityTool = "SpotBugs"
+			spotbugsVuln.Type = spotbugsOutput.SpotBugsIssue[i].Abbreviation
+			spotbugsVuln.Details = spotbugsOutput.SpotBugsIssue[i].Type
+			startLine := spotbugsOutput.SpotBugsIssue[i].SourceLine[j].Start
+			endLine := spotbugsOutput.SpotBugsIssue[i].SourceLine[j].End
+			spotbugsVuln.Code = fmt.Sprintf("Code beetween Line %s and Line %s.", startLine, endLine)
+			spotbugsVuln.Line = startLine
+			spotbugsVuln.File = spotbugsOutput.SpotBugsIssue[i].SourceLine[j].SourcePath
+
+			switch spotbugsOutput.SpotBugsIssue[i].Priority {
+			case "1":
+				spotbugsVuln.Confidence = "HIGH"
+			case "2":
+				spotbugsVuln.Confidence = "MEDIUM"
+			default:
+				spotbugsVuln.Confidence = "LOW"
+			}
+
+			rank, err := strconv.Atoi(spotbugsOutput.SpotBugsIssue[i].Rank)
+			if err != nil {
+				log.Warning("analyzeSpotBugs", "SPOTBUGS", 1039, "exception while reading rank from a spotbugs issue", err)
+				continue
+			}
+
+			switch {
+			case rank < highSeverityValue:
+				spotbugsVuln.Severity = "HIGH"
+				huskyCIspotbugsResults.HighVulns = append(huskyCIspotbugsResults.HighVulns, spotbugsVuln)
+			case rank < mediumSeverityValue:
+				spotbugsVuln.Severity = "MEDIUM"
+				huskyCIspotbugsResults.MediumVulns = append(huskyCIspotbugsResults.MediumVulns, spotbugsVuln)
+			default:
+				spotbugsVuln.Severity = "LOW"
+				huskyCIspotbugsResults.LowVulns = append(huskyCIspotbugsResults.LowVulns, spotbugsVuln)
+			}
+		}
+	}
+
+	spotbugsScan.Vulnerabilities = huskyCIspotbugsResults
+}