VYPR
Moderate severityNVD Advisory· Published Feb 6, 2019· Updated Aug 5, 2024

CVE-2019-1003012

CVE-2019-1003012

Description

Jenkins Blue Ocean Plugins 1.10.1 and earlier allow bypass of CSRF protection, enabling unauthorized data modification.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Jenkins Blue Ocean Plugins 1.10.1 and earlier allow bypass of CSRF protection, enabling unauthorized data modification.

Vulnerability

Jenkins Blue Ocean Plugins version 1.10.1 and earlier contain a data modification vulnerability in several files including blueocean-core-js/src/js/bundleStartup.js, fetch.ts, and APICrumbExclusion.java, allowing attackers to bypass all cross-site request forgery (CSRF) protection in the Blue Ocean API [2][3].

Exploitation

An attacker can exploit this by tricking a Jenkins user with access to Blue Ocean into visiting a specially crafted web page or link, which then performs unauthorized actions on the Jenkins instance through the Blue Ocean API without proper CSRF token validation [2].

Impact

Successful exploitation allows an attacker to perform arbitrary data modification operations within the Blue Ocean API, effectively impersonating the victim user and potentially altering Jenkins configuration, pipeline data, or other resources [2][3].

Mitigation

The vulnerability is fixed in Jenkins Blue Ocean Plugins version 1.10.2 (or later). Red Hat OpenShift Container Platform 3.11.82 includes the fix [1][4]. Users should upgrade to the latest version of the Blue Ocean plugin or update their OpenShift installation accordingly [2].

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
io.jenkins.blueocean:blueoceanMaven
< 1.10.21.10.2

Affected products

2

Patches

1
1a03020b5a50

[SECURITY-1201] Add CSRF token to post requests

https://github.com/jenkinsci/blueocean-pluginNicolae PascuDec 3, 2018via ghsa
24 files changed · +270 87
  • blueocean-bitbucket-pipeline/src/test/java/io/jenkins/blueocean/blueocean_bitbucket_pipeline/cloud/BbCloudPipelineCreateRequestTest.java+1 0 modified
    @@ -21,6 +21,7 @@ public void createPipeline() throws UnirestException, IOException {
             Map r = new PipelineBaseTest.RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, authenticatedUser.getId(), authenticatedUser.getId()))
    +                .crumb( crumb )
                     .post("/organizations/jenkins/pipelines/")
                     .data(ImmutableMap.of("name", "pipeline1", "$class", "io.jenkins.blueocean.blueocean_bitbucket_pipeline.BitbucketPipelineCreateRequest",
                             "scmConfig", ImmutableMap.of("id", BitbucketCloudScm.ID, "uri", apiUrl,
    
  • blueocean-bitbucket-pipeline/src/test/java/io/jenkins/blueocean/blueocean_bitbucket_pipeline/server/BitbucketPipelineCreateRequestTest.java+3 0 modified
    @@ -42,6 +42,7 @@ public void createdWithTraits() throws Exception {
             Map r = new PipelineBaseTest.RequestBuilder(baseUrl)
                 .status(201)
                 .jwtToken(getJwtToken(j.jenkins, authenticatedUser.getId(), authenticatedUser.getId()))
    +            .crumb( crumb )
                 .post("/organizations/jenkins/pipelines/")
                 .data(ImmutableMap.of(
                     "name","pipeline1",
    @@ -91,6 +92,7 @@ public void createPipelineBitbucketServerWithCredentialId() throws UnirestExcept
             Map r = new PipelineBaseTest.RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, authenticatedUser.getId(), authenticatedUser.getId()))
    +                .crumb( crumb )
                     .post("/organizations/jenkins/pipelines/")
                     .data(ImmutableMap.of(
                         "name","pipeline1",
    @@ -116,6 +118,7 @@ public void createPipelineBitbucketServerWithoutCredentialId() throws UnirestExc
             Map r = new PipelineBaseTest.RequestBuilder(baseUrl)
                 .status(201)
                 .jwtToken(getJwtToken(j.jenkins, authenticatedUser.getId(), authenticatedUser.getId()))
    +            .crumb( crumb )
                 .post("/organizations/jenkins/pipelines/")
                 .data(ImmutableMap.of(
                     "name","pipeline1",
    
  • blueocean-bitbucket-pipeline/src/test/java/io/jenkins/blueocean/blueocean_bitbucket_pipeline/server/BitbucketServerEndpointTest.java+13 0 modified
    @@ -36,6 +36,7 @@ public class BitbucketServerEndpointTest extends BbServerWireMock {
         public void setup() throws Exception {
             super.setup();
             token = getJwtToken(j.jenkins, authenticatedUser.getId(), authenticatedUser.getId());
    +        this.crumb = getCrumb( j.jenkins );
         }
     
         @Test
    @@ -47,6 +48,7 @@ public void testServerNotBitbucket() throws Exception {
             Map resp = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server",
                             "apiUrl", apiUrl
    @@ -69,6 +71,7 @@ public void testServerBadServer() throws Exception {
             Map resp = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server",
                             "apiUrl", "http://foobar/"
    @@ -90,6 +93,7 @@ public void testMissingParams() throws Exception {
             Map resp = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of())
                     .post(URL)
                     .build(Map.class);
    @@ -110,6 +114,7 @@ public void testMissingUrlParam() throws Exception {
             Map resp = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of("name", "foo"))
                     .post(URL)
                     .build(Map.class);
    @@ -129,6 +134,7 @@ public void testMissingNameParam() throws Exception {
             Map resp = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of("apiUrl", apiUrl))
                     .post(URL)
                     .build(Map.class);
    @@ -149,6 +155,7 @@ public void avoidDuplicateByUrl() throws Exception {
             Map server = request()
                     .status(200)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server",
                             "apiUrl", apiUrl
    @@ -161,6 +168,7 @@ public void avoidDuplicateByUrl() throws Exception {
             Map resp = server = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server 2",
                             "apiUrl", apiUrl
    @@ -185,6 +193,7 @@ public void createAndList() throws Exception {
             Map server = request()
                     .status(200)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server",
                             "apiUrl", apiUrl
    @@ -234,6 +243,7 @@ public void shouldFailOnIncompatibleVersionInAdd() throws UnirestException, IOEx
             Map server = request()
                     .status(400)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server",
                             "apiUrl", apiUrl
    @@ -257,6 +267,7 @@ public void shouldFailOnIncompatibleVersion() throws UnirestException, IOExcepti
             Map server = request()
                     .status(200)
                     .jwtToken(token)
    +                .crumb( crumb )
                     .data(ImmutableMap.of(
                             "name", "My Server",
                             "apiUrl", apiUrl
    @@ -293,6 +304,7 @@ public void createThenDelete() throws IOException {
                 .as(Void.class);
     
             httpRequest().Post(URL)
    +            .header( crumb.field, crumb.value )
                 .bodyJson(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", apiUrl
    @@ -334,6 +346,7 @@ public void should404OnDeleteNonexistent() throws IOException {
         private List getServers() {
             return request()
                     .status(200)
    +                .crumb( crumb )
                     .jwtToken(token)
                     .get(URL)
                     .build(List.class);
    
  • blueocean-core-js/src/js/bundleStartup.js+7 1 modified
    @@ -13,8 +13,14 @@ export function execute(done, config) {
             extensionData: window.$blueocean.jsExtensions,
             classMetadataProvider: (type, cb) => {
                 const fetch = require('./fetch').Fetch;
    +            const fetchOptions = {
    +                method: 'POST',
    +                headers: {
    +                    'Content-Type': 'application/json',
    +                },
    +            };
                 fetch
    -                .fetchJSON(`${appRoot}/rest/classes/${type}/`)
    +                .fetchJSON(`${appRoot}/rest/classes/${type}/`, { fetchOptions })
                     .then(cb)
                     .catch(fetch.consoleError);
             },
    
  • blueocean-core-js/src/js/fetch.ts+11 0 modified
    @@ -308,6 +308,12 @@ export class Fetch {
         static fetchJSON(url, { onSuccess, onError, fetchOptions, disableCapabilities, disableLoadingIndicator, ignoreRefreshHeader }: Fetch.FetchOpts = {}) {
             const fixedUrl = FetchFunctions.prefixUrl(url);
             let future;
    +        const crumbHeaderName = UrlConfig.getCrumbHeaderName();
    +
    +        if (crumbHeaderName && fetchOptions && fetchOptions.headers) {
    +            fetchOptions.headers[crumbHeaderName] = UrlConfig.getCrumbToken();
    +        }
    +
             if (!AppConfig.isJWTEnabled()) {
                 future = FetchFunctions.rawFetchJSON(fixedUrl, { onSuccess, onError, fetchOptions, disableLoadingIndicator, ignoreRefreshHeader });
             } else {
    @@ -341,6 +347,11 @@ export class Fetch {
          */
         static fetch(url, { onSuccess, onError, fetchOptions, disableLoadingIndicator, ignoreRefreshHeader }: Fetch.FetchOpts = {}) {
             const fixedUrl = FetchFunctions.prefixUrl(url);
    +        const crumbHeaderName = UrlConfig.getCrumbHeaderName();
    +
    +        if (crumbHeaderName && fetchOptions && fetchOptions.headers) {
    +            fetchOptions.headers[crumbHeaderName] = UrlConfig.getCrumbToken();
    +        }
     
             if (!AppConfig.isJWTEnabled()) {
                 return FetchFunctions.rawFetch(fixedUrl, { onSuccess, onError, fetchOptions, disableLoadingIndicator, ignoreRefreshHeader });
    
  • blueocean-core-js/src/js/i18n/i18n.js+7 1 modified
    @@ -42,7 +42,13 @@ function newPluginXHR(pluginName) {
                 if (logger.isDebugEnabled()) {
                     logger.debug('loading data for', url);
                 }
    -            Fetch.fetchJSON(url, { disableCapabilities: true, disableLoadingIndicator: true, ignoreRefreshHeader: true }).then(data => {
    +            const fetchOptions = {
    +                method: 'PUT',
    +                headers: {
    +                    'Content-Type': 'application/json',
    +                },
    +            };
    +            Fetch.fetchJSON(url, { disableCapabilities: true, disableLoadingIndicator: true, ignoreRefreshHeader: true, fetchOptions }).then(data => {
                     callback(data, { status: 200 });
                 });
             },
    
  • blueocean-core-js/src/js/rest/RunApi.js+1 1 modified
    @@ -1,8 +1,8 @@
     /**
      * Created by cmeyers on 8/29/16.
      */
    -import { Fetch } from '../fetch';
     import { UrlConfig } from '../urlconfig';
    +import { Fetch } from '../fetch';
     import { Utils } from '../utils';
     
     export class RunApi {
    
  • blueocean-core-js/src/js/urlconfig.js+28 0 modified
    @@ -1,6 +1,8 @@
     let jenkinsRootURL = '';
     let blueOceanAppURL = '/';
     let restBaseURL = '';
    +let crumbToken = '';
    +let crumbHeaderName = '';
     
     let loaded = false;
     
    @@ -23,6 +25,18 @@ function loadConfig() {
             // typically '/jenkins/blue/rest'
             restBaseURL = `${blueOceanAppURL}/rest`.replace(/\/\/+/g, '/'); // eliminate any duplicated slashes
     
    +        // load crumb token used for POST requests
    +        crumbToken = headElement.getAttribute('data-crumbtoken');
    +        if (typeof crumbToken !== 'string') {
    +            crumbToken = '';
    +        }
    +
    +        // load crumb header name used for POST requests
    +        crumbHeaderName = headElement.getAttribute('data-crumbtoken-field');
    +        if (typeof crumbHeaderName !== 'string') {
    +            crumbHeaderName = '';
    +        }
    +
             loaded = true;
         } catch (error) {
             // eslint-disable-next-line no-console
    @@ -47,6 +61,20 @@ export const UrlConfig = {
             return blueOceanAppURL;
         },
     
    +    getCrumbHeaderName() {
    +        if (!loaded) {
    +            loadConfig();
    +        }
    +        return crumbHeaderName;
    +    },
    +
    +    getCrumbToken() {
    +        if (!loaded) {
    +            loadConfig();
    +        }
    +        return crumbToken;
    +    },
    +
         getRestBaseURL() {
             if (!loaded) {
                 loadConfig();
    
  • blueocean-github-pipeline/src/test/java/io/jenkins/blueocean/blueocean_github_pipeline/GithubMockBase.java+4 0 modified
    @@ -101,8 +101,10 @@ public void setup() throws Exception {
     
             this.user = login("vivek", "Vivek Pandey", "vivek.pandey@gmail.com");
             this.githubApiUrl = String.format("http://localhost:%s",githubApi.port());
    +        this.crumb = getCrumb( j.jenkins );
         }
     
    +
         @After
         public void tearDown() {
             if (!perTestStubMappings.isEmpty()) {
    @@ -127,6 +129,7 @@ protected String createGithubCredential(User user) throws UnirestException {
                     .data(ImmutableMap.of("accessToken", accessToken))
                     .status(200)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .put("/organizations/" + getOrgName() + "/scm/github/validate/?apiUrl="+githubApiUrl)
                     .build(Map.class);
             String credentialId = (String) r.get("credentialId");
    @@ -142,6 +145,7 @@ protected String createGithubEnterpriseCredential(User user) throws UnirestExcep
                 .data(ImmutableMap.of("accessToken", accessToken))
                 .status(200)
                 .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +            .crumb( this.crumb )
                 .put("/organizations/" + getOrgName() + "/scm/github-enterprise/validate/?apiUrl="+githubApiUrl)
                 .build(Map.class);
             String credentialId = (String) r.get("credentialId");
    
  • blueocean-github-pipeline/src/test/java/io/jenkins/blueocean/blueocean_github_pipeline/GithubOrgFolderPermissionsTest.java+1 0 modified
    @@ -73,6 +73,7 @@ private void createGithubPipeline(boolean shouldSucceed) throws Exception {
             Map resp = new RequestBuilder(baseUrl)
                     .status(shouldSucceed ? 201 : 403)
                     .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID,null, githubApiUrl, pipelineName, "PR-demo"))
                     .build(Map.class);
    
  • blueocean-github-pipeline/src/test/java/io/jenkins/blueocean/blueocean_github_pipeline/GithubPipelineCreateRequestTest.java+10 0 modified
    @@ -63,6 +63,7 @@ public void createPipeline() throws UnirestException, IOException {
             Map r = new PipelineBaseTest.RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/jenkins/pipelines/")
                     .data(ImmutableMap.of("name", "pipeline1", "$class", "io.jenkins.blueocean.blueocean_github_pipeline.GithubPipelineCreateRequest",
                             "scmConfig", ImmutableMap.of("id", GithubScm.ID, "uri", githubApiUrl, "credentialId", credentialId,
    @@ -138,6 +139,7 @@ public void shouldFailForAnonUserWithCredentialIdMissing() throws Exception {
             String orgFolderName = "cloudbeers";
             Map resp = new RequestBuilder(baseUrl)
                     .status(401)
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID,null, githubApiUrl, orgFolderName, "PR-demo"))
                     .build(Map.class);
    @@ -152,6 +154,7 @@ public void shouldFailForAnonUserWithCredentialIdSent() throws Exception {
             String orgFolderName = "cloudbeers";
             Map resp = new RequestBuilder(baseUrl)
                     .status(401)
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID, credentialId, githubApiUrl, orgFolderName, "PR-demo"))
                     .build(Map.class);
    @@ -169,6 +172,7 @@ public void shouldFailForAuthedUserWithoutCredentialCreatedAndCredentialIdMissin
             Map resp = new RequestBuilder(baseUrl)
                     .status(400)
                     .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID,null, githubApiUrl, orgFolderName, "PR-demo"))
                     .build(Map.class);
    @@ -186,6 +190,7 @@ public void shouldFailForAuthedUserWithoutCredentialCreatedAndCredentialIdSent()
             Map resp = new RequestBuilder(baseUrl)
                     .status(400)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID, credentialId, githubApiUrl, orgFolderName, "PR-demo"))
                     .build(Map.class);
    @@ -202,6 +207,7 @@ public void shouldSucceedForAuthedUserWithCredentialCreatedAndCredentialIdMissin
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     // since credentialId will default to 'github', it's okay to omit it in request
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID, null, githubApiUrl, orgFolderName, "PR-demo"))
    @@ -219,6 +225,7 @@ public void shouldSucceedForAuthedUserWithCredentialCreatedAndCredentialIdSent()
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID, credentialId, githubApiUrl, orgFolderName, "PR-demo"))
                     .build(Map.class);
    @@ -235,6 +242,7 @@ public void shouldFailForAuthedUserWithCredentialCreatedAndBogusCredentialIdSent
             Map resp = new RequestBuilder(baseUrl)
                     .status(400)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     .data(GithubTestUtils.buildRequestBody(GithubScm.ID, "bogus-cred", githubApiUrl, orgFolderName, "PR-demo"))
                     .build(Map.class);
    @@ -251,6 +259,7 @@ public void shouldSucceedForAuthedUserWithCredentialCreatedAndCredentialIdMissin
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .post("/organizations/"+getOrgName()+"/pipelines/")
                     // since credentialId will default to 'github', it's okay to omit it in request
                     .data(GithubTestUtils.buildRequestBody(GithubEnterpriseScm.ID, null, githubApiUrl, orgFolderName, "PR-demo"))
    @@ -325,6 +334,7 @@ public void testOrgFolderIndexing() throws IOException, UnirestException {
             Map map = new RequestBuilder(baseUrl)
                     .post("/organizations/jenkins/pipelines/p/runs/")
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( this.crumb )
                     .data(ImmutableMap.of())
                     .status(200)
                     .build(Map.class);
    
  • blueocean-github-pipeline/src/test/java/io/jenkins/blueocean/blueocean_github_pipeline/GithubServerTest.java+13 1 modified
    @@ -34,10 +34,11 @@ public class GithubServerTest extends PipelineBaseTest {
         String token;
     
         @Before
    -    public void createUser() throws UnirestException {
    +    public void createUser() throws Exception {
             hudson.model.User user = User.get("alice");
             user.setFullName("Alice Cooper");
             token = getJwtToken(j.jenkins, "alice", "alice");
    +        this.crumb = getCrumb( j.jenkins );
         }
     
         @Test
    @@ -46,6 +47,7 @@ public void testServerNotGithub() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", getApiUrlCustomPath("/notgithub")
    @@ -68,6 +70,7 @@ public void testServerGithubEnterpriseTopLevelUrl() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", getApiUrl()
    @@ -90,6 +93,7 @@ public void testServerUnknownHost() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", "http://foobar/"
    @@ -111,6 +115,7 @@ public void testMissingParams() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of())
                 .post("/organizations/jenkins/scm/github-enterprise/servers/")
                 .build(Map.class);
    @@ -131,6 +136,7 @@ public void testMissingUrlParam() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of("name", "foo"))
                 .post("/organizations/jenkins/scm/github-enterprise/servers/")
                 .build(Map.class);
    @@ -150,6 +156,7 @@ public void testMissingNameParam() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of("apiUrl", getDefaultApiUrl()))
                 .post("/organizations/jenkins/scm/github-enterprise/servers/")
                 .build(Map.class);
    @@ -170,6 +177,7 @@ public void avoidDuplicateByUrl() throws Exception {
             Map server = request()
                 .status(200)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", getDefaultApiUrl()
    @@ -181,6 +189,7 @@ public void avoidDuplicateByUrl() throws Exception {
             Map resp = server = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server 2",
                     "apiUrl", getDefaultApiUrl()
    @@ -203,6 +212,7 @@ public void avoidDuplicateByName() throws Exception {
             Map server = request()
                 .status(200)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", getDefaultApiUrl()
    @@ -214,6 +224,7 @@ public void avoidDuplicateByName() throws Exception {
             Map resp = request()
                 .status(400)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", getDefaultApiUrl()
    @@ -238,6 +249,7 @@ public void createAndList() throws Exception {
             Map server = request()
                 .status(200)
                 .jwtToken(token)
    +            .crumb( crumb )
                 .data(ImmutableMap.of(
                     "name", "My Server",
                     "apiUrl", getDefaultApiUrl()
    
  • blueocean-git-pipeline/src/test/java/io/jenkins/blueocean/blueocean_git_pipeline/GitPipelineCreateRequestTest.java+1 0 modified
    @@ -45,6 +45,7 @@ public void createPipeline() throws UnirestException, IOException {
             Map r = new PipelineBaseTest.RequestBuilder(baseUrl)
                 .status(201)
                 .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +            .crumb( crumb )
                 .post("/organizations/jenkins/pipelines/")
                 .data(ImmutableMap.of("name", "pipeline1",
                     "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    
  • blueocean-git-pipeline/src/test/java/io/jenkins/blueocean/blueocean_git_pipeline/GitReadSaveTest.java+9 0 modified
    @@ -233,6 +233,7 @@ public void testGitScmValidate() throws Exception {
             // Validate bob via repositoryUrl
             Map r = new RequestBuilder(baseUrl)
                 .status(200)
    +            .crumb( crumb )
                 .jwtToken(getJwtToken(j.jenkins, bob.getId(), bob.getId()))
                 .put("/organizations/" + getOrgName() + "/scm/git/validate/")
                 .data(ImmutableMap.of(
    @@ -246,6 +247,7 @@ public void testGitScmValidate() throws Exception {
             String jobName = "test-token-validation";
             r = new RequestBuilder(baseUrl)
                 .status(201)
    +            .crumb( crumb )
                 .jwtToken(getJwtToken(j.jenkins, bob.getId(), bob.getId()))
                 .post("/organizations/" + getOrgName() + "/pipelines/")
                 .data(ImmutableMap.of(
    @@ -261,6 +263,7 @@ public void testGitScmValidate() throws Exception {
             // Test for existing pipeline/job
             r = new RequestBuilder(baseUrl)
                 .status(200)
    +            .crumb( crumb )
                 .jwtToken(getJwtToken(j.jenkins, bob.getId(), bob.getId()))
                 .put("/organizations/" + getOrgName() + "/scm/git/validate/")
                 .data(ImmutableMap.of(
    @@ -273,6 +276,7 @@ public void testGitScmValidate() throws Exception {
             // Test alice fails
             r = new RequestBuilder(baseUrl)
                 .status(428)
    +            .crumb( crumb )
                 .jwtToken(getJwtToken(j.jenkins, alice.getId(), alice.getId()))
                 .put("/organizations/" + getOrgName() + "/scm/git/validate/")
                 .data(ImmutableMap.of(
    @@ -282,6 +286,7 @@ public void testGitScmValidate() throws Exception {
     
             r = new RequestBuilder(baseUrl)
                 .status(428)
    +            .crumb( crumb )
                 .jwtToken(getJwtToken(j.jenkins, alice.getId(), alice.getId()))
                 .put("/organizations/" + getOrgName() + "/scm/git/validate/")
                 .data(ImmutableMap.of(
    @@ -331,6 +336,7 @@ private void testGitReadWrite(final @Nonnull GitReadSaveService.ReadSaveType typ
             Map r = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of(
                             "name", jobName,
    @@ -347,6 +353,7 @@ private void testGitReadWrite(final @Nonnull GitReadSaveService.ReadSaveType typ
             r = new RequestBuilder(baseUrl)
                     .status(200)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( crumb )
                     .get(urlJobPrefix + "/scm/content/?branch=master&path=Jenkinsfile&type="+type.name())
                     .build(Map.class);
     
    @@ -369,6 +376,7 @@ private void testGitReadWrite(final @Nonnull GitReadSaveService.ReadSaveType typ
             new RequestBuilder(baseUrl)
                     .status(200)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( crumb )
                     .put(urlJobPrefix + "/scm/content/")
                     .data(ImmutableMap.of("content", content))
                     .build(Map.class);
    @@ -382,6 +390,7 @@ private void testGitReadWrite(final @Nonnull GitReadSaveService.ReadSaveType typ
             // check to make sure we get the same thing from the service
             r = new RequestBuilder(baseUrl)
                     .status(200)
    +                .crumb( crumb )
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
                     .get(urlJobPrefix + "/scm/content/?branch=master&path=Jenkinsfile&type="+type.name())
                     .build(Map.class);
    
  • blueocean-git-pipeline/src/test/java/io/jenkins/blueocean/blueocean_git_pipeline/GitScmTest.java+13 0 modified
    @@ -67,6 +67,7 @@ private Map createCredentials(User user, Map credRequest) throws UnirestExceptio
             return new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/credentials/user/")
                     .data(credRequest).build(Map.class);
         }
    @@ -101,6 +102,7 @@ public void shouldCreateWithRemoteGitRepo() throws IOException, UnirestException
             Map r = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of("name", "demo",
                             "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    @@ -208,6 +210,7 @@ public void shouldFailForBadCredentialIdOnCreate() throws IOException, UnirestEx
             resp = new RequestBuilder(baseUrl)
                     .status(400)
                     .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of("name", "demo",
                             "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    @@ -229,6 +232,7 @@ public void shouldCreateGitMbp() throws IOException, UnirestException {
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins,"bob", "bob"))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of("name", "demo",
                             "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    @@ -282,6 +286,7 @@ public void shouldFailOnValidation3() throws IOException, UnirestException {
             Map resp = new RequestBuilder(baseUrl)
                     .status(400)
                     .jwtToken(getJwtToken(j.jenkins,"bob", "bob"))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of("name", "demo",
                             "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    @@ -306,6 +311,7 @@ public void shouldFailOnValidation4() throws IOException, UnirestException {
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins,"bob", "bob"))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of("name", "demo",
                             "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    @@ -319,6 +325,7 @@ public void shouldFailOnValidation4() throws IOException, UnirestException {
             resp = new RequestBuilder(baseUrl)
                     .status(400)
                     .jwtToken(getJwtToken(j.jenkins,"bob", "bob"))
    +                .crumb( crumb )
                     .post("/organizations/" + getOrgName() + "/pipelines/")
                     .data(ImmutableMap.of("name", "demo",
                             "$class", "io.jenkins.blueocean.blueocean_git_pipeline.GitPipelineCreateRequest",
    @@ -368,6 +375,7 @@ public void shouldNotProvideIdForMissingCredentials() throws Exception {
             Map resp = new RequestBuilder(baseUrl)
                 .status(200)
                 .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +            .crumb( crumb )
                 .get(repoPath)
                 .build(Map.class);
     
    @@ -384,6 +392,7 @@ public void shouldBePoliteAboutBadUrl() throws Exception {
             Map resp = new RequestBuilder(baseUrl)
                 .status(200)
                 .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +            .crumb( crumb )
                 .get(repoPath)
                 .build(Map.class);
     
    @@ -409,6 +418,7 @@ public void shouldCreateCredentialsWithDefaultId() throws Exception {
             Map resp = new RequestBuilder(baseUrl)
                 .status(200)
                 .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +            .crumb( crumb )
                 .data(params)
                 .put(scmValidatePath)
                 .build(Map.class);
    @@ -422,6 +432,7 @@ public void shouldCreateCredentialsWithDefaultId() throws Exception {
             Map resp2 = new RequestBuilder(baseUrl)
                 .status(200)
                 .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +            .crumb( crumb )
                 .get(repoPath)
                 .build(Map.class);
     
    @@ -451,6 +462,7 @@ public void shouldNotCreateCredentialsForBadUrl1() throws Exception {
             Map resp = new RequestBuilder(baseUrl)
                 .status(400)
                 .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +            .crumb( crumb )
                 .data(params)
                 .put(scmValidatePath)
                 .build(Map.class);
    @@ -481,6 +493,7 @@ public void shouldNotCreateCredentialsForBadUrl2() throws Exception {
             Map resp = new RequestBuilder(baseUrl)
                 .status(428)
                 .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +            .crumb( crumb )
                 .data(params)
                 .put(scmValidatePath)
                 .build(Map.class);
    
  • blueocean-pipeline-api-impl/src/test/java/io/jenkins/blueocean/rest/impl/pipeline/CredentialApiTest.java+25 0 modified
    @@ -12,6 +12,11 @@
     import com.mashape.unirest.http.exceptions.UnirestException;
     import hudson.ExtensionList;
     import hudson.model.User;
    +import org.apache.commons.io.IOUtils;
    +import org.apache.http.HttpResponse;
    +import org.apache.http.client.HttpClient;
    +import org.apache.http.client.methods.HttpPost;
    +import org.apache.http.impl.client.HttpClientBuilder;
     import org.junit.Assert;
     import org.junit.Test;
     
    @@ -129,6 +134,7 @@ public void createUsingUsernamePasswordInUserStore() throws IOException, Unirest
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +                .crumb( crumb )
                     .post("/organizations/jenkins/credentials/user/")
                     .data(                ImmutableMap.of("credentials",
                             new ImmutableMap.Builder<String,Object>()
    @@ -153,6 +159,7 @@ public void createSshCredentialUsingDirectSshInUserStore() throws IOException, U
             Map resp = new RequestBuilder(baseUrl)
                     .status(201)
                     .jwtToken(getJwtToken(j.jenkins,user.getId(), user.getId()))
    +                .crumb( crumb )
                     .post("/organizations/jenkins/credentials/user/")
                     .data(                ImmutableMap.of("credentials",
                             new ImmutableMap.Builder<String,Object>()
    @@ -186,4 +193,22 @@ public void createSshCredentialUsingDirectSshInUserStore() throws IOException, U
             Assert.assertEquals("SSH Username with private key", resp.get("typeName"));
             Assert.assertEquals("blueocean-git-domain", resp.get("domain"));
         }
    +
    +
    +    @Test
    +    public void crumbRejected() throws IOException, UnirestException {
    +        User user = login();
    +        HttpClient httpClient = HttpClientBuilder.create().build();
    +
    +        HttpPost post = new HttpPost( baseUrl + "/organizations/jenkins/credentials/user/" );
    +        post.addHeader( "Authorization", "Bearer " + getJwtToken(j.jenkins,user.getId(), user.getId()));
    +
    +        HttpResponse resp =  httpClient.execute( post );
    +        Assert.assertEquals(403, resp.getStatusLine().getStatusCode());
    +        //LOGGER.info( IOUtils.toString( resp.getEntity().getContent() ));
    +        // assert content contains No valid crumb was included in the request
    +        // olamy not sure as it can be i18n sensitive
    +    }
    +
    +
     }
    
  • blueocean-pipeline-api-impl/src/test/java/io/jenkins/blueocean/rest/impl/pipeline/MultiBranchTest.java+1 0 modified
    @@ -329,6 +329,7 @@ public void multiBranchPipelineIndex() throws Exception {
             Map map = new RequestBuilder(baseUrl)
                     .post("/organizations/jenkins/pipelines/p/runs/")
                     .jwtToken(getJwtToken(j.jenkins, user.getId(), user.getId()))
    +                .crumb( getCrumb( j.jenkins ) )
                     .data(ImmutableMap.of())
                     .status(200)
                     .build(Map.class);
    
  • blueocean-pipeline-api-impl/src/test/java/io/jenkins/blueocean/rest/impl/pipeline/PipelineBaseTest.java+66 33 modified
    @@ -14,6 +14,7 @@
     import hudson.model.Job;
     import hudson.model.Run;
     import hudson.model.User;
    +import hudson.security.csrf.CrumbIssuer;
     import hudson.tasks.Mailer;
     import io.jenkins.blueocean.commons.JsonConverter;
     import jenkins.model.Jenkins;
    @@ -77,6 +78,8 @@ public static void resetJWT() {
     
         protected String jwtToken;
     
    +    protected Crumb crumb;
    +
         protected String getContextPath(){
             return "blue/rest";
         }
    @@ -89,6 +92,8 @@ public void setup() throws Exception {
             }
             this.baseUrl = j.jenkins.getRootUrl() + getContextPath();
             this.jwtToken = getJwtToken(j.jenkins);
    +        this.crumb = getCrumb( j.jenkins );
    +
             Unirest.setObjectMapper(new ObjectMapper() {
                 public <T> T readValue(String value, Class<T> valueType) {
                     try {
    @@ -121,6 +126,19 @@ public String writeValue(Object value) {
     //        Unirest.setHttpClient();
         }
     
    +    protected static class Crumb {
    +        public String field, value;
    +    }
    +
    +    public static Crumb getCrumb(Jenkins jenkins) throws Exception {
    +
    +        Crumb crumb  = new Crumb();
    +        CrumbIssuer crumbIssuer = jenkins.getCrumbIssuer();
    +        crumb.field = crumbIssuer.getCrumbRequestField();
    +        crumb.value = crumbIssuer.getCrumb();
    +        return crumb;
    +    }
    +
         protected <T> T  get(String path, Class<T> type){
             return get(path,200, type);
         }
    @@ -180,6 +198,7 @@ protected Map<String, Object> post(String path, Object body, int expectedStatus)
                 HttpResponse<Map> response = Unirest.post(getBaseUrl(path))
                     .header("Content-Type","application/json")
                     .header("Authorization", "Bearer "+jwtToken)
    +                .header( crumb.field, crumb.value )
                     .body(body).asObject(Map.class);
                 Assert.assertEquals(expectedStatus, response.getStatus());
                 return response.getBody();
    @@ -419,6 +438,7 @@ public  class RequestBuilder {
             private String baseUrl;
             private int expectedStatus = 200;
             private String token;
    +        private Crumb crumb;
     
             private Map<String,String> headers = new HashMap<>();
     
    @@ -457,6 +477,11 @@ public RequestBuilder jwtToken(String token){
                 return this;
             }
     
    +        public RequestBuilder crumb(Crumb crumb){
    +            this.crumb = crumb;
    +            return this;
    +        }
    +
             public RequestBuilder data(Map data) {
                 this.data = data;
                 return this;
    @@ -498,50 +523,58 @@ public RequestBuilder delete(String url) {
                 return this;
             }
     
    -        public <T> T build(Class<T> clzzz) {
    +        public HttpRequest build() {
                 assert url != null;
                 assert url.startsWith("/");
    -            try {
    -                HttpRequest request;
    -                switch (method) {
    -                    case "PUT":
    -                        request = Unirest.put(getBaseUrl(url));
    -                        break;
    -                    case "POST":
    -                        request = Unirest.post(getBaseUrl(url));
    -                        break;
    -                    case "GET":
    -                        request = Unirest.get(getBaseUrl(url));
    -                        break;
    -                    case "DELETE":
    -                        request = Unirest.delete(getBaseUrl(url));
    -                        break;
    -                    default:
    -                        throw new RuntimeException("No default options");
     
    -                }
    -                request.header("Accept-Encoding","");
    -                if(!Strings.isNullOrEmpty(username) && !Strings.isNullOrEmpty(password)){
    -                    request.basicAuth(username, password);
    +            HttpRequest request;
    +            switch (method) {
    +                case "PUT":
    +                    request = Unirest.put(getBaseUrl(url));
    +                    break;
    +                case "POST":
    +                    request = Unirest.post(getBaseUrl(url));
    +                    break;
    +                case "GET":
    +                    request = Unirest.get(getBaseUrl(url));
    +                    break;
    +                case "DELETE":
    +                    request = Unirest.delete(getBaseUrl(url));
    +                    break;
    +                default:
    +                    throw new RuntimeException("No default options");
    +
    +            }
    +            if(crumb!=null){
    +                request.header( crumb.field, crumb.value );
    +            }
    +            request.header("Accept-Encoding","");
    +            if(!Strings.isNullOrEmpty(username) && !Strings.isNullOrEmpty(password)){
    +                request.basicAuth(username, password);
    +            }else{
    +                if (token == null) {
    +                    request.header("Authorization", "Bearer " + PipelineBaseTest.this.jwtToken);
                     }else{
    -                    if (token == null) {
    -                        request.header("Authorization", "Bearer " + PipelineBaseTest.this.jwtToken);
    -                    }else{
    -                        request.header("Authorization", "Bearer " + token);
    -                    }
    +                    request.header("Authorization", "Bearer " + token);
                     }
    +            }
     
    -                request.header("Content-Type", contentType);
    +            request.header("Content-Type", contentType);
     
    -                request.headers(headers);
    +            request.headers(headers);
     
    -                if(request instanceof HttpRequestWithBody && data != null) {
    -                    ((HttpRequestWithBody)request).body(data);
    -                }
    +            if(request instanceof HttpRequestWithBody && data != null) {
    +                ((HttpRequestWithBody)request).body(data);
    +            }
    +            return request;
    +        }
     
    +        public <T> T build(Class<T> clzzz) {
    +            try {
    +                HttpRequest request = build();
                     HttpResponse<T> response = request.asObject(clzzz);
                     Assert.assertEquals(response.getStatusText(), expectedStatus, response.getStatus());
    -                return response.getBody();
    +            return response.getBody();
                 } catch (UnirestException e) {
                     throw new RuntimeException(e);
                 }
    
  • blueocean-pipeline-api-impl/src/test/java/io/jenkins/blueocean/rest/impl/pipeline/RunImplTest.java+2 1 modified
    @@ -210,7 +210,8 @@ public void pipelineLatestRunIncludesRunning() throws Exception {
             String replayURL = String.format("/organizations/jenkins/pipelines/%s/runs/%s/replay/", p.getName(), idOfSecondRun);
             try {
                 Thread.sleep(200);
    -            request().post(replayURL).build(String.class);
    +
    +            request().crumb( getCrumb( j.jenkins ) ).post(replayURL).build(String.class);
             } catch (Exception e) {
                 Thread.sleep(200);
                 request().post(replayURL).build(String.class);
    
  • blueocean-rest-impl/src/test/java/io/jenkins/blueocean/service/embedded/BaseTest.java+29 0 modified
    @@ -12,6 +12,8 @@
     import hudson.model.Job;
     import hudson.model.Run;
     import hudson.model.User;
    +import hudson.security.csrf.CrumbIssuer;
    +import hudson.security.csrf.DefaultCrumbIssuer;
     import hudson.tasks.Mailer;
     import io.jenkins.blueocean.commons.JsonConverter;
     import jenkins.model.Jenkins;
    @@ -53,6 +55,8 @@ protected String getContextPath(){
     
         protected String jwtToken;
     
    +    protected Crumb crumb;
    +
         @Before
         public void setup() throws Exception {
             if(System.getProperty("DISABLE_HTTP_HEADER_TRACE") == null) {
    @@ -61,6 +65,7 @@ public void setup() throws Exception {
             }
             this.baseUrl = j.jenkins.getRootUrl() + getContextPath();
             this.jwtToken = getJwtToken(j.jenkins);
    +        this.crumb = getCrumb( j.jenkins );
             Unirest.setObjectMapper(new ObjectMapper() {
                 public <T> T readValue(String value, Class<T> valueType) {
                     try {
    @@ -153,6 +158,7 @@ protected Map<String, Object> post(String path, Object body, int expectedStatus)
                 HttpResponse<Map> response = Unirest.post(getBaseUrl(path))
                     .header("Content-Type","application/json")
                     .header("Authorization", "Bearer "+jwtToken)
    +                .header( crumb.field, crumb.value )
                     .body(body).asObject(Map.class);
                 Assert.assertEquals(expectedStatus, response.getStatus());
                 return response.getBody();
    @@ -321,6 +327,7 @@ public  class RequestBuilder {
             private String contentType = "application/json";
             private String baseUrl;
             private int expectedStatus = 200;
    +        private Crumb crumb;
     
             private String token;
     
    @@ -359,6 +366,11 @@ public RequestBuilder jwtToken(String token){
                 return this;
             }
     
    +        public RequestBuilder crumb(Crumb crumb){
    +            this.crumb = crumb;
    +            return this;
    +        }
    +
     
             public RequestBuilder data(Map data) {
                 this.data = data;
    @@ -433,6 +445,10 @@ public <T> HttpResponse<T> execute(Class<T> clzzz) {
                         request.basicAuth(username, password);
                     }
     
    +                if(crumb!=null){
    +                    request.header( crumb.field, crumb.value );
    +                }
    +
                     if(token == null) {
                         request.header("Authorization", "Bearer " + BaseTest.this.jwtToken);
                     }else{
    @@ -466,6 +482,19 @@ public static String getJwtToken(Jenkins jenkins) throws UnirestException {
             return token;
         }
     
    +    static class Crumb {
    +        String field, value;
    +    }
    +
    +    public static Crumb getCrumb(Jenkins jenkins) throws Exception {
    +
    +        Crumb crumb  = new Crumb();
    +        CrumbIssuer crumbIssuer = jenkins.getCrumbIssuer();
    +        crumb.field = crumbIssuer.getCrumbRequestField();
    +        crumb.value = crumbIssuer.getCrumb();
    +        return crumb;
    +    }
    +
         public static String getJwtToken(Jenkins jenkins, String username, String password) throws UnirestException {
              GetRequest request = Unirest.get(jenkins.getRootUrl()+"jwt-auth/token/").header("Accept", "*/*")
                 .header("Accept-Encoding","");
    
  • blueocean-rest-impl/src/test/java/io/jenkins/blueocean/service/embedded/PipelineApiTest.java+1 1 modified
    @@ -556,7 +556,7 @@ public void testNewPipelineQueueItem() throws Exception {
             p2.scheduleBuild2(0).waitForStart();
     
             // Run the third pipeline
    -        Map r = request().post("/organizations/jenkins/pipelines/pipeline3/runs/").build(Map.class);
    +        Map r = request().crumb( crumb ).post("/organizations/jenkins/pipelines/pipeline3/runs/").build(Map.class);
     
             // Ensure it is still in the queue
             assertNotNull(p3.getQueueItem());
    
  • blueocean-rest/src/main/java/io/jenkins/blueocean/rest/APICrumbExclusion.java+0 47 modified
    @@ -1,47 +0,0 @@
    -package io.jenkins.blueocean.rest;
    -
    -import java.io.IOException;
    -
    -import javax.servlet.FilterChain;
    -import javax.servlet.ServletException;
    -import javax.servlet.http.HttpServletRequest;
    -import javax.servlet.http.HttpServletResponse;
    -
    -import hudson.Extension;
    -import hudson.ExtensionList;
    -import hudson.security.csrf.CrumbExclusion;
    -import io.jenkins.blueocean.RootRoutable;
    -
    -/**
    - * This class forces the Blueocean API to require json for POSTs so that we do not need a crumb.
    - * @author Ivan Meredith
    - */
    -@Extension
    -public class APICrumbExclusion extends CrumbExclusion{
    -    @Override
    -    public boolean process(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException {
    -        String pathInfo = httpServletRequest.getPathInfo();
    -
    -        for (RootRoutable r : ExtensionList.lookup(RootRoutable.class)) {
    -            String path = getExclusionPath(r.getUrlName());
    -            if (pathInfo != null && pathInfo.startsWith(path)) {
    -                String header = httpServletRequest.getHeader("Content-Type");
    -                if(header != null && header.contains("application/json")) {
    -                    filterChain.doFilter(httpServletRequest, httpServletResponse);
    -                    return true;
    -                } else {
    -                    return false;
    -                }
    -
    -            }
    -        }
    -
    -        return false;
    -
    -    }
    -
    -    public String getExclusionPath(String route) {
    -        return "/blue/" + route + "/";
    -    }
    -
    -}
    
  • blueocean-web/src/main/java/io/jenkins/blueocean/BlueOceanUI.java+21 0 modified
    @@ -2,7 +2,10 @@
     
     import hudson.ExtensionList;
     import hudson.Main;
    +import hudson.security.csrf.CrumbIssuer;
     import io.jenkins.blueocean.dev.RunBundleWatches;
    +import jenkins.model.Jenkins;
    +import org.apache.commons.lang.StringUtils;
     import org.kohsuke.stapler.Stapler;
     import org.kohsuke.stapler.StaplerRequest;
     import org.slf4j.Logger;
    @@ -79,6 +82,24 @@ public String getLang() {
             return null;
         }
     
    +    /**
    +     * Get the crumb token value
    +     * @return the crumb token value or empty String if no {@link CrumbIssuer}
    +     */
    +    public String getCrumbToken() {
    +        CrumbIssuer crumbIssuer = Jenkins.get().getCrumbIssuer();
    +        return crumbIssuer == null ? StringUtils.EMPTY : crumbIssuer.getCrumb();
    +    }
    +
    +    /**
    +     * Get the crumb request field
    +     * @return the crumb request field or empty String if no {@link CrumbIssuer}
    +     */
    +    public String getCrumbRequestField() {
    +        CrumbIssuer crumbIssuer = Jenkins.get().getCrumbIssuer();
    +        return crumbIssuer == null ? StringUtils.EMPTY : crumbIssuer.getCrumbRequestField();
    +    }
    +
         public List<BluePageDecorator> getPageDecorators(){
             return BluePageDecorator.all();
         }
    
  • blueocean-web/src/main/resources/io/jenkins/blueocean/BlueOceanUI/index.jelly+3 1 modified
    @@ -21,7 +21,9 @@
                   data-resurl="${resURL}"
                   data-appurl="${rootURL}/${it.urlBase}"
                   data-servertime="${it.now}"
    -              data-adjuncturl="${rootURL}/${j.getAdjuncts('').rootURL}">
    +              data-adjuncturl="${rootURL}/${j.getAdjuncts('').rootURL}"
    +              data-crumbtoken-field="${it.crumbRequestField}"
    +              data-crumbtoken="${it.crumbToken}">
     
                 <title>Jenkins</title>
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.