# Patch Analysis: CVE-2026-50229 Apache Tomcat examples XSS

## Target Threat Model

Apache Tomcat's security model (published at https://tomcat.apache.org/security-model.html) explicitly accepts reports of vulnerabilities in web applications bundled with standard Tomcat distributions from the ASF. Administrative users and user-deployed web applications are considered trusted and out of scope, but the bundled `examples` web application is in scope. CVE-2026-50229 was therefore accepted as a Tomcat security issue because it resides in the shipped `webapps/examples` directory.

## What the Fix Changes

The fix is a single-line change in one file, applied to each maintained branch (9.0, 10.1, 11.0):

```diff
--- a/webapps/examples/jsp/num/numguess.jsp
+++ b/webapps/examples/jsp/num/numguess.jsp
@@ -20,7 +20,7 @@
 <%@ page import = "num.NumberGuessBean" %>
 
 <jsp:useBean id="numguess" class="num.NumberGuessBean" scope="session"/>
-<jsp:setProperty name="numguess" property="*"/>
+<jsp:setProperty name="numguess" property="guess"/>
```

- **File:** `webapps/examples/jsp/num/numguess.jsp`
- **Logic change:** The JSP container now binds only the `guess` request parameter to the `num.NumberGuessBean`. Previously, the wildcard `property="*"` bound every request parameter to any matching bean setter.
- **Sink unchanged:** The page still renders `<%= numguess.getHint() %>` unescaped. The fix prevents the attacker from reaching the sink by removing the ability to set the `hint` property via request parameters.
- **Bean unchanged:** `NumberGuessBean.java` was not modified. Its `setGuess()` method continues to compute the `hint` internally and only ever assigns one of the safe strings `higher`, `lower`, or `a number next time`.

## What the Fix Assumes

1. The only attacker-controllable request parameter that should influence the bean is `guess`.
2. `NumberGuessBean.setHint()` is an internal property that should never be directly populated from the request.
3. The HTML output of `getHint()` is safe because the value can only originate from `setGuess()`'s internal logic, which never produces markup.
4. No other example JSP shares the same vulnerability pattern (wildcard binding + unescaped rendering of a settable string property).

## Code Paths / Inputs the Fix Does NOT Cover

The patch is intentionally narrow. It does **not** modify:

- `webapps/examples/jsp/colors/colrs.jsp` — still uses `<jsp:setProperty name="cb" property="*" />`. However, the rendered `ColorGameBean` getters (`getColor1()`, `getColor2()`) return values derived from `background` and `foreground`, which are constrained to the literal colors `black`/`cyan` by `processRequest()`. An attacker can store arbitrary strings in the `color1`/`color2` fields, but those strings are not rendered.
- `webapps/examples/jsp/sessions/carts.jsp` — still uses `<jsp:setProperty name="cart" property="*" />`. The only string-bound property is `submit`, which is never reflected in the response. Item titles are rendered through `util.HTMLFilter.filter()`.
- Any output escaping in `numguess.jsp` itself. The page still writes `getHint()` directly into the HTML.
- Any container-level behavior of `<jsp:setProperty>` or the JavaBean introspector.

## Is the Fix Complete?

For the specific CVE-2026-50229 vulnerability, the fix is **complete**. The original exploit (`?hint=<script>alert(1)</script>`) no longer reaches the `setHint()` setter because only `guess` is bound. The `NumberGuessBean` does not expose any other path that lets an attacker-controlled `guess` value reach the unescaped `getHint()` output as raw markup.

The fix leaves a small latent hygiene issue: two other example JSPs continue to use wildcard property binding. Source inspection and runtime testing show they are not currently exploitable for XSS, but they remain a broader pattern that future changes could reintroduce.

## Comparison of Behavior Before and After the Fix

| Request | Before (10.1.55) | After (10.1.56) |
|---------|-------------------|-----------------|
| `GET /examples/jsp/num/numguess.jsp?guess=abc` | Session bean created, `hint` set to `a number next time` via `setGuess()` | Same behavior |
| `GET ...?hint=<script>alert(1)</script>` | Wildcard binds `hint` parameter to `setHint()`, overwriting the internal hint. Response renders `<script>alert(1)</script>` unescaped. | `hint` parameter is ignored because only `guess` is bound. Response renders the internally-computed hint (e.g., `a number next time`). |

## Variant Search Notes

Three example JSPs use wildcard property binding: `num/numguess.jsp`, `colors/colrs.jsp`, and `sessions/carts.jsp`. The fix only changes `numguess.jsp`. The other two were tested on the fixed Tomcat 10.1.56 binary:

- `colrs.jsp`: `color1`/`color2` payloads are accepted into the bean but are not rendered; the page only reflects the constrained `background`/`foreground` values.
- `carts.jsp`: `submit` is accepted but not reflected; cart items are escaped.

No bypass or alternate XSS trigger was reproduced on the fixed version.

## Recommendations for Closing Remaining Gaps

1. Apply the same explicit-property-binding pattern to `colrs.jsp` and `carts.jsp` (e.g., bind only `color1` and `color2`, or `itemId` and `submit`, respectively) to remove the wildcard-binding hygiene issue.
2. Add output escaping in `numguess.jsp` as a defense-in-depth measure (e.g., `<%= org.apache.commons.lang3.StringEscapeUtils.escapeHtml4(numguess.getHint()) %>` or use JSTL `<c:out>`), even though the current input path is blocked.
3. Review the remaining examples web application for any other JSPs that mix request parameter binding with unescaped scriptlet output.
