diff --git a/docs/header_names/allowed_framework_names.adoc b/docs/header_names/allowed_framework_names.adoc index d3d0ea4b62..550dbc7d80 100644 --- a/docs/header_names/allowed_framework_names.adoc +++ b/docs/header_names/allowed_framework_names.adoc @@ -44,6 +44,7 @@ * Java JWT * Java SE * Java JDBC API +* Java I/O API * Jdom2 * JSP * Legacy Mongo Java API diff --git a/rules/S2083/java/how-to-fix-it/java-se.adoc b/rules/S2083/java/how-to-fix-it/java-io-api.adoc similarity index 95% rename from rules/S2083/java/how-to-fix-it/java-se.adoc rename to rules/S2083/java/how-to-fix-it/java-io-api.adoc index ac8607bc2b..732f944ecf 100644 --- a/rules/S2083/java/how-to-fix-it/java-se.adoc +++ b/rules/S2083/java/how-to-fix-it/java-io-api.adoc @@ -1,4 +1,4 @@ -== How to fix it in Java SE +== How to fix it in Java I/O API === Code examples @@ -67,7 +67,7 @@ that the string `targetDirectory` does not end with a path separator: static private String targetDirectory = "/Users/John"; @GetMapping(value = "/endpoint") -public void endpoint(@RequestParam("folder") fileName) throws IOException { +public void endpoint(@RequestParam("folder") File fileName) throws IOException { String canonicalizedFileName = fileName.getCanonicalPath(); diff --git a/rules/S2083/java/rule.adoc b/rules/S2083/java/rule.adoc index 3a6722bf87..cb4bcf6019 100644 --- a/rules/S2083/java/rule.adoc +++ b/rules/S2083/java/rule.adoc @@ -6,7 +6,7 @@ include::../impact.adoc[] // How to fix it section -include::how-to-fix-it/java-se.adoc[] +include::how-to-fix-it/java-io-api.adoc[] == Resources diff --git a/rules/S2083/kotlin/how-to-fix-it/java-io-api.adoc b/rules/S2083/kotlin/how-to-fix-it/java-io-api.adoc new file mode 100644 index 0000000000..bb31872afb --- /dev/null +++ b/rules/S2083/kotlin/how-to-fix-it/java-io-api.adoc @@ -0,0 +1,94 @@ +== How to fix it in Java I/O API + +=== Code examples + +:code_impact: delete + +include::../../common/fix/code-rationale.adoc[] + +==== Noncompliant code example + +[source,kotlin,diff-id=1,diff-type=noncompliant] +---- +@Controller +class ExampleController { + companion object { + private const val TARGET_DIRECTORY = "/path/to/target/directory/" + } + + @GetMapping("/delete") + fun delete(@RequestParam("filename") filename: String) { + val file = File(TARGET_DIRECTORY + filename) + file.delete() + } +} +---- + +==== Compliant solution + +[source,kotlin,diff-id=1,diff-type=compliant] +---- +@Controller +class ExampleController { + companion object { + private const val TARGET_DIRECTORY = "/path/to/target/directory/" + private val TARGET_PATH = File(TARGET_DIRECTORY).toPath().normalize() + } + + @GetMapping("/delete") + fun delete(@RequestParam("filename") filename: String) { + val file = File(TARGET_PATH.toString() + filename) + if (!file.toPath().normalize().startsWith(TARGET_PATH)) { + throw IOException("Entry is outside of the target directory") + } + file.delete() + } +} +---- + +=== How does this work? + +:canonicalization_function: java.io.File.getCanonicalPath + +include::../../common/fix/self-validation.adoc[] + +=== Pitfalls + +include::../../common/pitfalls/partial-path-traversal.adoc[] + +For example, the following code is vulnerable to partial path injection. Note +that the string `targetDirectory` does not end with a path separator: + + +[source, kotlin] +---- +companion object { + private val targetDirectory: String = "/Users/John" +} + +@GetMapping("/endpoint") +fun endpoint(@RequestParam("folder") file: File) { + val canonicalizedFileName = file.getCanonicalPath() + if (!canonicalizedFileName .startsWith(targetDirectory)) { + throw IOException("Entry is outside of the target directory"); + } + file.delete() +} +---- + +This check can be bypassed because `"/Users/Johnny".startsWith("/Users/John")` +returns `true`. Thus, for validation, `"/Users/John"` should actually be +`"/Users/John/"`. + +**Warning**: Some functions, such as `.getCanonicalPath`, remove the +terminating path separator in their return value. + +The validation code should be tested to ensure that it cannot be impacted by this +issue. + +https://github.com/aws/aws-sdk-java/security/advisories/GHSA-c28r-hw5m-5gv3[Here is a real-life example of this vulnerability.] + + +:joining_docs: https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html +:joining_func: java.nio.file.Path.resolve + +include::../../common/pitfalls/oob-specific-path-joining.adoc[] diff --git a/rules/S2083/kotlin/metadata.json b/rules/S2083/kotlin/metadata.json new file mode 100644 index 0000000000..97202b93c7 --- /dev/null +++ b/rules/S2083/kotlin/metadata.json @@ -0,0 +1,33 @@ +{ + "securityStandards": { + "CWE": [ + 20, + 22 + ], + "OWASP": [ + "A5", + "A1" + ], + "OWASP Top 10 2021": [ + "A1", + "A3" + ], + "OWASP Mobile Top 10 2024": [ + "M4" + ], + "PCI DSS 3.2": [ + "6.5.8" + ], + "PCI DSS 4.0": [ + "6.2.4" + ], + "ASVS 4.0": [ + "12.3.1", + "5.1.3", + "5.1.4" + ], + "STIG ASD_V5R3": [ + "V-222609" + ] + } +} diff --git a/rules/S2083/kotlin/rule.adoc b/rules/S2083/kotlin/rule.adoc new file mode 100644 index 0000000000..cb4bcf6019 --- /dev/null +++ b/rules/S2083/kotlin/rule.adoc @@ -0,0 +1,29 @@ +== Why is this an issue? + +include::../rationale.adoc[] + +include::../impact.adoc[] + +// How to fix it section + +include::how-to-fix-it/java-io-api.adoc[] + +== Resources + +include::../common/resources/standards-mobile.adoc[] + +ifdef::env-github,rspecator-view[] + +''' +== Implementation Specification +(visible only on this page) + +include::../message.adoc[] + +''' +== Comments And Links +(visible only on this page) + +include::../comments-and-links.adoc[] + +endif::env-github,rspecator-view[]