# Patch Analysis for CVE-2026-57527 Variant Search

## Target and threat model scope

- Repository inspected: `https://github.com/zaproxy/zap-extensions`.
- Vulnerable commit tested: `7e686c0900e82bb73b57880c0328d012269f8741` (parent of the fix).
- Fixed commit tested: `ac6c3f94d38505bc0facea286a4d3728044c6e5c`.
- Latest/default cached source inspected: `c639b4e3fe10e472cbe8542de7fbe3964b4d4ce7`; the ViewState model still has the JSF registration commented out there.
- Threat model documentation: no `SECURITY.md` or explicit serialization threat-model document was present in the `zap-extensions` repository. The repository README and file headers identify ZAP as an HTTP/HTTPS proxy for assessing web application security, and the add-on README/help text describes ViewState request/response panels. For this analysis, inputs from proxied HTTP peers and proxied HTTP messages were treated as crossing ZAP's trust boundary. Local user-supplied add-on loading or local files were not treated as variant surfaces.

## What the fix changes

The fix in commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c` changes the ViewState add-on in these relevant ways:

1. `addOns/viewstate/src/main/java/org/zaproxy/zap/extension/viewstate/ViewStateModel.java`
   - Before the fix, the constructor registered three ViewState parameter names:
     - `__VIEWSTATE` as ASP ViewState
     - `__VIEWSTATEFIELDCOUNT` as ASP ViewState
     - `javax.faces.ViewState` as JSF ViewState
   - The patch comments out the JSF registration:
     ```java
     // viewstateParams.add(new ViewState(null, JSFViewState.KEY, "javax.faces.ViewState"));
     ```
   - This prevents `getViewStateParam()` from constructing a `JSFViewState` for both request-model and response-model instances.
2. `addOns/viewstate/src/main/javahelp/help/contents/viewstate.html` and translated/help metadata
   - The user-facing description changes from ASP/JSF ViewState support to ASP-only ViewState support.
3. `addOns/viewstate/viewstate.gradle.kts` and `gradle.properties`
   - The add-on description changes to `ASP ViewState Decoder and Editor`; release metadata is updated for version 4.
4. `CHANGELOG.md`
   - Version 4 records: `Disable support for JSF`.

No Java serialization filter, allowlist, or safe JSF parser was added. The vulnerable method `JSFViewState.decode()` still contains `new ObjectInputStream(...).readObject()`, but after the patch no normal ViewState panel registration reaches it for `javax.faces.ViewState`.

## Patch assumptions

The fix assumes:

- All attacker-controlled JSF ViewState deserialization in the add-on is reachable only after `ViewStateModel` registers the `javax.faces.ViewState` parameter with type `JSFViewState.KEY`.
- Removing that single registration globally is acceptable because JSF support is disabled, not merely hardened.
- The request and response ViewState panels both instantiate the same `ViewStateModel` constructor, so removing the constructor registration covers both panels.
- There are no other ZAP add-on code paths that independently instantiate `JSFViewState` from proxied HTTP input.

## Code paths and inputs covered by the fix

The patch covers both material ViewState panel entry points because both call the same constructor:

- Response panel:
  - `ExtensionHttpPanelViewStateView.ResponseSplitBodyViewStateViewFactory.getNewView()`
  - `new ViewStateModel(ViewStateModel.VS_ACTION_RESPONSE, null)`
  - `ViewStateModel.getData()`
  - `ViewStateModel.getViewStateParam(responseBody)`
- Request panel:
  - `ExtensionHttpPanelViewStateView.RequestSplitBodyViewStateViewFactory.getNewView()`
  - `new ViewStateModel(ViewStateModel.VS_ACTION_REQUEST, null)`
  - `ViewStateModel.getData()`
  - `ViewStateModel.getViewStateParam(requestBody)`

In the vulnerable version, the response path extracts hidden HTML input values using Jericho HTML parsing, while the request path parses URL/form parameters with `StandardParameterParser` and then URL-decodes the value. Both paths create `new JSFViewState(...)` only because the constructor registered the `javax.faces.ViewState` parameter.

## Variant candidate tested: request-panel body trigger

A materially distinct candidate was tested: attacker-influenced JSF ViewState in a proxied HTTP request body rather than in a malicious HTTP response. This reaches a different registered add-on factory and a different parsing branch:

- Original reproduced surface: malicious server response body -> `ResponseSplitBodyViewStateViewFactory` -> HTML hidden-input parsing.
- Variant candidate surface: proxied HTTP request body -> `RequestSplitBodyViewStateViewFactory` -> URL/form parameter parsing and URL decoding.

Runtime evidence in `bundle/logs/vuln_variant_request_vuln.log` confirms that the vulnerable add-on request panel registered `javax.faces.ViewState`, used the request factory, reached `JSFViewState.decode()`, and executed the payload. Runtime evidence in `bundle/logs/vuln_variant_request_fixed.log` confirms that the fixed add-on request panel had only the two ASP registrations and did not create marker/RCE files.

## Code paths not covered or considered out of scope

- Direct local calls to `JSFViewState.decode()` remain possible if trusted local code or a custom harness instantiates `JSFViewState` directly. That is not an add-on trust-boundary vulnerability by itself because it does not cross the proxied HTTP peer/message boundary.
- Other add-ons were scanned for `ObjectInputStream`/`JSFViewState`/`javax.faces.ViewState`. No other product path was found that independently deserializes JSF ViewState from proxied HTTP input. Wappalyzer contains passive fingerprint strings for `javax.faces.ViewState`; these do not deserialize Java objects and are unrelated to this root cause.
- ASP ViewState handling remains enabled. It uses the ASP parser path rather than `JSFViewState.decode()`/`ObjectInputStream.readObject()` and was not a same-root-cause JSF deserialization bypass.

## Behavior before and after the fix

- Vulnerable parent `7e686c0900e82bb73b57880c0328d012269f8741`:
  - Response panel: confirmed by the repro stage as RCE via `javax.faces.ViewState` hidden input.
  - Request panel: confirmed in this stage as RCE via request body parameter `javax.faces.ViewState=<serialized object>`.
- Fixed commit `ac6c3f94d38505bc0facea286a4d3728044c6e5c`:
  - Response panel: repro-stage negative control showed no JSF registration and no deserialization.
  - Request panel: variant-stage negative control showed `ViewStateModel.param_count=2`, `ViewStateModel.has_jsf_registration=false`, no marker file, and no RCE output file.
- Latest/default cached source `c639b4e3fe10e472cbe8542de7fbe3964b4d4ce7`:
  - Source inspection shows the same JSF registration remains commented out.

## Completeness assessment

The fix is complete for the same root cause in the ViewState add-on's normal request and response panel surfaces. The request-panel candidate is a valid alternate trigger on vulnerable versions, but it is not a bypass because the fixed commit disables the shared JSF registration before either request or response parsing can instantiate `JSFViewState`.

The main defense-in-depth gap is that `JSFViewState.decode()` still contains unrestricted Java deserialization. If future code re-enables JSF support or calls `JSFViewState` directly on proxied HTTP data, the same root cause would return. A more robust long-term fix would remove the JSF deserialization code entirely or replace it with a non-deserializing parser / strict `ObjectInputFilter` allowlist and add regression tests for both request and response ViewState panels.
