Skip to content

Implement endpoints with potential tainted code execution#5

Open
kyle-semgrep wants to merge 1 commit intomainfrom
newestblcktest
Open

Implement endpoints with potential tainted code execution#5
kyle-semgrep wants to merge 1 commit intomainfrom
newestblcktest

Conversation

@kyle-semgrep
Copy link
Owner

Added multiple endpoints demonstrating potential security vulnerabilities related to expression evaluation in Spring.

Added multiple endpoints demonstrating potential security vulnerabilities related to expression evaluation in Spring.
@cursor
Copy link

cursor bot commented Dec 11, 2025

You have run out of free Bugbot PR reviews for this billing cycle. This will reset on December 18.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +9 to +13
public String bad1(@RequestParam String input) {
ExpressionParser expressionParser = new SpelExpressionParser();
// ruleid: spring-tainted-code-execution
Expression expression = expressionParser.parseExpression(input).getValue();
String result = (String) expression.getValue();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add return statements to String endpoints

Method bad1 declares a String return type but falls through without returning anything, and the same pattern repeats in bad3 and ok1, so the class will not compile (missing return statement) and none of these endpoints can be exposed.

Useful? React with 👍 / 👎.

Comment on lines +16 to +18
@GetMapping("/api/bad1")
@ResponseBody
public String bad2(@RequestParam String input) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove duplicate /api/bad1 mapping

The second handler is also annotated with @GetMapping("/api/bad1") even though bad1 already uses that path above, which will cause Spring to reject the application context with an ambiguous mapping error before it starts.

Useful? React with 👍 / 👎.

Comment on lines +12 to +13
Expression expression = expressionParser.parseExpression(input).getValue();
String result = (String) expression.getValue();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse expression before evaluating

In bad1, Expression expression = expressionParser.parseExpression(input).getValue(); calls getValue() immediately and stores its result (an Object) into an Expression, which is a type mismatch that prevents compilation; the expression needs to be parsed first and evaluated separately.

Useful? React with 👍 / 👎.

Comment on lines +18 to +22
public String bad2(@RequestParam String input) {
if (expression == null) {
return null;
}
FacesContext context = getFacesContext();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Declare expression before use in bad2

The null check and final return in bad2 reference expression, but the method never declares or receives that variable, so it will not compile and the endpoint cannot be registered.

Useful? React with 👍 / 👎.

Comment on lines +60 to +63
@SuppressWarnings("unchecked") // Forced cast on T
@Override
public <T> T evaluateExpressionGet(FacesContext context, String expression, Class<? extends T> expectedType)
throws ELException {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Drop @OverRide or implement interface for evaluateExpressionGet

The evaluateExpressionGet method is annotated with @Override even though Test does not extend any type defining that signature and helper methods like getExpressionFactory() are undefined, so the file will not compile until the class implements the appropriate interface or the annotation and missing methods are fixed.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +10 to +13
ExpressionParser expressionParser = new SpelExpressionParser();
// ruleid: spring-tainted-code-execution
Expression expression = expressionParser.parseExpression(input).getValue();
String result = (String) expression.getValue();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Correct parseExpression assignment in bad1

In bad1 the variable expression is initialized with expressionParser.parseExpression(input).getValue(), but getValue() returns an Object, not a Expression, so this statement will not compile and the method cannot be built or run. Consider capturing the Expression from parseExpression and evaluating it separately.

Useful? React with 👍 / 👎.

Comment on lines +13 to +15
String result = (String) expression.getValue();
System.out.println(result);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge bad1 exits without returning a String

bad1 is declared to return String but falls through to the closing brace without a return value on the success path, which causes a compilation error and prevents the controller from building.

Useful? React with 👍 / 👎.

Comment on lines +19 to +20
if (expression == null) {
return null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge bad2 references undefined expression variable

bad2 checks and later dereferences expression, but that variable is never declared or assigned in the method, so the class will not compile and the endpoint cannot start.

Useful? React with 👍 / 👎.

Comment on lines +42 to +48
public String bad3(Model model, @RequestParam String input) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+"+input;
// ruleid: spring-tainted-code-execution
System.out.println(engine.eval(foo));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge bad3 declared String but returns nothing

bad3 is declared to return String yet exits after printing the evaluated script without any return statement, producing a compilation error that blocks the application from building.

Useful? React with 👍 / 👎.

Comment on lines +52 to +57
public String ok1(Model model, @RequestParam String input) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+3";
// ok: spring-tainted-code-execution
System.out.println(engine.eval(foo));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge ok1 declared String but returns nothing

ok1 also declares a String return type but falls through without returning, so the compiler will reject this controller and the route cannot be used.

Useful? React with 👍 / 👎.

Comment on lines +7 to +9
@GetMapping("/api/bad1")
@ResponseBody
public String bad1(@RequestParam String input) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Duplicate GET mapping for /api/bad1

Two methods (bad1 and bad2) are both annotated with @GetMapping("/api/bad1"), which will cause Spring to fail startup with an ambiguous mapping error once compilation issues are fixed.

Useful? React with 👍 / 👎.

ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+"+input;
// ruleid: spring-tainted-code-execution
System.out.println(engine.eval(foo));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified a blocking 🔴 issue in your code:
User data flows into a script engine or another means of dynamic code evaluation. This is unsafe and could lead to code injection or arbitrary code execution as a result. Avoid inputting user data into code execution or use proper sandboxing if user code evaluation is necessary.

Dataflow graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>vuln15.java</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L42 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 42] input</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L42 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 42] input</a>"]

            v3["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L45 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 45] foo</a>"]
        end
            v2 --> v3
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L47 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 47] foo</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink


Loading

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
System.out.println(engine.eval(foo));
// Sanitize input: Only allow numeric input to prevent code injection
if (!input.matches("\\d+")) {
throw new IllegalArgumentException("Invalid input: Only numeric values are allowed.");
}
int safeInput = Integer.parseInt(input);
int result = 40 + safeInput;
System.out.println(result);
View step-by-step instructions
  1. Do not pass the user input directly into a script engine or dynamic code execution method.
  2. Validate or sanitize the input value to ensure it contains only safe data (for example, only allow numbers if that's expected): if (!input.matches("\\d+")) { throw new IllegalArgumentException("Invalid input"); }.
  3. Replace the line String foo = "40+"+input; with logic that only uses safe, validated data. For example: int safeInput = Integer.parseInt(input); String foo = "40+" + safeInput;.
  4. Consider avoiding engine.eval(foo) completely. Instead, if you only want to calculate 40 + input, use standard Java math: int result = 40 + safeInput;.
  5. Only use a scripting engine if absolutely required, and never with user-supplied code. If sandboxing is needed, use a library that restricts the code execution capabilities of the engine.

Allowing user input to be executed as code is highly dangerous and can allow attackers to run arbitrary commands on the server. Use only strict, whitelisted data and avoid dynamic code execution wherever possible.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by spring-tainted-code-execution.

You can view more details about this finding in the Semgrep AppSec Platform.

expressionString = expressionString.substring(expressionString.indexOf("[") + 1, expressionString.indexOf("]"));
expressionString = "#{" + expressionString + "}";
// ruleid: spring-tainted-code-execution
ValueExpression dynaVE = factory.createValueExpression(elContext, expressionString, String.class);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified a blocking 🔴 issue in your code:
User data flows into a script engine or another means of dynamic code evaluation. This is unsafe and could lead to code injection or arbitrary code execution as a result. Avoid inputting user data into code execution or use proper sandboxing if user code evaluation is necessary.

Dataflow graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>vuln15.java</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L18 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 18] input</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L18 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 18] input</a>"]

            v3["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L24 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 24] expressionString</a>"]
        end
            v2 --> v3
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L34 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 34] expressionString</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink


Loading

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
ValueExpression dynaVE = factory.createValueExpression(elContext, expressionString, String.class);
// Validate and restrict user input before using in expressionString to prevent code injection
// Only allow simple variable/property names (alphanumeric and underscores), or throw exception if invalid
if (!expressionString.matches("#\\{[a-zA-Z0-9_\\.]+\\}")) {
throw new IllegalArgumentException("Invalid expression syntax.");
}
ValueExpression dynaVE = factory.createValueExpression(elContext, expressionString, String.class);
View step-by-step instructions
  1. Avoid passing user-controlled input directly to the expression evaluation engine (factory.createValueExpression) to prevent code injection.
  2. If user input is necessary for the expression, strictly validate and whitelist acceptable patterns of input before usage. For example, only allow simple variable names or specific safe values: if (!input.matches("[a-zA-Z0-9_]+")) { throw new IllegalArgumentException("Invalid input"); }.
  3. Replace all usage of factory.createValueExpression(elContext, expressionString, String.class) where expressionString contains unsanitized user input, with a safer alternative that does not allow dynamic code execution, or parse the input and safely map it to predefined expressions.
  4. Alternatively, if dynamic expression evaluation is absolutely required, use a secure sandbox or expression language configuration that restricts the set of accessible methods, classes, and properties available to the user input.

Expression injection enables attackers to execute arbitrary code, so any user input included in expressions must be validated and restricted to known-safe values.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by spring-tainted-code-execution.

You can view more details about this finding in the Semgrep AppSec Platform.

public String bad1(@RequestParam String input) {
ExpressionParser expressionParser = new SpelExpressionParser();
// ruleid: spring-tainted-code-execution
Expression expression = expressionParser.parseExpression(input).getValue();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified a blocking 🔴 issue in your code:
User data flows into a script engine or another means of dynamic code evaluation. This is unsafe and could lead to code injection or arbitrary code execution as a result. Avoid inputting user data into code execution or use proper sandboxing if user code evaluation is necessary.

Dataflow graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>vuln15.java</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L9 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 9] input</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L9 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 9] input</a>"]
        end
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/kyle-semgrep/bad-python-app-kyle-managed/blob/795966a770f03a0790876a78bd54c4674598810b/vuln15.java#L12 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 12] input</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink


Loading

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
Expression expression = expressionParser.parseExpression(input).getValue();
// Validate or whitelist user input before parsing it as an expression to prevent code injection
// Here, allow only simple alphanumeric identifiers (letters, numbers, and underscore); adjust as needed for your application context
if (!input.matches("[a-zA-Z0-9_]+")) {
throw new IllegalArgumentException("Invalid input");
}
Expression expression = expressionParser.parseExpression(input);
View step-by-step instructions
  1. Do not pass user-supplied input directly to expressionParser.parseExpression(input) as this allows users to execute arbitrary code.
  2. If dynamic expression evaluation is not strictly necessary, remove or replace the use of parseExpression on untrusted input.
    For example, validate or whitelist expected values in input, such as allowing only certain expression strings.
  3. If you must evaluate user input, implement strict validation using a whitelist, such as allowing only letters, numbers, or specific permitted expressions:
    if (!input.matches("[a-zA-Z0-9_]+")) throw new IllegalArgumentException("Invalid input");
  4. Alternatively, if your use case can be satisfied with a fixed set of operations, map input values to predefined expressions or functions rather than using parseExpression directly:
    switch (input) { case "add": ... break; default: throw ... }
  5. Never echo or log unsanitized user input or results of dynamic expression evaluation.

Passing untrusted user input to expression or script evaluators such as SpEL is dangerous because these APIs are designed to execute arbitrary code and can lead to severe security vulnerabilities.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by spring-tainted-code-execution.

You can view more details about this finding in the Semgrep AppSec Platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant