Skip to content

Commit

Permalink
feat(java): format string manipulation rule (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapet authored Jan 30, 2024
1 parent 4ced9f6 commit 67709b3
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
109 changes: 109 additions & 0 deletions rules/java/lang/format_string_manipulation.yml
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
18 changes: 18 additions & 0 deletions tests/java/lang/format_string_manipulation/test.js
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 tests/java/lang/format_string_manipulation/testdata/main.java
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"));
}
}

0 comments on commit 67709b3

Please sign in to comment.