Miscellaneous API Usage Recommendations

Implementing Jenkins-specific APIs

Callable and RoleSensitive

Callable is essentially an API of Jenkins through the controller/agent communication channel. This lets Jenkins plugins execute complex computations on the other end of such a channel.

Care needs to be taken to prevent users able to control agent processes from exploiting this and sending a malicious Callable to the controller for execution.

Follow the instructions in the Remoting Callables developer documentation to implement Callable safely.

CrumbExclusion

CrumbExclusion is an extension point that allows excluding certain URLs from CSRF protection.

Wherever possible, do not use it to allow bypassing CSRF protection.

Since Jenkins 2.96, requests using Basic authentication providing an API token do not need to provide a CSRF token (crumb). Most of the time, only unauthenticated requests (through UnprotectedRootAction) when integrating with external systems will need to bypass CSRF protection.

Implementations of CrumbExclusion need to only allow bypassing CSRF protection for the specific URLs that do not need it.

Make sure that the HttpServletRequest#getPathInfo() equals the specific allowed URL, or starts with a known safe prefix. Most URLs handling POST requests in Jenkins (web methods annotated using @POST or @RequirePOST) can receive arbitrary suffixes without impacting processing of the request, so checking for URLs containing a specific string will allow attackers to easily bypass all CSRF protection in Jenkins.

When disabling CSRF protection for an endpoint receiving POST requests, be sure to only allow performing safe actions, like informing Jenkins about events that are verified again afterwards anyway before actions are being taken. If the URLs without CSRF protection are provided by an UnprotectedRootAction, be sure to distinguish between authenticated and unauthenticated (anonymous) requests: Do not allow additional access or act on objects only because the current user has permission to access them.

UnprotectedRootAction

An UnprotectedRootAction is a RootAction that is available even to users without Overall/Read. Types implementing UnprotectedRootAction should be kept minimal to reduce the surface that is publicly exposed. Be careful to not open up access to otherwise protected objects to users lacking otherwise necessary permissions to access them by having public fields or getters. While many types check for permissions in StaplerProxy#getTarget, many others do not, and permission checks are expected to be implemented in the getters granting access to navigate to these objects.

See the Stapler reference documentation for how requests are handled in Jenkins.

Api / @Exported

When exposing objects using the Jenkins remote API, be sure to check necessary permissions in @Exported getters and only expose objects the current user has permission to know about and access. Do not rely on permission checks in internal APIs to exist.

Permission checks done in StaplerProxy#getTarget to control Stapler routing have no effect on Api / @Exported. For @ExportedBean annotated types, it may therefore be necessary to implement permission checks both in StaplerProxy, as well as on getters returning this type.

General APIs and Libraries

Starting Programs

Other programs should generally be invoked using a Launcher instance bound to the appropriate agent through its channel. Directly using Runtime#exec and similar Java APIs is usually a bug and can in some cases constitute a security vulnerability: While users with the permissions to configure and run jobs may be able to invoke arbitrary tools on those agents, they should not be able to run programs on the Jenkins controller.

Most Jenkins instances archive artifacts on the Jenkins controller file system, so even possible constraints, such as for the command to exist on the controller’s file system, or not taking any arguments, is trivially bypassed.

Dealing with SSL/TLS Connection Issues

Plugins that integrate with external services may encounter problems establishing connections via HTTPS due to various SSL/TLS issues. An easy way to prevent issues due to misconfigurations is to override the hostname verifier (to not perform hostname validation) and/or trust manager (to accept all certificates).

This is a very unsafe behavior and must never be the default in plugins distributed by the Jenkins project. Ignoring SSL/TLS errors is only permitted if the following constraints are both followed:

  1. Ignoring SSL/TLS errors is limited to the specific connections opened by the plugin. Plugins should never cause SSL/TLS errors to be ignored for the entire Jenkins instance. As a consequence, APIs like HttpsURLConnection#setDefaultHostnameVerifier or HttpsURLConnection#setDefaultSSLSocketFactory must not be called by plugins. The only exceptions to this rule are dedicated plugins with only this purpose, like skip-certificate-check.

  2. Ignoring SSL/TLS problems for specific connections (e.g. HttpsURLConnection#setHostnameVerifier or HttpsURLConnection#setSSLSocketFactory) is allowed, but users need to opt in to this behavior. By default, SSL/TLS problems must not be ignored.

Ideally, users have fine-grained control over which connections should ignore errors. For example, if a plugin allows integrating with a set of several different remote services, ignoring SSL/TLS errors should be a per-service option, rather than a single global option.

Deserialization Issues

Plugin developers need to be careful when working with user-provided content. This can take many forms, but common data formats are XML, JSON, and YAML, and are provided through potentially untrusted sources.

If you’re implementing form validation for a build step whose configuration is provided as XML, or a post-build step that processes a file in the workspace, be sure to treat the contents as not fully trusted.

Serialized Java Objects (ObjectInputStream)

Deserializing untrusted content can easily result in a remote code execution (RCE) vulnerability.

To prevent this, use ObjectInputStreamEx and pass hudson.remoting.ClassFilter.DEFAULT. This will integrate your deserialization mechanism with JEP-200.

Note that this may require adding a hudson.remoting.ClassFilter to your plugin that allows deserializing further safe types. Make sure to review these types for potentially unsafe readResolve and readObject implementations. If at all possible, prefer serializing object graphs using types already approved.

XML

Be sure to review the OWASP Cheat Sheet for XML External Entity prevention. All content should be treated as untrusted except in very specific circumstances. XMLUtils provides functionality to perform common XML operations safely.

When using jackson-databind, make sure to depend on 2.10.x or newer, and only use the "safe" replacement APIs to prevent deserialization vulnerabilities: Use activateDefaultTyping instead of enableDefaultTyping. See the documentation on polymorphic deserialization.

X-Stream is bundled with Jenkins. Jenkins plugins generally should not use it directly, but only through XStream2.

YAML

When processing YAML, be sure to look into the parser library’s security documentation. It needs to be configured to prevent the instantiation of arbitrary types.

When using SnakeYAML, make sure to never use the parameterless new Yaml() constructor, but to pass a SafeConstructor as argument (or otherwise restrict what can be deserialized). Additionally, use at least version 1.26 to get denial-of-service protection ("billion laughs"). Consider depending on the SnakeYAML API Plugin instead of bundling SnakeYAML with your plugin.

Groovy Scripting

Many Jenkins plugins allow the use of Groovy to extend their capabilities. For example, the Email Extension Plugin allows executing a script to determine whether an email should be sent.

All such functionality, if available to users without Overall/Administer, must integrate with Script Security Plugin.

Logging

When there is a necessity to log secrets, care must be taken to avoid accidental exposure. They must be logged at a level that is not visible by default in the system log. This implies setting the logging level to a level more verbose than INFO (i.e., CONFIG, FINE, FINER, or FINEST).