Skip to content

Commit

Permalink
Merge pull request #26 from h3xstream/feature/migration_to_sonarqube_5.6
Browse files Browse the repository at this point in the history
Migration to sonarqube 5.6 + plugin update
  • Loading branch information
h3xstream authored Jun 27, 2016
2 parents c5cc6b9 + f1d0c1e commit 18d1c1a
Show file tree
Hide file tree
Showing 56 changed files with 13,834 additions and 3,691 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: java
sudo: false
jdk:
- oraclejdk7
- oraclejdk8
install: true
script: ./travis.sh
env:
Expand Down
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@ Sonar Findbugs [![Build Status](https://travis-ci.org/SonarQubeCommunity/sonar-f
==========

## Description / Features

This plugin requires the [Java Plugin](http://docs.sonarqube.org/display/PLUG/Java+Plugin), and uses [FindBugs](http://findbugs.sourceforge.net/), [fb-contrib](http://fb-contrib.sourceforge.net/) and [Find Security Bugs](http://h3xstream.github.io/find-sec-bugs/) to provide coding rules.

Since version 3.0, the plugin embed FindBugs 3.0.0 which supports analysis of Java 8 bytecode but requires Java 1.7 to run (see Compatibility section)

A majority of the FindBugs rules have been rewritten in the Java plugin. Rewritten rules are marked "Deprecated" in the FindBugs plugin, but a [concise summary of replaced rules](http://dist.sonarsource.com/reports/coverage/findbugs.html) is available.

## Usage

In the quality profile, activate some rules from the FindBugs, fb-contrib or FindSecBugs rule repositories and run an analysis on your project.

### Compiled code

FindBugs requires the compiled classes to run.

Make sure that you compile your source code with debug information on (to get the line numbers in the Java bytecode). Debug is usually on by default unless you're compiling with Ant, in which case, you will need to turn it on explicitly. If the debug information is not available, the issues raised by FindBugs will be displayed at the beginning of the file because the correct line numbers were not available.


## Compatibility

Version 3.0.0 of findbugs requires java 1.7 to run. Please find below the compatibility matrix of the plugin.

Findbugs Plugin version|Embedded Findbugs version|Embedded Findsecbugs version|Embedded FB-Contrib version|Minimal Java version
---|---|---|---|---
2.4|2.0.3|N/A|5.2.1|1.6
3.0|3.0.0|N/A|6.0.0|1.7
3.2|3.0.1|1.3.0|6.0.0|1.7
3.3|3.0.1|1.4.2|6.2.3|1.7
----|-------|-------|-------|----
2.4 | 2.0.3 | N/A | 5.2.1 | 1.6
3.0 | 3.0.0 | N/A | 6.0.0 | 1.7
3.2 | 3.0.1 | 1.3.0 | 6.0.0 | 1.7
3.3 | 3.0.1 | 1.4.2 | 6.2.3 | 1.7
3.4 | 3.0.1 | 1.4.6 | 6.6.1 | 1.8
352 changes: 352 additions & 0 deletions generate_profiles/BuildFsbXmlFiles.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
import groovy.text.SimpleTemplateEngine
import groovy.xml.MarkupBuilder
import groovy.util.Node


//Includes all the bugs that are bundle with FindBugs by default
findBugsPatterns = ["XSS_REQUEST_PARAMETER_TO_SEND_ERROR",
"XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER",
"HRS_REQUEST_PARAMETER_TO_HTTP_HEADER",
"HRS_REQUEST_PARAMETER_TO_COOKIE",
"DMI_CONSTANT_DB_PASSWORD",
"DMI_EMPTY_DB_PASSWORD",
"SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE",
"SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING",
]

//Informational stuff that will interest Security Reviewer but will annoys the developers.
informationnalPatterns = ["SERVLET_PARAMETER",
"SERVLET_CONTENT_TYPE",
"SERVLET_SERVER_NAME",
"SERVLET_SESSION_ID",
"SERVLET_QUERY_STRING",
"SERVLET_HEADER",
"SERVLET_HEADER_REFERER",
"SERVLET_HEADER_USER_AGENT",
"COOKIE_USAGE",
"WEAK_FILENAMEUTILS",
"JAXWS_ENDPOINT",
"JAXRS_ENDPOINT",
"TAPESTRY_ENDPOINT",
"WICKET_ENDPOINT",
"FILE_UPLOAD_FILENAME",
"STRUTS1_ENDPOINT",
"STRUTS2_ENDPOINT",
"SPRING_ENDPOINT",
"HTTP_RESPONSE_SPLITTING",
"CRLF_INJECTION_LOGS",
"EXTERNAL_CONFIG_CONTROL",
"STRUTS_FORM_VALIDATION",
"ESAPI_ENCRYPTOR",
"ANDROID_BROADCAST",
"ANDROID_GEOLOCATION",
"ANDROID_WEB_VIEW_JAVASCRIPT",
"ANDROID_WEB_VIEW_JAVASCRIPT_INTERFACE"]

//All the cryptography related bugs. Usually with issues related to confidentiality or integrity of data in transit.
cryptoBugs = [
"WEAK_TRUST_MANAGER",
"WEAK_HOSTNAME_VERIFIER",
//"WEAK_MESSAGE_DIGEST", //Deprecated
"WEAK_MESSAGE_DIGEST_MD5",
"WEAK_MESSAGE_DIGEST_SHA1",
"CUSTOM_MESSAGE_DIGEST",
"HAZELCAST_SYMMETRIC_ENCRYPTION",
"NULL_CIPHER",
"UNENCRYPTED_SOCKET",
"DES_USAGE",
"RSA_NO_PADDING",
"RSA_KEY_SIZE",
"BLOWFISH_KEY_SIZE",
"STATIC_IV",
"ECB_MODE",
"PADDING_ORACLE",
"CIPHER_INTEGRITY"
]

majorBugsAuditOnly = [ //Mostly due to their high false-positive rate
"TRUST_BOUNDARY_VIOLATION"
]

//Important bugs but that have lower chance to get full compromise of system (see critical).
majorBugs = [
"PREDICTABLE_RANDOM",
"PATH_TRAVERSAL_IN",
"PATH_TRAVERSAL_OUT",
"REDOS",
"BAD_HEXA_CONVERSION",
"HARD_CODE_PASSWORD",
"HARD_CODE_KEY",
"XSS_REQUEST_WRAPPER",
"UNVALIDATED_REDIRECT",
"ANDROID_EXTERNAL_FILE_ACCESS",
"ANDROID_WORLD_WRITABLE",
"INSECURE_COOKIE",
"HTTPONLY_COOKIE",
"TRUST_BOUNDARY_VIOLATION",
"XSS_SERVLET",
]

criticalBugs = [ //RCE or powerful function
"COMMAND_INJECTION",
"XXE_SAXPARSER",
"XXE_XMLREADER",
"XXE_DOCUMENT",
"SQL_INJECTION_HIBERNATE",
"SQL_INJECTION_JDO",
"SQL_INJECTION_JPA",
"LDAP_INJECTION",
"XPATH_INJECTION",
"XML_DECODER",
"SCRIPT_ENGINE_INJECTION",
"SPEL_INJECTION",
"SQL_INJECTION_SPRING_JDBC",
"SQL_INJECTION_JDBC",
"EL_INJECTION",
"SEAM_LOG_INJECTION",
"OBJECT_DESERIALIZATION",
"MALICIOUS_XSLT"
]

majorJspBugs = ["XSS_REQUEST_PARAMETER_TO_JSP_WRITER",
"XSS_JSP_PRINT", "JSP_JSTL_OUT"]

//RCE from JSP specific functions (taglibs)
criticalJspBugs = ["JSP_INCLUDE","JSP_SPRING_EVAL","JSP_XSLT"]

exclusions = ['CUSTOM_INJECTION']

deprecatedRules = ["XSS_REQUEST_PARAMETER_TO_JSP_WRITER"]

////////////// Generate rules files

def getSonarPriority(String type,String category, String description) {
//FSB Specific
if (type in criticalBugs || type in criticalJspBugs) return "CRITICAL";
if (type in majorBugs || type in cryptoBugs || type in majorJspBugs) return "MAJOR";
if (type in informationnalPatterns) return "INFO"

//Findbugs critical base on the type or message
if(type.contains("IMPOSSIBLE")) {
return "CRITICAL"
}
if(description =~ /will result in [\w]+Exception at runtime/ || description =~ /will always throw a [\w]+Exception/) {
return "CRITICAL"
}

//Findbugs general
if(category in ["CORRECTNESS", "PERFORMANCE", "SECURITY","MULTI-THREADING","BAD_PRACTICE"]) return "MAJOR";
if(category in ["STYLE", "MALICIOUS_CODE", "I18N"]) return "INFO"

println("Unknown priority for "+type+" ("+category+")")
return "INFO";
}

//Plugin definition
class Plugin {
String file = ""
private Node fbConfXml = null;

InputStream getMessages() {
URL urlMsg1 = new URL("jar:file:deps/"+file+"!/messages.xml")
return urlMsg1.openStream()
}
InputStream getFindbugsConf() {
URL urlMsg1 = new URL("jar:file:deps/"+file+"!/findbugs.xml")
return urlMsg1.openStream()
}
String getCategory(String bugType) {
if(fbConfXml == null)
fbConfXml = new XmlParser().parse(getFindbugsConf())

def bug = fbConfXml."**".BugPattern.find { node-> node.@type == bugType}
if(bug == null) return null
if(bug == "NOISE") return null
return bug.@category
}
}

String getFindBugsCategory(List<Plugin> plugins, String bugType) {
for(plugin in plugins) {
category = plugin.getCategory(bugType)
if(category != null) {
return category;
}
}
return "EXPERIMENTAL"
}

FSB = new Plugin(file:"findsecbugs-plugin-1.4.6.jar")
FB = new Plugin(file:"findbugs-3.0.1.jar")
CONTRIB = new Plugin(file:"fb-contrib-6.6.1.jar")

/**
*
* @param rulesSetName Name of the rules set generate. The filename will be rules-RULESSETNAME.xml
* @param sources Handle of stream to the messages
* @param includedBugs Bug type to include
* @return
*/
def writeRules(String rulesSetName,List<Plugin> plugins,List<String> includedBugs) {


//Output file
File f = new File("out_sonar","rules-"+rulesSetName+".xml")
printf("Building ruleset %s (%s)%n", rulesSetName, f.getCanonicalPath())

//XML construction of the rules file
def xml = new MarkupBuilder(new PrintWriter(f))
xml.rules {
mkp.comment "This file is auto-generated."

def buildPattern = { pattern ->

category = getFindBugsCategory(plugins, pattern.attribute("type"))

if(category == "EXPERIMENTAL" || category == "NOISE") return;
if(category == "MT_CORRECTNESS") category = "MULTI-THREADING"

//if(rulesSetName == 'jsp') println pattern.attribute("type")

if(includedBugs.isEmpty() || includedBugs.contains(pattern.attribute("type"))) {
//if(rulesSetName == 'jsp') println "-INCLUDED"

rule(key: pattern.attribute("type"),
priority: getSonarPriority(pattern.attribute("type"),category,pattern.Details.text())) {

name(category.toLowerCase().capitalize().replace("_"," ") + " - " +pattern.ShortDescription.text())
configKey(pattern.attribute("type"))
description(pattern.Details.text().trim())

//OWASP TOP 10 2013
if (pattern.Details.text().toLowerCase().contains('injection') || pattern.Details.text().contains('A1-Injection')) {
tag("owasp-a1")
tag("injection")
}
if (pattern.Details.text().contains('A2-Broken_Authentication_and_Session_Management')) {
tag("owasp-a2")
}
if (pattern.attribute("type").contains("XSS") || pattern.Details.text().contains('A3-Cross-Site_Scripting')) {
tag("owasp-a3")
}
if (pattern.Details.text().contains('A4-Insecure_Direct_Object_References') || pattern.Details.text().contains('Path_Traversal')) {
tag("owasp-a4")
}
if (pattern.Details.text().contains('A5-Security_Misconfiguration')) {
tag("owasp-a5")
}
if (pattern.attribute('type').equals('HARD_CODE_PASSWORD') ||
pattern.attribute("type") in cryptoBugs ||
pattern.Details.text().contains('A6-Sensitive_Data_Exposure')) {
tag("owasp-a6")
tag("cryptography")
}
if (pattern.Details.text().contains('A7-Missing_Function_Level_Access_Control')) {
tag("owasp-a7")
}
if (pattern.Details.text().toLowerCase().contains('A8-Cross-Site_Request_Forgery')) {
tag("owasp-a8")
}
if (pattern.Details.text().toLowerCase().contains('A9-Using_Components_with_Known_Vulnerabilities')) {
tag("owasp-a9")
}
if (pattern.Details.text().toLowerCase().contains('A10-Unvalidated_Redirects_and_Forwards')) {
tag("owasp-a10")
}

//Misc tags

if (pattern.Details.text().toLowerCase().contains('wasc')) {
tag("wasc")
}
if (pattern.Details.text().toLowerCase().contains('cwe')) {
tag("cwe")
}
if (pattern.ShortDescription.text().toLowerCase().contains('android')) {
tag("android")
}
if (pattern.attribute("type").contains("JSP")) {
tag("jsp")
}

//Category related
tag(category.toLowerCase().replace("_","-"))


if(category in ['PERFORMANCE','CORRECTNESS','MULTI-THREADING']) {
tag("bug")
}

if(deprecatedRules.contains(pattern.attribute("type"))) {
status("DEPRECATED")
}
}
//name: pattern.ShortDescription.text(),
// 'description': pattern.Details.text(),
// 'type':pattern.attribute("type")])
}
}

plugins.forEach { plugin ->
patternsXml = new XmlParser().parse(plugin.getMessages())
patternsXml.BugPattern.each(buildPattern);
}
}
}

//FindBugs
writeRules("findbugs", [FB], [])
//Find Security Bugs
writeRules("findsecbugs", [FSB], informationnalPatterns + cryptoBugs + majorBugs + criticalBugs)
writeRules("jsp", [FSB], majorJspBugs + criticalJspBugs)
//FB-contrib
writeRules("fbcontrib", [CONTRIB], [])

////////////// Generate the profile files

def writeProfile(String profileName,List<String> includedBugs) {

File f = new File("out_sonar","profile-"+profileName+".xml")
printf("Building profile %s (%s)%n",profileName,f.getCanonicalPath())



def xml = new MarkupBuilder(new PrintWriter(f))
xml.FindBugsFilter {
mkp.comment "This file is auto-generated."


includedBugs.forEach { patternName ->

Match {
Bug(pattern: patternName)
}
}

}
}


def getAllPatternsFromPlugin(Plugin plugin) {
def patterns = [];

patternsXml = new XmlParser().parse(plugin.getMessages())
patternsXml.BugPattern.each { pattern ->

category = getFindBugsCategory([plugin], pattern.attribute("type"))

if (category == "EXPERIMENTAL" || category == "NOISE") return;
//if (category == "MT_CORRECTNESS") category = "MULTI-THREADING";

patterns << pattern.attribute("type");
}

return patterns;
}


writeProfile("findbugs-only", getAllPatternsFromPlugin(FB));
writeProfile("findbugs-and-fb-contrib", getAllPatternsFromPlugin(FB) + getAllPatternsFromPlugin(CONTRIB));
writeProfile("findbugs-security-audit", informationnalPatterns + cryptoBugs + majorBugs + majorBugsAuditOnly + criticalBugs + findBugsPatterns)
writeProfile("findbugs-security-minimal", cryptoBugs + majorBugs + criticalBugs + findBugsPatterns)
writeProfile("findbugs-security-jsp", majorJspBugs + criticalJspBugs)
Loading

0 comments on commit 18d1c1a

Please sign in to comment.