How Form Validation Works in Jenkins with FormValidation and Descriptor

Jenkins performs server-side form validation by matching Jelly UI fields to doCheckXyz methods on a plugin's Descriptor, which return FormValidation results that core renders into safe HTML and injects back into the page.

The jenkinsci/jenkins repository implements a built-in form validation pipeline that connects Jelly views to Java validation logic through two main abstractions: hudson.model.Descriptor and hudson.util.FormValidation. When a user types into a configured field, the frontend dispatches an AJAX request to a convention-based URL handled by the plugin descriptor, receives an HTML fragment generated by FormValidation, and displays it inline. This article traces the exact path from method discovery to secure response rendering.

Descriptor Check Method Discovery

Jenkins locates validation endpoints reflectively. In core/src/main/java/hudson/model/Descriptor.java, the method getCheckMethod(String fieldName) searches the descriptor class for a doCheckXyz method whose suffix corresponds to the form field name. The method returns a FormValidation.CheckMethod object that constructs the URL the client-side JavaScript will later invoke. This mechanism is what links an <f:entry> tag in a Jelly view to a specific Java method without explicit wiring.

Implementing doCheckXyz Validation Methods

Plugin authors implement validation logic by following a strict naming convention. A server-side check method must be named doCheckXyz, where Xyz maps to the form field, and it usually accepts @QueryParameter String value along with other annotated parameters as needed. The method must return a hudson.util.FormValidation instance.

Inside core/src/main/java/hudson/util/FormValidation.java, the class provides static factories for every severity level:

  • FormValidation.error(String msg) creates an error result rendered in red.
  • FormValidation.warning(String msg) creates a warning result rendered in yellow.
  • FormValidation.ok(String msg) creates a success result rendered in green.

These factories ultimately call respond(Kind kind, String html), which stores the severity and the escaped message payload.

Because FormValidation extends IOException, plugin code can also throw it as an exception to short-circuit validation and propagate the result:

public FormValidation doCheckAntVersion(@QueryParameter String f) {
    try {
        return FormValidation.ok(getAntVersion(new File(f)));
    } catch (FormValidation fv) {
        return fv;
    }
}

For common checks, use the built-in helpers instead of writing custom logic:

public class MyBuilderDescriptor extends Descriptor<MyBuilder> {

    public FormValidation doCheckTimeout(@QueryParameter String value) {
        return FormValidation.validateNonNegativeInteger(value);
    }
}

Executable paths can be validated with an additional custom callback:

public FormValidation doCheckGitPath(@QueryParameter String git) {
    return FormValidation.validateExecutable(git, exe -> {
        return exe.getName().contains("git")
            ? FormValidation.ok()
            : FormValidation.error("Not a git executable");
    });
}

You can also invoke these methods directly from Java code without the UI:

FormValidation fv = FormValidation.validatePositiveInteger("42");
if (fv.kind == FormValidation.Kind.OK) {
    // proceed with the valid value
}

This design means the same validation rule serves both interactive configuration and programmatic consumers.

FormValidation HTML Rendering and Security

When the AJAX request is served, the FormValidation object handles its own response serialization. The concrete instance's rendering logic—accessed through generateResponse—wraps the message in a <div> element carrying the CSS class ok, warning, or error. All message content is escaped to prevent injection attacks.

The response is emitted with the content type text/html;charset=UTF-8 and includes a strict Content-Security-Policy header defined in the same source file. This combination protects against XSS while allowing the browser to render the styled validation hint inline.

Client-Side Integration with Jelly and JavaScript

The frontend wiring is handled automatically by Jenkins' Jelly form tags. Tags such as <f:entry> emit a data-validation-url attribute whose value comes from CheckMethod.toStemUrl(). The client-side JavaScript in war/src/main/webapp/scripts/utilities.js listens for changes, posts the field value to that URL, and injects the returned HTML fragment directly into the DOM beside the input. This architecture gives users immediate feedback without refreshing the page.

Together, these components create a closed loop: the descriptor publishes the endpoint, the plugin author defines the rule, FormValidation packages a safe HTML payload, and the browser renders the result.

Summary

  • Descriptor.getCheckMethod discovers doCheckXyz methods reflectively and exposes a validation URL for each field.
  • Plugin authors write doCheckXyz methods that accept @QueryParameter values and return FormValidation via static helpers such as error(), warning(), and ok().
  • core/src/main/java/hudson/util/FormValidation.java constructs typed validation results and renders them as escaped HTML fragments wrapped in CSS classes.
  • The HTTP response carries a strict Content-Security-Policy header to mitigate XSS risks.
  • Frontend code in war/src/main/webapp/scripts/utilities.js manages AJAX transport and inline display for real-time feedback.

Frequently Asked Questions

How does Jenkins discover which method to call for form validation?

Jenkins uses a naming convention enforced by Descriptor.getCheckMethod(String fieldName) in core/src/main/java/hudson/model/Descriptor.java. This method reflectively locates a plugin descriptor method named doCheckXyz that matches the form field name and produces a FormValidation.CheckMethod containing the target URL.

What parameters can a doCheckXyz method accept?

The standard signature accepts @QueryParameter String value, which binds the current form input. You may add additional parameters annotated with @QueryParameter to supply sibling field values or context objects, and the method must always return a FormValidation result.

How does FormValidation prevent XSS when rendering validation messages?

During response generation in core/src/main/java/hudson/util/FormValidation.java, the message is escaped before it is wrapped in a <div> tag. The server also serves the response with a strict Content-Security-Policy header, preventing any injected markup from executing scripts in the browser.

Can FormValidation be used outside of the web UI?

Yes. Because FormValidation is a plain Java object that extends IOException and implements HttpResponse, backend code can invoke doCheckXyz methods directly. Callers simply inspect the returned object or catch it as an exception to enforce validation rules in tests, CLI commands, or background tasks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →