-
Notifications
You must be signed in to change notification settings - Fork 0
Add PR_8_kotlin for kotlin #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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} |
| 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} |
| 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} |
| 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() | ||||||||||||||||||||||||||||
| val query1 = ("SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" | ||||||||||||||||||||||||||||
| + category + "' ORDER BY PRICE") | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||||||||||||||||||||||||||||
| 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) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| 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} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.