Skip to content
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

feat(java): extend HTTP response splitting rule #209

Merged
merged 1 commit into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions rules/java/lang/http_response_splitting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,55 @@ patterns:
- variable: USER_INPUT
detection: java_lang_http_response_splitting_user_input
scope: result
- pattern: $<HTTP_SERVLET_RES>.$<METHOD>($<_>, $<USER_INPUT>);
filters:
- variable: HTTP_SERVLET_RES
detection: java_shared_lang_instance
scope: cursor
filters:
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE
regex: \A(javax\.servlet\.http\.)?HttpServletResponse\z
- variable: METHOD
values:
- setHeader
- addHeader
- variable: USER_INPUT
detection: java_lang_http_response_splitting_user_input
scope: result
- pattern: $<HTTP_SERVLET_RES>.$<METHOD>($<_>, $<USER_INPUT>);
filters:
- variable: HTTP_SERVLET_RES
detection: java_shared_lang_instance
scope: cursor
filters:
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE
regex: \A(javax\.servlet\.http\.)?HttpServletResponseWrapper\z
- variable: METHOD
values:
- setHeader
- addHeader
- variable: USER_INPUT
detection: java_lang_http_response_splitting_user_input
scope: result
- pattern: $<HTTP_SERVLET_RES>.$<METHOD>($<_>, $<USER_INPUT>);
filters:
- variable: HTTP_SERVLET_RES
detection: java_shared_lang_instance
scope: cursor
filters:
- variable: JAVA_SHARED_LANG_INSTANCE_TYPE
regex: \A(javax\.servlet\.http\.)?HttpServletRequest\z
- variable: METHOD
values:
- getParameter
- getParameterNames
- getParameterValues
- getParameterMap
- getHeader
- getPathInfo
- variable: USER_INPUT
detection: java_lang_http_response_splitting_user_input
scope: result
auxiliary:
- id: java_lang_http_response_splitting_user_input
sanitizer: java_lang_http_response_splitting_sanitizer
Expand Down
1 change: 1 addition & 0 deletions rules/java/shared/lang/user_input.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ patterns:
- getAttribute
- getInputStream
- getQueryString
- getPathInfo
- getParameter
- getParameterMap
- getParameterNames
Expand Down
18 changes: 14 additions & 4 deletions tests/java/lang/http_response_splitting/test.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
const { createInvoker, getEnvironment } = require("../../../helper.js")
const { createInvoker, createNewInvoker, getEnvironment } = require("../../../helper.js")
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname)

describe(ruleId, () => {
const invoke = createInvoker(ruleId, ruleFile, testBase)


test("bad", () => {
const testCase = "bad.java"
expect(invoke(testCase)).toMatchSnapshot();
})


test("ok", () => {
const testCase = "ok.java"
expect(invoke(testCase)).toMatchSnapshot();
})


// new test cases
const invokeV2 = createNewInvoker(ruleId, ruleFile, testBase)
test("http_response_splitting", () => {
const testCase = "main.java"

const results = invokeV2(testCase)

expect(results.Missing).toEqual([])
expect(results.Extra).toEqual([])
})
})
61 changes: 61 additions & 0 deletions tests/java/lang/http_response_splitting/testdata/main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package cookie;

import org.apache.commons.text.StringEscapeUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class FooBar extends HttpServlet {
public void bad(HttpServletRequest req, HttpServletResponse resp) {
String userInput = req.getParameter("barbaz");
// bearer:expected java_lang_http_response_splitting
resp.addHeader("MY_HEADER", userInput);

String paramNames = req.getParameterNames().nextElement();
// bearer:expected java_lang_http_response_splitting
resp.setHeader("PARAM_NAMES", paramNames);

String paramValues = req.getParameterValues("input")[0];
// bearer:expected java_lang_http_response_splitting
resp.addHeader("CUSTOM_HEADER", paramValues);

String paramMap = req.getParameterMap().get("input")[0];
// bearer:expected java_lang_http_response_splitting
resp.addHeader("CUSTOM_HEADER", paramMap);

String contextPath = req.getPathInfo();
// bearer:expected java_lang_http_response_splitting
resp.addHeader("PATH", contextPath);
}

public void alsoBad(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String header = req.getHeader("input");
// bearer:expected java_lang_http_response_splitting
resp.addHeader("MY_HEADER", header);
// bearer:expected java_lang_http_response_splitting
resp.setHeader("MY_HEADER", header);

String param = req.getParameter("input");
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(resp);
// bearer:expected java_lang_http_response_splitting
wrapper.addHeader("PARAM_HEADER", param);
// bearer:expected java_lang_http_response_splitting
wrapper.setHeader("PARAM_HEADER", param);
}

protected void ok(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("input");
String input = data.replaceAll("[\r\n]+", "");
resp.addHeader("SAFE_INPUT", input);
resp.setHeader("SAFE_INPUT", input);

var baz = StringEscapeUtils.unescapeJava(request.getParameter("baz"));
resp.setHeader("BAZ_VALUE", header);

String contextPath = req.getPathInfo();
contextPath = contextPath.replaceAll("[\r\n]+", "");
resp.addHeader("ALL_GOOD", contextPath);
}
}
Loading