Skip to content

Commit facd597

Browse files
committed
feat: warn at configuration time when the propagation URL is not https
doCheckPropagationUrl validated only well-formedness: an http:// URL got a green OK and the only signal that credentials would travel unencrypted was buried in the system log at notification time. The form now shows a warning for non-https propagation URLs, reaching the operator while typing; the value stays accepted, since TLS terminated on a trusted proxy in front of Bitbucket is a legitimate setup, and the runtime warning keeps covering the payload-derived URLs no form validates. Help text updated accordingly. The new @WithJenkins test pins ok for empty and https values, the warning with its https hint for http, and the error for malformed URLs.
1 parent 02f32c1 commit facd597

4 files changed

Lines changed: 79 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* **Build Server build-status links correctly for base URLs without an explicit port** - The pull request Server action appended `URL.getPort()` verbatim when building commit, approve and decline links, producing an unroutable `host:-1` address whenever the configured propagation URL (or the payload clone URL) carried no explicit port, so those status notifications failed silently. The port is now omitted when absent.
77

88
### Improvements
9+
* **Warn at configuration time when the propagation URL is not https** - The global configuration form now shows a warning when the *Propagation URL* uses plain `http`, since the credentials of build-status notifications would travel unencrypted. The value is still accepted, for setups terminating TLS on a trusted proxy in front of Bitbucket. This complements the runtime warning below, which also covers the payload-derived URLs no form validates.
910
* **Use the JVM's TLS configuration for Basic-auth Bitbucket API calls** - The Basic-auth API client now follows the JVM's standard JSSE/trust-store configuration (`javax.net.ssl.trustStore` and friends, proxy settings), as the Bearer-token client does since 3.3.9. A Bitbucket instance using a private CA or a self-signed certificate present in the JVM trust store now works on both authentication paths.
1011
* **Warn when build-status notifications are sent over plain HTTP** - The plugin now logs a warning when the Bitbucket URL used for a status notification is not `https://`, since the `Authorization` header (credentials or token) travels unencrypted. The check runs at the dispatch layer, so it covers all three authentication paths (Basic, Bearer and OAuth2), and warns once per origin (`scheme://host[:port]`) rather than per URL, so notification URLs embedding the commit hash do not flood the log. The request is still sent, so setups terminating TLS on a trusted reverse proxy keep working.
1112
* **Warn when a build-status notification does not succeed** - A notification answered with a non-successful HTTP status (rotated token, revoked credential, wrong repository, a misconfigured proxy answering with a redirect) previously left no trace at default log level, since the HTTP call itself succeeded. The plugin now logs a warning with the origin and status code, once per continuous episode: a repeat of the same status stays silent, and a successful response resets the state so the next incident warns again. The episode state is tracked per origin, without job or credential identity: on a Bitbucket host shared by many jobs, any job's success re-arms the warning and simultaneous same-status incidents collapse into one episode. The response body, truncated and stripped of control characters, is available at FINE. Transport failures are also logged with the full exception instead of only its message.

src/main/java/io/jenkins/plugins/bitbucketpushandpullrequest/config/BitBucketPPRPluginConfig.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,16 @@ public FormValidation doCheckPropagationUrl(@QueryParameter String value) {
115115
}
116116

117117
try {
118-
new URL(value); // This will throw MalformedURLException if the URL is not valid
118+
URL parsed = new URL(value); // throws MalformedURLException if the URL is not valid
119+
// Warn, do not reject: TLS terminated on a trusted reverse proxy in front of Bitbucket is
120+
// a legitimate setup. This reaches the operator while typing; the runtime warning still
121+
// covers payload-derived URLs that no form validates.
122+
if (!"https".equalsIgnoreCase(parsed.getProtocol())) {
123+
return FormValidation.warning(
124+
"This URL is not https: the credentials used for build status notifications will"
125+
+ " travel unencrypted. Use an https URL unless TLS is terminated by a trusted"
126+
+ " proxy in front of Bitbucket.");
127+
}
119128
return FormValidation.ok();
120129
} catch (MalformedURLException e) {
121130
return FormValidation.error("This is not a valid URL. Please enter a correct URL.");
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
<div>
22
<p>Optional endpoint used to propagate statuses.</p>
33
<p>It is mandatory in the case that your bitbucket repository supports only ssh cloning</p>
4+
<p>Use an https URL: over plain http the credentials of the build status notifications
5+
travel unencrypted. An http URL is still accepted, for setups where TLS is terminated
6+
by a trusted proxy in front of Bitbucket, but the form shows a warning.</p>
47
</div>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*******************************************************************************
2+
* The MIT License
3+
*
4+
* Copyright (C) 2018-2026, Christian Del Monte.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
7+
* associated documentation files (the "Software"), to deal in the Software without restriction,
8+
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
9+
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in all copies or
13+
* substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
16+
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
******************************************************************************/
21+
22+
package io.jenkins.plugins.bitbucketpushandpullrequest.config;
23+
24+
import static org.junit.jupiter.api.Assertions.assertEquals;
25+
import static org.junit.jupiter.api.Assertions.assertTrue;
26+
27+
import hudson.util.FormValidation;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
import org.jvnet.hudson.test.JenkinsRule;
31+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
32+
33+
/**
34+
* Validation of the propagation URL in the global configuration form: well-formedness as before,
35+
* plus a warning (not a rejection) for plain-http URLs, where the credentials of the build status
36+
* notifications would travel unencrypted. This reaches the operator at typing time; the runtime
37+
* warning covers the payload-derived URLs no form validates.
38+
*/
39+
@WithJenkins
40+
class BitBucketPPRPluginConfigValidationTest {
41+
42+
private JenkinsRule j;
43+
44+
@BeforeEach
45+
void setUp(JenkinsRule rule) {
46+
j = rule;
47+
}
48+
49+
@Test
50+
void propagationUrlValidationWarnsOnPlainHttp() {
51+
BitBucketPPRPluginConfig config = BitBucketPPRPluginConfig.getInstance();
52+
53+
assertEquals(FormValidation.Kind.OK, config.doCheckPropagationUrl("").kind);
54+
assertEquals(FormValidation.Kind.OK,
55+
config.doCheckPropagationUrl("https://bitbucket.example.org/scm/space/repo.git").kind);
56+
57+
FormValidation http =
58+
config.doCheckPropagationUrl("http://bitbucket.example.org/scm/space/repo.git");
59+
assertEquals(FormValidation.Kind.WARNING, http.kind);
60+
assertTrue(http.getMessage().contains("https"),
61+
() -> "the warning must point at https, got: " + http.getMessage());
62+
63+
assertEquals(FormValidation.Kind.ERROR, config.doCheckPropagationUrl("not a url").kind);
64+
}
65+
}

0 commit comments

Comments
 (0)