-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(java): format string manipulation rule (#205)
- Loading branch information
Showing
3 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
imports: | ||
- java_shared_lang_instance | ||
- java_shared_lang_user_input | ||
patterns: | ||
- pattern: String.format($<USER_INPUT>$<...>); | ||
filters: | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: String.format($<_>, $<USER_INPUT>$<...>); | ||
filters: | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: System.out.$<METHOD>($<USER_INPUT>$<...>); | ||
filters: | ||
- variable: METHOD | ||
values: | ||
- format | ||
- printf | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: System.out.$<METHOD>($<_>, $<USER_INPUT>$<...>); | ||
filters: | ||
- variable: METHOD | ||
values: | ||
- format | ||
- printf | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: $<JAVA_FORMATTER>.format($<USER_INPUT>$<...>); | ||
filters: | ||
- variable: JAVA_FORMATTER | ||
detection: java_shared_lang_instance | ||
scope: cursor | ||
filters: | ||
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE | ||
regex: \A(java\.util\.)?Formatter\z | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: $<JAVA_FORMATTER>.format($<_>, $<USER_INPUT>$<...>); | ||
filters: | ||
- variable: JAVA_FORMATTER | ||
detection: java_shared_lang_instance | ||
scope: cursor | ||
filters: | ||
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE | ||
regex: \A(java\.util\.)?Formatter\z | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: $<JAVA_PRINT_STREAM>.$<METHOD>($<USER_INPUT>$<...>); | ||
filters: | ||
- variable: JAVA_PRINT_STREAM | ||
detection: java_shared_lang_instance | ||
scope: cursor | ||
filters: | ||
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE | ||
regex: \A(java\.io\.)?PrintStream\z | ||
- variable: METHOD | ||
values: | ||
- format | ||
- printf | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
- pattern: $<JAVA_PRINT_STREAM>.$<METHOD>($<_>, $<USER_INPUT>$<...>); | ||
filters: | ||
- variable: JAVA_PRINT_STREAM | ||
detection: java_shared_lang_instance | ||
scope: cursor | ||
filters: | ||
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE | ||
regex: \A(java\.io\.)?PrintStream\z | ||
- variable: METHOD | ||
values: | ||
- format | ||
- printf | ||
- variable: USER_INPUT | ||
detection: java_shared_lang_user_input | ||
languages: | ||
- java | ||
metadata: | ||
description: "Externally controlled format string detected." | ||
remediation_message: | | ||
## Description | ||
For format functions, the first argument (or second if a locale is specified) is the format string itself. | ||
If unsanitized user input is passed as this format string argument, this puts the application at risk of attack. Malicious agents could pass format strings that result in data leaks or cause the application to throw exceptions, for example. | ||
## Remediations | ||
❌ Do not allow user input to be used as the format string | ||
For most Java formatting functions, this means never passing an externally-controlled string as the first argument (or second if a locale is specified) to the format function. | ||
```java | ||
// bad | ||
String.format(request.getParameter("foo"), "bar"); | ||
String.format(Locale.US, request.getParameter("foo"), "bar"); | ||
// okay | ||
String.format("Strings: %s", request.getParameter("foo"), "bar"); | ||
String.format(Locale.US, "Strings: %s", request.getParameter("foo"), "bar"); | ||
``` | ||
✅ Use hard-coded format strings instead | ||
## Resources | ||
- [OWASP Testing for Format String Injection](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/07-Input_Validation_Testing/13-Testing_for_Format_String_Injection) | ||
cwe_id: | ||
- 134 | ||
id: java_lang_format_string_manipulation | ||
documentation_url: https://docs.bearer.com/reference/rules/java_lang_format_string_manipulation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const { | ||
createNewInvoker, | ||
getEnvironment, | ||
} = require("../../../helper.js") | ||
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) | ||
|
||
describe(ruleId, () => { | ||
const invoke = createNewInvoker(ruleId, ruleFile, testBase) | ||
|
||
test("format_string_manipulation", () => { | ||
const testCase = "main.java" | ||
|
||
const results = invoke(testCase) | ||
|
||
expect(results.Missing).toEqual([]) | ||
expect(results.Extra).toEqual([]) | ||
}) | ||
}) |
47 changes: 47 additions & 0 deletions
47
tests/java/lang/format_string_manipulation/testdata/main.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package strings; | ||
import java.util.Formatter; | ||
import java.util.Locale; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import javax.servlet.http.HttpServlet; | ||
|
||
public class FooBar extends HttpServlet{ | ||
public void bad(HttpServletRequest request, HttpServletResponse response) { | ||
StringBuilder sb = new StringBuilder(); | ||
Formatter formatter = new Formatter(sb); | ||
String name = request.getParameter("name"); | ||
String bad = "Name: %s" + name; | ||
|
||
// bearer:expected java_lang_format_string_manipulation | ||
formatter.format(bad); | ||
// bearer:expected java_lang_format_string_manipulation | ||
formatter.format(Locale.US, bad, "Sir/Madam", "Mish", "Bear"); | ||
|
||
// bearer:expected java_lang_format_string_manipulation | ||
System.out.printf(bad, "Mr/s", "Mish", "Bear"); | ||
// bearer:expected java_lang_format_string_manipulation | ||
System.out.printf(Locale.UK, bad, "Mish", "Bear"); | ||
|
||
// bearer:expected java_lang_format_string_manipulation | ||
System.out.format(bad, "Mish", "Bear"); | ||
// bearer:expected java_lang_format_string_manipulation | ||
System.out.format(Locale.UK, bad); | ||
|
||
String bad2 = "Username: " + request.getParameter("selectedUsernameFormat"); | ||
|
||
// bearer:expected java_lang_format_string_manipulation | ||
String.format(request.getParameter("titleFormat"), "Dr", "Mish", "Bear"); | ||
// bearer:expected java_lang_format_string_manipulation | ||
String.format(bad2, "mishbear"); | ||
// bearer:expected java_lang_format_string_manipulation | ||
String.format(Locale.US, bad2, "mishbear"); | ||
} | ||
|
||
public void good(HttpServletRequest request, HttpServletResponse response) { | ||
StringBuilder sb = new StringBuilder(); | ||
Formatter formatter = new Formatter(sb); | ||
|
||
// user input is not the first argument to the formatter | ||
formatter.format("Mish", "Bear", request.getParameter("baz")); | ||
} | ||
} |