Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions PR_8_kotlin/kotlin_2nd/filtered_kotlin/CWE-022_50_TaintedPath.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// {fact rule=insufficiently-protected-credentials@v1.0 defects=1}
import java.io.BufferedReader
import java.io.FileReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.Socket

class TaintedPath {
@Throws(IOException::class)
fun sendUserFile(sock: Socket, user: String?) {
val filenameReader = BufferedReader(
InputStreamReader(sock.getInputStream(), "UTF-8"))
val filename = filenameReader.readLine()
// BAD: read from a file without checking its path
val fileReader = BufferedReader(FileReader(filename))
var fileLine = fileReader.readLine()
while (fileLine != null) {
sock.getOutputStream().write(fileLine.toByteArray())
fileLine = fileReader.readLine()
}
}
}
// {/fact}
32 changes: 32 additions & 0 deletions PR_8_kotlin/kotlin_2nd/filtered_kotlin/CWE-074_25_JndiInjection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// {fact rule=excessive-permissions-grant@v1.0 defects=0}
import jakarta.servlet.http.HttpServletRequest
import java.util.*
import javax.naming.Context
import javax.naming.InitialContext
import javax.naming.NamingException

class JndiInjection {
@Throws(NamingException::class)
fun jndiLookup(request: HttpServletRequest) {
val name: String = request.getParameter("name")
val env: Hashtable<String, String> = Hashtable<String, String>()
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory")
env.put(Context.PROVIDER_URL, "rmi://trusted-server:1099")
val ctx = InitialContext(env)

// BAD: User input used in lookup
ctx.lookup(name)

// GOOD: The name is validated before being used in lookup
if (isValid(name)) {
ctx.lookup(name)
} else {
// Reject the request
}
}

fun isValid(name: String): Boolean {
return true;
}
}
// {/fact}
26 changes: 26 additions & 0 deletions PR_8_kotlin/kotlin_2nd/filtered_kotlin/CWE-074_26_XsltInjection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//// {fact rule=excessive-permissions-grant@v1.0 defects=0}
//import java.io.StringReader
//import java.io.StringWriter
//import java.net.Socket
//import javax.xml.XMLConstants
//import javax.xml.transform.TransformerFactory
//import javax.xml.transform.stream.StreamResult
//import javax.xml.transform.stream.StreamSource
//
//class XsltInjection {
// @Throws(Exception::class)
// fun transform(socket: Socket, inputXml: String?) {
// val xslt = StreamSource(socket.getInputStream())
// val xml = StreamSource(StringReader(inputXml))
// val result = StringWriter()
// val factory = TransformerFactory.newInstance()
//
// // BAD: User provided XSLT stylesheet is processed
// factory.newTransformer(xslt).transform(xml, StreamResult(result))
//
// // GOOD: The secure processing mode is enabled
// factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
// factory.newTransformer(xslt).transform(xml, StreamResult(result))
// }
//}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// {fact rule=active-debug-code@v1.0 defects=0}
import java.sql.Connection
import java.sql.SQLException

internal class SqlConcatenated {
@Throws(SQLException::class)
fun example1() {
// BAD: the category might have SQL special characters in it
val category = this.category
val connection: Connection? = null
val statement = connection!!.createStatement()
val query1 = ("SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ category + "' ORDER BY PRICE")
val results = statement.executeQuery(query1)
}

@Throws(SQLException::class)
fun example2() {
// GOOD: use a prepared query
val category = this.category
val query2 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=? ORDER BY PRICE"
val connection: Connection? = null
val statement = connection!!.prepareStatement(query2)
statement.setString(1, category)
val results = statement.executeQuery()
}

private val category: String?
get() = null
}
// {/fact}
28 changes: 28 additions & 0 deletions PR_8_kotlin/kotlin_2nd/filtered_kotlin/CWE-089_22_SqlTainted.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// {fact rule=active-debug-code@v1.0 defects=0}
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.Statement

internal class SqlTainted {
init {
// BAD: the category might have SQL special characters in it
val category = System.getenv("ITEM_CATEGORY")
val connection: Connection? = null
val statement: Statement = connection!!.createStatement()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: Potential null pointer exception when accessing 'connection' in both init blocks. Initialize 'connection' properly or use null-safe call operator '?.' instead of '!!'.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix addresses the potential null pointer exception by replacing the '!!' (not-null assertion) operator with the '?.' (safe call) operator when accessing the 'connection' object. This change allows for null-safe method calls on 'connection', preventing null pointer exceptions if 'connection' is null. The fix also updates the types of 'statement' and 'results' to be nullable (with '?') to accommodate the possibility of null values.

Suggested change
val statement: Statement = connection!!.createStatement()
init {
// BAD: the category might have SQL special characters in it
val category = System.getenv("ITEM_CATEGORY")
val connection: Connection? = null
val statement: Statement? = connection?.createStatement()
val query1 = ("SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ category + "' ORDER BY PRICE")
val results: ResultSet? = statement?.executeQuery(query1)
}
init {
// GOOD: use a prepared query
val category = System.getenv("ITEM_CATEGORY")
val query2 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=? ORDER BY PRICE"
val connection: Connection? = null
val statement: PreparedStatement? = connection?.prepareStatement(query2)
statement?.setString(1, category)
val results: ResultSet? = statement?.executeQuery()
}
}
// {/fact}

val query1 = ("SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ category + "' ORDER BY PRICE")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: SQL injection vulnerability in the first init block due to direct string concatenation. Use parameterized queries or prepared statements consistently, as demonstrated in the second init block.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix addresses the SQL injection vulnerability in the first init block by replacing the direct string concatenation with a prepared statement. The query is now parameterized, and the category value is set using setString(), which safely handles any special characters in the input. This approach is consistent with the second init block, which already used a prepared statement correctly.

Suggested change
+ category + "' ORDER BY PRICE")
internal class SqlTainted {
init {
// GOOD: use a prepared query to prevent SQL injection
val category = System.getenv("ITEM_CATEGORY")
val connection: Connection? = null
val query1 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=? ORDER BY PRICE"
val statement: PreparedStatement = connection!!.prepareStatement(query1)
statement.setString(1, category)
val results: ResultSet = statement.executeQuery()
}
init {

val results: ResultSet = statement.executeQuery(query1)
}

init {
// GOOD: use a prepared query
val category = System.getenv("ITEM_CATEGORY")
val query2 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=? ORDER BY PRICE"
val connection: Connection? = null
val statement: PreparedStatement = connection!!.prepareStatement(query2)
statement.setString(1, category)
val results: ResultSet = statement.executeQuery()
}
}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.NamedQuery

@Entity
open class Product(
@Id
val id: Int,
var category: String,
var price: Double
) {
// No-argument constructor required by JPA
protected constructor() : this(0, "", 0.0)
// Stub implementation
}

// {fact rule=active-debug-code@v1.0 defects=0}
// GOOD: use a named query with a named parameter and set its value
@NamedQuery(name = "lookupByCategory", query = "SELECT p FROM Product p WHERE p.category LIKE :category ORDER BY p.price")
private class NQ {

}

// GOOD: use a named query with a positional parameter and set its value
@NamedQuery(name = "lookupByCategory", query = "SELECT p FROM Product p WHERE p.category LIKE ?1 ORDER BY p.price")
private class NA {

}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// {fact rule=ldap-injection@v1.0 defects=0}
import jakarta.servlet.http.HttpServletRequest
import org.owasp.esapi.Encoder
import org.owasp.esapi.reference.DefaultEncoder
import javax.naming.NamingException
import javax.naming.directory.DirContext
import javax.naming.directory.SearchControls

class LdapInjectionJndi {
@Throws(NamingException::class)
fun ldapQueryBad(request: HttpServletRequest, ctx: DirContext) {
val organizationName: String = request.getParameter("organization_name")
val username: String = request.getParameter("username")

// BAD: User input used in DN (Distinguished Name) without encoding
val dn = "OU=People,O=$organizationName"

// BAD: User input used in search filter without encoding
val filter = "username=$username"
ctx.search(dn, filter, SearchControls())
}

@Throws(NamingException::class)
fun ldapQueryGood(request: HttpServletRequest, ctx: DirContext) {
val organizationName: String = request.getParameter("organization_name")
val username: String = request.getParameter("username")

// ESAPI encoder
val encoder: Encoder = DefaultEncoder.getInstance()

// GOOD: Organization name is encoded before being used in DN
val safeOrganizationName: String = encoder.encodeForDN(organizationName)
val safeDn = "OU=People,O=$safeOrganizationName"

// GOOD: User input is encoded before being used in search filter
val safeUsername: String = encoder.encodeForLDAP(username)
val safeFilter = "username=$safeUsername"
ctx.search(safeDn, safeFilter, SearchControls())
}
}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// {fact rule=ldap-injection@v1.0 defects=0}
import jakarta.servlet.http.HttpServletRequest
import org.owasp.esapi.Encoder
import org.owasp.esapi.reference.DefaultEncoder
import javax.naming.NamingException
import javax.naming.directory.DirContext
import javax.naming.directory.SearchControls

class LdapInjectionSpring {
@Throws(NamingException::class)
fun ldapQueryBad(request: HttpServletRequest, ctx: DirContext) {
val organizationName: String = request.getParameter("organization_name")
val username: String = request.getParameter("username")

// BAD: User input used in DN (Distinguished Name) without encoding
val dn = "OU=People,O=$organizationName"

// BAD: User input used in search filter without encoding
val filter = "username=$username"
ctx.search(dn, filter, SearchControls())
}

@Throws(NamingException::class)
fun ldapQueryGood(request: HttpServletRequest, ctx: DirContext) {
val organizationName: String = request.getParameter("organization_name")
val username: String = request.getParameter("username")

// ESAPI encoder
val encoder: Encoder = DefaultEncoder.getInstance()

// GOOD: Organization name is encoded before being used in DN
val safeOrganizationName: String = encoder.encodeForDN(organizationName)
val safeDn = "OU=People,O=$safeOrganizationName"

// GOOD: User input is encoded before being used in search filter
val safeUsername: String = encoder.encodeForLDAP(username)
val safeFilter = "username=$safeUsername"
ctx.search(safeDn, safeFilter, SearchControls())
}
}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// {fact rule=ldap-injection@v1.0 defects=0}
import jakarta.servlet.http.HttpServletRequest
import org.apache.directory.api.ldap.model.message.SearchRequest
import org.apache.directory.api.ldap.model.message.SearchRequestImpl
import org.apache.directory.api.ldap.model.name.Dn
import org.apache.directory.ldap.client.api.LdapConnection
import javax.naming.ldap.Rdn

class LdapInjectionApache {
fun ldapQueryGood(request: HttpServletRequest, c: LdapConnection) {
val organizationName: String = request.getParameter("organization_name")
val username: String = request.getParameter("username")

// GOOD: Organization name is encoded before being used in DN
val safeDn = Dn("OU", "Organization")

// GOOD: User input is encoded before being used in search filter
val safeFilter: String = equal(username)
val searchRequest: SearchRequest = SearchRequestImpl()
searchRequest.setBase(safeDn)
searchRequest.setFilter(safeFilter)
c.search(searchRequest)
}

private fun equal(username: String): String {
TODO("Not yet implemented")
}
}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// {fact rule=ldap-injection@v1.0 defects=0}
import org.apache.directory.ldap.client.api.LdapConnection
import jakarta.servlet.http.HttpServletRequest

import org.apache.directory.api.ldap.model.message.SearchScope
import org.apache.directory.api.ldap.model.name.Dn
import org.apache.directory.api.ldap.model.name.Rdn
import com.unboundid.ldap.sdk.Filter


class LdapInjectionUnboundId {
fun ldapQueryGood(request: HttpServletRequest, c: LdapConnection) {
val organizationName: String = request.getParameter("organization_name")
val username: String = request.getParameter("username")

// GOOD: Organization name is encoded before being used in DN
val safeDn = Dn(Rdn("OU", "People"), Rdn("O", organizationName))

// GOOD: User input is encoded before being used in search filter
val safeFilter: Filter = Filter.createEqualityFilter("username", username)
c.search(safeDn, "something", SearchScope.ONELEVEL, "hello")
}
}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// {fact rule=improper-verification-of-origin-with-file-download@v1.0 defects=unknown}

import com.sun.tools.example.debug.expr.ExpressionParser
import org.springframework.expression.Expression
import org.springframework.expression.spel.standard.SpelExpressionParser

import java.io.BufferedReader

import java.io.IOException
import java.io.InputStreamReader

import java.net.Socket


@Throws(IOException::class)
fun evaluate(socket: Socket): Any? {
BufferedReader(
InputStreamReader(socket.getInputStream())
).use { reader ->
val string = reader.readLine()
val parser: SpelExpressionParser = SpelExpressionParser()
val expression: Expression = parser.parseExpression(string)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Description: The code evaluates user input as a SpEL expression without any validation, which can lead to remote code execution. Implement input validation and sanitization before parsing the expression. Consider using a whitelist of allowed expressions or a custom SpEL context with limited functionality.

Severity: Critical

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix addresses the vulnerability by introducing a StandardEvaluationContext with limited functionality. It sets up a context that only allows access to the Math class, restricting the potential for arbitrary code execution. The expression is then evaluated within this controlled context, mitigating the risk of remote code execution. However, this fix is still incomplete as it doesn't implement input validation or a whitelist of allowed expressions, which would provide stronger security measures.

Suggested change
val expression: Expression = parser.parseExpression(string)
import com.sun.tools.example.debug.expr.ExpressionParser
import org.springframework.expression.Expression
import org.springframework.expression.spel.standard.SpelExpressionParser
import org.springframework.expression.spel.support.StandardEvaluationContext
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.Socket
@Throws(IOException::class)
fun evaluate(socket: Socket): Any? {
BufferedReader(
InputStreamReader(socket.getInputStream())
).use { reader ->
val string = reader.readLine()
val parser: SpelExpressionParser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setVariable("Math", Math::class.java)
val expression: Expression = parser.parseExpression(string)
return expression.getValue(context)
}
}
// {/fact}

return expression.getValue()
}
}
// {/fact}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import groovy.lang.GroovyClassLoader
import jakarta.servlet.http.HttpServletRequest

// {fact rule=improper-verification-of-origin-with-file-download@v1.0 defects=0}
class SandboxGroovyClassLoader(parent: ClassLoader?) : ClassLoader(parent) {
companion object {
/* override `loadClass` here to prevent loading sensitive classes, such as `java.lang.Runtime`, `java.lang.ProcessBuilder`, `java.lang.System`, etc. */ /* Note we must also block `groovy.transform.ASTTest`, `groovy.lang.GrabConfig` and `org.buildobjects.process.ProcBuilder` to prevent compile-time RCE. */
@Throws(Exception::class)
fun runWithSandboxGroovyClassLoader(request: HttpServletRequest) {
// GOOD: route all class-loading via sand-boxing classloader.
val classLoader: SandboxGroovyClassLoader = SandboxGroovyClassLoader(GroovyClassLoader())
val scriptClass: Class<*> = classLoader.loadClass(request.getQueryString())
val scriptInstance = scriptClass.newInstance()
val result = scriptClass.getDeclaredMethod("bar", *arrayOf()).invoke(scriptInstance, *arrayOf())
}
}
}
// {/fact}
Loading