Security context
Medium· 4.3GHSA-g28p-6mcc-v4rv CVE-2026-53439CWE-862Published Jun 10, 2026

Jenkins exposes other users' timezone and view names to users with Overall/Read permission

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

0 → fixed in 2.555.32.556 → fixed in 2.568

Details

Missing permission checks in Jenkins 2.567 and earlier, LTS 2.555.2 and earlier allow attackers with Overall/Read permission to determine other users' configured timezone and to enumerate view names of other users' "My Views".

The fix

[SECURITY-3713]

Kevin-CB· May 29, 2026, 07:39 AM+208−00586de4255
core/src/main/java/hudson/model/MyViewsProperty.java+2 −0
@@ -287,6 +287,8 @@ public ListBoxModel doFillPrimaryViewNameItems(@AncestorInPath User user) throws
ListBoxModel items = new ListBoxModel();
user = user == null ? User.current() : user;
if (user != null) {
+ user.checkPermission(Jenkins.ADMINISTER);
+
MyViewsProperty property = user.getProperty(MyViewsProperty.class);
if (property == null) {
property = new MyViewsProperty();
core/src/main/java/hudson/model/TimeZoneProperty.java+4 −0
@@ -13,6 +13,7 @@
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
+import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -88,6 +89,9 @@ public boolean isEnabled() {
}
public ListBoxModel doFillTimeZoneNameItems(@AncestorInPath User user) {
+ if (user != null) {
+ user.checkPermission(Jenkins.ADMINISTER);
+ }
String userTimezone = user != null ? forUser(user) : forCurrentUser();
ListBoxModel items = new ListBoxModel();
items.add(Messages.TimeZoneProperty_DisplayDefaultTimeZone(), "");
test/src/test/java/hudson/model/Security3713Test.java+202 −0
@@ -0,0 +1,202 @@
+package hudson.model;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+import java.net.URL;
+import jenkins.model.Jenkins;
+import org.htmlunit.HttpMethod;
+import org.htmlunit.Page;
+import org.htmlunit.WebRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.MockAuthorizationStrategy;
+import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
+
+@WithJenkins
+class Security3713Test {
+
+ private static final String TIME_ZONE = "Antarctica/Troll";
+
+ private JenkinsRule j;
+
+ @BeforeEach
+ void setUp(JenkinsRule rule) {
+ j = rule;
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void regularUserCannotAccessOtherUserViewNames() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ MyViewsProperty aliceProperty = new MyViewsProperty(null);
+ MyView customView = new MyView("Alice's View", aliceProperty);
+ alice.addProperty(aliceProperty);
+ aliceProperty.setUser(alice);
+ aliceProperty.addView(customView);
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+
+ assertThat(page.getWebResponse().getStatusCode(), is(403));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void adminCanAccessOtherUserViewNames() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ MyViewsProperty aliceProperty = new MyViewsProperty(null);
+ MyView customView = new MyView("Alice's View", aliceProperty);
+ alice.addProperty(aliceProperty);
+ aliceProperty.setUser(alice);
+ aliceProperty.addView(customView);
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("Alice's View"));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void sameUserCanAccessOwnViewNames() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ MyViewsProperty aliceProperty = new MyViewsProperty(null);
+ MyView customView = new MyView("Alice's View", aliceProperty);
+ alice.addProperty(aliceProperty);
+ aliceProperty.setUser(alice);
+ aliceProperty.addView(customView);
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("alice");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("Alice's View"));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void regularUserCannotAccessOtherUserTimeZone() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ alice.addProperty(new TimeZoneProperty(TIME_ZONE));
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+
+ assertThat(page.getWebResponse().getStatusCode(), is(403));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void adminCanAccessOtherUserTimeZone() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ alice.addProperty(new TimeZoneProperty(TIME_ZONE));
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void sameUserCanAccessOwnTimeZone() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ alice.addProperty(new TimeZoneProperty(TIME_ZONE));
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("alice");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));
+ }
+ }
+}

[SECURITY-3713]

Kevin-CB· Jun 2, 2026, 10:14 AM+208−098fe05f175
core/src/main/java/hudson/model/MyViewsProperty.java+2 −0
@@ -287,6 +287,8 @@ public ListBoxModel doFillPrimaryViewNameItems(@AncestorInPath User user) throws
ListBoxModel items = new ListBoxModel();
user = user == null ? User.current() : user;
if (user != null) {
+ user.checkPermission(Jenkins.ADMINISTER);
+
MyViewsProperty property = user.getProperty(MyViewsProperty.class);
if (property == null) {
property = new MyViewsProperty();
core/src/main/java/hudson/model/TimeZoneProperty.java+4 −0
@@ -13,6 +13,7 @@
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
+import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -88,6 +89,9 @@ public boolean isEnabled() {
}
public ListBoxModel doFillTimeZoneNameItems(@AncestorInPath User user) {
+ if (user != null) {
+ user.checkPermission(Jenkins.ADMINISTER);
+ }
String userTimezone = user != null ? forUser(user) : forCurrentUser();
ListBoxModel items = new ListBoxModel();
items.add(Messages.TimeZoneProperty_DisplayDefaultTimeZone(), "");
test/src/test/java/hudson/model/Security3713Test.java+202 −0
@@ -0,0 +1,202 @@
+package hudson.model;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+import java.net.URL;
+import jenkins.model.Jenkins;
+import org.htmlunit.HttpMethod;
+import org.htmlunit.Page;
+import org.htmlunit.WebRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.MockAuthorizationStrategy;
+import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
+
+@WithJenkins
+class Security3713Test {
+
+ private static final String TIME_ZONE = "Antarctica/Troll";
+
+ private JenkinsRule j;
+
+ @BeforeEach
+ void setUp(JenkinsRule rule) {
+ j = rule;
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void regularUserCannotAccessOtherUserViewNames() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ MyViewsProperty aliceProperty = new MyViewsProperty(null);
+ MyView customView = new MyView("Alice's View", aliceProperty);
+ alice.addProperty(aliceProperty);
+ aliceProperty.setUser(alice);
+ aliceProperty.addView(customView);
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+
+ assertThat(page.getWebResponse().getStatusCode(), is(403));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void adminCanAccessOtherUserViewNames() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ MyViewsProperty aliceProperty = new MyViewsProperty(null);
+ MyView customView = new MyView("Alice's View", aliceProperty);
+ alice.addProperty(aliceProperty);
+ aliceProperty.setUser(alice);
+ aliceProperty.addView(customView);
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("Alice's View"));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void sameUserCanAccessOwnViewNames() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ MyViewsProperty aliceProperty = new MyViewsProperty(null);
+ MyView customView = new MyView("Alice's View", aliceProperty);
+ alice.addProperty(aliceProperty);
+ aliceProperty.setUser(alice);
+ aliceProperty.addView(customView);
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("alice");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("Alice's View"));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void regularUserCannotAccessOtherUserTimeZone() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ alice.addProperty(new TimeZoneProperty(TIME_ZONE));
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+
+ assertThat(page.getWebResponse().getStatusCode(), is(403));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void adminCanAccessOtherUserTimeZone() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");
+ authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ alice.addProperty(new TimeZoneProperty(TIME_ZONE));
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("bob");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));
+ }
+ }
+
+ @Test
+ @Issue("SECURITY-3713")
+ void sameUserCanAccessOwnTimeZone() throws Exception {
+ j.jenkins.setCrumbIssuer(null);
+ j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
+ User alice = User.getOrCreateByIdOrFullName("alice");
+
+ MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();
+ authStrategy.grant(Jenkins.READ).everywhere().to("alice");
+ j.jenkins.setAuthorizationStrategy(authStrategy);
+
+ alice.addProperty(new TimeZoneProperty(TIME_ZONE));
+ alice.save();
+
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ wc.login("alice");
+
+ URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");
+ WebRequest request = new WebRequest(url, HttpMethod.POST);
+ Page page = wc.getPage(request);
+ String body = page.getWebResponse().getContentAsString();
+
+ assertThat(page.getWebResponse().getStatusCode(), is(200));
+ assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));
+ }
+ }
+}

References