Skip to content

Commit

Permalink
feat(java): add insecure allow origin rule
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapet committed Feb 2, 2024
1 parent 44a3d13 commit 8849693
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
59 changes: 59 additions & 0 deletions rules/java/lang/insecure_allow_origin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
imports:
- java_shared_lang_instance
- java_shared_lang_servlet_request
- java_shared_lang_user_input
patterns:
- pattern: |
$<RES>.$<METHOD>($<HEADER>, $<USER_INPUT>);
filters:
- variable: 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: HEADER
string_regex: \A(?i)(Access-Control-Allow-Origin)\z
- variable: USER_INPUT
detection: java_lang_insecure_allow_origin_user_input
auxiliary:
- id: java_lang_insecure_allow_origin_user_input
patterns:
- pattern: $<USER_INPUT>;
filters:
- variable: USER_INPUT
detection: java_shared_lang_user_input
scope: cursor
- pattern: $<USER_INPUT_REQUEST>.$<REQUEST_GET_METHOD>().getAttribute();
filters:
- variable: USER_INPUT_REQUEST
detection: java_shared_lang_servlet_request
scope: cursor
- variable: REQUEST_GET_METHOD
values:
- getSession
- getServletContext
languages:
- java
metadata:
description: Unsanitized user input in Access-Control-Allow-Origin
remediation_message: |
## Description
Do not use unverified user-defined input to define Access-Control-Allow-Origin.
This can lead to unintended user access to sensitive data.
## Remediations
❌ Avoid defining origins with user input wherever possible.
✅ If unavoidable, be sure to verify the input or to use a safe-list.
## Resources
- [OWASP Origin & Access-Control-Allow-Origin](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/07-Testing_Cross_Origin_Resource_Sharing)
cwe_id:
- 942
id: java_lang_insecure_allow_origin
documentation_url: https://docs.bearer.com/reference/rules/java_lang_insecure_allow_origin
29 changes: 29 additions & 0 deletions rules/java/shared/lang/user_input.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ patterns:
- variable: JAVA_SHARED_LANG_USER_INPUT_PARAM_MAP
detection: java_shared_lang_user_input_param_map
scope: cursor
- pattern: $<JAVA_SHARED_LANG_USER_INPUT_PARAM_MAP>.get()[$<_>];
filters:
- variable: JAVA_SHARED_LANG_USER_INPUT_PARAM_MAP
detection: java_shared_lang_user_input_param_map
scope: cursor
- pattern: $<JAVA_SHARED_LANG_USER_INPUT_PARAM_NAMES>.nextElement();
filters:
- variable: JAVA_SHARED_LANG_USER_INPUT_PARAM_NAMES
detection: java_shared_lang_user_input_param_names
scope: cursor
- pattern: $<JAVA_SHARED_LANG_USER_INPUT_PARAM_VALUES>[$<_>];
filters:
- variable: JAVA_SHARED_LANG_USER_INPUT_PARAM_VALUES
detection: java_shared_lang_user_input_param_values
scope: cursor
auxiliary:
- id: java_shared_lang_user_input_cookies
patterns:
Expand All @@ -54,6 +69,20 @@ auxiliary:
- variable: JAVA_SHARED_LANG_USER_INPUT_REQUEST
detection: java_shared_lang_servlet_request
scope: cursor
- id: java_shared_lang_user_input_param_names
patterns:
- pattern: $<JAVA_SHARED_LANG_USER_INPUT_REQUEST>.getParameterNames();
filters:
- variable: JAVA_SHARED_LANG_USER_INPUT_REQUEST
detection: java_shared_lang_servlet_request
scope: cursor
- id: java_shared_lang_user_input_param_values
patterns:
- pattern: $<JAVA_SHARED_LANG_USER_INPUT_REQUEST>.getParameterValues();
filters:
- variable: JAVA_SHARED_LANG_USER_INPUT_REQUEST
detection: java_shared_lang_servlet_request
scope: cursor
metadata:
description: "Java user input."
id: java_shared_lang_user_input
18 changes: 18 additions & 0 deletions tests/java/lang/insecure_allow_origin/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("insecure_allow_origin", () => {
const testCase = "main.java"

const results = invoke(testCase)

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

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

public class Foo extends HttpServlet {
@Override
protected void bad(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String paramValue = request.getParameter("bad");
String header = request.getHeader("bad");
String queryString = request.getQueryString();

String[] parameterValues = request.getParameterValues("URL");
Enumeration<String> parameterNames = request.getParameterNames();
Map<String, String[]> parameterMap = request.getParameterMap();

String indexedParameterValue = parameterValues[0];
String parameterNamesElem = parameterNames.nextElement();
String indexedValueFromParameterMap = parameterMap.get("URL")[0];

if (paramValue != null) {
// bearer:expected java_lang_insecure_allow_origin
response.setHeader("Access-Control-Allow-Origin", paramValue);

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("Access-Control-Allow-Origin", paramValue);

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", paramValue);

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", header);

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", indexedParameterValue);

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", parameterNamesElem);

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", indexedValueFromParameterMap);

String[] keyValuePairs = queryString.split("=");
String lastPair = keyValuePairs[keyValuePairs.length - 1];
// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", lastPair);

String headerName = "ACCESS-CONTROL-ALLOW-ORIGIN";

// bearer:expected java_lang_insecure_allow_origin
response.addHeader(headerName, paramValue);

return;
}
}

public void badSessionAttr(HttpServletRequest request, HttpServletResponse response) throws ServletException {
request.getSession().setAttribute("someAttrName", request.getParameter("bad"));
String sessionAttr = (String) request.getSession().getAttribute("attributeName");

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", sessionAttr);
}

public void badRequestAttr(HttpServletRequest request, HttpServletResponse response) throws ServletException {
request.setAttribute("someAttrName",request.getParameter("bad"));
String requestAttr = (String) request.getAttribute("someAttrName");

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", requestAttr);
}

public void badServletContext(HttpServletRequest request, HttpServletResponse response) throws ServletException {
request.getServletContext().setAttribute("someAttrName",request.getParameter("bad"));
String contextAttr = (String) request.getServletContext().getAttribute("someAttrName");

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("access-control-allow-origin", contextAttr);
}

public void badModifiedPath(HttpServletRequest request, HttpServletResponse response) throws ServletException {
String pathInfo = request.getPathInfo();
String modifiedPath = pathInfo.replaceFirst("/","");

// bearer:expected java_lang_insecure_allow_origin
response.addHeader("Access-Control-Allow-Origin", modifiedPath);
}

public void ok(HttpServletRequest request, HttpServletResponse response) throws ServletException {
String paramValue = request.getParameter("bad");
if paramValue != null {
// set some other header with user-input
response.setHeader("X-Example-Header", paramValue);
}

String pathInfo = request.getPathInfo();
String modifiedPath = pathInfo.replaceFirst("/","");
// set some other header with user-input
response.setHeader("X-Example-Header", modifiedPath);

response.setHeader("Access-Control-Allow-Origin", "https://example.com");
response.addHeader("Access-Control-Allow-Origin", "https://example.com");
response.addHeader("Access-Control-Allow-Origin", getFromList("key"));
// bad for other reasons!
response.addHeader("Access-Control-Allow-Origin", "*");
}

public String getFromList(String key){
HashMap<String, String> corsList = new HashMap<>();
corsList.put("key", "https://example.com");

return corsList.get(key);
}
}

0 comments on commit 8849693

Please sign in to comment.