mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Merge pull request #11550 from atorralba/atorralba/kotlin/adapt-path-sanitizer
Kotlin: Adapt PathSanitizer
This commit is contained in:
@@ -279,7 +279,7 @@ public class Test {
|
||||
}
|
||||
|
||||
private void blockListGuardValidation(String path) throws Exception {
|
||||
if (path.contains("..") || !path.startsWith("/data"))
|
||||
if (path.contains("..") || path.startsWith("/data"))
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
|
||||
499
java/ql/test/library-tests/pathsanitizer/TestKt.kt
Normal file
499
java/ql/test/library-tests/pathsanitizer/TestKt.kt
Normal file
@@ -0,0 +1,499 @@
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import android.net.Uri
|
||||
|
||||
class TestKt {
|
||||
fun source(): Any? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun sink(o: Any?) {}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun exactPathMatchGuardValidation(path: String?) {
|
||||
if (!path.equals("/safe/path")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun exactPathMatchGuard() {
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.equals("/safe/path"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as URI?
|
||||
if (source!!.equals(URI("http://safe/uri")))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as File?
|
||||
if (source!!.equals(File("/safe/file")))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as Uri?
|
||||
if (source!!.equals(Uri.parse("http://safe/uri")))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
exactPathMatchGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun allowListGuardValidation(path: String?) {
|
||||
if (path!!.contains("..") || !path.startsWith("/safe")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun allowListGuard() {
|
||||
// Prefix check by itself is not enough
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
} else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalGuard + allowListGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.contains("..") && source.startsWith("/safe"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.indexOf("..") == -1 && source.startsWith("/safe"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.lastIndexOf("..") == -1 && source.startsWith("/safe"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalSanitizer + allowListGuard
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalPath
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: File = source!!.canonicalFile
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalFile.toString()
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
val normalized: Path = Paths.get(source).normalize()
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.startsWith("/safe")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.regionMatches(0, "/safe", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.matches("/safe/.*".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
// validation method
|
||||
run {
|
||||
val source = source() as String?
|
||||
allowListGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
// PathInjectionSanitizer + partial string match is considered unsafe
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.contains("/safe")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.regionMatches(1, "/safe", 0, 5)) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (normalized.matches(".*/safe/.*".toRegex())) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun dotDotCheckGuardValidation(path: String?) {
|
||||
if (!path!!.startsWith("/safe") || path.contains("..")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun dotDotCheckGuard() {
|
||||
// dot dot check by itself is not enough
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.contains("..")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
} else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// allowListGuard + dotDotCheckGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe") && !source.contains(".."))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe") && source.indexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/safe") || source.indexOf("..") != -1)
|
||||
sink(source) // $ hasTaintFlow
|
||||
else
|
||||
sink(source) // Safe
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/safe") && source.lastIndexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// blockListGuard + dotDotCheckGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data") && !source.contains(".."))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data") && source.indexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.startsWith("/data") || source.indexOf("..") != -1)
|
||||
sink(source) // $ hasTaintFlow
|
||||
else
|
||||
sink(source) // Safe
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data") && source.lastIndexOf("..") == -1)
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// validation method
|
||||
run {
|
||||
val source = source() as String?
|
||||
dotDotCheckGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun blockListGuardValidation(path: String?) {
|
||||
if (path!!.contains("..") || path.startsWith("/data")) throw Exception()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun blockListGuard() {
|
||||
// Prefix check by itself is not enough
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.startsWith("/data")) {
|
||||
sink(source) // $ hasTaintFlow
|
||||
} else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalGuard + blockListGuard
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (!source!!.contains("..") && !source.startsWith("/data"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.indexOf("..") == -1 && !source.startsWith("/data"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
if (source!!.lastIndexOf("..") == -1 && !source.startsWith("/data"))
|
||||
sink(source) // Safe
|
||||
else
|
||||
sink(source) // $ hasTaintFlow
|
||||
}
|
||||
// PathTraversalSanitizer + blockListGuard
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalPath
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: File = source!!.canonicalFile
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source: File? = source() as File?
|
||||
val normalized: String = source!!.canonicalFile.toString()
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
val normalized: Path = Paths.get(source).normalize()
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // Safe
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.startsWith("/data")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.regionMatches(0, "/data", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.matches("/data/.*".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
// validation method
|
||||
run {
|
||||
val source = source() as String?
|
||||
blockListGuardValidation(source)
|
||||
sink(source) // Safe
|
||||
}
|
||||
// PathInjectionSanitizer + partial string match with disallowed words
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.contains("/")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.regionMatches(1, "/", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.matches("/".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
// PathInjectionSanitizer + partial string match with disallowed prefixes
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.contains("/data")) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.regionMatches(1, "/data", 0, 5)) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
run {
|
||||
val source = source() as String?
|
||||
// normalize().toString() gets extracted as Object.toString, stopping taint here
|
||||
val normalized: String = Paths.get(source).normalize().toString()
|
||||
if (!normalized.matches(".*/data/.*".toRegex())) {
|
||||
sink(source) // $ SPURIOUS: hasTaintFlow
|
||||
sink(normalized) // Safe
|
||||
} else {
|
||||
sink(source) // $ hasTaintFlow
|
||||
sink(normalized) // $ MISSING: hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/google-android-9.0.0
|
||||
//codeql-extractor-kotlin-options: ${testdir}/../../stubs/google-android-9.0.0
|
||||
|
||||
Reference in New Issue
Block a user