diff --git a/PR_6_scala/scala/cookie/rule-CookieHTTPOnly.scala b/PR_6_scala/scala/cookie/rule-CookieHTTPOnly.scala
new file mode 100644
index 0000000..d523084
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookieHTTPOnly.scala
@@ -0,0 +1,31 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cookie
+
+import javax.servlet.http.Cookie
+import javax.servlet.http.HttpServletResponse
+
+class CookieHTTPOnly {
+ // {fact rule=sensitive-cookie-without-http-only-flag@v1.0 defects=1}
+ def danger(res: HttpServletResponse): Unit = {
+ // ruleid: scala_cookie_rule-CookieHTTPOnly
+ val cookie = new Cookie("key", "value")
+ cookie.setSecure(true)
+ cookie.setMaxAge(60)
+ cookie.setHttpOnly(false) // danger
+
+ res.addCookie(cookie)
+ }
+ // {/fact}
+
+ // cookie.setHttpOnly(true) is missing
+ // {fact rule=sensitive-cookie-without-http-only-flag@v1.0 defects=1}
+ def danger2(res: HttpServletResponse): Unit = {
+ // ruleid: scala_cookie_rule-CookieHTTPOnly
+ val cookie = new Cookie("key", "value")
+ cookie.setSecure(true)
+ cookie.setMaxAge(60)
+ res.addCookie(cookie)
+ }
+ // {/fact}
+}
+
diff --git a/PR_6_scala/scala/cookie/rule-CookieHTTPOnly.yml b/PR_6_scala/scala/cookie/rule-CookieHTTPOnly.yml
new file mode 100644
index 0000000..5cfad9e
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookieHTTPOnly.yml
@@ -0,0 +1,34 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-CookieHTTPOnly"
+ languages:
+ - "scala"
+ message: |
+ A new cookie is created without the HttpOnly flag set. The HttpOnly flag is a directive to the
+ browser to make sure that the cookie can not be red by malicious script. When a user is the
+ target of a "Cross-Site Scripting", the attacker would benefit greatly from getting the session
+ id for example.
+ metadata:
+ category: "security"
+ cwe: "CWE-1004"
+ shortDescription: "Sensitive Cookie Without 'HttpOnly' Flag"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ pattern-either:
+ - patterns:
+ - pattern: |
+ val $C = new javax.servlet.http.Cookie(..., ...);
+ ...
+ $RESP.addCookie($C);
+ - pattern-not-inside: |
+ val $C = new javax.servlet.http.Cookie(..., ...);
+ ...
+ $C.setHttpOnly(true);
+ ...
+ $RESP.addCookie($C);
+ - pattern: "(javax.servlet.http.Cookie $C).setHttpOnly(false);"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/cookie/rule-CookieInsecure.scala b/PR_6_scala/scala/cookie/rule-CookieInsecure.scala
new file mode 100644
index 0000000..cbd9306
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookieInsecure.scala
@@ -0,0 +1,45 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cookie
+
+import javax.servlet.http.Cookie
+import javax.servlet.http.HttpServletResponse
+
+
+class CookieInsecure {
+ def danger(res: HttpServletResponse): Unit = {
+ // {fact rule=sensitive-information-leak@v1.0 defects=1}
+ // ruleid: scala_cookie_rule-CookieInsecure
+ val cookie = new Cookie("key", "value")
+ cookie.setHttpOnly(true)
+ // {/fact}
+ cookie.setMaxAge(60)
+ // {fact rule=sensitive-information-leak@v1.0 defects=1}
+ // ruleid: scala_cookie_rule-CookieInsecure
+ cookie.setSecure(false) // danger
+ // {/fact}
+
+ res.addCookie(cookie)
+ }
+
+ // {fact rule=sensitive-information-leak@v1.0 defects=1}
+ // cookie.setSecure(true); is missing
+ def danger2(res: HttpServletResponse): Unit = {
+ // ruleid: scala_cookie_rule-CookieInsecure
+ val cookie = new Cookie("key", "value")
+ cookie.setHttpOnly(true)
+ cookie.setMaxAge(60)
+ res.addCookie(cookie)
+ }
+ // {/fact}
+
+ // {fact rule=sensitive-information-leak@v1.0 defects=0}
+ def ok(res: HttpServletResponse): Unit = {
+ val cookie = new Cookie("key", "value")
+ cookie.setHttpOnly(true)
+ cookie.setMaxAge(60)
+ cookie.setSecure(true) // safe
+
+ res.addCookie(cookie)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/cookie/rule-CookieInsecure.yml b/PR_6_scala/scala/cookie/rule-CookieInsecure.yml
new file mode 100644
index 0000000..b8566ec
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookieInsecure.yml
@@ -0,0 +1,33 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-CookieInsecure"
+ languages:
+ - "scala"
+ message: |
+ "A new cookie is created without the Secure flag set. The Secure flag is a
+ directive to the browser to make sure that the cookie is not sent for insecure communication
+ (http://)"
+ metadata:
+ category: "security"
+ cwe: "CWE-539"
+ shortDescription: "Information Exposure Through Persistent Cookies"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-not-inside: |
+ val $C = new javax.servlet.http.Cookie(..., ...);
+ ...
+ $C.setSecure(true);
+ ...
+ $RESP.addCookie($C);
+ - pattern-either:
+ - pattern: |
+ val $C = new javax.servlet.http.Cookie(..., ...);
+ ...
+ $RESP.addCookie($C);
+ - pattern: "($C:javax.servlet.http.Cookie).setSecure(false);"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/cookie/rule-CookiePersistent.scala b/PR_6_scala/scala/cookie/rule-CookiePersistent.scala
new file mode 100644
index 0000000..85a0195
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookiePersistent.scala
@@ -0,0 +1,19 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cookie
+
+import javax.servlet.http.Cookie
+import javax.servlet.http.HttpServletResponse
+
+class CookiePersistent {
+ // {fact rule=insecure-cookie@v1.0 defects=1}
+ def danger(res: HttpServletResponse): Unit = {
+ val cookie = new Cookie("key", "value")
+ cookie.setSecure(true)
+ cookie.setHttpOnly(true)
+ // ruleid: scala_cookie_rule-CookiePersistent
+ cookie.setMaxAge(31536000) // danger
+
+ res.addCookie(cookie)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/cookie/rule-CookiePersistent.yml b/PR_6_scala/scala/cookie/rule-CookiePersistent.yml
new file mode 100644
index 0000000..d653ba2
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookiePersistent.yml
@@ -0,0 +1,25 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-CookiePersistent"
+ languages:
+ - "scala"
+ message: |
+ "Storing sensitive data in a persistent cookie for an extended period can lead to a breach of
+ confidentiality or account compromise."
+ metadata:
+ category: "security"
+ cwe: "CWE-614"
+ shortDescription: "Sensitive Cookie in HTTPS Session Without 'Secure' Attribute"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern: |
+ ($C: Cookie).setMaxAge($AGE)
+ - metavariable-comparison:
+ comparison: "$AGE >= 31536000"
+ metavariable: "$AGE"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/cookie/rule-CookieUsage.scala b/PR_6_scala/scala/cookie/rule-CookieUsage.scala
new file mode 100644
index 0000000..4b9c0af
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookieUsage.scala
@@ -0,0 +1,35 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cookie
+
+import javax.servlet.ServletException
+import javax.servlet.http.Cookie
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.io.IOException
+
+class CookieUsage {
+ @Override
+ @throws[ServletException]
+ @throws[IOException]
+ protected def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ for (cookie <- req.getCookies) {
+ // {fact rule=insecure-cookie@v1.0 defects=1}
+ // ruleid: scala_cookie_rule-CookieUsage
+ cookie.getName
+ // {/fact}
+ // {fact rule=insecure-cookie@v1.0 defects=1}
+ // ruleid: scala_cookie_rule-CookieUsage
+ cookie.getValue
+ // {/fact}
+ // {fact rule=insecure-cookie@v1.0 defects=1}
+ // ruleid: scala_cookie_rule-CookieUsage
+ cookie.getPath
+ // {/fact}
+ }
+ }
+
+ def getCookieName(req: HttpServletRequest) = {
+ val c: Cookie = req.getCookies.head
+ c.getName
+ }
+}
diff --git a/PR_6_scala/scala/cookie/rule-CookieUsage.yml b/PR_6_scala/scala/cookie/rule-CookieUsage.yml
new file mode 100644
index 0000000..28d1e66
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-CookieUsage.yml
@@ -0,0 +1,38 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-CookieUsage"
+ languages:
+ - "scala"
+ message: |
+ The information stored in a custom cookie should not be sensitive or related to the session.
+ In most cases, sensitive data should only be stored in session and referenced by the user's
+ session cookie.
+ metadata:
+ category: "security"
+ cwe: "CWE-614"
+ shortDescription: "Sensitive Cookie in HTTPS Session Without 'Secure' Attribute"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |
+ def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = {
+ ...
+ }
+ - pattern-either:
+ - patterns:
+ - pattern-inside: |
+ for ($C <- $REQ.getCookies) {
+ ...
+ }
+ - pattern-either:
+ - pattern: "$C.getName"
+ - pattern: "$C.getValue"
+ - pattern: "$C.getPath"
+ - pattern: "($C: Cookie).getName()"
+ - pattern: "($C: Cookie).getValue"
+ - pattern: "($C: Cookie).getPath"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/cookie/rule-HttpResponseSplitting.scala b/PR_6_scala/scala/cookie/rule-HttpResponseSplitting.scala
new file mode 100644
index 0000000..c1ed2b6
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-HttpResponseSplitting.scala
@@ -0,0 +1,80 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cookie
+
+import javax.servlet.ServletException
+import javax.servlet.http.Cookie
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import javax.servlet.http.HttpServletResponseWrapper
+import java.io.IOException
+
+
+class HttpResponseSplitting extends HttpServlet {
+
+ // {fact rule=http-response-splitting@v1.0 defects=1}
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val input = req.getParameter("input")
+ val c = new Cookie("name", null)
+ // ruleid: scala_cookie_rule-HttpResponseSplitting
+ c.setValue(input)
+ c.setHttpOnly(true)
+ c.setSecure(true)
+ resp.addCookie(c)
+ }
+ // {/fact}
+
+ // {fact rule=http-response-splitting@v1.0 defects=1}
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doPost(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val input = req.getParameter("input")
+ // ruleid: scala_cookie_rule-HttpResponseSplitting
+ val c = new Cookie("name", input)
+ c.setHttpOnly(true)
+ c.setSecure(true)
+ resp.addCookie(c)
+ }
+ // {/fact}
+
+ // {fact rule=http-response-splitting@v1.0 defects=1}
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doDelete(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val data = req.getParameter("input")
+ val input = data.replaceAll("\n", "")
+ // ruleid: scala_cookie_rule-HttpResponseSplitting
+ val c = new Cookie("name", input)
+ c.setHttpOnly(true)
+ c.setSecure(true)
+ resp.addCookie(c)
+ }
+ // {/fact}
+
+ // {fact rule=http-response-splitting@v1.0 defects=0}
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doOptions(req: HttpServletRequest, resp: HttpServletResponse): Unit = { // BAD
+ val tainted = req.getParameter("input")
+ resp.setHeader("test", tainted)
+ // OK: False negative but reported by spotbugs
+ val data = req.getParameter("input")
+ val normalized = data.replaceAll("\n", "\n")
+ resp.setHeader("test", normalized)
+ val normalized2 = data.replaceAll("\n", req.getParameter("test"))
+ resp.setHeader("test2", normalized2)
+ // OK
+ val normalized3 = org.apache.commons.text.StringEscapeUtils.unescapeJava(tainted)
+ resp.setHeader("test3", normalized3)
+ val normalized4 = getString(tainted)
+ resp.setHeader("test4", normalized4)
+ val wrapper = new HttpServletResponseWrapper(resp)
+ wrapper.addHeader("test", tainted)
+ wrapper.setHeader("test2", tainted)
+ }
+ // {/fact}
+
+ private def getString(s: String) = s
+}
diff --git a/PR_6_scala/scala/cookie/rule-HttpResponseSplitting.yml b/PR_6_scala/scala/cookie/rule-HttpResponseSplitting.yml
new file mode 100644
index 0000000..f48c697
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-HttpResponseSplitting.yml
@@ -0,0 +1,42 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-HttpResponseSplitting"
+ languages:
+ - "scala"
+ message: |
+ When an HTTP request contains unexpected CR and LF characters, the server may respond with an
+ output stream that is interpreted as two different HTTP responses (instead of one). An attacker
+ can control the second response and mount attacks such as cross-site scripting and cache
+ poisoning attacks.
+ metadata:
+ category: "security"
+ cwe: "CWE-113"
+ shortDescription: "Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP
+ Response Splitting')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ mode: "taint"
+ pattern-sanitizers:
+ - patterns:
+ - pattern-inside: |-
+ $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
+ - pattern: "$STR"
+ - metavariable-regex:
+ metavariable: "$REPLACE_CHAR"
+ regex: "(.*\\\\r\\\\n.*)"
+ - metavariable-regex:
+ metavariable: "$REPLACE"
+ regex: "(?!(\\\\r\\\\n))"
+ - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
+ - pattern: "org.owasp.encoder.Encode.forUri(...)"
+ - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
+ pattern-sinks:
+ - pattern: "new javax.servlet.http.Cookie(\"$KEY\", ...)"
+ - pattern: "($C:javax.servlet.http.Cookie).setValue(...)"
+ pattern-sources:
+ - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getParameter(...)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/cookie/rule-RequestParamToCookie.yml b/PR_6_scala/scala/cookie/rule-RequestParamToCookie.yml
new file mode 100644
index 0000000..19f2e55
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-RequestParamToCookie.yml
@@ -0,0 +1,45 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-RequestParamToCookie"
+ languages:
+ - "scala"
+ message: |
+ This code constructs an HTTP Cookie using an untrusted HTTP parameter. If this cookie is added
+ to an HTTP response, it will allow a HTTP response splitting vulnerability. See
+ http://en.wikipedia.org/wiki/HTTP_response_splitting for more information.
+ metadata:
+ category: "security"
+ cwe: "CWE-113"
+ shortDescription: "Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP
+ Response Splitting')"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sanitizers:
+ - patterns:
+ - pattern-inside: |-
+ $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
+ - pattern: "$STR"
+ - metavariable-regex:
+ metavariable: "$REPLACE_CHAR"
+ regex: "(.*\\\\r\\\\n.*)"
+ - metavariable-regex:
+ metavariable: "$REPLACE"
+ regex: "(?!(\\\\r\\\\n))"
+ - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
+ - pattern: "org.owasp.encoder.Encode.forUri(...)"
+ - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
+ pattern-sinks:
+ - pattern: "new javax.servlet.http.Cookie(\"$KEY\", ...);"
+ - patterns:
+ - pattern-inside: |
+ $C = new javax.servlet.http.Cookie("$KEY", ...);
+ ...
+ - pattern: "$C.setValue(...);"
+ pattern-sources:
+ - pattern: "($REQ: HttpServletRequest).getParameter(...);"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/cookie/rule-RequestParamToHeader.yml b/PR_6_scala/scala/cookie/rule-RequestParamToHeader.yml
new file mode 100644
index 0000000..6cf7124
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-RequestParamToHeader.yml
@@ -0,0 +1,43 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-RequestParamToHeader"
+ languages:
+ - "scala"
+ message: |
+ This code directly writes an HTTP parameter to an HTTP header, which allows for a HTTP
+ response splitting vulnerability. See http://en.wikipedia.org/wiki/HTTP_response_splitting for
+ more information.
+ metadata:
+ category: "security"
+ cwe: "CWE-113"
+ shortDescription: "Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP
+ Response Splitting')"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sanitizers:
+ - patterns:
+ - pattern-inside: |-
+ $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
+ - pattern: "$STR"
+ - metavariable-regex:
+ metavariable: "$REPLACE_CHAR"
+ regex: "(.*\\\\r\\\\n.*)"
+ - metavariable-regex:
+ metavariable: "$REPLACE"
+ regex: "(?!(\\\\r\\\\n))"
+ - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
+ - pattern: "org.owasp.encoder.Encode.forUri(...)"
+ - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
+ pattern-sinks:
+ - pattern: "($RES: HttpServletResponse).setHeader(\"$KEY\", ...);"
+ - pattern: "($RES: HttpServletResponse).addHeader(\"$KEY\", ...);"
+ - pattern: "($WRP: HttpServletResponseWrapper).setHeader(\"$KEY\", ...);"
+ - pattern: "($WRP: HttpServletResponseWrapper).addHeader(\"$KEY\", ...);"
+ pattern-sources:
+ - pattern: "($REQ: HttpServletRequest).getParameter(...);"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/cookie/rule-TrustBoundaryViolation.scala b/PR_6_scala/scala/cookie/rule-TrustBoundaryViolation.scala
new file mode 100644
index 0000000..97a343c
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-TrustBoundaryViolation.scala
@@ -0,0 +1,61 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cookie
+
+import javax.servlet.http.HttpServletRequest
+
+
+class TrustBoundaryViolation { //Taint input
+ // {fact rule=resource-leak@v1.0 defects=1}
+ def setSessionAttributeNameTainted(req: HttpServletRequest): Unit = {
+ val input = req.getParameter("input")
+ // ruleid: scala_cookie_rule-TrustBoundaryViolation
+ req.getSession.setAttribute(input, "true")
+ }
+ // {/fact}
+
+ // {fact rule=resource-leak@v1.0 defects=1}
+ def setSessionAttributeValueTainted(req: HttpServletRequest): Unit = {
+ val input = req.getParameter("input")
+ // ruleid: scala_cookie_rule-TrustBoundaryViolation
+ req.getSession.setAttribute("user", input)
+ }
+ // {/fact}
+
+ // {fact rule=resource-leak@v1.0 defects=1}
+ //Unknown source
+ def setSessionAttributeNameUnknownSource(req: HttpServletRequest, input: String): Unit = {
+ // ruleid: scala_cookie_rule-TrustBoundaryViolation
+ req.getSession.setAttribute(input, "true")
+ }
+ // {/fact}
+
+ // {fact rule=resource-leak@v1.0 defects=1}
+ def setSessionAttributeValueUnknownSource(req: HttpServletRequest, input: String): Unit = {
+ // ruleid: scala_cookie_rule-TrustBoundaryViolation
+ req.getSession.setAttribute("user", input) //Reported as low
+ }
+ // {/fact}
+
+ // {fact rule=resource-leak@v1.0 defects=1}
+ //Legacy api
+ def setSessionAttributeNameUnknownSourceLegacy(req: HttpServletRequest, input: String): Unit = {
+ // ruleid: scala_cookie_rule-TrustBoundaryViolation
+ req.getSession.putValue(input, "true")
+ }
+ // {/fact}
+
+ // {fact rule=resource-leak@v1.0 defects=1}
+ def setSessionAttributeValueUnknownSourceLegacy(req: HttpServletRequest, input: String): Unit = {
+ // ruleid: scala_cookie_rule-TrustBoundaryViolation
+ req.getSession.putValue("user", input)
+ }
+ // {/fact}
+
+ // {fact rule=resource-leak@v1.0 defects=0}
+ //Safe
+ def setSessionAttributeSafe(req: HttpServletRequest, input: String): Unit = {
+ if ("enable".equals(input)) req.getSession.setAttribute("user", "true")
+ else req.getSession.setAttribute("user", "false")
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/cookie/rule-TrustBoundaryViolation.yml b/PR_6_scala/scala/cookie/rule-TrustBoundaryViolation.yml
new file mode 100644
index 0000000..01358d5
--- /dev/null
+++ b/PR_6_scala/scala/cookie/rule-TrustBoundaryViolation.yml
@@ -0,0 +1,31 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cookie_rule-TrustBoundaryViolation"
+ languages:
+ - "scala"
+ message: |
+ A trust boundary can be thought of as line drawn through a program. On one side
+ of the line, data is untrusted. On the other side of the line, data is assumed
+ to be trustworthy. The purpose of validation logic is to allow data to safely
+ cross the trust boundary - to move from untrusted to trusted. A trust boundary
+ violation occurs when a program blurs the line between what is trusted and what
+ is untrusted. By combining trusted and untrusted data in the same data
+ structure, it becomes easier for programmers to mistakenly trust unvalidated
+ data.
+ metadata:
+ category: "security"
+ cwe: "CWE-501"
+ shortDescription: "Trust Boundary Violation"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - patterns:
+ - pattern: "($H: HttpServletRequest). ... .setAttribute($ARG1, $ARG2)"
+ - pattern-not: "($H: HttpServletRequest). ... .setAttribute(\"...\", \"...\")"
+ - patterns:
+ - pattern: "($H: HttpServletRequest). ... .putValue($ARG1, $ARG2)"
+ - pattern-not: "($H: HttpServletRequest). ... .putValue(\"...\", \"...\")"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/cors/rule-PermissiveCORS.scala b/PR_6_scala/scala/cors/rule-PermissiveCORS.scala
new file mode 100644
index 0000000..32abda6
--- /dev/null
+++ b/PR_6_scala/scala/cors/rule-PermissiveCORS.scala
@@ -0,0 +1,71 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package cors
+
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.io.IOException
+
+class PermissiveCORS extends HttpServlet {
+ override protected def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ falsePositiveCORS(resp)
+ resp.getWriter.print(req.getSession.getAttribute("secret"))
+ }
+
+ private def falsePositiveCORS(resp: HttpServletResponse): Unit = {
+ resp.addHeader("Access-Control-Allow-Origin", "http://example.com") // OK
+ }
+
+ // Overly permissive Cross-domain requests accepted
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def addPermissiveCORS(resp: HttpServletResponse): Unit = {
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.addHeader("Access-Control-Allow-Origin", "*") // BAD
+
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def addPermissiveCORS2(resp: HttpServletResponse): Unit = {
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.addHeader("access-control-allow-origin", "*")
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def addWildcardsCORS(resp: HttpServletResponse): Unit = {
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.addHeader("Access-Control-Allow-Origin", "*.example.com")
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def addNullCORS(resp: HttpServletResponse): Unit = {
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.addHeader("Access-Control-Allow-Origin", "null")
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def setPermissiveCORS(resp: HttpServletResponse): Unit = {
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.setHeader("Access-Control-Allow-Origin", "*")
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def setPermissiveCORSWithRequestVariable(resp: HttpServletResponse, req: HttpServletRequest): Unit = {
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.setHeader("Access-Control-Allow-Origin", req.getParameter("tainted"))
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cors-policy@v1.0 defects=1}
+ def setPermissiveCORSWithRequestVariable2(resp: HttpServletResponse, req: HttpServletRequest): Unit = {
+ val header = req.getParameter("tainted")
+ // ruleid: scala_cors_rule-PermissiveCORS
+ resp.addHeader("access-control-allow-origin", header)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/cors/rule-PermissiveCORS.yml b/PR_6_scala/scala/cors/rule-PermissiveCORS.yml
new file mode 100644
index 0000000..a80ac4c
--- /dev/null
+++ b/PR_6_scala/scala/cors/rule-PermissiveCORS.yml
@@ -0,0 +1,56 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cors_rule-PermissiveCORS"
+ languages:
+ - "scala"
+ message: |
+ Prior to HTML5, Web browsers enforced the Same Origin Policy which ensures that in order for
+ JavaScript to access the contents of a Web page, both the JavaScript and the Web page must
+ originate from the same domain. Without the Same Origin Policy, a malicious website could serve
+ up JavaScript that loads sensitive information from other websites using a client's
+ credentials, cull through it, and communicate it back to the attacker. HTML5 makes it possible
+ for JavaScript to access data across domains if a new HTTP header called
+ Access-Control-Allow-Origin is defined. With this header, a Web server defines which other
+ domains are allowed to access its domain using cross-origin requests. However, caution should
+ be taken when defining the header because an overly permissive CORS policy will allow a
+ malicious application to communicate with the victim application in an inappropriate way,
+ leading to spoofing, data theft, relay and other attacks.
+ metadata:
+ category: "security"
+ cwe: "CWE-942"
+ shortDescription: "Permissive Cross-domain Policy with Untrusted Domains"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ pattern-either:
+ - patterns:
+ - pattern-either:
+ - pattern: "($RESP:javax.servlet.http.HttpServletResponse).setHeader(\"$HEADER\",
+ \"$VAL\")"
+ - pattern: "($RESP:javax.servlet.http.HttpServletResponse).addHeader(\"$HEADER\",
+ \"$VAL\")"
+ - metavariable-regex:
+ metavariable: "$HEADER"
+ regex: "(?i)(Access-Control-Allow-Origin)"
+ - metavariable-regex:
+ metavariable: "$VAL"
+ regex: "(\\*|null)"
+ - patterns:
+ - pattern-inside: |
+ $REQVAL = ($REQ: javax.servlet.http.HttpServletRequest).getParameter(...)
+ ...
+ - pattern-either:
+ - pattern-inside: |-
+ ($RESP:javax.servlet.http.HttpServletResponse).setHeader("$HEADER", $REQVAL)
+ - pattern-inside: |-
+ ($RESP:javax.servlet.http.HttpServletResponse).addHeader("$HEADER", $REQVAL)
+ - patterns:
+ - pattern-either:
+ - pattern-inside: |-
+ ($RESP:javax.servlet.http.HttpServletResponse).setHeader("$HEADER",($REQ: javax.servlet.http.HttpServletRequest).getParameter(...))
+ - pattern-inside: |-
+ ($RESP:javax.servlet.http.HttpServletResponse).addHeader("$HEADER",($REQ: javax.servlet.http.HttpServletRequest).getParameter(...))
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/cors/rule-PermissiveCORSInjection.scala b/PR_6_scala/scala/cors/rule-PermissiveCORSInjection.scala
new file mode 100644
index 0000000..cce7f72
--- /dev/null
+++ b/PR_6_scala/scala/cors/rule-PermissiveCORSInjection.scala
@@ -0,0 +1,2 @@
+// License: MIT (c) GitLab Inc.
+// TODO: Placeholder file; add example code.
\ No newline at end of file
diff --git a/PR_6_scala/scala/cors/rule-PermissiveCORSInjection.yml b/PR_6_scala/scala/cors/rule-PermissiveCORSInjection.yml
new file mode 100644
index 0000000..fd0a9de
--- /dev/null
+++ b/PR_6_scala/scala/cors/rule-PermissiveCORSInjection.yml
@@ -0,0 +1,39 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_cors_rule-PermissiveCORSInjection"
+ languages:
+ - "java"
+ message: |
+ Prior to HTML5, Web browsers enforced the Same Origin Policy which ensures that in order for
+ JavaScript to access the contents of a Web page, both the JavaScript and the Web page must
+ originate from the same domain. Without the Same Origin Policy, a malicious website could serve
+ up JavaScript that loads sensitive information from other websites using a client's
+ credentials, cull through it, and communicate it back to the attacker. HTML5 makes it possible
+ for JavaScript to access data across domains if a new HTTP header called
+ Access-Control-Allow-Origin is defined. With this header, a Web server defines which other
+ domains are allowed to access its domain using cross-origin requests. However, caution should
+ be taken when defining the header because an overly permissive CORS policy will allow a
+ malicious application to communicate with the victim application in an inappropriate way,
+ leading to spoofing, data theft, relay and other attacks.
+ metadata:
+ category: "security"
+ cwe: "CWE-942"
+ shortDescription: "Permissive Cross-domain Policy with Untrusted Domains"
+ technology:
+ - "java"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sinks:
+ - patterns:
+ - pattern-either:
+ - pattern: "(HttpServletResponse $RES).setHeader(\"$HEADER\", ...)"
+ - pattern: "(HttpServletResponse $RES).addHeader(\"$HEADER\", ...)"
+ - metavariable-regex:
+ metavariable: "$HEADER"
+ regex: "(?i)(Access-Control-Allow-Origin)"
+ pattern-sources:
+ - pattern: "(HttpServletRequest $REQ).getParameter(...)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/crypto/rule-BlowfishKeySize.scala b/PR_6_scala/scala/crypto/rule-BlowfishKeySize.scala
new file mode 100644
index 0000000..a21aa1b
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-BlowfishKeySize.scala
@@ -0,0 +1,16 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import javax.crypto.KeyGenerator
+import java.security.NoSuchAlgorithmException
+
+class BlowfishKeySize {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ def danger(): Unit = {
+ // ruleid: scala_crypto_rule-BlowfishKeySize
+ val keyGen = KeyGenerator.getInstance("Blowfish")
+ keyGen.init(64)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/crypto/rule-BlowfishKeySize.yml b/PR_6_scala/scala/crypto/rule-BlowfishKeySize.yml
new file mode 100644
index 0000000..2e14c0d
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-BlowfishKeySize.yml
@@ -0,0 +1,27 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-BlowfishKeySize"
+ languages:
+ - "scala"
+ message: |
+ A small key size makes the ciphertext vulnerable to brute force attacks. At least 128 bits of
+ entropy should be used when generating the key if use of Blowfish is required.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |
+ $KEYGEN = javax.crypto.KeyGenerator.getInstance("Blowfish", ...);
+ ...
+ $KEYGEN.init($KEY_SIZE);
+ - metavariable-comparison:
+ comparison: "$KEY_SIZE < 128"
+ metavariable: "$KEY_SIZE"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-CipherCommon.scala b/PR_6_scala/scala/crypto/rule-CipherCommon.scala
new file mode 100644
index 0000000..5033caa
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherCommon.scala
@@ -0,0 +1,69 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import javax.crypto.BadPaddingException
+import javax.crypto.Cipher
+import javax.crypto.IllegalBlockSizeException
+import javax.crypto.NoSuchPaddingException
+import java.security.InvalidKeyException
+import java.security.Key
+import java.security.NoSuchAlgorithmException
+
+
+class CipherCommon { // Detects: CIPHER_INTEGRITY, PADDING_ORACLE
+ @throws[NoSuchPaddingException]
+ @throws[NoSuchAlgorithmException]
+ @throws[IllegalBlockSizeException]
+ @throws[BadPaddingException]
+ @throws[InvalidKeyException]
+ def noIntegrityAndOraclePaddingAttack(key: Key, plainText: Array[Byte]): Unit = {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ val c = Cipher.getInstance("AES/CBC/PKCS5Padding")
+ // {/fact}
+ c.init(Cipher.ENCRYPT_MODE, key)
+ val cipherText = c.doFinal(plainText)
+ }
+
+ // Detects: CIPHER_INTEGRITY, ECB_MODE
+ @throws[NoSuchPaddingException]
+ @throws[NoSuchAlgorithmException]
+ @throws[IllegalBlockSizeException]
+ @throws[BadPaddingException]
+ @throws[InvalidKeyException]
+ def noIntegrity(key: Key, plainText: Array[Byte]): Unit = {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ val c = Cipher.getInstance("DESede/ECB/PKCS5Padding")
+ // {/fact}
+ c.init(Cipher.ENCRYPT_MODE, key)
+ val cipherText = c.doFinal(plainText)
+ }
+
+ // Detects: CIPHER_INTEGRITY, DES_USAGE
+ @throws[NoSuchPaddingException]
+ @throws[NoSuchAlgorithmException]
+ @throws[IllegalBlockSizeException]
+ @throws[BadPaddingException]
+ @throws[InvalidKeyException]
+ def noIntegrityAndDESUsage(key: Key, plainText: Array[Byte]): Unit = {
+ // {fact rule=insecure-cryptography@v1.0 defects=0}
+ val c = Cipher.getInstance("DES/GCM/PKCS5Padding")
+ // {/fact}
+ c.init(Cipher.ENCRYPT_MODE, key)
+ val cipherText = c.doFinal(plainText)
+ }
+
+
+ // Detects: CIPHER_INTEGRITY, TDES_USAGE
+ @throws[NoSuchPaddingException]
+ @throws[NoSuchAlgorithmException]
+ @throws[IllegalBlockSizeException]
+ @throws[BadPaddingException]
+ @throws[InvalidKeyException]
+ def noIntegrityAndDESedeUsage(key: Key, plainText: Array[Byte]): Unit = {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ val c = Cipher.getInstance("DESede/CBC/PKCS5Padding")
+ // {/fact}
+ c.init(Cipher.ENCRYPT_MODE, key)
+ val cipherText = c.doFinal(plainText)
+ }
+}
diff --git a/PR_6_scala/scala/crypto/rule-CipherDESInsecure.yml b/PR_6_scala/scala/crypto/rule-CipherDESInsecure.yml
new file mode 100644
index 0000000..1e80b83
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherDESInsecure.yml
@@ -0,0 +1,25 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-CipherDESInsecure"
+ languages:
+ - "scala"
+ message: |
+ DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage
+ of AES block ciphers instead of DES.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |-
+ javax.crypto.Cipher.getInstance("$ALG")
+ - metavariable-regex:
+ metavariable: "$ALG"
+ regex: "^(DES)/.*"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-CipherDESedeInsecure.yml b/PR_6_scala/scala/crypto/rule-CipherDESedeInsecure.yml
new file mode 100644
index 0000000..6b6c58f
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherDESedeInsecure.yml
@@ -0,0 +1,25 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-CipherDESedeInsecure"
+ languages:
+ - "scala"
+ message: |
+ Triple DES (also known as 3DES or DESede) is considered strong ciphers for modern
+ applications. NIST recommends the usage of AES block ciphers instead of 3DES.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |-
+ javax.crypto.Cipher.getInstance("$ALG")
+ - metavariable-regex:
+ metavariable: "$ALG"
+ regex: "^(DESede)/.*"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-CipherECBMode.yml b/PR_6_scala/scala/crypto/rule-CipherECBMode.yml
new file mode 100644
index 0000000..2ffda91
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherECBMode.yml
@@ -0,0 +1,25 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-CipherECBMode"
+ languages:
+ - "scala"
+ message: |
+ An authentication cipher mode which provides better confidentiality of the encrypted data
+ should be used instead of Electronic Code Book (ECB) mode, which does not provide good
+ confidentiality. Specifically, ECB mode produces the same output for the same input each time.
+ This allows an attacker to intercept and replay the data.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern-inside: |-
+ javax.crypto.Cipher.getInstance("...")
+ - pattern-regex: "(AES|DES(ede)?)(/ECB/*)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/crypto/rule-CipherIntegrity.scala b/PR_6_scala/scala/crypto/rule-CipherIntegrity.scala
new file mode 100644
index 0000000..cce7f72
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherIntegrity.scala
@@ -0,0 +1,2 @@
+// License: MIT (c) GitLab Inc.
+// TODO: Placeholder file; add example code.
\ No newline at end of file
diff --git a/PR_6_scala/scala/crypto/rule-CipherIntegrity.yml b/PR_6_scala/scala/crypto/rule-CipherIntegrity.yml
new file mode 100644
index 0000000..263815b
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherIntegrity.yml
@@ -0,0 +1,32 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-CipherIntegrity"
+ languages:
+ - "scala"
+ message: |
+ The ciphertext produced is susceptible to alteration by an adversary. This mean that the
+ cipher provides no way to detect that the data has been tampered with. If the ciphertext can be
+ controlled by an attacker, it could be altered without detection.
+ metadata:
+ category: "security"
+ cwe: "CWE-353"
+ shortDescription: "Missing Support for Integrity Check"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern-inside: |-
+ javax.crypto.Cipher.getInstance("...")
+ - pattern-either:
+ - pattern-regex: "(/CBC/PKCS5Padding)"
+ - pattern-regex: "(AES|DES(ede)?)(/ECB/*)"
+ - pattern-regex: "(AES|DES(ede)?)(/CBC/*)"
+ - pattern-regex: "(AES|DES(ede)?)(/OFB/*)"
+ - pattern-regex: "(AES|DES(ede)?)(/CTR/*)"
+ - pattern-not-regex: ".*/(CCM|CWC|OCB|EAX|GCM)/.*"
+ - pattern-not-regex: "^(RSA)/.*"
+ - pattern-not-regex: "^(ECIES)$"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/crypto/rule-CipherPaddingOracle.scala b/PR_6_scala/scala/crypto/rule-CipherPaddingOracle.scala
new file mode 100644
index 0000000..cce7f72
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherPaddingOracle.scala
@@ -0,0 +1,2 @@
+// License: MIT (c) GitLab Inc.
+// TODO: Placeholder file; add example code.
\ No newline at end of file
diff --git a/PR_6_scala/scala/crypto/rule-CipherPaddingOracle.yml b/PR_6_scala/scala/crypto/rule-CipherPaddingOracle.yml
new file mode 100644
index 0000000..9791007
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CipherPaddingOracle.yml
@@ -0,0 +1,27 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-CipherPaddingOracle"
+ languages:
+ - "scala"
+ message: |
+ This specific mode of CBC with PKCS5Padding is susceptible to padding oracle attacks. An
+ adversary could potentially decrypt the message if the system exposed the difference between
+ plaintext with invalid padding or valid padding. The distinction between valid and invalid
+ padding is usually revealed through distinct error messages being returned for each condition.
+ metadata:
+ category: "security"
+ cwe: "CWE-696"
+ shortDescription: "Incorrect Behavior Order"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern-inside: |-
+ javax.crypto.Cipher.getInstance("...")
+ - pattern-regex: "(/CBC/PKCS5Padding)"
+ - pattern-not-regex: "^(RSA)/.*"
+ - pattern-not-regex: "^(ECIES)$"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/crypto/rule-CustomMessageDigest.scala b/PR_6_scala/scala/crypto/rule-CustomMessageDigest.scala
new file mode 100644
index 0000000..1ef29b3
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CustomMessageDigest.scala
@@ -0,0 +1,36 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.security.MessageDigest
+import java.util._
+
+
+// {fact rule=insecure-cryptography@v1.0 defects=1}
+// ruleid: scala_crypto_rule-CustomMessageDigest
+class CustomMessageDigest extends MessageDigest("WEAK") {
+ private val buffer = new ByteArrayOutputStream
+
+ override protected def engineUpdate(input: Byte): Unit = {
+ buffer.write(input)
+ }
+
+ @Override protected def engineUpdate(input: Array[Byte], offset: Int, len: Int): Unit = {
+ try buffer.write(input)
+ catch {
+ case e: IOException =>
+ throw new RuntimeException(e)
+ }
+ }
+
+ override protected def engineDigest = {
+ val content = buffer.toByteArray
+ Arrays.copyOf(content, 8)
+ }
+
+ override protected def engineReset(): Unit = {
+ buffer.reset
+ }
+}
+// {/fact}
diff --git a/PR_6_scala/scala/crypto/rule-CustomMessageDigest.yml b/PR_6_scala/scala/crypto/rule-CustomMessageDigest.yml
new file mode 100644
index 0000000..b80f029
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-CustomMessageDigest.yml
@@ -0,0 +1,25 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-CustomMessageDigest"
+ languages:
+ - "scala"
+ message: |
+ Implementing a custom MessageDigest is error-prone. National Institute of Standards and
+ Technology(NIST) recommends the use of SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, or
+ SHA-512/256.
+ metadata:
+ category: "security"
+ cwe: "CWE-327"
+ shortDescription: "Use of a Broken or Risky Cryptographic Algorithm"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern: |
+ class $CLAZZ extends java.security.MessageDigest(...) {
+ ...
+ }
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-DefaultHTTPClient.scala b/PR_6_scala/scala/crypto/rule-DefaultHTTPClient.scala
new file mode 100644
index 0000000..522a401
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-DefaultHTTPClient.scala
@@ -0,0 +1,21 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import org.apache.http.client.HttpClient
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.client.methods.HttpUriRequest
+import org.apache.http.impl.client.DefaultHttpClient
+import java.io.IOException
+
+
+class DefaultHTTPClient {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[IOException]
+ def danger(): Unit = {
+ // ruleid: scala_crypto_rule-DefaultHTTPClient
+ val client = new DefaultHttpClient
+ val request = new HttpGet("https://test.com")
+ client.execute(request)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/crypto/rule-DefaultHTTPClient.yml b/PR_6_scala/scala/crypto/rule-DefaultHTTPClient.yml
new file mode 100644
index 0000000..4023bd4
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-DefaultHTTPClient.yml
@@ -0,0 +1,20 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-DefaultHTTPClient"
+ patterns:
+ - pattern: "new org.apache.http.impl.client.DefaultHttpClient(...)"
+ message: |
+ DefaultHttpClient with default constructor is not compatible with TLS 1.2
+ languages:
+ - "scala"
+ severity: "WARNING"
+ metadata:
+ shortDescription: "Inadequate encryption strength"
+ category: "security"
+ cwe: "CWE-326"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
diff --git a/PR_6_scala/scala/crypto/rule-HazelcastSymmetricEncryption.scala b/PR_6_scala/scala/crypto/rule-HazelcastSymmetricEncryption.scala
new file mode 100644
index 0000000..455abfe
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-HazelcastSymmetricEncryption.scala
@@ -0,0 +1,43 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// scaffold: dependencies=com.hazelcast.hazelcast@3.12.12
+package crypto
+
+import com.hazelcast.config.Config
+import com.hazelcast.config.MapConfig
+import com.hazelcast.config.NetworkConfig
+import com.hazelcast.config.SymmetricEncryptionConfig
+import com.hazelcast.core.Hazelcast
+import com.hazelcast.core.IMap
+
+
+class HazelcastSymmetricEncryption {
+ var cacheMap: IMap[String, String] = null
+ def init(): Unit = { //Specific map time to live
+ val myMapConfig = new MapConfig()
+ myMapConfig.setName("cachetest")
+ myMapConfig.setTimeToLiveSeconds(10)
+ //Package config
+ val myConfig = new Config()
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ //Symmetric Encryption
+ // ruleid: scala_crypto_rule-HazelcastSymmetricEncryption
+ val symmetricEncryptionConfig = new SymmetricEncryptionConfig
+ symmetricEncryptionConfig.setAlgorithm("DESede")
+ symmetricEncryptionConfig.setSalt("saltysalt")
+ symmetricEncryptionConfig.setPassword("lamepassword")
+ symmetricEncryptionConfig.setIterationCount(1337)
+ // {/fact}
+ //Weak Network config..
+ val networkConfig = new NetworkConfig()
+ networkConfig.setSymmetricEncryptionConfig(symmetricEncryptionConfig)
+ myConfig.setNetworkConfig(networkConfig)
+ Hazelcast.newHazelcastInstance(myConfig)
+ cacheMap = Hazelcast.getOrCreateHazelcastInstance.getMap("cachetest")
+ }
+
+ def put(key: String, value: String): Unit = {
+ cacheMap.put(key, value)
+ }
+
+ def get(key: String) = cacheMap.get(key)
+}
diff --git a/PR_6_scala/scala/crypto/rule-HazelcastSymmetricEncryption.yml b/PR_6_scala/scala/crypto/rule-HazelcastSymmetricEncryption.yml
new file mode 100644
index 0000000..2eca604
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-HazelcastSymmetricEncryption.yml
@@ -0,0 +1,22 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-HazelcastSymmetricEncryption"
+ languages:
+ - "scala"
+ message: |
+ The network communications for Hazelcast is configured to use a symmetric cipher (probably DES
+ or Blowfish). Those ciphers alone do not provide integrity or secure authentication. The use of
+ asymmetric encryption is preferred.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern: "new com.hazelcast.config.SymmetricEncryptionConfig()"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-InsufficientKeySizeRsa.scala b/PR_6_scala/scala/crypto/rule-InsufficientKeySizeRsa.scala
new file mode 100644
index 0000000..c03405f
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-InsufficientKeySizeRsa.scala
@@ -0,0 +1,126 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+
+package crypto
+
+import java.security._
+import java.security.spec.RSAKeyGenParameterSpec
+
+
+/**
+ * The key size might need to be adjusted in the future.
+ * http://en.wikipedia.org/wiki/Key_size#Asymmetric_algorithm_key_lengths
+ */
+class InsufficientKeySizeRsa {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ def weakKeySize1 = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(512) //BAD
+
+ keyGen.generateKeyPair
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ def weakKeySize2 = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(128, new SecureRandom) //BAD //Different signature
+
+ keyGen.generateKeyPair
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ @throws[InvalidAlgorithmParameterException]
+ def weakKeySize3ParameterSpec = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(new RSAKeyGenParameterSpec(128, RSAKeyGenParameterSpec.F4))
+ val key = keyGen.generateKeyPair
+ key
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ @throws[InvalidAlgorithmParameterException]
+ def weakKeySize4ParameterSpec = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(new RSAKeyGenParameterSpec(128, RSAKeyGenParameterSpec.F4), new SecureRandom)
+ val key = keyGen.generateKeyPair
+ key
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ def weakKeySize5Recommended = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(1024) //BAD with lower priority
+
+ keyGen.generateKeyPair
+ }
+ // {/fact}
+
+ @throws[NoSuchAlgorithmException]
+ @throws[InvalidAlgorithmParameterException]
+ def okKeySizeParameterSpec = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ keyGen.initialize(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4)) //Different signature
+
+ keyGen.generateKeyPair
+ }
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ @throws[NoSuchProviderException]
+ def weakKeySizeWithProviderString = {
+ val keyGen = KeyPairGenerator.getInstance("RSA", "BC")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(1024)
+ keyGen.generateKeyPair
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ def weakKeySizeWithProviderObject1 = {
+ val keyGen = KeyPairGenerator.getInstance("RSA")
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(1024)
+ keyGen.generateKeyPair
+ }
+ // {/fact}
+
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ @throws[NoSuchAlgorithmException]
+ def weakKeySizeWithProviderObject2 = {
+ val p = new ExampleProvider("info")
+ val keyGen = KeyPairGenerator.getInstance("RSA", p)
+ // ruleid: scala_crypto_rule-InsufficientKeySizeRsa
+ keyGen.initialize(1024)
+ keyGen.generateKeyPair
+ }
+ // {/fact}
+
+ @throws[NoSuchAlgorithmException]
+ @throws[NoSuchProviderException]
+ def strongKeySizeWithProviderString = {
+ val keyGen = KeyPairGenerator.getInstance("RSA", "BC")
+ keyGen.initialize(2048) // OK: n >= 2048
+
+ keyGen.generateKeyPair
+ }
+
+ private class ExampleProvider(info: String) extends Provider("example", 0.0, info) {
+ def this() {
+ this("example")
+ }
+ }
+}
diff --git a/PR_6_scala/scala/crypto/rule-InsufficientKeySizeRsa.yml b/PR_6_scala/scala/crypto/rule-InsufficientKeySizeRsa.yml
new file mode 100644
index 0000000..df82560
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-InsufficientKeySizeRsa.yml
@@ -0,0 +1,32 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-InsufficientKeySizeRsa"
+ languages:
+ - "scala"
+ message: |
+ Detected an insufficient key size for DSA. NIST recommends a key size
+ of 2048 or higher.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - patterns:
+ - pattern-inside: |
+ $GEN = KeyPairGenerator.getInstance($ALG, ...);
+ ...
+ - pattern-either:
+ - pattern: "$VAR.initialize($SIZE, ...)"
+ - pattern: "new java.security.spec.RSAKeyGenParameterSpec($SIZE, ...)"
+ - metavariable-comparison:
+ comparison: "$SIZE < 2048"
+ metavariable: "$SIZE"
+ - metavariable-regex:
+ metavariable: "$ALG"
+ regex: "\"(RSA|DSA)\""
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-NullCipher.scala b/PR_6_scala/scala/crypto/rule-NullCipher.scala
new file mode 100644
index 0000000..cce7f72
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-NullCipher.scala
@@ -0,0 +1,2 @@
+// License: MIT (c) GitLab Inc.
+// TODO: Placeholder file; add example code.
\ No newline at end of file
diff --git a/PR_6_scala/scala/crypto/rule-NullCipher.yml b/PR_6_scala/scala/crypto/rule-NullCipher.yml
new file mode 100644
index 0000000..0d01545
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-NullCipher.yml
@@ -0,0 +1,21 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-NullCipher"
+ languages:
+ - "scala"
+ message: |
+ The NullCipher implements the Cipher interface by returning ciphertext identical to the
+ supplied plaintext. In a few contexts, such as testing, a NullCipher may be appropriate. Avoid
+ using the NullCipher. Its accidental use can introduce a significant confidentiality risk.
+ metadata:
+ category: "security"
+ cwe: "CWE-327"
+ shortDescription: "Use of a Broken or Risky Cryptographic Algorithm"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ pattern: "new javax.crypto.NullCipher()"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-NullCipherUse.scala b/PR_6_scala/scala/crypto/rule-NullCipherUse.scala
new file mode 100644
index 0000000..f917186
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-NullCipherUse.scala
@@ -0,0 +1,45 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import java.io.UnsupportedEncodingException
+import java.security.InvalidAlgorithmParameterException
+import java.security.KeyException
+import javax.crypto.BadPaddingException
+import javax.crypto.Cipher
+import javax.crypto.IllegalBlockSizeException
+import javax.crypto.NullCipher
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+
+object NullCipherUse {
+ @throws[Exception]
+ def main(args: Array[String]): Unit = {
+ val pt = "AAAAAAAAAAAAAAAA".getBytes("UTF-8")
+ val expectedCihper = Cipher.getInstance("AES/CBC/NoPadding")
+ val doNothingCihper = new NullCipher
+ printHex(encryptWithCipher(expectedCihper, pt))
+ printHex(encryptWithCipher(doNothingCihper, pt))
+ }
+
+ @throws[KeyException]
+ @throws[InvalidAlgorithmParameterException]
+ @throws[IllegalBlockSizeException]
+ @throws[BadPaddingException]
+ @throws[UnsupportedEncodingException]
+ def encryptWithCipher(cipher: Cipher, value: Array[Byte]) = { //Key generation
+ val passkey = "BBBBBBBBBBBBBBBB".getBytes("UTF-8")
+ val expectedCihper = cipher
+ val key = new SecretKeySpec(passkey, "AES")
+ //Setting the key
+ expectedCihper.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new Array[Byte](expectedCihper.getBlockSize)))
+ cipher.doFinal(value)
+ }
+
+ private def printHex(resultBytes: Array[Byte]): Unit = {
+ for (b <- resultBytes) {
+ System.out.print(Integer.toHexString(b & 0xFF))
+ }
+ System.out.println
+ }
+}
diff --git a/PR_6_scala/scala/crypto/rule-RsaNoPadding.scala b/PR_6_scala/scala/crypto/rule-RsaNoPadding.scala
new file mode 100644
index 0000000..4bb7ca0
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-RsaNoPadding.scala
@@ -0,0 +1,45 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// source: https://github.com/find-sec-bugs/find-sec-bugs/blob/master/findsecbugs-samples-java/src/test/java/testcode/crypto/RsaNoPadding.java
+// hash: a7694d0
+
+package crypto
+
+import javax.crypto.Cipher
+
+
+/**
+ * Code sample taken from : http://cwe.mitre.org/data/definitions/780.html
+ */
+class RsaNoPadding {
+ @throws[Exception]
+ def rsaCipherOk(): Unit = {
+ Cipher.getInstance("RSA/ECB/OAEPWithMD5AndMGF1Padding")
+ Cipher.getInstance("RSA")
+ Cipher.getInstance("RSA/ECB/OAEPWithMD5AndMGF1Padding", "BC")
+ }
+
+ @throws[Exception]
+ def rsaCipherWeak(): Unit = {
+ // {fact rule=insecure-rsa-algorithm@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-RsaNoPadding
+ Cipher.getInstance("RSA/NONE/NoPadding")
+ // {/fact}
+ // {fact rule=insecure-rsa-algorithm@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-RsaNoPadding
+ Cipher.getInstance("RSA/NONE/NoPadding", "BC")
+ // {/fact}
+ }
+
+ @throws[Exception]
+ def dataflowCipherWeak(): Unit = {
+ val cipher1 = null
+ Cipher.getInstance(cipher1)
+ val cipher2 = "RSA/NONE/NoPadding"
+ // {fact rule=insecure-rsa-algorithm@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-RsaNoPadding
+ Cipher.getInstance(cipher2)
+ // {/fact}
+ val cipher3 = null
+ Cipher.getInstance(cipher3)
+ }
+}
diff --git a/PR_6_scala/scala/crypto/rule-RsaNoPadding.yml b/PR_6_scala/scala/crypto/rule-RsaNoPadding.yml
new file mode 100644
index 0000000..fec4191
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-RsaNoPadding.yml
@@ -0,0 +1,25 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-RsaNoPadding"
+ languages:
+ - "scala"
+ message: |
+ The software uses the RSA algorithm but does not incorporate Optimal Asymmetric
+ Encryption Padding (OAEP), which might weaken the encryption.
+ metadata:
+ cwe: "CWE-780"
+ shortDescription: "Use of RSA Algorithm without OAEP"
+ security-severity: "MEDIUM"
+ category: "security"
+ owasp:
+ - "A3:2017-Sensitive Data Exposure"
+ - "A02:2021-Cryptographic Failures"
+ patterns:
+ - pattern: "javax.crypto.Cipher.getInstance(\"$ALG\",...)"
+ - metavariable-regex:
+ metavariable: "$ALG"
+ regex: ".*NoPadding.*"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-WeakMessageDigest.scala b/PR_6_scala/scala/crypto/rule-WeakMessageDigest.scala
new file mode 100644
index 0000000..d639f76
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-WeakMessageDigest.scala
@@ -0,0 +1,99 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import java.security.MessageDigest
+import java.security.NoSuchAlgorithmException
+import java.security.NoSuchProviderException
+import java.security.Provider
+import java.security.Signature
+
+
+object WeakMessageDigest {
+ @throws[NoSuchProviderException]
+ @throws[NoSuchAlgorithmException]
+ def weakDigestMoreSig(): Unit = {
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD5", "SUN")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD4", "SUN")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD2", "SUN")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD5")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD4")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD2")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD5", new WeakMessageDigest.ExampleProvider)
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD4", new WeakMessageDigest.ExampleProvider)
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("MD2", new WeakMessageDigest.ExampleProvider)
+ // {/fact}
+ MessageDigest.getInstance("SHA", "SUN")
+ MessageDigest.getInstance("SHA", new WeakMessageDigest.ExampleProvider)
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("SHA1", "SUN")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("SHA1", new WeakMessageDigest.ExampleProvider)
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("SHA-1", "SUN")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ MessageDigest.getInstance("SHA-1", new WeakMessageDigest.ExampleProvider)
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=0}
+ MessageDigest.getInstance("sha-384", "SUN") //OK!
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=0}
+ MessageDigest.getInstance("SHA-512", "SUN")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ Signature.getInstance("MD5withRSA")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ Signature.getInstance("MD2withDSA", "X")
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakMessageDigest
+ Signature.getInstance("SHA1withRSA", new WeakMessageDigest.ExampleProvider)
+ // {/fact}
+ // {fact rule=insecure-cryptography@v1.0 defects=0}
+ Signature.getInstance("SHA256withRSA") //OK
+ // {/fact}
+
+ Signature.getInstance("uncommon name", "")
+ }
+
+ private class ExampleProvider(info: String) extends Provider("example", 0.0, info) {
+ def this() {
+ this("example")
+ }
+ }
+}
diff --git a/PR_6_scala/scala/crypto/rule-WeakMessageDigest.yml b/PR_6_scala/scala/crypto/rule-WeakMessageDigest.yml
new file mode 100644
index 0000000..aa8aded
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-WeakMessageDigest.yml
@@ -0,0 +1,26 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-WeakMessageDigest"
+ languages:
+ - "scala"
+ message: |
+ DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage
+ of AES block ciphers instead of DES.
+ metadata:
+ category: "security"
+ cwe: "CWE-326"
+ shortDescription: "Inadequate Encryption Strength"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - pattern: "MessageDigest.getInstance(\"$ALG\", ...)"
+ - pattern: "Signature.getInstance(\"$ALG\", ...)"
+ - metavariable-regex:
+ metavariable: "$ALG"
+ regex: "(.*(MD5|MD4|MD2|SHA1|SHA-1).*)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/crypto/rule-WeakTLSProtocol.scala b/PR_6_scala/scala/crypto/rule-WeakTLSProtocol.scala
new file mode 100644
index 0000000..49926cf
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-WeakTLSProtocol.scala
@@ -0,0 +1,32 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package crypto
+
+import java.security.NoSuchAlgorithmException
+import org.apache.http.impl.client.DefaultHttpClient
+import javax.net.ssl.SSLContext
+import java.lang.reflect.Array
+
+
+object WeakTLSProtocol {
+ def main(args: Array): Unit = {
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakTLSProtocol
+ new DefaultHttpClient // BAD
+ // {/fact}
+
+ try {
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_crypto_rule-WeakTLSProtocol
+ val context1 = SSLContext.getInstance("SSL") // BAD
+ // {/fact}
+
+ // {fact rule=improper-certificate-validation@v1.0 defects=0}
+ val context2 = SSLContext.getInstance("TLS") // OK
+ // {/fact}
+ } catch {
+ case e: NoSuchAlgorithmException =>
+ // TODO Auto-generated catch block
+ e.printStackTrace
+ }
+ }
+}
diff --git a/PR_6_scala/scala/crypto/rule-WeakTLSProtocol.yml b/PR_6_scala/scala/crypto/rule-WeakTLSProtocol.yml
new file mode 100644
index 0000000..37ce0ff
--- /dev/null
+++ b/PR_6_scala/scala/crypto/rule-WeakTLSProtocol.yml
@@ -0,0 +1,27 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_crypto_rule-WeakTLSProtocol"
+ languages:
+ - "scala"
+ message: |
+ A HostnameVerifier that accept any host are often use because of certificate
+ reuse on many hosts. As a consequence, this is vulnerable to Man-in-the-middleattacks
+ attacks since the client will trust any certificate.
+ metadata:
+ category: "security"
+ cwe: "CWE-295"
+ shortDescription: "Improper Certificate Validation"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - pattern: "new org.apache.http.impl.client.DefaultHttpClient()"
+ - pattern: "javax.net.ssl.SSLContext.getInstance(\"SSL\")"
+ - patterns:
+ - pattern-inside: |
+ import javax.net.ssl._
+ ...
+ - pattern: "SSLContext.getInstance(\"SSL\")"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/endpoint/rule-InsecureServlet.scala b/PR_6_scala/scala/endpoint/rule-InsecureServlet.scala
new file mode 100644
index 0000000..621e497
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-InsecureServlet.scala
@@ -0,0 +1,57 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// source: https://github.com/find-sec-bugs/find-sec-bugs/blob/master/findsecbugs-samples-java/src/test/java/testcode/endpoint/BasicHttpServlet.java
+// hash: a7694d0
+
+package endpoint
+
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.io.IOException
+import java.util
+
+
+class InsecureServlet extends HttpServlet {
+
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ useParameters(req)
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-InsecureServlet
+ resp.getWriter.print("")
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-InsecureServlet
+ resp.getWriter.print("
Welcome to " + req.getServerName)
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-InsecureServlet
+ val sqlQuery = "UPDATE sessions(last_visit) VALUES(now()) WHERE where sid = '" + req.getRequestedSessionId + "')"
+ // {/fact}
+
+ resp.getWriter.print("")
+ val referrer = req.getHeader("Referer") //Should have a higher priority
+ if (referrer != null && referrer.startsWith("http://company.ca")) {
+ req.getHeader("Host")
+ req.getHeader("User-Agent")
+ req.getHeader("X-Requested-With")
+ }
+ }
+
+ private def useParameters(req: HttpServletRequest): Unit = {
+ val username = req.getParameter("username").asInstanceOf[String]
+ val roles = req.getParameterValues("roles").asInstanceOf[Array[String]]
+ val price = req.getParameterMap.get("hidden_price_value")
+ val parameters = req.getParameterNames
+ var isAdmin = false
+ while ( {
+ parameters.hasMoreElements
+ }) if (parameters.nextElement.equals("admin_mode")) {
+ isAdmin = true
+ }
+ }
+}
diff --git a/PR_6_scala/scala/endpoint/rule-InsecureServlet.yml b/PR_6_scala/scala/endpoint/rule-InsecureServlet.yml
new file mode 100644
index 0000000..96af547
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-InsecureServlet.yml
@@ -0,0 +1,46 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_endpoint_rule-InsecureServlet"
+ languages:
+ - "scala"
+ message: |
+ The Servlet can read GET and POST parameters from various methods. The
+ value obtained should be considered unsafe."
+ metadata:
+ category: "security"
+ cwe: "CWE-20"
+ shortDescription: "Improper Input Validation"
+ security-severity: "MEDIUM"
+ mode: "taint"
+ pattern-sanitizers:
+ - pattern: "Encode.forHtml(...)"
+ - pattern: "org.owasp.esapi.Encoder.encodeForSQL(...)"
+ pattern-sinks:
+ - patterns:
+ - pattern-either:
+ - pattern: |
+ ... + $PAR
+ - pattern: |
+ ... + $PAR + ...
+ - pattern: |
+ $PAR + ...
+ - pattern: "$PAR"
+ pattern-sources:
+ - pattern-either:
+ - pattern: "($REQ: HttpServletRequest).getContentType"
+ - pattern: "($REQ: HttpServletRequest).getServerName"
+ - pattern: "($REQ: HttpServletRequest).getRequestedSessionId"
+ - pattern: "($REQ: HttpServletRequest).getParameterValues(...)"
+ - pattern: "($REQ: HttpServletRequest).getParameterMap"
+ - pattern: "($REQ: HttpServletRequest).getParameterNames"
+ - pattern: "($REQ: HttpServletRequest).getParameter(...)"
+ - patterns:
+ - pattern-inside: |
+ ($REQ: HttpServletRequest).getSession
+ - pattern: "$SESS.getAttribute(\"...\")"
+ - pattern: |
+ ($REQ: HttpServletRequest).getSession.getAttribute("...")
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/endpoint/rule-JaxRsEndpoint.scala b/PR_6_scala/scala/endpoint/rule-JaxRsEndpoint.scala
new file mode 100644
index 0000000..db1e260
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-JaxRsEndpoint.scala
@@ -0,0 +1,57 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package endpoint
+
+import javax.ws.rs.Path
+import org.apache.commons.text.StringEscapeUtils
+
+
+@Path("/test")
+class JaxRsEndpoint {
+ def randomFunc(s: String) = s
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @Path("/hello0")
+ // ruleid: scala_endpoint_rule-JaxRsEndpoint
+ def danger0(user: String) = "Hello " + user // BAD
+ // {/fact}
+
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @Path("/hello1")
+ def danger1(user: String) = {
+ // ruleid: scala_endpoint_rule-JaxRsEndpoint
+ val tainted = randomFunc(user)
+ "Hello " + tainted
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @Path("/hello2")
+ def danger3(user: String) = {
+ // ruleid: scala_endpoint_rule-JaxRsEndpoint
+ "Hello " + user
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @Path("/hello2")
+ def danger4(user: String): String = {
+ // ruleid: scala_endpoint_rule-JaxRsEndpoint
+ return "Hello " + user
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ @Path("/hello2")
+ def ok1(user: String) = {
+ // ok: scala_endpoint_rule-JaxRsEndpoint
+ val sanitized = StringEscapeUtils.unescapeJava(user)
+ "Hello " + sanitized // OK
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ def ok2(user: String): String = {
+ return "Hello " + user // OK
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/endpoint/rule-JaxRsEndpoint.yml b/PR_6_scala/scala/endpoint/rule-JaxRsEndpoint.yml
new file mode 100644
index 0000000..569af88
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-JaxRsEndpoint.yml
@@ -0,0 +1,34 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_endpoint_rule-JaxRsEndpoint"
+ languages:
+ - "scala"
+ message: |
+ This method is part of a REST Web Service (JSR311). The security of this web service should be
+ analyzed. For example:
+ - Authentication, if enforced, should be tested.
+ - Access control, if enforced, should be tested.
+ - The inputs should be tracked for potential vulnerabilities.
+ - The communication should ideally be over SSL.
+ - If the service supports writes (e.g., via POST), its vulnerability to CSRF should be
+ investigated.
+ metadata:
+ category: "security"
+ cwe: "CWE-20"
+ shortDescription: "Improper Input Validation"
+ technology:
+ - "scala"
+ security-severity: "INFO"
+ mode: "taint"
+ pattern-sinks:
+ - pattern: "<...$VAR...>"
+ pattern-sources:
+ - patterns:
+ - pattern-inside: |
+ @javax.ws.rs.Path("...")
+ def $FUNC(..., $VAR: $TYPE, ...) = ...
+ - pattern: "$VAR"
+ severity: "INFO"
diff --git a/PR_6_scala/scala/endpoint/rule-JaxWsEndpoint.scala b/PR_6_scala/scala/endpoint/rule-JaxWsEndpoint.scala
new file mode 100644
index 0000000..aad6976
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-JaxWsEndpoint.scala
@@ -0,0 +1,66 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package endpoint
+
+import org.apache.commons.text.StringEscapeUtils
+import javax.jws.WebMethod
+import javax.jws.WebService
+
+
+@WebService
+class JaxWsEndpoint {
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ @WebMethod(operationName = "timestamp")
+ def ping = System.currentTimeMillis // OK
+ // {/fact}
+
+ def randomFunc(s: String) = s
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @WebMethod
+ // ruleid: scala_endpoint_rule-JaxWsEndpoint
+ def danger0(user: String) = "Hello " + user // BAD
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @WebMethod
+ def danger1(user: String) = {
+ // ruleid: scala_endpoint_rule-JaxWsEndpoint
+ val tainted = randomFunc(user)
+ "Hello " + tainted
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ @WebMethod
+ def danger3(user: String) = {
+ // ruleid: scala_endpoint_rule-JaxWsEndpoint
+ "Hello " + user
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ @WebMethod(action="/hello2")
+ def ok1(user: String) = {
+ // ok: scala_endpoint_rule-JaxWsEndpoint
+ val sanitized = StringEscapeUtils.unescapeJava(user)
+ "Hello " + sanitized // OK
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ def ok2(user: String): String = {
+ return "Hello " + user // OK
+ }
+ // {/fact}
+
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ @WebMethod
+ def ok3(user: String) = {
+ // ok: scala_endpoint_rule-JaxWsEndpoint
+ val sanitized = StringEscapeUtils.unescapeJava(user)
+ "Hello " + sanitized
+ }
+ // {/fact}
+
+ def ok4 = 8000
+}
diff --git a/PR_6_scala/scala/endpoint/rule-JaxWsEndpoint.yml b/PR_6_scala/scala/endpoint/rule-JaxWsEndpoint.yml
new file mode 100644
index 0000000..0940f6a
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-JaxWsEndpoint.yml
@@ -0,0 +1,33 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_endpoint_rule-JaxWsEndpoint"
+ languages:
+ - "scala"
+ message: |
+ This method is part of a SOAP Web Service (JSR224). The security of this web service should be
+ analyzed. For example:
+ - Authentication, if enforced, should be tested.
+ - Access control, if enforced, should be tested.
+ - The inputs should be tracked for potential vulnerabilities.
+ - The communication should ideally be over SSL.
+ metadata:
+ category: "security"
+ cwe: "CWE-20"
+ owasp: "A7:2017-Cross-Site Scripting (XSS)"
+ shortDescription: "Improper Input Validation"
+ technology:
+ - "scala"
+ security-severity: "INFO"
+ mode: "taint"
+ pattern-sinks:
+ - pattern: "<...$VAR...>"
+ pattern-sources:
+ - patterns:
+ - pattern-inside: |
+ @javax.jws.WebMethod(...)
+ def $FUNC(..., $VAR: $TYPE, ...) = ...
+ - pattern: "$VAR"
+ severity: "INFO"
diff --git a/PR_6_scala/scala/endpoint/rule-UnencryptedSocket.scala b/PR_6_scala/scala/endpoint/rule-UnencryptedSocket.scala
new file mode 100644
index 0000000..e0ba50e
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-UnencryptedSocket.scala
@@ -0,0 +1,89 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+import javax.net.ssl.SSLServerSocketFactory
+import javax.net.ssl.SSLSocketFactory
+import java.io._
+import java.net.InetAddress
+import java.net.ServerSocket
+import java.net.Socket
+
+object UnencryptedSocket {
+ @throws[IOException]
+ // {fact rule=insecure-connection@v1.0 defects=0}
+ private[this] def sslServerSocket(): Unit = {
+ // ok: scala_endpoint_rule-UnencryptedSocket
+ val ssoc = SSLServerSocketFactory.getDefault.createServerSocket(1234)
+ ssoc.close
+ // {/fact}
+ }
+
+ @throws[IOException]
+ private[this] def plainServerSocket(): Unit = {
+ val ssoc = new ServerSocket(1234)
+ ssoc.close
+ }
+
+ @throws[IOException]
+ private[this] def otherConstructors(): Unit = {
+ val ssoc1 = new ServerSocket
+ ssoc1.close
+ val ssoc2 = new ServerSocket(1234, 10)
+ ssoc2.close
+ val address = Array(127.toByte, 0.toByte, 0.toByte, 1.toByte)
+ val ssoc3 = new ServerSocket(1234, 10, InetAddress.getByAddress(address))
+ ssoc3.close
+ }
+
+ @throws[IOException]
+ // {fact rule=insecure-connection@v1.0 defects=0}
+ private[this] def sslSocket(): Unit = {
+ // ok: scala_endpoint_rule-UnencryptedSocket
+ val soc = SSLSocketFactory.getDefault.createSocket("www.google.com", 443)
+ doGetRequest(soc)
+ // {/fact}
+ }
+
+ @throws[IOException]
+ private[this] def plainSocket(): Unit = {
+ // {fact rule=insecure-connection@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-UnencryptedSocket
+ val soc = new Socket("www.google.com", 80)
+ doGetRequest(soc)
+ // {/fact}
+ }
+
+ @throws[IOException]
+ private[this] def other(): Unit = {
+ // {fact rule=insecure-connection@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-UnencryptedSocket
+ val soc1 = new Socket("www.google.com", 80, true)
+ // {/fact}
+ doGetRequest(soc1)
+ val address = Array(127.toByte, 0.toByte, 0.toByte, 1.toByte)
+ // {fact rule=insecure-connection@v1.0 defects=1}
+ val soc2 =
+ // ruleid: scala_endpoint_rule-UnencryptedSocket
+ new Socket("www.google.com", 80, InetAddress.getByAddress(address), 13337)
+ // {/fact}
+ doGetRequest(soc2)
+ val remoteAddress = Array(74.toByte, 125.toByte, 226.toByte, 193.toByte)
+ // {fact rule=insecure-connection@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-UnencryptedSocket
+ val soc3 = new Socket(InetAddress.getByAddress(remoteAddress), 80)
+ // {/fact}
+ doGetRequest(soc2)
+ }
+
+ @throws[IOException]
+ private[this] def doGetRequest(soc: Socket): Unit = {
+ val w = new PrintWriter(soc.getOutputStream)
+ w.write("GET / HTTP/1.0\nHost: www.google.com\n\n")
+ w.flush
+ val r = new BufferedReader(new InputStreamReader(soc.getInputStream))
+ var line = r.readLine
+ while (line != null) {
+ println(line)
+ line = r.readLine
+ }
+ soc.close
+ }
+}
diff --git a/PR_6_scala/scala/endpoint/rule-UnencryptedSocket.yml b/PR_6_scala/scala/endpoint/rule-UnencryptedSocket.yml
new file mode 100644
index 0000000..c3b0795
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-UnencryptedSocket.yml
@@ -0,0 +1,24 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_endpoint_rule-UnencryptedSocket"
+ languages:
+ - "scala"
+ message: |
+ Beyond using an SSL socket, you need to make sure your use of SSLSocketFactory
+ does all the appropriate certificate validation checks to make sure you are not
+ subject to man-in-the-middle attacks. Please read the OWASP Transport Layer
+ Protection Cheat Sheet for details on how to do this correctly.
+ metadata:
+ cwe: "CWE-319"
+ shortDescription: "Cleartext transmission of sensitive information"
+ security-severity: "MEDIUM"
+ owasp:
+ - "A3:2017-Sensitive Data Exposure"
+ - "A02:2021-Cryptographic Failures"
+ category: "security"
+ patterns:
+ - pattern: "new java.net.Socket(...)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/endpoint/rule-UnvalidatedRedirect.scala b/PR_6_scala/scala/endpoint/rule-UnvalidatedRedirect.scala
new file mode 100644
index 0000000..144123c
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-UnvalidatedRedirect.scala
@@ -0,0 +1,45 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package endpoint
+
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.io.IOException
+
+
+class UnvalidatedRedirect extends HttpServlet {
+
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val url = req.getParameter("urlRedirect")
+ unvalidatedRedirect1(resp, url)
+ }
+
+ // {fact rule=open-redirect@v1.0 defects=1}
+ @throws[IOException]
+ private def unvalidatedRedirect1(resp: HttpServletResponse, url: String): Unit = {
+ // ruleid: scala_endpoint_rule-UnvalidatedRedirect
+ if (url != null) resp.sendRedirect(url)
+ }
+ // {/fact}
+
+ // {fact rule=open-redirect@v1.0 defects=1}
+ def unvalidatedRedirect2(resp: HttpServletResponse, url: String): Unit = {
+ // ruleid: scala_endpoint_rule-UnvalidatedRedirect
+ if (url != null) resp.addHeader("Location", url)
+ }
+ // {/fact}
+
+ ///The following cases are safe for sure
+ @throws[IOException]
+ def falsePositiveRedirect1(resp: HttpServletResponse): Unit = {
+ val url = "/Home"
+ if (url != null) resp.sendRedirect(url)
+ }
+
+ def falsePositiveRedirect2(resp: HttpServletResponse): Unit = {
+ resp.addHeader("Location", "/login.jsp")
+ }
+}
diff --git a/PR_6_scala/scala/endpoint/rule-UnvalidatedRedirect.yml b/PR_6_scala/scala/endpoint/rule-UnvalidatedRedirect.yml
new file mode 100644
index 0000000..22b22f7
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-UnvalidatedRedirect.yml
@@ -0,0 +1,32 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_endpoint_rule-UnvalidatedRedirect"
+ languages:
+ - "scala"
+ message: |
+ Unvalidated redirects occur when an application redirects a user to a
+ destination URL specified by a user supplied parameter that is not validated.
+ Such vulnerabilities can be used to facilitate phishing attacks.
+ metadata:
+ category: "security"
+ cwe: "CWE-601"
+ shortDescription: "URL Redirection to Untrusted Site ('Open Redirect')"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern-either:
+ - patterns:
+ - pattern: "($REQ: HttpServletResponse).sendRedirect(...)"
+ - pattern-not: "($REQ: HttpServletResponse).sendRedirect(\"...\")"
+ - patterns:
+ - pattern: "($REQ: HttpServletResponse).addHeader(...)"
+ - pattern-not: "($REQ: HttpServletResponse).addHeader(\"...\", \"...\")"
+ - patterns:
+ - pattern: "($REQ: HttpServletResponse).encodeURL(...)"
+ - pattern-not: "($REQ: HttpServletResponse).encodeURL(\"...\")"
+ - patterns:
+ - pattern: "($REQ: HttpServletResponse).encodeRedirectUrl(...)"
+ - pattern-not: "($REQ: HttpServletResponse).encodeRedirectUrl(\"...\")"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/endpoint/rule-WeakHostNameVerification.scala b/PR_6_scala/scala/endpoint/rule-WeakHostNameVerification.scala
new file mode 100644
index 0000000..241f494
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-WeakHostNameVerification.scala
@@ -0,0 +1,94 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package endpoint
+
+import javax.net.ssl._
+import java.security.KeyManagementException
+import java.security.NoSuchAlgorithmException
+import java.security.cert.CertificateException
+import java.security.cert.X509Certificate
+
+
+class WeakHostNameVerification {
+ def useAllHosts(): Unit = {
+ HttpsURLConnection.setDefaultHostnameVerifier(new AllHosts)
+ }
+
+ @throws[NoSuchAlgorithmException]
+ @throws[KeyManagementException]
+ def useTrustAllManager(): Unit = {
+ val trustAllCerts = Array[TrustManager](new TrustAllManager)
+ val sslContext = SSLContext.getInstance("SSL")
+ sslContext.init(null, trustAllCerts, null)
+ HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory)
+ }
+
+ def useSecurityBypasser(): Unit = {
+ SecurityBypasser.destroyAllSSLSecurityForTheEntireVMForever()
+ }
+}
+
+class AllHosts extends HostnameVerifier {
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-WeakHostNameVerification
+ def verify(hostname: String, session: SSLSession) = true
+ // {/fact}
+}
+
+class TrustAllManager extends X509TrustManager {
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-WeakHostNameVerification
+ @throws[CertificateException]
+ def checkClientTrusted(cert: Array[X509Certificate], authType: String): Unit = {
+ }
+ // {/fact}
+
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-WeakHostNameVerification
+ @throws[CertificateException]
+ def checkServerTrusted(cert: Array[X509Certificate], authType: String): Unit = {
+ }
+ // {/fact}
+
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-WeakHostNameVerification
+ def getAcceptedIssuers(): Array[X509Certificate] = null
+ // {/fact}
+}
+
+class TrustAllManagerTwo extends X509TrustManager {
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-WeakHostNameVerification
+ @throws[CertificateException]
+ def checkClientTrusted(cert: Array[X509Certificate], authType: String): Unit = {
+ }
+ // {/fact}
+
+ // {fact rule=improper-certificate-validation@v1.0 defects=1}
+ // ruleid: scala_endpoint_rule-WeakHostNameVerification
+ @throws[CertificateException]
+ def checkServerTrusted(cert: Array[X509Certificate], authType: String): Unit = {
+ }
+ // {/fact}
+
+ def getAcceptedIssuers(): Array[X509Certificate] = {
+ Array()
+ }
+}
+
+object SecurityBypasser {
+ def destroyAllSSLSecurityForTheEntireVMForever(): Unit = {
+ try {
+ val trustAllCerts = Array[TrustManager](new TrustAllManager)
+ val sslContext = SSLContext.getInstance("SSL")
+ sslContext.init(null, trustAllCerts, null)
+ HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory)
+ HttpsURLConnection.setDefaultHostnameVerifier(new AllHosts)
+ } catch {
+ case e: NoSuchAlgorithmException =>
+ e.printStackTrace
+ case e: KeyManagementException =>
+ e.printStackTrace
+ }
+ }
+}
+
diff --git a/PR_6_scala/scala/endpoint/rule-WeakHostNameVerification.yml b/PR_6_scala/scala/endpoint/rule-WeakHostNameVerification.yml
new file mode 100644
index 0000000..f1945b9
--- /dev/null
+++ b/PR_6_scala/scala/endpoint/rule-WeakHostNameVerification.yml
@@ -0,0 +1,43 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_endpoint_rule-WeakHostNameVerification"
+ languages:
+ - "scala"
+ message: |
+ A HostnameVerifier that accept any host are often use because of certificate
+ reuse on many hosts. As a consequence, this is vulnerable to Man-in-the-middle
+ attacks since the client will trust any certificate.
+ metadata:
+ category: "security"
+ cwe: "CWE-295"
+ shortDescription: "Improper Certificate Validation"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - patterns:
+ - pattern-inside: |
+ class $V extends HostnameVerifier {
+ ...
+ }
+ - pattern-either:
+ - pattern: "def verify(...) = true"
+ - pattern: |
+ def verify(...) = {
+ return true
+ }
+ - patterns:
+ - pattern-inside: |
+ class $V extends X509TrustManager {
+ ...
+ }
+ - pattern-either:
+ - pattern: "def checkClientTrusted(...): Unit = {}"
+ - pattern: "def checkServerTrusted(...): Unit = {}"
+ - pattern: "def checkClientTrusted(...) = {}"
+ - pattern: "def checkServerTrusted(...) = {}"
+ - pattern: "def getAcceptedIssuers(): Array[X509Certificate] = null"
+ - pattern: "def getAcceptedIssuers(): Array[X509Certificate] = {}"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/file/rule-FileUploadFileName.scala b/PR_6_scala/scala/file/rule-FileUploadFileName.scala
new file mode 100644
index 0000000..0eda184
--- /dev/null
+++ b/PR_6_scala/scala/file/rule-FileUploadFileName.scala
@@ -0,0 +1,25 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package file
+
+import org.apache.commons.fileupload.FileItem
+import org.apache.commons.fileupload.FileUploadException
+import org.apache.commons.fileupload.disk.DiskFileItemFactory
+import org.apache.commons.fileupload.servlet.ServletFileUpload
+import javax.servlet.http.HttpServletRequest
+import java.util._
+import scala.jdk.CollectionConverters._
+
+class FileUploadFileName {
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ @throws[FileUploadException]
+ def handleFileCommon(req: HttpServletRequest): Unit = {
+ val upload = new ServletFileUpload(new DiskFileItemFactory())
+ val fileItems = upload.parseRequest(req)
+ for (item <- fileItems.asScala) {
+ // ruleid: scala_file_rule-FileUploadFileName
+ println("Saving " + item.getName() + "...")
+ }
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/file/rule-FileUploadFileName.yml b/PR_6_scala/scala/file/rule-FileUploadFileName.yml
new file mode 100644
index 0000000..8ee3c70
--- /dev/null
+++ b/PR_6_scala/scala/file/rule-FileUploadFileName.yml
@@ -0,0 +1,36 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_file_rule-FileUploadFileName"
+ languages:
+ - "scala"
+ message: |
+ The filename provided by the FileUpload API can be tampered with by the client to reference
+ unauthorized files. The provided filename should be properly validated to ensure it's properly
+ structured, contains no unauthorized path characters (e.g., / \), and refers to an authorized
+ file.
+ metadata:
+ category: "security"
+ cwe: "CWE-22"
+ shortDescription: "Improper limitation of a pathname to a restricted directory
+ ('Path Traversal')"
+ technology:
+ - "scala"
+ owasp:
+ - "A5:2017-Broken Access Control"
+ - "A01:2021-Broken Access Control"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern: |
+ def $FUNC (..., $REQ: HttpServletRequest, ... ) = {
+ ...
+ val $FILES = ($SFU: ServletFileUpload).parseRequest($REQ)
+ ...
+ for ($FILE <- $FILES.asScala) {
+ ...
+ }
+ }
+ - pattern: "$ITEM.getName()"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/file/rule-FilenameUtils.scala b/PR_6_scala/scala/file/rule-FilenameUtils.scala
new file mode 100644
index 0000000..944839c
--- /dev/null
+++ b/PR_6_scala/scala/file/rule-FilenameUtils.scala
@@ -0,0 +1,71 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package file
+
+import org.apache.commons.io.FilenameUtils._
+import java.io.File
+import java.io.IOException
+
+
+object FilenameUtils {
+ @throws[IOException]
+ def main(args: Array[String]): Unit = {
+ val maliciousPath = "/test%00/././../../././secret/note.cfg\u0000example.jpg"
+ testPath(maliciousPath)
+ }
+
+ @throws[IOException]
+ private def testPath(maliciousPath: String): Unit = {
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ val path = normalize(maliciousPath)
+ System.out.println("Expected:" + path + " -> Actual:" + canonical(path))
+ // {/fact}
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ val extension = getExtension(maliciousPath)
+ // {/fact}
+
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ System.out.println("Expected:" + extension + " -> Actual:" + getExtension(canonical(path)))
+ // {/fact}
+
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ val isExt = isExtension(maliciousPath, "jpg")
+ // {/fact}
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ System.out.println("Expected:" + isExt + " -> Actual:" + isExtension(canonical(path), "jpg"))
+ // {/fact}
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ val name = getName(maliciousPath)
+ // {/fact}
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ System.out.println("Expected:" + name + " -> Actual:" + getName(canonical(name)))
+ // {/fact}
+
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ val baseName = getBaseName(maliciousPath)
+ // {/fact}
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // ruleid: scala_file_rule-FilenameUtils
+ System.out.println("Expected:" + baseName + " -> Actual:" + getBaseName(canonical(baseName)))
+ // {/fact}
+ }
+
+ @throws[IOException]
+ private def canonical(path: String) = new File(path).getCanonicalPath
+}
+
diff --git a/PR_6_scala/scala/file/rule-FilenameUtils.yml b/PR_6_scala/scala/file/rule-FilenameUtils.yml
new file mode 100644
index 0000000..2de6b02
--- /dev/null
+++ b/PR_6_scala/scala/file/rule-FilenameUtils.yml
@@ -0,0 +1,44 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_file_rule-FilenameUtils"
+ languages:
+ - "scala"
+ message: |
+ A file is opened to read its content. The filename comes from an input
+ parameter. If an unfiltered parameter is passed to this file API, files from an
+ arbitrary filesystem location could be read.
+ metadata:
+ category: "security"
+ cwe: "CWE-22"
+ shortDescription: "Improper limitation of a pathname to a restricted directory
+ ('Path Traversal')"
+ technology:
+ - "scala"
+ owasp:
+ - "A5:2017-Broken Access Control"
+ - "A01:2021-Broken Access Control"
+ security-severity: "MEDIUM"
+ pattern-either:
+ - patterns:
+ - pattern-inside: |
+ import org.apache.commons.io.FilenameUtils._
+ ...
+ - pattern-either:
+ - pattern: "normalize(...)"
+ - pattern: "getExtension(...)"
+ - pattern: "isExtensions(...)"
+ - pattern: "isExtension(...)"
+ - pattern: "getName(...)"
+ - pattern: "getBaseName(...)"
+ - patterns:
+ - pattern-either:
+ - pattern: "org.apache.commons.io.FilenameUtils.normalize(...)"
+ - pattern: "org.apache.commons.io.FilenameUtils.getExtension(...)"
+ - pattern: "org.apache.commons.io.FilenameUtils.isExtensions(...)"
+ - pattern: "org.apache.commons.io.FilenameUtils.isExtension(...)"
+ - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
+ - pattern: "org.apache.commons.io.FilenameUtils.getBaseName(...)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/form/rule-FormValidate.scala b/PR_6_scala/scala/form/rule-FormValidate.scala
new file mode 100644
index 0000000..5c7df80
--- /dev/null
+++ b/PR_6_scala/scala/form/rule-FormValidate.scala
@@ -0,0 +1,23 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package form
+
+import org.apache.struts.validator.ValidatorForm
+// {fact rule=improper-input-validation@v1.0 defects=1}
+// ruleid: scala_form_rule-FormValidate
+class FormValidate extends ValidatorForm {
+ private var name: String = null
+ private var email: String = null
+
+ def getName = name
+
+ def setName(n: String) {
+ this.name = n
+ }
+
+ def getEmail = email
+
+ def setEmail(email: String) {
+ this.email = email
+ }
+}
+// {/fact}
diff --git a/PR_6_scala/scala/form/rule-FormValidate.yml b/PR_6_scala/scala/form/rule-FormValidate.yml
new file mode 100644
index 0000000..37ef381
--- /dev/null
+++ b/PR_6_scala/scala/form/rule-FormValidate.yml
@@ -0,0 +1,26 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_form_rule-FormValidate"
+ languages:
+ - "scala"
+ message: |
+ Form inputs should have minimal input validation. Preventive validation helps provide defense
+ in depth against a variety of risks.
+ metadata:
+ category: "security"
+ cwe: "CWE-20"
+ shortDescription: "Improper Input Validation"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |
+ class $CLASS extends $SC {
+ ...
+ }
+ - metavariable-regex:
+ metavariable: "$SC"
+ regex: "(ActionForm|ValidatorForm)"
+ - pattern-not: "public void validate() { ... }"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-AWSQueryInjection.scala b/PR_6_scala/scala/inject/rule-AWSQueryInjection.scala
new file mode 100644
index 0000000..ae01149
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-AWSQueryInjection.scala
@@ -0,0 +1,64 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import java.io.IOException
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import javax.servlet.http.HttpServlet
+import com.amazonaws.auth.BasicAWSCredentials
+import com.amazonaws.services.simpledb.AmazonSimpleDB
+import com.amazonaws.services.simpledb.AmazonSimpleDBClient
+import com.amazonaws.services.simpledb.model.SelectRequest
+import com.amazonaws.services.simpledb.model.SelectResult
+
+
+class AWSQueryInjection extends HttpServlet {
+ @throws[IOException]
+ override def doGet(request: HttpServletRequest, response: HttpServletResponse): Unit = {
+ try {
+ val customerID = request.getParameter("customerID")
+ val awsCredentials = new BasicAWSCredentials("test", "test")
+ val sdbc = new AmazonSimpleDBClient(awsCredentials)
+ val query = "select * from invoices where customerID = '" + customerID
+ // {fact rule=nosql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-AWSQueryInjection
+ val sdbResult = sdbc.select(new SelectRequest(query)) //BAD
+ // {/fact}
+
+ // {fact rule=nosql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-AWSQueryInjection
+ val sdbResult2 = sdbc.select(new SelectRequest(query, false))
+ // {/fact}
+ val sdbRequest = new SelectRequest()
+
+ // {fact rule=nosql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-AWSQueryInjection
+ val sdbResult3 = sdbc.select(sdbRequest.withSelectExpression(query))
+ // {/fact}
+ val query2 = "select * from invoices where customerID = 123"
+ // {fact rule=nosql-injection@v1.0 defects=0}
+ val sdbResult4 = sdbc.select(new SelectRequest(query2)) //OK
+ // {/fact}
+ } catch {
+ case _: Throwable =>
+ }
+ }
+
+ def danger(customerID: Nothing, productCategory: Nothing): Unit = {
+ val sdbc = AmazonSimpleDBClient.builder.build
+ val query = "select * from invoices where productCategory = '" + productCategory + "' and customerID = '" + customerID + "' order by '"
+ // {fact rule=nosql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-AWSQueryInjection
+ val sdbResult = sdbc.select(new SelectRequest(query))
+ // {/fact}
+ }
+
+ def danger2(customerID: Nothing, productCategory: Nothing): Unit = {
+ val sdbc = AmazonSimpleDBClient.builder.build
+ val query = "select * from invoices where productCategory = '" + productCategory + "' and customerID = '" + customerID + "' order by '"
+ // {fact rule=nosql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-AWSQueryInjection
+ val sdbResult = sdbc.select(new SelectRequest(query, false))
+ // {/fact}
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-AWSQueryInjection.yml b/PR_6_scala/scala/inject/rule-AWSQueryInjection.yml
new file mode 100644
index 0000000..2ecff5d
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-AWSQueryInjection.yml
@@ -0,0 +1,46 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-AWSQueryInjection"
+ languages:
+ - "scala"
+ message: |
+ Constructing SimpleDB queries containing user input can allow an attacker to view unauthorized
+ records.
+ metadata:
+ category: "security"
+ cwe: "CWE-943"
+ shortDescription: "Improper Neutralization of Special Elements in Data Query Logic"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sinks:
+ - pattern: "new com.amazonaws.services.simpledb.model.SelectRequest($QUERY, ...);"
+ - patterns:
+ - pattern-inside: |
+ $DB.select(($SR: com.amazonaws.services.simpledb.model.SelectRequest).withSelectExpression($QUERY,...));
+ - pattern: "$QUERY"
+ - metavariable-pattern:
+ metavariable: "$DB"
+ pattern-either:
+ - pattern: "($DB: com.amazonaws.services.simpledb.AmazonSimpleDB)"
+ - pattern: "($DB: com.amazonaws.services.simpledb.AmazonSimpleDBClient)"
+ pattern-sources:
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = {
+ ...
+ }
+ - pattern: "$REQ"
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(..., $X: $TYPE, ...): $RET_TYPE = {
+ ...
+ $QUERY = <...$X...>
+ ...
+ }
+ - pattern: "$QUERY"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/inject/rule-BeanPropertyInjection.scala b/PR_6_scala/scala/inject/rule-BeanPropertyInjection.scala
new file mode 100644
index 0000000..01cdbc0
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-BeanPropertyInjection.scala
@@ -0,0 +1,58 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// scaffold: dependencies=commons-beanutils.commons-beanutils@1.9.4
+package inject
+
+import org.apache.commons.beanutils.BeanUtils
+import org.apache.commons.beanutils.BeanUtilsBean
+import javax.servlet.http.HttpServletRequest
+import java.lang.reflect.InvocationTargetException
+import java.util
+
+
+class BeanPropertyInjection {
+
+ // {fact rule=external-control-setting@v1.0 defects=1}
+ @throws[InvocationTargetException]
+ @throws[IllegalAccessException]
+ def danger(bean: Nothing, request: HttpServletRequest): Unit = {
+ val map = new util.HashMap[String, String]()
+ // ruleid: scala_inject_rule-BeanPropertyInjection
+ map.put("test", request.getParameter("test"))
+ BeanUtils.populate(bean, map)
+ }
+ // {/fact}
+
+ @throws[InvocationTargetException]
+ @throws[IllegalAccessException]
+ def danger2(bean: Nothing, request: HttpServletRequest): Unit = {
+ val map = new util.HashMap[String, String]()
+ val names = request.getParameterNames
+ // {fact rule=external-control-setting@v1.0 defects=1}
+ // ruleid: scala_inject_rule-BeanPropertyInjection
+ while ( {
+ names.hasMoreElements
+ }) {
+ val name = names.nextElement.asInstanceOf[Nothing]
+ map.put(name, request.getParameterValues(name).last)
+ }
+ BeanUtils.populate(bean, map)
+ // {/fact}
+ }
+
+ @throws[InvocationTargetException]
+ @throws[IllegalAccessException]
+ def danger3(bean: Nothing, request: HttpServletRequest): Unit = {
+ val map = new util.HashMap[String, String]()
+ val names = request.getParameterNames
+ // {fact rule=external-control-setting@v1.0 defects=1}
+ // ruleid: scala_inject_rule-BeanPropertyInjection
+ while ( {
+ names.hasMoreElements
+ }) {
+ val name = names.nextElement.asInstanceOf[Nothing]
+ map.put(name, request.getParameterValues("x").last)
+ }
+ new BeanUtilsBean().populate(bean, map)
+ // {/fact}
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-BeanPropertyInjection.yml b/PR_6_scala/scala/inject/rule-BeanPropertyInjection.yml
new file mode 100644
index 0000000..4764f9d
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-BeanPropertyInjection.yml
@@ -0,0 +1,42 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-BeanPropertyInjection"
+ languages:
+ - "scala"
+ message: |
+ An attacker can set arbitrary bean properties that can compromise system integrity. An
+ attacker can leverage this functionality to access special bean properties like
+ class.classLoader that will allow them to override system properties and potentially execute
+ arbitrary code.
+ metadata:
+ category: "security"
+ cwe: "CWE-15"
+ shortDescription: "External Control of System or Configuration Setting"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern-inside: |-
+ def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = { ... }
+ - pattern-either:
+ - pattern: |
+ $MAP.put(..., $REQ.getParameter(...))
+ ...
+ $BEAN_UTIL.populate(..., $MAP)
+ - pattern: |
+ while (...) {
+ ...
+ $MAP.put(..., $REQ.getParameterValues(...). ...)
+ }
+ ...
+ $BEAN_UTIL.populate(..., $MAP)
+ - metavariable-pattern:
+ metavariable: "$BEAN_UTIL"
+ pattern-either:
+ - pattern: "(BeanUtilsBean $B)"
+ - pattern: "new BeanUtilsBean()"
+ - pattern: "org.apache.commons.beanutils.BeanUtils"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/inject/rule-CLRFInjectionLogs.scala b/PR_6_scala/scala/inject/rule-CLRFInjectionLogs.scala
new file mode 100644
index 0000000..2fbf84e
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CLRFInjectionLogs.scala
@@ -0,0 +1,150 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// scaffold: dependencies=com.amazonaws.aws-java-sdk-simpledb@1.12.187
+package inject
+
+import javax.servlet.http.HttpServletRequest
+import java.util.ResourceBundle
+import java.util.function.Supplier
+import java.util.logging._
+
+
+object CLRFInjectionLogs {
+ var req = null
+}
+
+class CLRFInjectionLogs {
+ def javaUtilLogging(req: HttpServletRequest): Unit = {
+ val tainted = req.getParameter("test")
+ val safe = "safe"
+ val logger = Logger.getLogger(classOf[Nothing].getName)
+ logger.setLevel(Level.ALL)
+ val handler = new ConsoleHandler
+ handler.setLevel(Level.ALL)
+ logger.addHandler(handler)
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.config(tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.entering(tainted, safe)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.entering("safe", safe, tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.entering(safe, "safe", Array[String](tainted))
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.exiting(safe, tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.exiting(safe, "safe", tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.fine(tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.finer(tainted.trim)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.finest(tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.info(tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.log(Level.INFO, tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.log(Level.INFO, tainted, safe)
+ // {/fact}
+
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.log(Level.INFO, "safe", Array[String](tainted))
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.log(Level.INFO, tainted)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.logp(Level.INFO, tainted, safe, "safe")
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.logp(Level.INFO, safe, "safe", tainted, safe)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.logp(Level.INFO, "safe", safe.toLowerCase, safe, Array[String](tainted))
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.logrb(Level.INFO, tainted, "safe", "bundle", safe, Array[String](safe))
+ // {/fact}
+
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.severe(tainted + "safe" + safe)
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.warning(tainted.replaceAll("\n", "")) // still insecure (CR not replaced)
+ // {/fact}
+
+ // these should not be reported
+ logger.fine(safe)
+ logger.log(Level.INFO, "safe".toUpperCase, safe + safe)
+ logger.logp(Level.INFO, safe, safe, safe, Array[String](safe))
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.logrb(Level.INFO, safe, safe, tainted + "bundle", safe) // bundle name can be tainted
+ // {/fact}
+
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.info(tainted.replace('\n', ' ').replace('\r', ' '))
+ var encoded = tainted.replace("\r", "").toUpperCase
+ encoded = "safe" + encoded.toLowerCase
+ // {/fact}
+
+ // {fact rule=file-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CLRFInjectionLogs
+ logger.warning(encoded.replace("\n", " (new line)"))
+ logger.fine(tainted.replaceAll("[\r\n]+", ""))
+ // {/fact}
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-CLRFInjectionLogs.yml b/PR_6_scala/scala/inject/rule-CLRFInjectionLogs.yml
new file mode 100644
index 0000000..68a2ede
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CLRFInjectionLogs.yml
@@ -0,0 +1,66 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-CLRFInjectionLogs"
+ languages:
+ - "scala"
+ message: |
+ When data from an untrusted source is put into a logger and not neutralized correctly, an
+ attacker could forge log entries or include malicious content. Inserted false entries could be
+ used to skew statistics, distract the administrator or even to implicate another party in the
+ commission of a malicious act. If the log file is processed automatically, the attacker can
+ render the file unusable by corrupting the format of the file or injecting unexpected
+ characters. An attacker may also inject code or other commands into the log file and take
+ advantage of a vulnerability in the log processing utility (e.g. command injection or XSS).
+ metadata:
+ category: "security"
+ cwe: "CWE-93"
+ shortDescription: "Improper Neutralization of CRLF Sequences ('CRLF Injection')"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sanitizers:
+ - patterns:
+ - pattern-inside: |-
+ $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
+ - pattern: "$STR"
+ - metavariable-regex:
+ metavariable: "$REPLACE_CHAR"
+ regex: "(.*\\\\r\\\\n.*)"
+ - metavariable-regex:
+ metavariable: "$REPLACE"
+ regex: "(?!(\\\\r\\\\n))"
+ - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
+ - pattern: "org.owasp.encoder.Encode.forUri(...)"
+ - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
+ pattern-sinks:
+ - patterns:
+ - patterns:
+ - pattern: "$LOGGER.$METHOD(...,<...$TAINTED...>,...)"
+ - focus-metavariable: "$TAINTED"
+ - metavariable-regex:
+ metavariable: "$METHOD"
+ regex: "(log|logp|logrb|entering|exiting|fine|finer|finest|info|debug|trace|warn|warning|config|error|severe)"
+ - metavariable-pattern:
+ metavariable: "$LOGGER"
+ pattern-either:
+ - pattern: "Logger"
+ - pattern: "log"
+ - pattern: "logger"
+ - pattern: "org.pmw.tinylog.Logger"
+ - pattern: "org.apache.log4j.Logger"
+ - pattern: "org.apache.logging.log4j.Logger"
+ - pattern: "org.slf4j.Logger"
+ - pattern: "org.apache.commons.logging.Log"
+ - pattern: "java.util.logging.Logger"
+ pattern-sources:
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(..., $REQ: HttpServletRequest, ...) : $TYPE = {
+ ...
+ }
+ - pattern: "$REQ.getParameter(...)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/inject/rule-CommandInjection.scala b/PR_6_scala/scala/inject/rule-CommandInjection.scala
new file mode 100644
index 0000000..3af79b3
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CommandInjection.scala
@@ -0,0 +1,38 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// scaffold: dependencies=com.amazonaws.aws-java-sdk-simpledb@1.12.187
+package inject
+
+import java.io.IOException
+import java.util.Arrays
+
+class CommandInjection {
+ @throws[IOException]
+ def danger(cmd: String): Unit = {
+ val r = Runtime.getRuntime
+ r.exec(cmd)
+ r.exec(Array[String]("test"))
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CommandInjection
+ r.exec(Array[String]("bash", cmd))
+ // {/fact}
+
+
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CommandInjection
+ r.exec(Array[String]("/bin/sh", "-c", cmd))
+ // {/fact}
+ }
+
+ def danger2(cmd: String): Unit = {
+ val b = new ProcessBuilder()
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CommandInjection
+ b.command(cmd)
+ // {/fact}
+ b.command("test")
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-CommandInjection
+ b.command(Arrays.asList("/bin/sh", "-c", cmd))
+ // {/fact}
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-CommandInjection.yml b/PR_6_scala/scala/inject/rule-CommandInjection.yml
new file mode 100644
index 0000000..186efaf
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CommandInjection.yml
@@ -0,0 +1,69 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-CommandInjection"
+ languages:
+ - "scala"
+ message: |
+ The highlighted API is used to execute a system command. If unfiltered input is passed to this
+ API, it can lead to arbitrary command execution.
+ metadata:
+ category: "security"
+ cwe: "CWE-78"
+ shortDescription: "Improper Neutralization of Special Elements used in an OS Command
+ ('OS Command Injection')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ pattern-either:
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(..., $PARAM: String, ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ val $RT = Runtime.getRuntime
+ ...
+ - pattern-either:
+ - pattern: "$RT.exec($PARAM)"
+ - pattern: |
+ var $CMDARR = new Array[String]("$SHELL",...,$PARAM,...)
+ ...
+ $RT.exec($CMDARR,...)
+ - pattern: "$RT.exec(Array[String](\"$SHELL\",...,$PARAM,...), ...)"
+ - pattern: "$RT.exec(java.util.String.format(\"...\", ...,$PARAM,...))"
+ - pattern: "$RT.exec(($A: String) + ($B: String))"
+ - metavariable-regex:
+ metavariable: "$SHELL"
+ regex: "(/.../)?(sh|bash|ksh|csh|tcsh|zsh)$"
+ - pattern-not: "$RT.exec(\"...\",\"...\",\"...\",...)"
+ - pattern-not: "$RT.exec(new Array[String](\"...\",\"...\",\"...\",...),...)"
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(...,$PARAM: String, ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ val $PB = new ProcessBuilder()
+ ...
+ - pattern-either:
+ - pattern: "$PB.command($PARAM,...)"
+ - patterns:
+ - pattern-either:
+ - pattern: "$PB.command(\"$SHELL\",...,$PARAM,...)"
+ - pattern: |
+ var $CMDARR = java.util.Arrays.asList("$SHELL",...,$PARAM,...)
+ ...
+ $PB.command($CMDARR,...)
+ - pattern: "$PB.command(java.util.Arrays.asList(\"$SHELL\",...,$PARAM,...),...)"
+ - pattern: "$PB.command(java.util.String.format(\"...\", ...,$PARAM,...))"
+ - pattern: "$PB.command(($A: String) + ($B: String))"
+ - metavariable-regex:
+ metavariable: "$SHELL"
+ regex: "(/.../)?(sh|bash|ksh|csh|tcsh|zsh)$"
+ - pattern-not: "$PB.command(\"...\",\"...\",\"...\",...)"
+ - pattern-not: |
+ $PB.command(java.util.Arrays.asList("...","...","...",...))
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-CustomInjection.scala b/PR_6_scala/scala/inject/rule-CustomInjection.scala
new file mode 100644
index 0000000..1e01c03
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CustomInjection.scala
@@ -0,0 +1,111 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// scaffold: dependencies=com.amazonaws.aws-java-sdk-simpledb@1.12.187
+import java.sql.Connection
+import java.sql.ResultSet
+import java.sql.SQLException
+import java.sql.Statement
+import javax.sql.DataSource
+
+class CustomInjection {
+ // {fact rule=sql-injection@v1.0 defects=1}
+ @throws[SQLException]
+ def danger(dataSource: DataSource, input: String): Unit = {
+ val sql = "select * from Users where name = " + input
+ val connection = dataSource.getConnection
+ try {
+ val statement = connection.createStatement
+ try {
+ val resultSet = statement.executeQuery(sql)
+ System.out.println(resultSet)
+ } catch {
+ case _: Throwable =>
+ } finally if (statement != null) statement.close()
+ } catch {
+ case _: Throwable =>
+ }
+ }
+ // {/fact}
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ @throws[SQLException]
+ def danger2(dataSource: DataSource, input: String): Unit = {
+ val value = String.format("%s", input)
+ val sql = "select * from Users where name = " + input
+ val connection = dataSource.getConnection
+ try {
+ val statement = connection.createStatement
+ try {
+ val resultSet = statement.executeQuery(sql)
+ System.out.println(resultSet)
+ } catch {
+ case _: Throwable =>
+ } finally if (statement != null) statement.close()
+ } catch {
+ case _: Throwable =>
+ }
+ }
+ // {/fact}
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ @throws[SQLException]
+ def danger3(dataSource: DataSource, input: String): Unit = {
+ val connection = dataSource.getConnection
+ try {
+ val statement = connection.createStatement
+ try {
+ val resultSet =
+ // ruleid: scala_inject_rule-CustomInjection
+ statement.executeQuery("select * from Users where name = " + input)
+ System.out.println(resultSet)
+ } catch {
+ case _: Throwable =>
+ } finally if (statement != null) statement.close()
+ } catch {
+ case _: Throwable =>
+ }
+ }
+ // {/fact}
+
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ @throws[SQLException]
+ def danger4(dataSource: DataSource, input: String): Unit = {
+ val connection = dataSource.getConnection
+ try {
+ val statement = connection.createStatement
+ try {
+ // ruleid: scala_inject_rule-CustomInjection
+ val resultSet = statement.executeQuery(
+ "select * from Users where name = " + String.format("%s", input)
+ )
+ System.out.println(resultSet)
+ } catch {
+ case _: Throwable =>
+ } finally if (statement != null) statement.close()
+ } catch {
+ case _: Throwable =>
+ }
+ }
+ // {/fact}
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ @throws[SQLException]
+ def danger5(dataSource: DataSource, input: String): Unit = {
+ val connection = dataSource.getConnection
+ try {
+ val statement = connection.createStatement
+ try {
+ // ruleid: scala_inject_rule-CustomInjection
+ val resultSet = statement.executeQuery(
+ String.format("select * from Users where name = %s", input)
+ )
+ System.out.println(resultSet)
+ } catch {
+ case _: Throwable =>
+ } finally if (statement != null) statement.close()
+ } catch {
+ case _: Throwable =>
+ }
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/inject/rule-CustomInjection.yml b/PR_6_scala/scala/inject/rule-CustomInjection.yml
new file mode 100644
index 0000000..ddbb711
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CustomInjection.yml
@@ -0,0 +1,42 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-CustomInjection"
+ languages:
+ - "scala"
+ message: |
+ The method identified is susceptible to injection. The input should be validated and properly
+ escaped.
+ metadata:
+ category: "security"
+ cwe: "CWE-89"
+ shortDescription: "Improper Neutralization of Special Elements used in an SQL
+ Command ('SQL Injection')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - pattern-inside: |
+ val $ST = connection.createStatement
+ ...
+ - pattern-either:
+ - pattern: |
+ val $QUERY = ... + $VAR + ...
+ ...
+ $ST.executeQuery($QUERY)
+ - pattern: |
+ val $QUERY = ... + $VAR
+ ...
+ $ST.executeQuery($QUERY)
+ - pattern: |
+ val $QUERY = String.format("...",...,$VAR,...)
+ ...
+ $ST.executeQuery($QUERY)
+ - pattern: "$ST.executeQuery(($SB: StringBuilder).toString())"
+ - pattern: "$ST.executeQuery(... + $VAR + ...)"
+ - pattern: "$ST.executeQuery(... + $VAR)"
+ - pattern: "$ST.executeQuery(...,String.format(\"...\",...,$VAR,...), ...)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-CustomInjectionSQLString.scala b/PR_6_scala/scala/inject/rule-CustomInjectionSQLString.scala
new file mode 100644
index 0000000..cce7f72
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CustomInjectionSQLString.scala
@@ -0,0 +1,2 @@
+// License: MIT (c) GitLab Inc.
+// TODO: Placeholder file; add example code.
\ No newline at end of file
diff --git a/PR_6_scala/scala/inject/rule-CustomInjectionSQLString.yml b/PR_6_scala/scala/inject/rule-CustomInjectionSQLString.yml
new file mode 100644
index 0000000..a4908c1
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-CustomInjectionSQLString.yml
@@ -0,0 +1,46 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-CustomInjectionSQLString"
+ languages:
+ - "scala"
+ message: |
+ The method identified is susceptible to injection. The input should be validated and properly
+ escaped.
+ metadata:
+ category: "security"
+ cwe: "CWE-89"
+ shortDescription: "Improper Neutralization of Special Elements used in an SQL
+ Command ('SQL Injection')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |
+ def $FOO(..., $SQLIN: String, ...): $TYPE = {
+ ...
+ }
+ - pattern-either:
+ - pattern: |
+ "$SQL_STR" + $SQLIN
+ - pattern: "String.format(\"$SQL_STR\", ... + $SQLIN + ...)"
+ - pattern: |
+ "$SQL_STR".concat(...)
+ - pattern: "(StringBuilder $BUILDER). ... .append(\"$SQL_STR\")"
+ - patterns:
+ - pattern-inside: |
+ StringBuilder $BUILDER = new StringBuilder(... + "$SQL_STR" + ...);
+ ...
+ - pattern: "$BUILDER.append(...)"
+ - pattern-not: "$BUILDER.append(\"...\")"
+ - patterns:
+ - pattern-inside: |
+ $QUERY = "$SQL_STR";
+ ...
+ - pattern: "$QUERY += ..."
+ - metavariable-regex:
+ metavariable: "$SQL_STR"
+ regex: "(?i)(select|insert|create|update|alter|delete|drop)\\b"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-ELInjection.scala b/PR_6_scala/scala/inject/rule-ELInjection.scala
new file mode 100644
index 0000000..b6fa4ab
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-ELInjection.scala
@@ -0,0 +1,31 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import javax.el._
+import javax.faces.context.FacesContext
+
+
+class ELInjection {
+ // {fact rule=code-injection@v1.0 defects=1}
+ def valueExpr(expression: String) = {
+ val context = FacesContext.getCurrentInstance
+ val expressionFactory = context.getApplication.getExpressionFactory
+ val elContext = context.getELContext
+ // ruleid: scala_inject_rule-ELInjection
+ val vex = expressionFactory.createValueExpression(elContext, expression, classOf[Nothing])
+ vex.getValue(elContext).asInstanceOf[Nothing]
+ }
+ // {/fact}
+
+
+ // {fact rule=code-injection@v1.0 defects=1}
+ def methodExpr(expression: String) = {
+ val context = FacesContext.getCurrentInstance
+ val expressionFactory = context.getApplication.getExpressionFactory
+ val elContext = context.getELContext
+ // ruleid: scala_inject_rule-ELInjection
+ val ex = expressionFactory.createMethodExpression(elContext, expression, classOf[Nothing], null)
+ ex.getMethodInfo(elContext)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/inject/rule-ELInjection.yml b/PR_6_scala/scala/inject/rule-ELInjection.yml
new file mode 100644
index 0000000..7974484
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-ELInjection.yml
@@ -0,0 +1,35 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-ELInjection"
+ languages:
+ - "scala"
+ message: |
+ An expression is built with a dynamic value. The source of the value(s) should be verified to
+ avoid that unfiltered values fall into this risky code evaluation.
+ metadata:
+ category: "security"
+ cwe: "CWE-94"
+ shortDescription: "Improper Control of Generation of Code ('Code Injection')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |
+ import javax.el._
+ ...
+ - pattern-either:
+ - pattern-inside: |
+ def $FUNC(..., $EXPR: String, ...) : $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ def $FUNC(..., $EXPR: String, ...) = {
+ ...
+ }
+ - pattern-either:
+ - pattern: "$X.createValueExpression(..., $EXPR, ...)"
+ - pattern: "$X.createMethodExpression(..., $EXPR, ...)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-FileDisclosure.scala b/PR_6_scala/scala/inject/rule-FileDisclosure.scala
new file mode 100644
index 0000000..d6137ee
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-FileDisclosure.scala
@@ -0,0 +1,98 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+// source: https://github.com/find-sec-bugs/find-sec-bugs/blob/master/findsecbugs-samples-java/src/test/java/testcode/file/FileDisclosure.java
+// hash: a7694d0
+package inject
+
+import java.io.IOException
+import org.apache.struts.action.ActionForward
+import org.springframework.web.servlet.ModelAndView
+import javax.servlet.RequestDispatcher
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.util
+
+
+// REQUESTDISPATCHER_FILE_DISCLOSURE
+class FileDisclosure extends HttpServlet {
+ @throws[IOException]
+ override def doGet(request: HttpServletRequest, response: HttpServletResponse): Unit = {
+ try {
+ val returnURL = request.getParameter("returnURL")
+ /** ****Struts ActionForward vulnerable code tests***** */
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ // ruleid: scala_inject_rule-FileDisclosure
+ val forward = new ActionForward(returnURL) //BAD
+ // {/fact}
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ // ruleid: scala_inject_rule-FileDisclosure
+ val forward2 = new ActionForward(returnURL, true)
+ // {/fact}
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ // ruleid: scala_inject_rule-FileDisclosure
+ val forward3 = new ActionForward("name", returnURL, true)
+ // {/fact}
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ // ruleid: scala_inject_rule-FileDisclosure
+ val forward4 = new ActionForward("name", returnURL, true)
+ val forward5 = new ActionForward
+
+ // {/fact}
+
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ // ruleid: scala_inject_rule-FileDisclosure
+ forward5.setPath(returnURL) //BAD
+ // {/fact}
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=0}
+ //false positive test - returnURL moved from path to name (safe argument)
+ val forward6 = new ActionForward(returnURL, "path", true) //OK
+ // {/fact}
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ /** ****Spring ModelAndView vulnerable code tests***** */
+ // ruleid: scala_inject_rule-FileDisclosure
+ val mv = new ModelAndView(returnURL)
+ // {/fact}
+
+ // {fact rule=external-access-to-files-or-directories@v1.0 defects=1}
+ val mv4 = new ModelAndView
+ // ruleid: scala_inject_rule-FileDisclosure
+ mv4.setViewName(returnURL)
+ // {/fact}
+
+
+ //false positive test - returnURL moved from viewName to modelName (safe argument)
+ } catch {
+ case e: Exception =>
+ System.out.println(e)
+ }
+ }
+
+ @throws[IOException]
+ def doGet2(request: HttpServletRequest, response: HttpServletResponse): Unit = {
+ try {
+ val jspFile = request.getParameter("jspFile")
+ var requestDispatcher = request.getRequestDispatcher(jspFile)
+ // {fact rule=file-system-access@v1.0 defects=1}
+ // ruleid: scala_inject_rule-FileDisclosure
+ requestDispatcher.include(request, response)
+ // {/fact}
+
+ // {fact rule=file-system-access@v1.0 defects=1}
+ requestDispatcher = request.getSession.getServletContext.getRequestDispatcher(jspFile)
+ // ruleid: scala_inject_rule-FileDisclosure
+ requestDispatcher.forward(request, response)
+ // {/fact}
+
+
+ } catch {
+ case e: Exception =>
+ System.out.println(e)
+ }
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-FileDisclosure.yml b/PR_6_scala/scala/inject/rule-FileDisclosure.yml
new file mode 100644
index 0000000..c6df376
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-FileDisclosure.yml
@@ -0,0 +1,60 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-FileDisclosure"
+ languages:
+ - "scala"
+ message: |
+ Constructing a server-side redirect path with user input could allow an
+ attacker to download application binaries (including application classes or
+ jar files) or view arbitrary files within protected directories.
+ metadata:
+ category: "security"
+ cwe: "CWE-552"
+ shortDescription: "Files or Directories Accessible to External Parties"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sinks:
+ - patterns:
+ - pattern: "new org.springframework.web.servlet.ModelAndView($FST)"
+ - pattern: "$FST"
+ - patterns:
+ - pattern: "new org.springframework.web.servlet.ModelAndView($FST, $SND)"
+ - pattern: "$FST"
+ - patterns:
+ - pattern: "new org.springframework.web.servlet.ModelAndView($FST, $SND, $TRD)"
+ - pattern: "$FST"
+ - patterns:
+ - pattern: "new org.apache.struts.action.ActionForward($FST)"
+ - pattern: "$FST"
+ - patterns:
+ - pattern: "new org.apache.struts.action.ActionForward($FST, $SND)"
+ - pattern: "$FST"
+ - patterns:
+ - pattern: "new org.apache.struts.action.ActionForward($FST, $SND, $TRD)"
+ - pattern: "$SND"
+ - patterns:
+ - pattern: "new org.apache.struts.action.ActionForward($FST, $SND, $TRD)"
+ - pattern: "$TRD"
+ - patterns:
+ - pattern-inside: |
+ $ACTION = new org.apache.struts.action.ActionForward()
+ ...
+ - pattern: "$ACTION.setPath(...)"
+ - patterns:
+ - pattern-inside: |
+ $MVC = new org.springframework.web.servlet.ModelAndView()
+ ...
+ - pattern: "$MVC.setViewName(...);"
+ - patterns:
+ - pattern-inside: |
+ $REQ = $HTTP.getRequestDispatcher(...)
+ ...
+ - pattern-either:
+ - pattern: "$REQ.include($FST, $SND)"
+ - pattern: "$REQ.forward($FST, $SND)"
+ pattern-sources:
+ - pattern: "($VAR: javax.servlet.http.HttpServletRequest).getParameter(...)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/inject/rule-HttpParameterPollution.scala b/PR_6_scala/scala/inject/rule-HttpParameterPollution.scala
new file mode 100644
index 0000000..9586705
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-HttpParameterPollution.scala
@@ -0,0 +1,49 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import org.apache.commons.httpclient.methods.GetMethod
+import org.apache.http.client.methods.HttpGet
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.io.IOException
+import java.net.URLEncoder
+import com.google.common.net.UrlEscapers.urlPathSegmentEscaper
+
+
+class HttpParameterPollution extends HttpServlet {
+ override def doGet(request: HttpServletRequest, response: HttpServletResponse): Unit = {
+ try {
+ val item = request.getParameter("item")
+ //in HttpClient 4.x, there is no GetMethod anymore. Instead there is HttpGet
+ // {fact rule=os-command-injection@v1.0 defects=0}
+ val httpget = new HttpGet("http://host.com?param=" + URLEncoder.encode(item)) //OK
+ // {/fact}
+
+
+
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-HttpParameterPollution
+ val httpget2 = new HttpGet("http://host.com?param=" + item) //BAD
+ // {/fact}
+
+ // {fact rule=os-command-injection@v1.0 defects=0}
+ val httpget3 = new HttpGet("http://host.com?param=" + urlPathSegmentEscaper().escape(item))
+ // {/fact}
+
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-HttpParameterPollution
+ val get = new GetMethod("http://host.com?param=" + item)
+ // {/fact}
+
+
+ // {fact rule=os-command-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-HttpParameterPollution
+ get.setQueryString("item=" + item) //BAD
+ // {/fact}
+ } catch {
+ case e: Exception =>
+ System.out.println(e)
+ }
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-HttpParameterPollution.yml b/PR_6_scala/scala/inject/rule-HttpParameterPollution.yml
new file mode 100644
index 0000000..515b02d
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-HttpParameterPollution.yml
@@ -0,0 +1,34 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-HttpParameterPollution"
+ languages:
+ - "scala"
+ message: |
+ Concatenating unvalidated user input into a URL can allow an attacker to override the value of
+ a request parameter. Attacker may be able to override existing parameter values, inject a new
+ parameter or exploit variables out of a direct reach. HTTP Parameter Pollution (HPP) attacks
+ consist of injecting encoded query string delimiters into other existing parameters. If a web
+ application does not properly sanitize the user input, a malicious user may compromise the
+ logic of the application to perform either client-side or server-side attacks.
+ metadata:
+ category: "security"
+ cwe: "CWE-88"
+ shortDescription: "Improper Neutralization of Argument Delimiters in a Command
+ ('Argument Injection')"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ mode: "taint"
+ pattern-sanitizers:
+ - pattern: "java.net.URLEncoder.encode(...)"
+ - pattern: "com.google.common.net.UrlEscapers.urlPathSegmentEscaper().escape(...)"
+ pattern-sinks:
+ - pattern: "new org.apache.http.client.methods.HttpGet(...)"
+ - pattern: "new org.apache.commons.httpclient.methods.GetMethod(...)"
+ - pattern: "($GM: org.apache.commons.httpclient.methods.GetMethod).setQueryString(...)"
+ pattern-sources:
+ - pattern: "($REQ: HttpServletRequest ).getParameter(...)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/inject/rule-LDAPInjection.scala b/PR_6_scala/scala/inject/rule-LDAPInjection.scala
new file mode 100644
index 0000000..aeec848
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-LDAPInjection.scala
@@ -0,0 +1,185 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import org.springframework.ldap.core.DefaultNameClassPairMapper
+import org.springframework.ldap.core.DirContextProcessor
+import org.springframework.ldap.core.LdapEntryIdentificationContextMapper
+import org.springframework.ldap.core.LdapTemplate
+import org.springframework.ldap.core.support.CountNameClassPairCallbackHandler
+import org.springframework.ldap.core.support.DefaultIncrementalAttributesMapper
+import javax.naming.Context
+import javax.naming.NamingEnumeration
+import javax.naming.NamingException
+import javax.naming.directory.DirContext
+import javax.naming.directory.InitialDirContext
+import javax.naming.directory.SearchControls
+import javax.naming.directory.SearchResult
+import java.util
+import java.util.Properties
+
+
+object LDAPInjection {
+ private val ldapURI = "ldaps://ldap.server.com/dc=ldap,dc=server,dc=com"
+ private val contextFactory = "com.sun.jndi.ldap.LdapCtxFactory"
+
+ /** *************** JNDI LDAP ********************* */
+ private[inject] def authenticate(username: String, password: String) = try {
+ val props = new Properties();
+ props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")
+ props.put(Context.PROVIDER_URL, "ldap://ldap.example.com")
+ props.put(Context.REFERRAL, "ignore")
+ props.put(Context.SECURITY_PRINCIPAL, dnFromUser(username))
+ props.put(Context.SECURITY_CREDENTIALS, password)
+ new InitialDirContext(props)
+ true
+ } catch {
+ case e: Exception =>
+ false
+ }
+
+ @throws[NamingException]
+ private def dnFromUser(username: String) = {
+ val props = new Properties();
+ props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")
+ props.put(Context.PROVIDER_URL, "ldap://ldap.example.com")
+ props.put(Context.REFERRAL, "ignore")
+ val context = new InitialDirContext(props)
+ val ctrls = new SearchControls
+ ctrls.setReturningAttributes(Array[String]("givenName", "sn"))
+ ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE)
+ val answers = context.search("dc=People,dc=example,dc=com", "(uid=" + username + ")", ctrls)
+ val result = answers.next
+ result.getNameInNamespace
+ }
+
+ @throws[Exception]
+ private def ldapContext(env: util.Hashtable[String,String]) = {
+ env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory)
+ env.put(Context.PROVIDER_URL, ldapURI)
+ env.put(Context.SECURITY_AUTHENTICATION, "none")
+ val ctx = new InitialDirContext(env)
+ ctx
+ }
+
+ @throws[Exception]
+ def testBind(dn: Nothing, password: String): Boolean = {
+ val env = new util.Hashtable[String,String]
+ env.put(Context.SECURITY_AUTHENTICATION, "simple") //false positive
+
+ env.put(Context.SECURITY_PRINCIPAL, dn)
+ env.put(Context.SECURITY_CREDENTIALS, password)
+ try ldapContext(env)
+ catch {
+ case e: Exception =>
+ return false
+ }
+ true
+ }
+
+ /** *************** JNDI LDAP SPECIAL ********************* */
+ @throws[NamingException]
+ def main(param: Nothing): Unit = {
+ val ctx: DirContext = null
+ val base = "ou=users,ou=system"
+ val sc = new SearchControls()
+ sc.setSearchScope(SearchControls.SUBTREE_SCOPE)
+ val filter = "(&(objectclass=person))(|(uid=" + param + ")(street={0}))"
+ val filters = Array[Object]("The streetz 4 Ms bar")
+ System.out.println("Filter " + filter)
+ ctx.search(base, filter, filters, sc)
+ }
+}
+
+class LDAPInjection {
+ /** *************** SPRING LDAP ********************* */
+ @throws[NamingException]
+ def queryVulnerableToInjection(template: LdapTemplate, jndiInjectMe: String, searchControls: SearchControls, dirContextProcessor: DirContextProcessor): Unit = {
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.list(jndiInjectMe)
+ // {/fact}
+
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.list(jndiInjectMe, new DefaultNameClassPairMapper)
+ // {/fact}
+
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.list(jndiInjectMe, new CountNameClassPairCallbackHandler)
+ // {/fact}
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.lookup(jndiInjectMe)
+ // {/fact}
+ val mapper = new DefaultIncrementalAttributesMapper("")
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.lookup(jndiInjectMe, mapper)
+ // {/fact}
+
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.lookup(jndiInjectMe, mapper)
+ // {/fact}
+
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", searchControls, mapper)
+ // {/fact}
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", searchControls, mapper, dirContextProcessor)
+ // {/fact}
+
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", searchControls, mapper, dirContextProcessor)
+ // {/fact}
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", searchControls, mapper, dirContextProcessor)
+ // {/fact}
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", mapper)
+ // {/fact}
+
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", SearchControls.OBJECT_SCOPE, new Array[String](0), mapper)
+ // {/fact}
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", SearchControls.OBJECT_SCOPE, mapper)
+ // {/fact}
+
+ // {fact rule=ldap-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-LDAPInjection
+ template.search(jndiInjectMe, "dn=1", mapper)
+ // {/fact}
+ }
+
+ @throws[NamingException]
+ def safeQuery(template: LdapTemplate, searchControls: String, dirContextProcessor: DirContextProcessor): Unit = {
+ val safeQuery = "uid=test"
+ template.list(safeQuery)
+ template.list(safeQuery, new DefaultNameClassPairMapper)
+ template.list(safeQuery, new CountNameClassPairCallbackHandler())
+ template.lookup(safeQuery)
+ val mapper = new DefaultIncrementalAttributesMapper("")
+ template.lookup(safeQuery, mapper)
+ template.lookup(safeQuery, mapper)
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-LDAPInjection.yml b/PR_6_scala/scala/inject/rule-LDAPInjection.yml
new file mode 100644
index 0000000..651eb7d
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-LDAPInjection.yml
@@ -0,0 +1,52 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-LDAPInjection"
+ languages:
+ - "scala"
+ message: |
+ Just like SQL, all inputs passed to an LDAP query need to be passed in safely. Unfortunately,
+ LDAP doesn't have prepared statement interfaces like SQL. Therefore, the primary defense
+ against LDAP injection is strong input validation of any untrusted data before including it in
+ an LDAP query.
+ metadata:
+ category: "security"
+ cwe: "CWE-90"
+ shortDescription: "Improper Neutralization of Special Elements used in an LDAP
+ Query ('LDAP Injection')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - pattern-inside: |
+ def $FUNC(..., $VAR: String, ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ def $FUNC(..., $X: String, ...): $TYPE = {
+ ...
+ $VAR = ... + $X;
+ ...
+ }
+ - pattern-either:
+ - pattern: "($P: java.util.Properties).put($KEY, $VAR)"
+ - pattern: "$CTX.lookup(..., $VAR, ...)"
+ - pattern: "$CTX.search(..., $VAR, ...)"
+ - pattern: "$CTX.list(..., $VAR, ...)"
+ - metavariable-pattern:
+ metavariable: "$CTX"
+ pattern-either:
+ - pattern: "($CTX: javax.naming.directory.DirContext)"
+ - pattern: "($CTX: javax.naming.directory.Context)"
+ - pattern: "($CTX: javax.naming.Context)"
+ - pattern: "($CTX: javax.naming.directory.InitialDirContext)"
+ - pattern: "($CTX: javax.naming.ldap.LdapContext)"
+ - pattern: "($CTX: com.unboundid.ldap.sdk.LDAPConnection)"
+ - pattern: "($CTX: javax.naming.event.EventDirContext)"
+ - pattern: "($CTX: com.sun.jndi.ldap.LdapCtx)"
+ - pattern: "($CTX: org.springframework.ldap.core.LdapTemplate)"
+ - pattern: "($CTX: org.springframework.ldap.core.LdapOperations)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-OgnlInjection.scala b/PR_6_scala/scala/inject/rule-OgnlInjection.scala
new file mode 100644
index 0000000..36aba23
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-OgnlInjection.scala
@@ -0,0 +1,178 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import com.opensymphony.xwork2.ognl.OgnlReflectionProvider
+import com.opensymphony.xwork2.ognl.OgnlUtil
+import com.opensymphony.xwork2.util.TextParseUtil
+import ognl.OgnlException
+import com.opensymphony.xwork2.ognl
+
+import javax.management.ReflectionException
+import java.beans.IntrospectionException
+import java.util
+
+
+class OgnlInjection {
+ @throws[OgnlException]
+ @throws[ReflectionException]
+ def unsafeOgnlUtil(ognlUtil: OgnlUtil, input: String, propsInput: util.HashMap[String,String]): Unit = {
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setValue(input, null, null, "12345")
+ // {/fact}
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.getValue(input, null, null, null
+ // {/fact}
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setProperty(input, "12345", null, null)
+ // {/fact}
+
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setProperty(input, "12345", null, null, true)
+ // {/fact}
+
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setProperties(propsInput, new Object())
+ // {/fact}
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setProperties(propsInput, null, null, true)
+ // {/fact}
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setProperties(propsInput, null, true)
+ // {/fact}
+
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.setProperties(propsInput, null)
+ // {/fact}
+ //ognlUtil.callMethod(input, null, null);
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.compile(input)
+ // {/fact}
+
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ ognlUtil.compile(input)
+ // {/fact}
+ }
+
+ @throws[OgnlException]
+ @throws[ReflectionException]
+ def safeOgnlUtil(ognlUtil: OgnlUtil): Unit = {
+ val input = "thisissafe"
+ val map = new util.HashMap[String,String]()
+ ognlUtil.setValue(input, null, null, "12345")
+ ognlUtil.getValue(input, null, null, null)
+ ognlUtil.setProperty(input, "12345", null, null)
+ ognlUtil.setProperty(input, "12345", null, null, true)
+ ognlUtil.setProperties(map, null, null)
+ ognlUtil.setProperties(map, null, null, true)
+ ognlUtil.setProperties(map, null, true)
+ ognlUtil.setProperties(map, null)
+ ognlUtil.compile(input)
+ ognlUtil.compile(input)
+ }
+
+ @throws[ReflectionException]
+ @throws[IntrospectionException]
+ def unsafeOgnlReflectionProvider(input: Nothing, propsInput: Nothing, reflectionProvider: Nothing, `type`: Nothing): Unit = {
+ var reflectionProvider: OgnlReflectionProvider = null
+ reflectionProvider.getGetMethod(`type`, input)
+ reflectionProvider.getSetMethod(`type`, input)
+ reflectionProvider.getField(`type`, input)
+ reflectionProvider.setProperties(propsInput, null, null, true)
+ reflectionProvider.setProperties(propsInput, null, null)
+ reflectionProvider.setProperties(propsInput, null)
+ reflectionProvider.setProperty(input, "test", null, null)
+ // reflectionProvider.setProperty( input, "test",null, null, true);
+ reflectionProvider.getValue(input, null, null)
+ reflectionProvider.setValue(input, null, null, null)
+ }
+
+ @throws[IntrospectionException]
+ @throws[ReflectionException]
+ def safeOgnlReflectionProvider(reflectionProvider: Nothing, `type`: Nothing): Unit = {
+ var reflectionProvider: OgnlReflectionProvider = null
+ val input = "thisissafe"
+ val constant1 = ""
+ val constant2 = ""
+
+ val map = new util.HashMap[String,String]()
+ reflectionProvider.getGetMethod(`type`, input)
+ reflectionProvider.getSetMethod(`type`, input)
+ reflectionProvider.getField(`type`, input)
+ reflectionProvider.setProperties(map, null, null, true)
+ reflectionProvider.setProperties(map, null, null)
+ reflectionProvider.setProperties(map, null)
+ reflectionProvider.setProperty("test", constant1, null, null)
+ // reflectionProvider.setProperty("test", constant2, null, null, true);
+ reflectionProvider.getValue(input, null, null)
+ reflectionProvider.setValue(input, null, null, null)
+ }
+
+ def unsafeTextParseUtil(input: String): Unit = {
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ TextParseUtil.translateVariables(input, null)
+ // {/fact}
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ TextParseUtil.translateVariables(input, null, null)
+ // {/fact}
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ TextParseUtil.translateVariables('a', input, null)
+ // {/fact}
+
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ TextParseUtil.translateVariables('a', input, null, null)
+ // {/fact}
+
+
+
+ // {fact rule=expression-language-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-OgnlInjection
+ TextParseUtil.translateVariables('a', input, null, null, null, 0)
+ // {/fact}
+ }
+
+ def safeTextParseUtil(stack: Nothing, parsedValueEvaluator: Nothing, `type`: Nothing): Unit = {
+ val input = "1+1"
+ TextParseUtil.translateVariables(input, stack)
+ TextParseUtil.translateVariables(input, stack, parsedValueEvaluator)
+ TextParseUtil.translateVariables('a', input, stack)
+ TextParseUtil.translateVariables('a', input, stack, `type`)
+ TextParseUtil.translateVariables('a', input, stack, `type`, parsedValueEvaluator, 0)
+ }
+}
diff --git a/PR_6_scala/scala/inject/rule-OgnlInjection.yml b/PR_6_scala/scala/inject/rule-OgnlInjection.yml
new file mode 100644
index 0000000..9455df4
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-OgnlInjection.yml
@@ -0,0 +1,96 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-OgnlInjection"
+ patterns:
+ - pattern-either:
+ - pattern-inside: |
+ def $FUNC(..., $VAR: String, ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ def $FUNC(..., $VAR: Map[$K,$V], ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ def $FUNC(..., $VAR: java.util.HashMap[$K,$V], ...): $TYPE = {
+ ...
+ }
+ - pattern-either:
+ - pattern: "com.opensymphony.xwork2.util.TextParseUtil.translateVariables(...,
+ $VAR, ...)"
+ - pattern: "com.opensymphony.xwork2.util.TextParseUtil.translateVariablesCollection(...,
+ $VAR, ...)"
+ - pattern: "com.opensymphony.xwork2.util.TextParseUtil.shallBeIncluded(..., $VAR,
+ ...)"
+ - pattern: "com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.TextParser).evaluate(..., $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.OgnlTextParser).evaluate(..., $VAR,
+ ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getGetMethod(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getSetMethod(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getField(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setProperties(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setProperty(...,$VAR,
+ ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getValue(...,$VAR,
+ ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setValue(...,$VAR,
+ ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getGetMethod(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getSetMethod(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getField(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setProperties(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setProperty(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getValue(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setValue(...,
+ $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setProperties(..., $VAR,
+ ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setProperty(..., $VAR,
+ ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).getValue(..., $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setValue(..., $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).callMethod(..., $VAR, ...)"
+ - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).compile(..., $VAR, ...)"
+ - pattern: "($P:org.apache.struts2.util.VelocityStrutsUtil).evaluate(...)"
+ - pattern: "org.apache.struts2.util.StrutsUtil.findString(...)"
+ - pattern: "org.apache.struts2.util.StrutsUtil.findValue(..., $VAL)"
+ - pattern: "org.apache.struts2.util.StrutsUtil.getText(...)"
+ - pattern: "org.apache.struts2.util.StrutsUtil.translateVariables(...)"
+ - pattern: "org.apache.struts2.util.StrutsUtil.makeSelectList(..., $VAR, ...)"
+ - pattern: "($T:org.apache.struts2.views.jsp.ui.OgnlTool).findValue(..., $VAR,
+ ...)"
+ - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).findString(...)"
+ - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).findValue(..., $VAR,
+ ...)"
+ - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).setValue(..., $VAR, ...)"
+ - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).setParameter(..., $VAR,
+ ...)"
+ message: |
+ "A expression is built with a dynamic value. The source of the value(s) should be verified to
+ avoid that unfiltered values fall into this risky code evaluation."
+ languages:
+ - "scala"
+ severity: "WARNING"
+ metadata:
+ shortDescription: "Expression injection (OGNL)"
+ category: "security"
+ cwe: "CWE-917"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
diff --git a/PR_6_scala/scala/inject/rule-PathTraversal.scala b/PR_6_scala/scala/inject/rule-PathTraversal.scala
new file mode 100644
index 0000000..c8d27dc
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-PathTraversal.scala
@@ -0,0 +1,74 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.FileReader
+import java.io.FileWriter
+import java.io.IOException
+import java.io.RandomAccessFile
+import java.net.URI
+import java.net.URISyntaxException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+
+
+object PathTraversal {
+ private[inject] val safefinalString = "SAFE"
+ // {fact rule=path-traversal@v1.0 defects=1}
+ // DETECTS: PATH_TRAVERSAL_IN
+ @throws[IOException]
+ @throws[URISyntaxException]
+ def unsafe(input: String): Unit = {
+ new File(input)
+ new File("test/" + input, "misc.jpg")
+ new RandomAccessFile(input, "r")
+ new FileReader(input)
+ new FileInputStream(input)
+ new FileWriter("safe".toUpperCase)
+ new File(new String("safe"))
+ File.createTempFile(input, "safe")
+ File.createTempFile("safe", input)
+ new File(safefinalString)
+ }
+ // {/fact}
+}
+
+class PathTraversal { // nio path traversal, DETECTS: PATH_TRAVERSAL_IN
+ // {fact rule=path-traversal@v1.0 defects=1}
+ def loadFile(path: String): Unit = {
+ Paths.get(path)
+ Paths.get(path, "foo")
+ Paths.get(path, "foo", "bar")
+ Paths.get("foo", path)
+ Paths.get("foo", "bar", path)
+ Paths.get("foo")
+ Paths.get("foo", "bar")
+ Paths.get("foo", "bar", "allsafe")
+ }
+ // {/fact}
+
+ @throws[IOException]
+ def tempDir(input: String): Unit = {
+ val p = Paths.get("/")
+ Files.createTempFile(p, input, "")
+ Files.createTempFile(p, "", input)
+ Files.createTempFile(input, "")
+ Files.createTempFile("", input)
+ Files.createTempDirectory(p, input)
+ Files.createTempDirectory(input)
+ }
+
+ // DETECTS: PATH_TRAVERSAL_OUT
+ // {fact rule=path-traversal@v1.0 defects=1}
+ @throws[IOException]
+ def pathTraversalWrite(input: String): Unit = {
+ new FileWriter(input)
+ new FileWriter(input, true)
+ new FileWriter(input)
+ new FileWriter(input, true)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/inject/rule-PathTraversalIn.yml b/PR_6_scala/scala/inject/rule-PathTraversalIn.yml
new file mode 100644
index 0000000..45c1ed3
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-PathTraversalIn.yml
@@ -0,0 +1,64 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-PathTraversalIn"
+ languages:
+ - "scala"
+ message: |
+ A file is opened to read its content. The filename comes from an input parameter. If an
+ unfiltered parameter is passed to this file API, files from an arbitrary filesystem location
+ could be read. This rule identifies potential path traversal vulnerabilities. In many cases,
+ the constructed file path cannot be controlled by the user.
+ metadata:
+ owasp:
+ - "A5:2017-Broken Access Control"
+ - "A01:2021-Broken Access Control"
+ category: "security"
+ cwe: "CWE-22"
+ shortDescription: "Improper limitation of a pathname to a restricted directory
+ ('Path Traversal')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-either:
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(...,$ARGS: Array[String], ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ $VAR = $ARGS($IDX)
+ ...
+ - pattern-inside: |
+ def $FUNC(...,$VAR: String, ...): $TYPE = {
+ ...
+ }
+ - pattern-not-inside: |
+ ...
+ org.apache.commons.io.FilenameUtils.getName($VAR)
+ ...
+ - pattern-either:
+ - patterns:
+ - pattern-inside: |
+ $U = new java.net.URI($VAR)
+ ...
+ - pattern-either:
+ - pattern: "new java.io.File($U)"
+ - pattern: "java.nio.file.Paths.get($U)"
+ - pattern: "new java.io.RandomAccessFile(..., $VAR,...)"
+ - pattern: "new java.io.FileReader(<...$VAR...>, ...)"
+ - pattern: "new javax.activation.FileDataSource(..., $VAR, ...)"
+ - pattern: "new java.io.FileInputStream(..., $VAR, ...)"
+ - pattern: "new java.io.File(<...$VAR...>, ...)"
+ - pattern: "java.nio.file.Paths.get(...,$VAR,...)"
+ - pattern: "java.io.File.createTempFile(...,$VAR, ...)"
+ - pattern: "java.io.File.createTempDirectory(...,$VAR,...)"
+ - pattern: "java.nio.file.Files.createTempFile(..., $VAR, ...)"
+ - pattern: "java.nio.file.Files.createTempDirectory(..., $VAR, ...)"
+ - pattern: "scala.io.Source.from(<...$VAR...>)"
+ - pattern: "scala.io.Source.fromFile(<...$VAR...>)"
+ - pattern: "scala.io.Source.fromString(<...$VAR...>)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/inject/rule-PathTraversalOut.yml b/PR_6_scala/scala/inject/rule-PathTraversalOut.yml
new file mode 100644
index 0000000..770aa0c
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-PathTraversalOut.yml
@@ -0,0 +1,50 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-PathTraversalOut"
+ languages:
+ - "scala"
+ message: |
+ A file is opened to write to its contents. The filename comes from an input parameter. If an
+ unfiltered parameter is passed to this file API, files at an arbitrary filesystem location
+ could be modified. This rule identifies potential path traversal vulnerabilities. In many
+ cases, the constructed file path cannot be controlled by the user.
+ metadata:
+ owasp:
+ - "A5:2017-Broken Access Control"
+ - "A01:2021-Broken Access Control"
+ category: "security"
+ cwe: "CWE-22"
+ shortDescription: "Improper limitation of a pathname to a restricted directory
+ ('Path Traversal')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ mode: "taint"
+ pattern-sanitizers:
+ - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
+ pattern-sinks:
+ - patterns:
+ - pattern-inside: |-
+ new java.io.FileWriter($PATH, ...)
+ - pattern: "$PATH"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.FileOutputStream($PATH, ...)
+ - pattern: "$PATH"
+ pattern-sources:
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(..., $ARGS: Array[String], ...): $TYPE = {
+ ...
+ }
+ - pattern: "$ARGS[$IDX]"
+ - patterns:
+ - pattern-inside: |
+ def $FUNC(..., $VAR: String, ...): $TYPE = {
+ ...
+ }
+ - pattern: "$VAR"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-SpotbugsPathTraversal.scala b/PR_6_scala/scala/inject/rule-SpotbugsPathTraversal.scala
new file mode 100644
index 0000000..7b0148f
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-SpotbugsPathTraversal.scala
@@ -0,0 +1,107 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.FileReader
+import java.io.FileWriter
+import java.io.IOException
+import java.io.RandomAccessFile
+import java.net.URI
+import java.net.URISyntaxException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+
+
+class SpotbugsPathTraversal extends HttpServlet { // DETECTS: PT_ABSOLUTE_PATH_TRAVERSAL
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ @Override
+ @throws[ServletException]
+ @throws[IOException]
+ override protected def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val input = req.getParameter("input")
+ new File(input + "/abs/path") // BAD, DETECTS: PT_RELATIVE_PATH_TRAVERSAL
+ }
+ // {/fact}
+
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ @throws[ServletException]
+ @throws[IOException]
+ protected def danger2(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val input1 = req.getParameter("input1")
+ new File(input1) // BAD
+ }
+ // {/fact}
+
+ @throws[ServletException]
+ @throws[IOException]
+ @throws[URISyntaxException]
+ protected def danger3(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ // {fact rule=path-traversal@v1.0 defects=1}
+ val input = req.getParameter("test")
+ new File(input)
+ new File("test/" + input, "misc.jpg")
+ new RandomAccessFile(input, "r") // BAD, DETECTS: PT_ABSOLUTE_PATH_TRAVERSAL
+
+ new File(new URI(input))
+ new FileReader(input)
+ new FileInputStream(input)
+ // false positive test
+ new RandomAccessFile("safe", input)
+ new FileWriter("safe".toUpperCase)
+ new File(new URI("safe"))
+ File.createTempFile(input, "safe")
+ File.createTempFile("safe", input)
+ // {/fact}
+ }
+
+ // nio path traversal
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ def loadFile(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val path = req.getParameter("test")
+ Paths.get(path)
+ Paths.get(path, "foo")
+ Paths.get(path, "foo", "bar")
+ Paths.get("foo", path)
+ Paths.get("foo", "bar", path)
+ Paths.get("foo")
+ Paths.get("foo", "bar")
+ Paths.get("foo", "bar", "allsafe")
+ }
+ // {/fact}
+
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ @throws[IOException]
+ def tempDir(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val input = req.getParameter("test")
+ val p = Paths.get("/")
+ Files.createTempFile(p, input, "")
+ Files.createTempFile(p, "", input)
+ Files.createTempFile(input, "")
+ Files.createTempFile("", input)
+ Files.createTempDirectory(p, input)
+ Files.createTempDirectory(input)
+ }
+ // {/fact}
+
+ // {fact rule=path-traversal@v1.0 defects=1}
+ @throws[IOException]
+ def writer(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
+ val input = req.getParameter("test")
+ new FileWriter(input)
+ new FileWriter(input, true)
+ new FileOutputStream(input)
+ new FileOutputStream(input, true)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/inject/rule-SpotbugsPathTraversalAbsolute.yml b/PR_6_scala/scala/inject/rule-SpotbugsPathTraversalAbsolute.yml
new file mode 100644
index 0000000..d2f9582
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-SpotbugsPathTraversalAbsolute.yml
@@ -0,0 +1,61 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-SpotbugsPathTraversalAbsolute"
+ languages:
+ - "scala"
+ message: |
+ "The software uses an HTTP request parameter to construct a pathname that should be within a
+ restricted directory, but it does not properly neutralize absolute path sequences such as
+ "/abs/path" that can resolve to a location that is outside of that directory. See
+ http://cwe.mitre.org/data/definitions/36.html for more information."
+ metadata:
+ owasp:
+ - "A5:2017-Broken Access Control"
+ - "A01:2021-Broken Access Control"
+ category: "security"
+ cwe: "CWE-22"
+ shortDescription: "Improper limitation of a pathname to a restricted directory
+ ('Path Traversal')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ mode: "taint"
+ pattern-sanitizers:
+ - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
+ pattern-sinks:
+ - patterns:
+ - pattern-inside: |
+ $U = new java.net.URI($VAR)
+ - pattern-either:
+ - pattern-inside: |-
+ new java.io.File($U)
+ - pattern-inside: |-
+ java.nio.file.Paths.get($U)
+ - pattern: "$VAR"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.RandomAccessFile($INPUT,...)
+ - pattern: "$INPUT"
+ - pattern: "new java.io.FileReader(...)"
+ - pattern: "new javax.activation.FileDataSource(...)"
+ - pattern: "new java.io.FileInputStream(...)"
+ - pattern: "new java.io.File(...)"
+ - pattern: "java.nio.file.Paths.get(...)"
+ - pattern: "java.io.File.createTempFile(...)"
+ - pattern: "java.io.File.createTempDirectory(...)"
+ - pattern: "java.nio.file.Files.createTempFile(...)"
+ - pattern: "java.nio.file.Files.createTempDirectory(...)"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.FileWriter($PATH, ...)
+ - pattern: "$PATH"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.FileOutputStream($PATH, ...)
+ - pattern: "$PATH"
+ pattern-sources:
+ - pattern: "($REQ: HttpServletRequest ).getParameter(...)"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-SpotbugsPathTraversalRelative.yml b/PR_6_scala/scala/inject/rule-SpotbugsPathTraversalRelative.yml
new file mode 100644
index 0000000..2653ec5
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-SpotbugsPathTraversalRelative.yml
@@ -0,0 +1,67 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-SpotbugsPathTraversalRelative"
+ languages:
+ - "scala"
+ message: |
+ "The software uses an HTTP request parameter to construct a pathname that should be within a
+ restricted directory, but it does not properly neutralize sequences such as ".." that can
+ resolve to a location that is outside of that directory. See
+ http://cwe.mitre.org/data/definitions/23.html for more information."
+ metadata:
+ owasp:
+ - "A5:2017-Broken Access Control"
+ - "A01:2021-Broken Access Control"
+ category: "security"
+ cwe: "CWE-22"
+ shortDescription: "Improper limitation of a pathname to a restricted directory
+ ('Path Traversal')"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ mode: "taint"
+ pattern-sanitizers:
+ - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
+ pattern-sinks:
+ - patterns:
+ - pattern-inside: |
+ $U = new java.net.URI($VAR)
+ - pattern-either:
+ - pattern-inside: |-
+ new java.io.File($U)
+ - pattern-inside: |-
+ java.nio.file.Paths.get($U)
+ - pattern: "$VAR"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.RandomAccessFile($INPUT,...)
+ - pattern: "$INPUT"
+ - pattern: "new java.io.FileReader(...)"
+ - pattern: "new javax.activation.FileDataSource(...)"
+ - pattern: "new java.io.FileInputStream(...)"
+ - pattern: "new java.io.File(...)"
+ - pattern: "java.nio.file.Paths.get(...)"
+ - pattern: "java.io.File.createTempFile(...)"
+ - pattern: "java.io.File.createTempDirectory(...)"
+ - pattern: "java.nio.file.Files.createTempFile(...)"
+ - pattern: "java.nio.file.Files.createTempDirectory(...)"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.FileWriter($PATH, ...)
+ - pattern: "$PATH"
+ - patterns:
+ - pattern-inside: |-
+ new java.io.FileOutputStream($PATH, ...)
+ - pattern: "$PATH"
+ pattern-sources:
+ - patterns:
+ - pattern-inside: |
+ $P = ($REQ: HttpServletRequest ).getParameter(...);
+ ...
+ - pattern-either:
+ - pattern: "$P + ..."
+ - pattern: "... + $P"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/inject/rule-SqlInjection.scala b/PR_6_scala/scala/inject/rule-SqlInjection.scala
new file mode 100644
index 0000000..1c8ca14
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-SqlInjection.scala
@@ -0,0 +1,130 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package inject
+
+import org.hibernate.Session
+import javax.jdo.Extent
+import javax.jdo.JDOHelper
+import javax.jdo.PersistenceManager
+import javax.jdo.PersistenceManagerFactory
+import javax.persistence.criteria.CriteriaBuilder
+import javax.persistence.criteria.CriteriaQuery
+import java.util
+import org.springframework.jdbc.core.JdbcTemplate
+
+
+object SqlInjection {
+ private val CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, " + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, " + "refresh_token_validity, additional_information, autoapprove"
+ private val DEFAULT_INSERT_STATEMENT = "insert into oauth_client_details (" + CLIENT_FIELDS + ")" + "values (?,?,?,?,?,?,?,?,?,?,?)"
+ private val pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional")
+
+ def getPM = pmfInstance.getPersistenceManager
+}
+
+class SqlInjection {
+ private val jdbcTemplate: org.springframework.jdbc.core.JdbcTemplate = null
+
+ class UserEntity {
+ private var id = null
+ private var test = null
+
+ def getTest = test
+
+ def setTest(test: Nothing): Unit = {
+ this.test = test
+ }
+
+ def getId = id
+
+ def setId(id: Nothing): Unit = {
+ this.id = id
+ }
+ }
+
+ def testJdoQueries(input: String): Unit = {
+ val pm: javax.jdo.PersistenceManager = SqlInjection.getPM
+ // {fact rule=sql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-SqlInjection
+ pm.newQuery("select * from Users where name = " + input)
+ // {/fact}
+
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-SqlInjection
+ pm.newQuery("sql", "select * from Products where name = " + input)
+ // {/fact}
+
+
+
+ // Test for false positive
+ pm.newQuery("select * from Config")
+ val query = "select * from Config"
+ pm.newQuery(query)
+ pm.newQuery("sql", query)
+ }
+
+ def testJdoQueriesAdditionalMethodSig(input: String): Unit = {
+ val pm: javax.jdo.PersistenceManager = SqlInjection.getPM
+ pm.newQuery(classOf[SqlInjection#UserEntity], "id == 1")
+ // {fact rule=sql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-SqlInjection
+ pm.newQuery(classOf[SqlInjection#UserEntity], "id == " + input)
+ // {/fact}
+
+
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-SqlInjection
+ pm.newQuery(null.asInstanceOf[String], "id == " + input)
+ // {/fact}
+
+
+ pm.newQuery(null.asInstanceOf[String], "id == 1")
+ }
+
+ def testHibernate(input: String): Unit = {
+ val session: Session = null
+ val cb = session.getCriteriaBuilder
+ val query:CriteriaQuery[Object] = null
+ // should not be reported
+ session.createQuery(query)
+
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ // should be reported
+ // ruleid: scala_inject_rule-SqlInjection
+ session.createQuery(input)
+ // {/fact}
+
+
+
+ val cq = cb.createQuery(classOf[String])
+ }
+
+ def good(clientDetails: Nothing): Unit = {
+ val statementUsingConstants = "insert into oauth_client_details (" + SqlInjection.CLIENT_FIELDS + ")" + "values (?,?,?,?,?,?,?,?,?,?,?)"
+ jdbcTemplate.update(statementUsingConstants, clientDetails)
+ }
+
+ def good2(clientDetails: Nothing): Unit = {
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ // ruleid: scala_inject_rule-SqlInjection
+ jdbcTemplate.update(SqlInjection.DEFAULT_INSERT_STATEMENT, clientDetails)
+ // {/fact}
+
+
+ }
+ // {fact rule=sql-injection@v1.0 defects=1}
+ def bad(clientDetails: Nothing): Unit = {
+ val stmtUsingFuncParam = "test" + clientDetails + "test"
+ jdbcTemplate.update(stmtUsingFuncParam, clientDetails)
+ }
+ // {/fact}
+
+ // {fact rule=sql-injection@v1.0 defects=1}
+ def badInline(clientDetails: Nothing): Unit = {
+ // ruleid: scala_inject_rule-SqlInjection
+ jdbcTemplate.update("test" + clientDetails + "test", clientDetails)
+ }
+ // {/fact}
+}
diff --git a/PR_6_scala/scala/inject/rule-SqlInjection.yml b/PR_6_scala/scala/inject/rule-SqlInjection.yml
new file mode 100644
index 0000000..d4829ee
--- /dev/null
+++ b/PR_6_scala/scala/inject/rule-SqlInjection.yml
@@ -0,0 +1,256 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_inject_rule-SqlInjection"
+ languages:
+ - "scala"
+ message: |
+ The input values included in SQL queries need to be passed in safely. Bind
+ variables in prepared statements can be used to easily mitigate the risk of
+ SQL injection.
+ metadata:
+ category: "security"
+ cwe: "CWE-89"
+ owasp: "A1:2017-Injection"
+ shortDescription: "Improper Neutralization of Special Elements used in an SQL
+ Command ('SQL Injection')"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern-not-inside: |
+ $ARG = ...
+ ...
+ - pattern-not-inside: |
+ object $CLAZZ {
+ ...
+ $ARG = ...
+ ...
+ }
+ - pattern-not-inside: |
+ class $CLAZZ {
+ ...
+ $ARG = ...
+ ...
+ }
+ - pattern-either:
+ - patterns:
+ - pattern: "($PM:javax.jdo.PersistenceManager).newQuery(<...$ARG...>)"
+ - pattern-not: "($PM:javax.jdo.PersistenceManager).newQuery(\"...\")"
+ - patterns:
+ - pattern: "($PM:javax.jdo.PersistenceManager).newQuery(..., <...$ARG...>)"
+ - pattern-not: "($PM:javax.jdo.PersistenceManager).newQuery(..., \"...\")"
+ - patterns:
+ - pattern: "($Q: javax.jdo.Query).setFilter(<...$ARG...>)"
+ - pattern-not: "($Q: javax.jdo.Query).setFilter(\"...\")"
+ - patterns:
+ - pattern: "($Q: javax.jdo.Query).setGrouping(<...$ARG...>)"
+ - pattern-not: "($Q: javax.jdo.Query).setGrouping(\"...\")"
+ - patterns:
+ - pattern: "($Q: javax.jdo.Query).setGrouping(<...$ARG...>)"
+ - pattern-not: "($Q: javax.jdo.Query).setGrouping(\"...\")"
+ - patterns:
+ - pattern: "($H: org.hibernate.criterion.Restrictions).sqlRestriction(<...$ARG...>,
+ ...)"
+ - pattern-not: "($H: org.hibernate.criterion.Restrictions).sqlRestriction(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($S: org.hibernate.Session).createQuery(<...$ARG...>, ...)"
+ - pattern-not: "($S: org.hibernate.Session).createQuery(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: org.hibernate.Session).createSQLQuery(<...$ARG...>, ...)"
+ - pattern-not: "($S: org.hibernate.Session).createSQLQuery(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Statement).executeQuery(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Statement).createSQLQuery(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Statement).execute(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Statement).execute(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Statement).executeUpdate(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Statement).executeUpdate(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Statement).executeLargeUpdate(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Statement).executeLargeUpdate(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Statement).addBatch(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Statement).addBatch(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.PreparedStatement).executeQuery(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.PreparedStatement).executeQuery(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.PreparedStatement).execute(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.PreparedStatement).execute(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.PreparedStatement).executeUpdate(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.PreparedStatement).executeUpdate(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.PreparedStatement).executeLargeUpdate(<...$ARG...>,
+ ...)"
+ - pattern-not: "($S: java.sql.PreparedStatement).executeLargeUpdate(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($S: java.sql.PreparedStatement).addBatch(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.PreparedStatement).addBatch(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Connection).prepareCall(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Connection).prepareCall(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Connection).prepareStatement(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Connection).prepareStatement(\"...\", ...)"
+ - patterns:
+ - pattern: "($S: java.sql.Connection).nativeSQL(<...$ARG...>, ...)"
+ - pattern-not: "($S: java.sql.Connection).nativeSQL(\"...\", ...)"
+ - patterns:
+ - pattern: "new org.springframework.jdbc.core.PreparedStatementCreatorFactory(<...$ARG...>,
+ ...)"
+ - pattern-not: "new org.springframework.jdbc.core.PreparedStatementCreatorFactory(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "(org.springframework.jdbc.core.PreparedStatementCreatorFactory $F).newPreparedStatementCreator(<...$ARG...>,
+ ...)"
+ - pattern-not: "(org.springframework.jdbc.core.PreparedStatementCreatorFactory
+ $F).newPreparedStatementCreator(\"...\", ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).batchUpdate(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).batchUpdate(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).execute(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).execute(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).query(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).query(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForList(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForList(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForMap(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForMap(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForRowSet(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForRowSet(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForInt(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForInt(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForLong(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForLong(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).update(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).update(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).batchUpdate(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).batchUpdate(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).execute(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).execute(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).query(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).query(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForList(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForList(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForMap(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForMap(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForObject(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForObject(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForRowSet(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForRowSet(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForInt(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForInt(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForLong(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForLong(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).update(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).update(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: io.vertx.sqlclient.SqlClient).query(<...$ARG...>, ...)"
+ - pattern-not: "($O: io.vertx.sqlclient.SqlClient).query(\"...\", ...)"
+ - patterns:
+ - pattern: "($O: io.vertx.sqlclient.SqlClient).preparedQuery(<...$ARG...>, ...)"
+ - pattern-not: "($O: io.vertx.sqlclient.SqlClient).preparedQuery(\"...\", ...)"
+ - patterns:
+ - pattern: "($O: io.vertx.sqlclient.SqlConnection).prepare(<...$ARG...>, ...)"
+ - pattern-not: "($O: io.vertx.sqlclient.SqlConnection).prepare(\"...\", ...)"
+ - patterns:
+ - pattern: "($O: org.apache.turbine.om.peer.BasePeer).executeQuery(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.apache.turbine.om.peer.BasePeer).executeQuery(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: org.apache.torque.util.BasePeer).executeQuery(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: org.apache.torque.util.BasePeer).executeQuery(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "($O: javax.persistence.EntityManager).createQuery(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: javax.persistence.EntityManager).createQuery(\"...\", ...)"
+ - patterns:
+ - pattern: "($O: javax.persistence.EntityManager).createNativeQuery(<...$ARG...>,
+ ...)"
+ - pattern-not: "($O: javax.persistence.EntityManager).createNativeQuery(\"...\",
+ ...)"
+ - patterns:
+ - pattern: "anorm.SQL(<...$ARG...>)"
+ - pattern-not: "anorm.SQL(\"...\")"
+ - patterns:
+ - pattern-inside: |
+ import anorm._
+ ...
+ - pattern: "SQL(<...$ARG...>)"
+ - pattern-not: "SQL(\"...\")"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/ldap/rule-AnonymousLDAP.scala b/PR_6_scala/scala/ldap/rule-AnonymousLDAP.scala
new file mode 100644
index 0000000..1d0c078
--- /dev/null
+++ b/PR_6_scala/scala/ldap/rule-AnonymousLDAP.scala
@@ -0,0 +1,39 @@
+// License: LGPL-3.0 License (c) find-sec-bugs
+package ldap
+
+import java.util._
+import javax.naming.Context
+import javax.naming.directory.DirContext
+import javax.naming.directory.InitialDirContext
+
+object AnonymousLDAP {
+ private val ldapURI = "ldaps://ldap.server.com/dc=ldap,dc=server,dc=com"
+ private val contextFactory = "com.sun.jndi.ldap.LdapCtxFactory"
+
+ @throws[Exception]
+ private def ldapContext(env: Hashtable[String, String]) = {
+ env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory)
+ env.put(Context.PROVIDER_URL, ldapURI)
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ // ruleid: scala_ldap_rule-AnonymousLDAP
+ env.put(Context.SECURITY_AUTHENTICATION, "none")
+ // {/fact}
+ val ctx = new InitialDirContext(env)
+ ctx
+ }
+
+ @throws[Exception]
+ def testBind(dn: String, password: String): Boolean = {
+ val env = new Hashtable[String, String]
+ env.put(Context.SECURITY_AUTHENTICATION, "simple") //false positive
+
+ env.put(Context.SECURITY_PRINCIPAL, dn)
+ env.put(Context.SECURITY_CREDENTIALS, password)
+ try ldapContext(env)
+ catch {
+ case e: javax.naming.AuthenticationException =>
+ return false
+ }
+ true
+ }
+}
diff --git a/PR_6_scala/scala/ldap/rule-AnonymousLDAP.yml b/PR_6_scala/scala/ldap/rule-AnonymousLDAP.yml
new file mode 100644
index 0000000..c8e5b49
--- /dev/null
+++ b/PR_6_scala/scala/ldap/rule-AnonymousLDAP.yml
@@ -0,0 +1,23 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_ldap_rule-AnonymousLDAP"
+ languages:
+ - "scala"
+ message: |
+ Without proper access control, executing an LDAP statement that contains a
+ user-controlled value can allow an attacker to abuse poorly configured LDAP
+ context
+ metadata:
+ category: "security"
+ cwe: "CWE-20"
+ shortDescription: "Improper Input Validation"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern-inside: |
+ import javax.naming.Context;
+ ...
+ - pattern: "$ENV.put(Context.SECURITY_AUTHENTICATION, \"none\");"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/ldap/rule-EntryPoisoning.scala b/PR_6_scala/scala/ldap/rule-EntryPoisoning.scala
new file mode 100644
index 0000000..5e019d4
--- /dev/null
+++ b/PR_6_scala/scala/ldap/rule-EntryPoisoning.scala
@@ -0,0 +1,38 @@
+// License: MIT (c) GitLab Inc.
+package ldap
+
+import javax.naming.directory.SearchControls
+
+class EntryPoisoning {
+ private val scope = 0
+ private val countLimit = 0
+ private val timeLimit = 0
+ private val attributes = null
+ private val deref = false
+
+ def unsafe1(): Unit = {
+ // {fact rule=improper-input-validation@v1.0 defects=1}
+ // ruleid: scala_ldap_rule-EntryPoisoning
+ new SearchControls(scope, countLimit, timeLimit, attributes, true, //!! It will flag line 14 ... the beginning of the call
+ deref)
+ // {/fact}
+ }
+
+ def unsafe2(): Unit = {
+ val ctrl = new SearchControls()
+ ctrl.setReturningObjFlag(true) //!!
+
+ }
+
+ // {fact rule=improper-input-validation@v1.0 defects=0}
+ def safe1(): Unit = {
+ new SearchControls(scope, countLimit, timeLimit, attributes, false, //OK
+ deref)
+ }
+ // {/fact}
+
+ def safe2(): Unit = {
+ val ctrl = new SearchControls()
+ ctrl.setReturningObjFlag(false)
+ }
+}
diff --git a/PR_6_scala/scala/ldap/rule-EntryPoisoning.yml b/PR_6_scala/scala/ldap/rule-EntryPoisoning.yml
new file mode 100644
index 0000000..f57d396
--- /dev/null
+++ b/PR_6_scala/scala/ldap/rule-EntryPoisoning.yml
@@ -0,0 +1,21 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_ldap_rule-EntryPoisoning"
+ languages:
+ - "scala"
+ message: |
+ Without proper access control, executing an LDAP statement that contains a
+ user-controlled value can allow an attacker to abuse poorly configured LDAP
+ context
+ metadata:
+ category: "security"
+ cwe: "CWE-20"
+ shortDescription: "Improper Input Validation"
+ security-severity: "CRITICAL"
+ patterns:
+ - pattern: "new javax.naming.directory.SearchControls($SCOPE, $CLIMIT, $TLIMIT,
+ $ATTR, true, $DEREF)"
+ severity: "ERROR"
diff --git a/PR_6_scala/scala/password/rule-ConstantDBPassword.yml b/PR_6_scala/scala/password/rule-ConstantDBPassword.yml
new file mode 100644
index 0000000..03177a9
--- /dev/null
+++ b/PR_6_scala/scala/password/rule-ConstantDBPassword.yml
@@ -0,0 +1,21 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_password_rule-ConstantDBPassword"
+ languages:
+ - "scala"
+ message: |
+ This code creates a database connect using a hardcoded, constant password. Anyone with access
+ to either the source code or the compiled code can easily learn the password.
+ metadata:
+ category: "security"
+ cwe: "CWE-259"
+ shortDescription: "Use of Hard-coded Password"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"...\");"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/password/rule-EmptyDBPassword.yml b/PR_6_scala/scala/password/rule-EmptyDBPassword.yml
new file mode 100644
index 0000000..6852f15
--- /dev/null
+++ b/PR_6_scala/scala/password/rule-EmptyDBPassword.yml
@@ -0,0 +1,21 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_password_rule-EmptyDBPassword"
+ languages:
+ - "scala"
+ message: |
+ This code creates a database connect using a blank or empty password. This indicates that the
+ database is not protected by a password.
+ metadata:
+ category: "security"
+ cwe: "CWE-259"
+ shortDescription: "Use of Hard-coded Password"
+ technology:
+ - "scala"
+ security-severity: "MEDIUM"
+ patterns:
+ - pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"\");"
+ severity: "WARNING"
diff --git a/PR_6_scala/scala/password/rule-HardcodeKey.yml b/PR_6_scala/scala/password/rule-HardcodeKey.yml
new file mode 100644
index 0000000..e534df4
--- /dev/null
+++ b/PR_6_scala/scala/password/rule-HardcodeKey.yml
@@ -0,0 +1,66 @@
+# yamllint disable
+# License: MIT (c) GitLab Inc.
+# yamllint enable
+---
+rules:
+- id: "scala_password_rule-HardcodeKey"
+ languages:
+ - "scala"
+ message: |
+ Cryptographic keys should not be kept in the source code. The source code can be widely shared
+ in an enterprise environment, and is certainly shared in open source. To be managed safely,
+ passwords and secret keys should be stored in separate configuration files or keystores.
+ metadata:
+ category: "security"
+ cwe: "CWE-321"
+ shortDescription: "Use of Hard-coded Cryptographic Key"
+ technology:
+ - "scala"
+ security-severity: "CRITICAL"
+ pattern-either:
+ - patterns:
+ - pattern-not-inside: |
+ def $FUNC(...,$KEY_BYTES: Array[Byte], ...): $TYPE = {
+ ...
+ }
+ - pattern-inside: |
+ $KEY = Array[Byte](...)
+ ...
+ - pattern-either:
+ - pattern: "new DESKeySpec($KEY)"
+ - pattern: "new DESedeKeySpec($KEY)"
+ - pattern: "new KerberosKey(..., $KEY,...)"
+ - pattern: "new SecretKeySpec($KEY, ...)"
+ - pattern: "new X509EncodedKeySpec($KEY)"
+ - pattern: "new PKCS8EncodedKeySpec($KEY)"
+ - pattern: "new KeyRep(..., $KEY)"
+ - pattern: "new KerberosTicket(...,$KEY,...)"
+ - patterns:
+ - pattern-inside: |
+ $KEY = ... .getBytes(...)
+ ...
+ - pattern-either:
+ - pattern: "new DESKeySpec($KEY)"
+ - pattern: "new DESedeKeySpec($KEY)"
+ - pattern: "new KerberosKey(..., $KEY,...)"
+ - pattern: "new SecretKeySpec($KEY, ...)"
+ - pattern: "new X509EncodedKeySpec($KEY)"
+ - pattern: "new PKCS8EncodedKeySpec($KEY)"
+ - pattern: "new KeyRep(..., $KEY)"
+ - pattern: "new KerberosTicket(...,$KEY,...)"
+ - patterns:
+ - pattern-not-inside: |
+ def $FUNC(..., $PRIVATE_KEY: BigInteger, ...): $TYPE = {
+ ...
+ }
+ - pattern-either:
+ - pattern: "new DSAPrivateKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new DSAPublicKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new DHPrivateKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new DHPublicKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new ECPrivateKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new RSAPrivateKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new RSAMultiPrimePrivateCrtKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new RSAPrivateCrtKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ - pattern: "new RSAPublicKeySpec($PRIVATE_KEY: BigInteger, ...)"
+ severity: "ERROR"