From 7560bb5a5c25c152a566eb2edeee96bcef443500 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Aug 2023 18:08:10 +0100 Subject: [PATCH 001/305] Bump org.jenkins-ci.plugins:display-url-api from 2.3.8 to 2.3.9 (#8397) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index 11a2057e69ab..e94559ee386d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -95,7 +95,7 @@ THE SOFTWARE. org.jenkins-ci.plugins display-url-api - 2.3.8 + 2.3.9 From 2c28368086bfc0a2b5a8a07d5160e4a15a217a87 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sat, 19 Aug 2023 18:08:21 +0100 Subject: [PATCH 002/305] Remove 'track-mouse' JS (#8391) --- core/src/main/resources/lib/layout/pane.jelly | 2 +- .../main/webapp/scripts/hudson-behavior.js | 22 ------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/core/src/main/resources/lib/layout/pane.jelly b/core/src/main/resources/lib/layout/pane.jelly index 7e10c3d59d71..e7c4a04a8245 100644 --- a/core/src/main/resources/lib/layout/pane.jelly +++ b/core/src/main/resources/lib/layout/pane.jelly @@ -52,7 +52,7 @@ THE SOFTWARE. -
+
diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index 4197fa903207..7f682b1bc33e 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -1786,28 +1786,6 @@ function rowvgStartEachRow(recursive, f) { }, ); - Behaviour.specify(".track-mouse", "-track-mouse", ++p, function (element) { - var DOM = YAHOO.util.Dom; - - element.addEventListener("mouseenter", function () { - element.classList.add("mouseover"); - - var mousemoveTracker = function (event) { - var elementRegion = DOM.getRegion(element); - if ( - event.x < elementRegion.left || - event.x > elementRegion.right || - event.y < elementRegion.top || - event.y > elementRegion.bottom - ) { - element.classList.remove("mouseover"); - document.removeEventListener("mousemove", mousemoveTracker); - } - }; - document.addEventListener("mousemove", mousemoveTracker); - }); - }); - window.addEventListener("load", function () { // Add a class to the bottom bar when it's stuck to the bottom of the screen const el = document.querySelector("#bottom-sticker"); From 7ad6a020d319a982cedd65857f245246e34dc47e Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sat, 19 Aug 2023 18:08:35 +0100 Subject: [PATCH 003/305] Remove unused CSS classes (#8372) Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> --- war/src/main/scss/base/style.scss | 245 +----------------------------- 1 file changed, 1 insertion(+), 244 deletions(-) diff --git a/war/src/main/scss/base/style.scss b/war/src/main/scss/base/style.scss index ca452ac136a5..e1957b7f01c1 100644 --- a/war/src/main/scss/base/style.scss +++ b/war/src/main/scss/base/style.scss @@ -22,19 +22,6 @@ * THE SOFTWARE. */ -/* Panel */ -@media (width >= 1600px) { - body#jenkins.j-hide-left #main-panel { - max-width: 75%; - } -} - -@media (width >= 2000px) { - body#jenkins.j-hide-left #main-panel { - max-width: 85%; - } -} - form { margin: 0; } @@ -97,23 +84,6 @@ td.no-wrap { border-radius: var(--form-input-border-radius); } -/* #header .login { - position: relative; - top: 6px; - color: white; - margin-right: 10px; -} - -#header .login a, #header .login a:visited { - color: white; - text-decoration: none; -} - -#header .login a:hover { - text-decoration: underline; - color: #ccc; -} */ - a.lowkey:link { text-decoration: none; color: inherit; @@ -170,17 +140,6 @@ table.tab { border-collapse: collapse; } -td.selected_tab { - vertical-align: middle; - border: 1px #090 solid; - background: #fff; -} - -td.tab_filler { - background: #fff; - border-bottom: 1px #090 solid; -} - td.tab { vertical-align: middle; border: 1px #090 solid; @@ -230,10 +189,6 @@ pre.console { white-space: nowrap; } -.setting-checkbox { - font-weight: 500; -} - .setting-help { margin-left: 0.5rem; margin-right: 0.5rem; @@ -288,84 +243,15 @@ pre.console { flex-shrink: 1; } -/* div that looks like a hyperlink */ -.pseudoLink { - cursor: pointer; -} - .advancedBody { display: none; } -.scm_info { - width: 480px; -} - -.build-keep { - font-weight: bold; -} - -.task-header { - display: block; - border-bottom: 1px #090 solid; - font-weight: bold; - font-size: var(--font-size-xs); -} - .jenkins-not-applicable { color: darkgrey; font-style: italic; } -.smallfont { - font-size: 9px; -} - -#foldertab { - padding: 4px 0; - margin-left: 0; - border-bottom: 1px solid #090; - font: - bold 12px Helvetica, - Arial, - sans-serif; -} - -#foldertab li { - list-style: none; - margin: 0; - display: inline; -} - -#foldertab li a { - padding: 4px 0.5em; - margin-left: 3px; - border: 1px solid #090; - border-bottom: none; - background: #090; - text-decoration: none; -} - -#foldertab li a:link { - color: white; -} - -#foldertab li a:visited { - color: white; -} - -#foldertab li a:hover { - color: white; - background: #6c0; - border-color: #6c0; -} - -#foldertab li a#current { - background: white; - border-bottom: 1px solid white; - color: black; -} - .changeset-message { padding: 0.8rem 1rem; border-radius: 10px; @@ -382,25 +268,6 @@ pre.console { color: gray; } -#login-field { - vertical-align: middle; - padding-right: 1em; - width: 1px; -} - -#login-field span { - white-space: nowrap; - color: white; -} - -#login-field a { - /* - link inside login field should be always white. - If I set this to inherit, it won't work in IE7 - */ - color: white; -} - .bottom-sticker, #bottom-sticker { position: sticky; @@ -535,76 +402,6 @@ img.icon-help { vertical-align: text-top; } -/* ====================== project view tab bar ===================================== */ -#viewList { - border: none; - margin-bottom: 0; - width: 100%; - white-space: nowrap; -} - -#viewList td { - padding: 0; -} - -#viewList td.inactive { - border: solid 1px #ccc; - border-bottom-color: #bbb; -} - -#viewList td.inactive:hover { - background-color: #777; -} - -#viewList td.inactive a { - text-decoration: none; - color: #444; -} - -#viewList td.inactive a:hover { - color: #f1f1f1; -} - -#viewList td.noleft { - border-left: none; -} - -#viewList td.noright { - border-right: none; -} - -#viewList td.active { - border: solid 1px #bbb; - padding: 0.5em; - border-bottom: none; - vertical-align: middle; - background-color: rgb(240, 240, 240); - font-weight: bold; - white-space: nowrap; -} - -#viewList td.filler { - border: none; - border-bottom: solid 1px #bbb; - width: 100%; - text-align: right; -} - -#viewList a { - display: block; - padding: 0.5em; - white-space: nowrap; -} - -#projectstatus .header { - border-bottom: 1px solid var(--bigtable-header-border-color); -} - -#projectstatus > thead > th { - text-align: left; - // padding: 7px 0px; -} - /* ============================ list view entries ======================== */ div.listview-jobs { max-height: 300px; @@ -613,24 +410,6 @@ div.listview-jobs { padding-left: 15px; } -/* ============================ parameters form ========================== */ - -table.parameters { - border-collapse: collapse; -} - -table.parameters > tbody > tr:first-child > td { - padding-top: 4px; -} - -table.parameters .setting-description { - padding-bottom: 4px; -} - -table.parameters > tbody:hover { - background-color: var(--table-parameters-bg--hover); -} - /* ============================ health report hover ========================== */ .healthReport a { @@ -1051,24 +830,6 @@ textarea { resize: vertical; } -/* - * TODO(andipabst): Not used after https://github.com/jenkinsci/jenkins/pull/4299, - * remove once there are no more dependencies - */ -#plugins .compatWarning { - white-space: normal; - margin-top: 0.5em; - padding-left: 2em; - color: #f00; -} - -#plugins .securityWarning { - white-space: normal; - margin-top: 0.5em; - padding-left: 2em; - color: #f00; -} - /* ========================= progress bar ========================= */ table.progress-bar { @@ -1143,6 +904,7 @@ table.progress-bar.red td.progress-bar-done { } /* ========================= tags/labels ================== */ +// Used in core/src/main/java/hudson/util/TagCloud.java#getClassName /* tag0 is the least important tag in a tag cloud */ .tag0 { @@ -1265,11 +1027,6 @@ select.select-ajax-pending { } /* ========================= Button styles ================= */ -.btn-box { - display: block; - margin-top: 2em; -} - #disable-project { margin-top: 6px; } From 7f3fa048ca0c8094a552d02d5863c49fc06ea42a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Sat, 19 Aug 2023 13:08:49 -0400 Subject: [PATCH 004/305] [JENKINS-61452] Tolerate corrupt Base64 in `PlainTextConsoleOutputStream` (#8378) --- .../console/PlainTextConsoleOutputStream.java | 12 ++++++++- .../console/AnnotatedLargeTextTest.java | 26 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java b/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java index f3b5bd8adf7e..70bc85b7b110 100644 --- a/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java +++ b/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java @@ -28,6 +28,10 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.commons.lang.StringEscapeUtils; /** * Filters out console notes. @@ -36,6 +40,8 @@ */ public class PlainTextConsoleOutputStream extends LineTransformationOutputStream.Delegating { + private static final Logger LOGGER = Logger.getLogger(PlainTextConsoleOutputStream.class.getName()); + /** * */ @@ -64,7 +70,11 @@ protected void eol(byte[] in, int sz) throws IOException { int rest = sz - next; ByteArrayInputStream b = new ByteArrayInputStream(in, next, rest); - ConsoleNote.skip(new DataInputStream(b)); + try { + ConsoleNote.skip(new DataInputStream(b)); + } catch (IOException x) { + LOGGER.log(Level.FINE, "Failed to skip annotation from \"" + StringEscapeUtils.escapeJava(new String(in, next, rest, Charset.defaultCharset())) + "\"", x); + } int bytesUsed = rest - b.available(); // bytes consumed by annotations written += bytesUsed; diff --git a/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java b/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java index dcd3b96c3210..825bd2b60460 100644 --- a/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java +++ b/test/src/test/java/hudson/console/AnnotatedLargeTextTest.java @@ -27,6 +27,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.matchesRegex; import static org.junit.Assert.assertEquals; import hudson.MarkupText; @@ -52,7 +53,7 @@ public class AnnotatedLargeTextTest { public static JenkinsRule r = new JenkinsRule(); @Rule - public LoggerRule logging = new LoggerRule().record(ConsoleAnnotationOutputStream.class, Level.FINE).capture(100); + public LoggerRule logging = new LoggerRule().record(ConsoleAnnotationOutputStream.class, Level.FINE).record(PlainTextConsoleOutputStream.class, Level.FINE).capture(100); @Test public void smokes() throws Exception { @@ -138,6 +139,29 @@ public void badMac() throws Exception { + "AAA\\u001B[0myour home.\\n\"")); // TODO assert that this is IOException: MAC mismatch } + @Issue("JENKINS-61452") + @Test + public void corruptedNote() throws Exception { + ByteBuffer buf = new ByteBuffer(); + PrintStream ps = new PrintStream(buf, true, StandardCharsets.UTF_8); + ps.print("Some text.\n"); + ps.print("Go back to " + TestNote.encodeTo("/root", "your home") + ".\n"); + ps.print("More text.\n"); + String original = buf.toString(); + String corrupted = original.replace("+", "\u0000"); + buf = new ByteBuffer(); + buf.write(corrupted.getBytes()); + AnnotatedLargeText text = new AnnotatedLargeText<>(buf, StandardCharsets.UTF_8, true, null); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + text.writeLogTo(0, baos); + assertThat(baos.toString(StandardCharsets.UTF_8), matchesRegex("Some text[.]\nGo back to .*your home[.]\nMore text[.]\n")); + assertThat(logging.getMessages(), hasItem(matchesRegex("Failed to skip annotation from .+"))); + StringWriter w = new StringWriter(); + text.writeHtmlTo(0, w); + assertThat(w.toString(), matchesRegex("Some text[.]\nGo back to .*your home[.]\nMore text[.]\n")); + assertThat(logging.getMessages(), hasItem(matchesRegex("Failed to resurrect annotation from .+"))); + } + /** Simplified version of {@link HyperlinkNote}. */ static class TestNote extends ConsoleNote { private final String url; From b9cb7d4789d1116e6e02bd0ae27ec8706e082bad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 09:26:26 +0200 Subject: [PATCH 005/305] Update Yarn to v3.6.2 (#8401) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Alexander Brandes --- war/package.json | 2 +- war/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/war/package.json b/war/package.json index b17678f16c79..f81857f5701e 100644 --- a/war/package.json +++ b/war/package.json @@ -62,5 +62,5 @@ "defaults", "not IE 11" ], - "packageManager": "yarn@3.6.1" + "packageManager": "yarn@3.6.2" } diff --git a/war/pom.xml b/war/pom.xml index 963a3b0d50fc..809ecf86c968 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -50,8 +50,8 @@ THE SOFTWARE. 1.22.19 - 3.6.1 - 811210abb5fb5751da12ead8a9cbc0c150b07e43ac9cbedec6752d22abfd2bd6 + 3.6.2 + 506981787ad29ec22e9c904a0d630c3b03b2e5c078b074990404aabbf2fb007b From 7fb3cfe114ab7daa332546a82335298dae91fc5b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 09:26:43 +0200 Subject: [PATCH 006/305] Update dependency postcss-scss to v4.0.7 (#8400) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- war/package.json | 2 +- war/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/war/package.json b/war/package.json index f81857f5701e..0466a98c8b52 100644 --- a/war/package.json +++ b/war/package.json @@ -37,7 +37,7 @@ "postcss": "8.4.28", "postcss-loader": "7.3.3", "postcss-preset-env": "9.1.1", - "postcss-scss": "4.0.6", + "postcss-scss": "4.0.7", "prettier": "3.0.2", "sass": "1.65.1", "sass-loader": "13.3.2", diff --git a/war/yarn.lock b/war/yarn.lock index 59ebeeb428f1..69141cdd076d 100644 --- a/war/yarn.lock +++ b/war/yarn.lock @@ -4367,7 +4367,7 @@ __metadata: postcss: 8.4.28 postcss-loader: 7.3.3 postcss-preset-env: 9.1.1 - postcss-scss: 4.0.6 + postcss-scss: 4.0.7 prettier: 3.0.2 sass: 1.65.1 sass-loader: 13.3.2 @@ -6013,12 +6013,12 @@ __metadata: languageName: node linkType: hard -"postcss-scss@npm:4.0.6": - version: 4.0.6 - resolution: "postcss-scss@npm:4.0.6" +"postcss-scss@npm:4.0.7": + version: 4.0.7 + resolution: "postcss-scss@npm:4.0.7" peerDependencies: postcss: ^8.4.19 - checksum: 133a1cba31e2e167f4e841e66ec6a798eaf44c7911f9182ade0b5b1e71a8198814aa390b8c9d5db6b01358115232e5b15b1a4f8c5198acfccfb1f3fdbd328cdf + checksum: 579aa6807bf4934dd9bd3f8a257736c1121e189f2aa23853512e2a1be1005bac0136020d08e626998bcd3537080a1403f7a03a8cae277f3e0e314eb3c779db9c languageName: node linkType: hard From 70effdf2dd7935fda493cc9f88a857f1e6b592f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:49:56 -0700 Subject: [PATCH 007/305] Bump org.jenkins-ci.plugins:script-security from 1269.v639888f5e366 to 1271.vdede89739a_81 (#8408) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index e94559ee386d..5073b1052f2a 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -101,7 +101,7 @@ THE SOFTWARE. org.jenkins-ci.plugins script-security - 1269.v639888f5e366 + 1271.vdede89739a_81 From 0d9a2603ec8fe8b820ab77b0d3e9fc648c31c9cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:50:13 -0700 Subject: [PATCH 008/305] Bump org.jenkins-ci.main:jenkins-test-harness from 2053.v0ea_6fc5d99b_f to 2058.va_7b_41a_286207 (#8409) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pom.xml b/test/pom.xml index 5073b1052f2a..2440a09e1b19 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -116,7 +116,7 @@ THE SOFTWARE. ${project.groupId} jenkins-test-harness - 2053.v0ea_6fc5d99b_f + 2058.va_7b_41a_286207 test From 8abf635fe92eb1e2303d73d88a21c61f5aeb0bb4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:50:42 -0700 Subject: [PATCH 009/305] Update dependency sass to v1.66.0 (#8402) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- war/package.json | 2 +- war/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/war/package.json b/war/package.json index 0466a98c8b52..d44a45f8cd2c 100644 --- a/war/package.json +++ b/war/package.json @@ -39,7 +39,7 @@ "postcss-preset-env": "9.1.1", "postcss-scss": "4.0.7", "prettier": "3.0.2", - "sass": "1.65.1", + "sass": "1.66.0", "sass-loader": "13.3.2", "style-loader": "3.3.3", "stylelint": "15.10.2", diff --git a/war/yarn.lock b/war/yarn.lock index 69141cdd076d..432c19b91922 100644 --- a/war/yarn.lock +++ b/war/yarn.lock @@ -4369,7 +4369,7 @@ __metadata: postcss-preset-env: 9.1.1 postcss-scss: 4.0.7 prettier: 3.0.2 - sass: 1.65.1 + sass: 1.66.0 sass-loader: 13.3.2 sortablejs: 1.15.0 style-loader: 3.3.3 @@ -6412,16 +6412,16 @@ __metadata: languageName: node linkType: hard -"sass@npm:1.65.1": - version: 1.65.1 - resolution: "sass@npm:1.65.1" +"sass@npm:1.66.0": + version: 1.66.0 + resolution: "sass@npm:1.66.0" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 33e325fc80cd07489992e0814cd4929496f87493ffe78c423c2dbafa5746a574e6f3bde20c2a3e4ea47b16ee3d6bc5afcf1d36b405227b829d6c4c9ddcc7f8e2 + checksum: ae292e6a41a8812c0206c528885969e2f6f35870397e6d5bc33477927fd09faffda196aefe2cfe49e0c57f1448129b522f845bb134f3b6a131b184e3be86cf92 languageName: node linkType: hard From a4589ec6d86b7b08794da7e4c9714ccdc0864eff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:51:21 -0700 Subject: [PATCH 010/305] Bump org.apache.ant:ant from 1.10.13 to 1.10.14 (#8405) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/pom.xml b/bom/pom.xml index 537d08f11a36..f2373994789c 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -189,7 +189,7 @@ THE SOFTWARE. org.apache.ant ant - 1.10.13 + 1.10.14 org.apache.commons From 7a783c3e175543db8b94128b760ff95b484fbaac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:52:11 -0700 Subject: [PATCH 011/305] Bump org.springframework.security:spring-security-bom from 5.8.5 to 5.8.6 (#8407) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/pom.xml b/bom/pom.xml index f2373994789c..7f0162f63cf5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -64,7 +64,7 @@ THE SOFTWARE. org.springframework.security spring-security-bom - 5.8.5 + 5.8.6 pom import From e9886db619e511a5acf8a873d51b13591dcf9c47 Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon, 21 Aug 2023 22:23:41 +0100 Subject: [PATCH 012/305] Move app bar for plugins (#8376) * Move app bar for plugins, make sidebar sticky * Update _updateSite.js * Remove sticky side panel --- core/src/main/resources/hudson/PluginManager/available.jelly | 2 -- core/src/main/resources/hudson/PluginManager/installed.jelly | 2 -- core/src/main/resources/hudson/PluginManager/sidepanel.jelly | 1 + core/src/main/resources/hudson/PluginManager/updates.jelly | 2 -- war/src/main/scss/form/search-bar.scss | 1 + 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/main/resources/hudson/PluginManager/available.jelly b/core/src/main/resources/hudson/PluginManager/available.jelly index 212deb493567..b0fa8f8fe5a3 100644 --- a/core/src/main/resources/hudson/PluginManager/available.jelly +++ b/core/src/main/resources/hudson/PluginManager/available.jelly @@ -32,8 +32,6 @@ THE SOFTWARE. - -
- - diff --git a/core/src/main/resources/hudson/PluginManager/sidepanel.jelly b/core/src/main/resources/hudson/PluginManager/sidepanel.jelly index 6c45382c9e03..21755b32072d 100644 --- a/core/src/main/resources/hudson/PluginManager/sidepanel.jelly +++ b/core/src/main/resources/hudson/PluginManager/sidepanel.jelly @@ -28,6 +28,7 @@ THE SOFTWARE. + diff --git a/core/src/main/resources/hudson/PluginManager/updates.jelly b/core/src/main/resources/hudson/PluginManager/updates.jelly index 6bf1982d89fe..25402731273b 100644 --- a/core/src/main/resources/hudson/PluginManager/updates.jelly +++ b/core/src/main/resources/hudson/PluginManager/updates.jelly @@ -35,8 +35,6 @@ THE SOFTWARE. - -
Date: Mon, 21 Aug 2023 23:25:19 +0200 Subject: [PATCH 013/305] [JENKINS-71848] Remove admin monitors popup from `/manage/` again (#8388) [JENKINS-71848] Remove admin monitors popup from /manage/ again Co-authored-by: Daniel Beck --- .../management/AdministrativeMonitorsDecorator.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java b/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java index eb9685f54ba0..cc1d5214e0ae 100644 --- a/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java +++ b/core/src/main/java/jenkins/management/AdministrativeMonitorsDecorator.java @@ -28,6 +28,7 @@ import hudson.Extension; import hudson.diagnosis.ReverseProxySetupMonitor; import hudson.model.AdministrativeMonitor; +import hudson.model.ManageJenkinsAction; import hudson.model.PageDecorator; import hudson.util.HudsonIsLoading; import hudson.util.HudsonIsRestarting; @@ -53,9 +54,6 @@ public class AdministrativeMonitorsDecorator extends PageDecorator { private final Collection ignoredJenkinsRestOfUrls = new ArrayList<>(); public AdministrativeMonitorsDecorator() { - // redundant - ignoredJenkinsRestOfUrls.add("manage"); - // otherwise this would be added to every internal context menu building request ignoredJenkinsRestOfUrls.add("contextMenu"); @@ -165,6 +163,11 @@ public Collection getMonitorsToDisplay() { return null; } + // Don't show on Manage Jenkins + if (o instanceof ManageJenkinsAction) { + return null; + } + // don't show for some URLs served directly by Jenkins if (o instanceof Jenkins) { String url = a.getRestOfUrl(); From c1a6c6c9ddf432e8eae3db68d3ee13c1f762d785 Mon Sep 17 00:00:00 2001 From: Markus Winter Date: Mon, 21 Aug 2023 23:27:55 +0200 Subject: [PATCH 014/305] Replace hetero-list YUI button and menu with new style button and tippy.js menu (#8340) * refresh hetero-list UI use new button style replace the YUI menu with a tippy menu * fix lint errors * avoid blanks * prettier * keep button in div * fix test * fix test #2 * convert inputs to buttons for plugins * prettier * remove unnecessary style definition * remove adjunct * adjust filter --------- Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> --- .../filter-menu-button/filter-menu-button.js | 70 ----- .../main/resources/lib/form/hetero-list.jelly | 5 +- .../lib/form/hetero-list/hetero-list.js | 201 ------------- .../test/java/lib/form/HeteroListTest.java | 8 +- .../js/components/dropdowns/hetero-list.js | 276 ++++++++++++++++++ war/src/main/js/components/dropdowns/index.js | 2 + .../main/js/components/dropdowns/templates.js | 9 +- war/src/main/js/components/dropdowns/utils.js | 9 +- war/src/main/js/util/keyboard.js | 50 +++- war/src/main/js/util/symbols.js | 2 + war/src/main/scss/modules/buttons.scss | 3 +- war/src/main/scss/modules/dropdowns.scss | 46 ++- 12 files changed, 389 insertions(+), 292 deletions(-) delete mode 100644 core/src/main/resources/lib/form/filter-menu-button/filter-menu-button.js delete mode 100644 core/src/main/resources/lib/form/hetero-list/hetero-list.js create mode 100644 war/src/main/js/components/dropdowns/hetero-list.js diff --git a/core/src/main/resources/lib/form/filter-menu-button/filter-menu-button.js b/core/src/main/resources/lib/form/filter-menu-button/filter-menu-button.js deleted file mode 100644 index e81ccc43fe7a..000000000000 --- a/core/src/main/resources/lib/form/filter-menu-button/filter-menu-button.js +++ /dev/null @@ -1,70 +0,0 @@ -// prettier-ignore -window.createFilterMenuButton = function ( - button, - menu, - menuAlignment, - menuMinScrollHeight -) { - var MIN_NUM_OPTIONS = 5; - var menuButton = new YAHOO.widget.Button(button, { - type: "menu", - menu: menu, - menualignment: menuAlignment, - menuminscrollheight: menuMinScrollHeight, - }); - - var filter = _createFilterMenuButton(menuButton._menu); - - menuButton._menu.element.appendChild(filter); - menuButton._menu.showEvent.subscribe(function () { - _applyFilterKeyword(menuButton._menu, filter.firstElementChild); - filter.style.display = - _getItemList(menuButton._menu).children.length >= MIN_NUM_OPTIONS - ? "" - : "NONE"; - }); - menuButton._menu.setInitialFocus = function () { - setTimeout(function () { - filter.firstElementChild.focus(); - }, 0); - }; - - return menuButton; -}; - -function _createFilterMenuButton(menu) { - const filterInput = document.createElement("input"); - filterInput.classList.add("jenkins-input"); - filterInput.setAttribute("placeholder", "Filter"); - filterInput.setAttribute("spellcheck", "false"); - filterInput.setAttribute("type", "search"); - - filterInput.addEventListener("input", (event) => - _applyFilterKeyword(menu, event.currentTarget), - ); - filterInput.addEventListener("keypress", (event) => { - if (event.key === "Enter") { - event.preventDefault(); - } - }); - - const filterContainer = document.createElement("div"); - filterContainer.appendChild(filterInput); - - return filterContainer; -} - -function _applyFilterKeyword(menu, filterInput) { - const filterKeyword = (filterInput.value || "").toLowerCase(); - const itemList = _getItemList(menu); - let item, match; - for (item of itemList.children) { - match = item.innerText.toLowerCase().includes(filterKeyword); - item.style.display = match ? "" : "NONE"; - } - menu.align(); -} - -function _getItemList(menu) { - return menu.body.children[0]; -} diff --git a/core/src/main/resources/lib/form/hetero-list.jelly b/core/src/main/resources/lib/form/hetero-list.jelly index 66fe2dd91773..b817c936c4e8 100644 --- a/core/src/main/resources/lib/form/hetero-list.jelly +++ b/core/src/main/resources/lib/form/hetero-list.jelly @@ -107,8 +107,6 @@ THE SOFTWARE. - -
@@ -156,7 +154,8 @@ THE SOFTWARE.
- +
diff --git a/core/src/main/resources/lib/form/hetero-list/hetero-list.js b/core/src/main/resources/lib/form/hetero-list/hetero-list.js deleted file mode 100644 index a51423bf6e0b..000000000000 --- a/core/src/main/resources/lib/form/hetero-list/hetero-list.js +++ /dev/null @@ -1,201 +0,0 @@ -// @include lib.form.filter-menu-button.filter-menu-button - -// do the ones that extract innerHTML so that they can get their original HTML before -// other behavior rules change them (like YUI buttons.) -Behaviour.specify( - "DIV.hetero-list-container", - "hetero-list", - -100, - function (e) { - if (isInsideRemovable(e)) { - return; - } - - // components for the add button - var menu = document.createElement("SELECT"); - // In case nested content also uses hetero-list - var btn = Array.from(e.querySelectorAll("INPUT.hetero-list-add")).pop(); - if (!btn) { - return; - } - YAHOO.util.Dom.insertAfter(menu, btn); - - var prototypes = e.lastElementChild; - while (!prototypes.classList.contains("prototypes")) { - prototypes = prototypes.previousElementSibling; - } - var insertionPoint = prototypes.previousElementSibling; // this is where the new item is inserted. - - // extract templates - var templates = []; - var children = prototypes.children; - for (var i = 0; i < children.length; i++) { - var n = children[i]; - var name = n.getAttribute("name"); - var tooltip = n.getAttribute("tooltip"); - var descriptorId = n.getAttribute("descriptorId"); - // YUI Menu interprets this
-

General usage statistics

-
-

Jenkins reports the following general usage statistics:

-
    -
  • Your Jenkins version
  • -
  • For your controller and each agent, the OS type and number of executors
  • -
  • Installed plugins and their versions
  • -
  • Number of items (like jobs) of each item type
  • -
-

- This does not report any personally identifiable information. The only information reported by Jenkins is information inherently revealed by the HTTP protocol, such as the IP address. - - These usage statistics are aggregated, updated monthly, and published to stats.jenkins.io -

-
+ + +

General usage statistics

+
+

Jenkins reports the following general usage statistics:

+
    +
  • Your Jenkins version
  • +
  • For your controller and each agent, the OS type and number of executors
  • +
  • Installed plugins and their versions
  • +
  • Number of items (like jobs) of each item type
  • +
+

+ This does not report any personally identifiable information. + The only information reported by Jenkins is information inherently revealed by the HTTP protocol, such as the IP address. + These usage statistics are aggregated, updated monthly, and published to stats.jenkins.io +

+
+

Telemetry collection

- In addition to the general usage statistics listed above, the Jenkins project collects telemetry data from specific trials to inform future development. + + + The Jenkins project collects telemetry data from specific trials to inform future development. + + + In addition to the general usage statistics listed above, the Jenkins project collects telemetry data from specific trials to inform future development. + + Each trial has a specific purpose and a defined end date, after which collection stops, independent of the installed versions of Jenkins or plugins. Once a trial is complete, the trial results may be aggregated and shared with the developer community.

From 012fa004515ec9f925fb022ddff9a4a7f2e3491b Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Thu, 5 Oct 2023 12:41:04 +0200 Subject: [PATCH 170/305] [JENKINS-71252][JENKINS-70793] Multiple form validation fixes (#8422) * [JENKINS-71252] Show form validation for hidden elements * [JENKINS-70793] Clear all validation output on update --------- Co-authored-by: Daniel Beck --- war/src/main/webapp/scripts/hudson-behavior.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js index cd35a3cd6032..7284a14e2ba1 100644 --- a/war/src/main/webapp/scripts/hudson-behavior.js +++ b/war/src/main/webapp/scripts/hudson-behavior.js @@ -639,17 +639,7 @@ function updateValidationArea(validationArea, content) { // Only change content if different, causes an unnecessary animation otherwise if (validationArea.innerHTML !== content) { validationArea.innerHTML = content; - validationArea.style.height = - validationArea.children[0].offsetHeight + "px"; - - // Only include the notice in the validation-error-area, move all other elements out - if (validationArea.children.length > 1) { - Array.from(validationArea.children) - .slice(1) - .forEach((element) => { - validationArea.after(element); - }); - } + validationArea.style.height = "auto"; Behaviour.applySubtree(validationArea); // For errors with additional details, apply the subtree to the expandable details pane From 32834c5cf7d75f656639d9543f80e878fd7c0a6c Mon Sep 17 00:00:00 2001 From: Divya S <130471287+divyasivasamy@users.noreply.github.com> Date: Thu, 5 Oct 2023 19:33:00 +0530 Subject: [PATCH 171/305] [JENKINS-71971][JEP-237] FIPS-140 compliant version of HudsonPrivateSecurityRealm (#8393) Implement a password storage using PBKDF2 when in FIPS mode for HudsonPrivateSecurityRealm Co-authored-by: James Nord Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> --- .../security/HudsonPrivateSecurityRealm.java | 155 ++++++++++++++++-- .../hudson/security/PasswordHashEncoder.java | 30 ++++ .../HudsonPrivateSecurityRealmTest.java | 146 +++++++++++++++++ .../HudsonPrivateSecurityRealmFIPSTest.java | 133 +++++++++++++++ .../HudsonPrivateSecurityRealmTest.java | 47 ++++++ 5 files changed, 495 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java/hudson/security/PasswordHashEncoder.java create mode 100644 core/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java create mode 100644 test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index fb93f116163f..5d78c8887c8f 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -50,6 +50,9 @@ import java.lang.reflect.Constructor; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -57,9 +60,12 @@ import java.util.List; import java.util.Random; import java.util.Set; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -70,6 +76,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import jenkins.model.Jenkins; +import jenkins.security.FIPS140; import jenkins.security.SecurityListener; import jenkins.security.seed.UserSeedProperty; import jenkins.util.SystemProperties; @@ -524,14 +531,23 @@ public User createAccount(String userName, String password) throws IOException { } /** - * Creates a new user account by registering a JBCrypt Hashed password with the user. + * Creates a new user account by registering a Hashed password with the user. * * @param userName The user's name - * @param hashedPassword A hashed password, must begin with {@code #jbcrypt:} + * @param hashedPassword A hashed password, must begin with {@code getPasswordHeader()} + * @see #getPasswordHeader() */ public User createAccountWithHashedPassword(String userName, String hashedPassword) throws IOException { if (!PASSWORD_ENCODER.isPasswordHashed(hashedPassword)) { - throw new IllegalArgumentException("this method should only be called with a pre-hashed password"); + final String message; + if (hashedPassword == null) { + message = "The hashed password cannot be null"; + } else if (hashedPassword.startsWith(getPasswordHeader())) { + message = "The hashed password was hashed with the correct algorithm, but the format was not correct"; + } else { + message = "The hashed password was hashed with an incorrect algorithm. Jenkins is expecting " + getPasswordHeader(); + } + throw new IllegalArgumentException(message); } User user = User.getById(userName, true); user.addProperty(Details.fromHashedPassword(hashedPassword)); @@ -885,9 +901,9 @@ public Category getCategory() { // TODO can we instead use BCryptPasswordEncoder from Spring Security, which has its own copy of BCrypt so we could drop the special library? /** - * {@link PasswordEncoder} that uses jBCrypt. + * {@link PasswordHashEncoder} that uses jBCrypt. */ - private static class JBCryptEncoder implements PasswordEncoder { + static class JBCryptEncoder implements PasswordHashEncoder { // in jBCrypt the maximum is 30, which takes ~22h with laptop late-2017 // and for 18, it's "only" 20s @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") @@ -911,6 +927,7 @@ public boolean matches(CharSequence rawPassword, String encodedPassword) { * implementation defined in jBCrypt and the Wikipedia page. * */ + @Override public boolean isHashValid(String hash) { Matcher matcher = BCRYPT_PATTERN.matcher(hash); if (matcher.matches()) { @@ -925,34 +942,131 @@ public boolean isHashValid(String hash) { } } - /* package */ static final JBCryptEncoder JBCRYPT_ENCODER = new JBCryptEncoder(); + static class PBKDF2PasswordEncoder implements PasswordHashEncoder { + + private static final String STRING_SEPARATION = ":"; + private static final int KEY_LENGTH_BITS = 512; + private static final int SALT_LENGTH_BYTES = 16; + // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 + // ~230ms on an Intel i7-10875H CPU (JBCryptEncoder is ~57ms) + private static final int ITTERATIONS = 210_000; + private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA512"; + + private volatile SecureRandom random; // defer construction until we need to use it to not delay startup in the case of lack of entropy. + + // $PBDKF2 is already checked before we get here. + // $algorithm(HMACSHA512) : rounds : salt_in_hex $ mac_in_hex + private static final Pattern PBKDF2_PATTERN = + Pattern.compile("^\\$HMACSHA512\\:" + ITTERATIONS + "\\:[a-f0-9]{" + (SALT_LENGTH_BYTES * 2) + "}\\$[a-f0-9]{" + ((KEY_LENGTH_BITS / 8) * 2) + "}$"); + + @Override + public String encode(CharSequence rawPassword) { + try { + return generatePasswordHashWithPBKDF2(rawPassword); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException("Unable to generate password with PBKDF2WithHmacSHA512", e); + } + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + try { + return validatePassword(rawPassword.toString(), encodedPassword); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException("Unable to check password with PBKDF2WithHmacSHA512", e); + } + } + + private String generatePasswordHashWithPBKDF2(CharSequence password) throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] salt = generateSalt(); + PBEKeySpec spec = new PBEKeySpec(password.toString().toCharArray(), salt, ITTERATIONS, KEY_LENGTH_BITS); + byte[] hash = generateSecretKey(spec); + return "$HMACSHA512:" + ITTERATIONS + STRING_SEPARATION + Util.toHexString(salt) + "$" + Util.toHexString(hash); + } + + private static byte[] generateSecretKey(PBEKeySpec spec) throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); + return secretKeyFactory.generateSecret(spec).getEncoded(); + } + + private SecureRandom secureRandom() { + // lazy initialisation so that we do not block startup due to entropy + if (random == null) { + synchronized (this) { + if (random == null) { + random = new SecureRandom(); + } + } + } + return random; + } + + private byte[] generateSalt() { + byte[] salt = new byte[SALT_LENGTH_BYTES]; + secureRandom().nextBytes(salt); + return salt; + } + + @Override + public boolean isHashValid(String hash) { + Matcher matcher = PBKDF2_PATTERN.matcher(hash); + return matcher.matches(); + } + + private static boolean validatePassword(String password, String storedPassword) throws NoSuchAlgorithmException, InvalidKeySpecException { + String[] parts = storedPassword.split("[:$]"); + int iterations = Integer.parseInt(parts[2]); + + byte[] salt = Util.fromHexString(parts[3]); + byte[] hash = Util.fromHexString(parts[4]); + + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), + salt, iterations, hash.length * 8 /* bits in a byte */); + + byte[] generatedHashValue = generateSecretKey(spec); + + return MessageDigest.isEqual(hash, generatedHashValue); + } + + } + + /* package */ static final PasswordHashEncoder PASSWORD_HASH_ENCODER = FIPS140.useCompliantAlgorithms() ? new PBKDF2PasswordEncoder() : new JBCryptEncoder(); + + + private static final String PBKDF2 = "$PBKDF2"; + private static final String JBCRYPT = "#jbcrypt:"; + + /** + * Magic header used to detect if a password is hashed. + */ + private static String getPasswordHeader() { + return FIPS140.useCompliantAlgorithms() ? PBKDF2 : JBCRYPT; + } + // TODO check if DelegatingPasswordEncoder can be used /** - * Wraps {@link #JBCRYPT_ENCODER}. + * Wraps {@link #PASSWORD_HASH_ENCODER}. * There used to be a SHA-256-based encoder but this is long deprecated, and insecure anyway. */ /* package */ static class MultiPasswordEncoder implements PasswordEncoder { - /** - * Magic header used to detect if a password is bcrypt hashed. - */ - private static final String JBCRYPT_HEADER = "#jbcrypt:"; /* CLASSIC encoder outputs "salt:hash" where salt is [a-z]+, so we use unique prefix '#jbcyrpt" - to designate JBCRYPT-format hash. + to designate JBCRYPT-format hash and $PBKDF2 to designate PBKDF2 format hash. '#' is neither in base64 nor hex, which makes it a good choice. */ + @Override public String encode(CharSequence rawPassword) { - return JBCRYPT_HEADER + JBCRYPT_ENCODER.encode(rawPassword); + return getPasswordHeader() + PASSWORD_HASH_ENCODER.encode(rawPassword); } @Override public boolean matches(CharSequence rawPassword, String encPass) { if (isPasswordHashed(encPass)) { - return JBCRYPT_ENCODER.matches(rawPassword, encPass.substring(JBCRYPT_HEADER.length())); + return PASSWORD_HASH_ENCODER.matches(rawPassword, encPass.substring(getPasswordHeader().length())); } else { return false; } @@ -965,9 +1079,18 @@ public boolean isPasswordHashed(String password) { if (password == null) { return false; } - return password.startsWith(JBCRYPT_HEADER) && JBCRYPT_ENCODER.isHashValid(password.substring(JBCRYPT_HEADER.length())); + if (password.startsWith(getPasswordHeader())) { + return PASSWORD_HASH_ENCODER.isHashValid(password.substring(getPasswordHeader().length())); + } + if (password.startsWith(FIPS140.useCompliantAlgorithms() ? JBCRYPT : PBKDF2)) { + // switch the header to see if this is using a different encryption + LOGGER.log(Level.WARNING, "A password appears to be stored (or is attempting to be stored) that was created with a different" + + " hashing/encryption algorithm, check the FIPS-140 state of the system has not changed inadvertently"); + } else { + LOGGER.log(Level.FINE, "A password appears to be stored (or is attempting to be stored) that is not hashed/encrypted."); + } + return false; } - } public static final MultiPasswordEncoder PASSWORD_ENCODER = new MultiPasswordEncoder(); diff --git a/core/src/main/java/hudson/security/PasswordHashEncoder.java b/core/src/main/java/hudson/security/PasswordHashEncoder.java new file mode 100644 index 000000000000..14c0da93ba91 --- /dev/null +++ b/core/src/main/java/hudson/security/PasswordHashEncoder.java @@ -0,0 +1,30 @@ +/* + * The MIT License + * + * Copyright (c) 2023, Cloudbees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.security; + +import org.springframework.security.crypto.password.PasswordEncoder; + +interface PasswordHashEncoder extends PasswordEncoder { + boolean isHashValid(String hash); +} diff --git a/core/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java b/core/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java new file mode 100644 index 000000000000..9d7e2b7b5980 --- /dev/null +++ b/core/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java @@ -0,0 +1,146 @@ +package hudson.security; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import hudson.security.HudsonPrivateSecurityRealm.JBCryptEncoder; +import hudson.security.HudsonPrivateSecurityRealm.PBKDF2PasswordEncoder; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.time.Duration; +import javax.crypto.SecretKeyFactory; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class HudsonPrivateSecurityRealmTest { + + // MySecurePassword + private static final String PBKDF2_HMAC_SHA512_ENCODED_PASSWORD = + "$HMACSHA512:210000:30f9e0a5470a8bc67f128ca1aae25dd4$88abaca4f442caeff0096ec0f75df2d77cc31a956c564133232f4d2532a72c8d4380a718d5b2a3dccab9e752027eeadd8f9f2c0c624505531bf3a57ec7d08aad"; + + /* + * This exists so that we can easily check the complexity of how long this takes (ie is the number of iterations we + * use correct for the state of CPUs). + * We do not want to assert that the range < x and > y as that would make the test flaky on overloaded + * or slow hardware, so this is commented out but left for ease of running locally when desired. + */ + //@Test + public void timingPBKDF2() { + // ignore the salt generation - check just matching.... + PBKDF2PasswordEncoder encoder = new PBKDF2PasswordEncoder(); + String encoded = encoder.encode("thisIsMyPassword1"); + + long start = System.nanoTime(); + for (int i = 0; i < 10; i++) { + System.out.println(encoder.matches("thisIsMyPassword" + i, encoded)); + } + long end = System.nanoTime(); + long duration = end - start; + long duration_per_iteration = duration / 10; + + Duration d = Duration.ofNanos(duration_per_iteration); + System.out.println("PBKDF2 took " + d.toNanos() + "ns"); + System.out.println("PBKDF2 took " + d.toMillis() + "ms"); + System.out.println("PBKDF2 took " + d.toSeconds() + "s"); + } + + /* + * This exists so that we can easily check the complexity of how long this takes (ie is the number of iterations we + * use correct for the state of CPUs). + * We do not want to assert that the range < x and > y as that would make the test flaky on overloaded + * or slow hardware, so this is commented out but left for ease of running locally when desired. + */ + //@Test + public void timingBcrypt() { + // ignore the salt generation - check just matching.... + JBCryptEncoder encoder = new JBCryptEncoder(); + String encoded = encoder.encode("thisIsMyPassword1"); + + long start = System.nanoTime(); + for (int i = 0; i < 10; i++) { + System.out.println(encoder.matches("thisIsMyPassword" + i, encoded)); + } + long end = System.nanoTime(); + long duration = end - start; + long duration_per_iteration = duration / 10; + + Duration d = Duration.ofNanos(duration_per_iteration); + System.out.println("BCrypt took " + d.toNanos() + "ns"); + System.out.println("BCrypt took " + d.toMillis() + "ms"); + System.out.println("BCrypt took " + d.toSeconds() + "s"); + } + + @Test + public void testPBKDF2RegExp() { + PBKDF2PasswordEncoder encoder = new PBKDF2PasswordEncoder(); + String encoded = encoder.encode("thisIsMyPassword"); + assertTrue(encoder.isHashValid(encoded)); + + // and a static one for other tests... + assertTrue(encoder.isHashValid(PBKDF2_HMAC_SHA512_ENCODED_PASSWORD)); + + assertFalse(encoder.isHashValid( + "$HMACSHA512:1000:fe899fcfcef4302ec3f0d36164efefdc$0781364eae9dac4ef1c4c3bf34c28e13965b46105fec0b6fcf4bae78e246fb5e51a1694fff19acac2dfb37b16055092644f3682c25beea9a7a286bf94e52f63b"), + "not enough iterations"); + assertFalse(encoder.isHashValid( + "$HMACSHA512:500000:fe899fcfcef4302ec3f0d36164efefdc$0781364eae9dac4ef1c4c3bf34c28e13965b46105fec0b6fcf4bae78e246fb5e51a1694fff19acac2dfb37b16055092644f3682c25beea9a7a286bf94e52f63b"), + "too many iterations"); + assertFalse(encoder.isHashValid( + "$HMACSHA512:210000:fe899fcfcef4302ec3f0d36164efef$0781364eae9dac4ef1c4c3bf34c28e13965b46105fec0b6fcf4bae78e246fb5e51a1694fff19acac2dfb37b16055092644f3682c25beea9a7a286bf94e52f63b"), + "salt too short"); + assertFalse(encoder.isHashValid( + "$HMACSHA512:210000:f6865c02cc759fd061db0f3121a093e094$0781364eae9dac4ef1c4c3bf34c28e13965b46105fec0b6fcf4bae78e246fb5e51a1694fff19acac2dfb37b16055092644f3682c25beea9a7a286bf94e52f63b"), + "salt too long"); + assertFalse(encoder.isHashValid( + "$HMACSHA512:210000:f6865c02cc759fd061db0f3121a093e094$079bd3a0c2851248343584a9a4625360e9ebb13c36be49542268d2ebdbd1fb71f004db9ce7335a61885985e32e08cb20215f"), + "hash result too short"); + assertFalse(encoder.isHashValid( + "$HMACSHA512:210000:f6865c02cc759fd061db0f3121a093e094$0781364eae9dac4ef1c4c3bf34c28e13965b46105fec0b6fcf4bae78e246fb5e51a1694fff19acac2dfb37b16055092644f3682c25beea9a7a286bf94e52f63b42662"), + "hash result too long"); + assertFalse(encoder.isHashValid( + "$HMACSHA256:210000:f6865c02cc759fd061db0f3121a093e094$0781364eae9dac4ef1c4c3bf34c28e13965b46105fec0b6fcf4bae78e246fb5e51a1694fff19acac2dfb37b16055092644f3682c25beea9a7a286bf94e52f63b42662"), + "wrong format"); + assertFalse(encoder.isHashValid( + "::$sfdfssdf"), + "wrong format"); + + } + + @Test + public void testPBKDF2PasswordMatching() { + PBKDF2PasswordEncoder encoder = new PBKDF2PasswordEncoder(); + String encoded = encoder.encode("thisIsMyPassword"); + assertTrue(encoder.matches("thisIsMyPassword", encoded)); + assertFalse(encoder.matches("thisIsNotMyPassword", encoded)); + } + + @Test + public void passwordPBKDF2WithMissingAgorithm() throws Exception { + HudsonPrivateSecurityRealm.PBKDF2PasswordEncoder pbkdf2PasswordEncoder = new HudsonPrivateSecurityRealm.PBKDF2PasswordEncoder(); + try (var ignored = mockStatic(SecretKeyFactory.class)) { + when(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")).thenThrow(NoSuchAlgorithmException.class); + assertThrows(RuntimeException.class, () -> pbkdf2PasswordEncoder.encode("password")); + + assertTrue(pbkdf2PasswordEncoder.isHashValid(PBKDF2_HMAC_SHA512_ENCODED_PASSWORD)); + assertThrows(RuntimeException.class, () -> pbkdf2PasswordEncoder.matches("MySecurePassword", PBKDF2_HMAC_SHA512_ENCODED_PASSWORD)); + } + } + + @Test + public void passwordPBKDF2HashWithInvalidKeySpec() throws Exception { + HudsonPrivateSecurityRealm.PBKDF2PasswordEncoder pbkdf2PasswordEncoder = new HudsonPrivateSecurityRealm.PBKDF2PasswordEncoder(); + try (var ignored = mockStatic(SecretKeyFactory.class)) { + SecretKeyFactory skf = mock(SecretKeyFactory.class); + when(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")).thenReturn(skf); + when(skf.generateSecret(Mockito.any())).thenThrow(InvalidKeySpecException.class); + assertThrows(RuntimeException.class, () -> pbkdf2PasswordEncoder.encode("password")); + + assertTrue(pbkdf2PasswordEncoder.isHashValid(PBKDF2_HMAC_SHA512_ENCODED_PASSWORD)); + assertThrows(RuntimeException.class, () -> pbkdf2PasswordEncoder.matches("MySecurePassword", PBKDF2_HMAC_SHA512_ENCODED_PASSWORD)); + } + } +} diff --git a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java new file mode 100644 index 000000000000..155e9cab1adc --- /dev/null +++ b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java @@ -0,0 +1,133 @@ +/* + * The MIT License + * + * Copyright (c) 2015, CloudBees, Inc. and others + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThrows; + +import hudson.model.User; +import hudson.security.HudsonPrivateSecurityRealm.Details; +import java.util.logging.Level; +import org.hamcrest.Matcher; +import org.htmlunit.FailingHttpStatusCodeException; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.jvnet.hudson.test.FlagRule; +import org.jvnet.hudson.test.For; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.JenkinsRule.WebClient; +import org.jvnet.hudson.test.LoggerRule; + + +@For(HudsonPrivateSecurityRealm.class) +public class HudsonPrivateSecurityRealmFIPSTest { + + // the jbcrypt encoded for of "a" without the quotes + private static final String JBCRYPT_ENCODED_PASSWORD = "#jbcrypt:$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"; + + @ClassRule + // do not use the FIPS140 class here as that initializes the field before we set the property! + public static TestRule flagRule = FlagRule.systemProperty("jenkins.security.FIPS140.COMPLIANCE", "true"); + + @Rule + public LoggerRule lr = new LoggerRule().record(HudsonPrivateSecurityRealm.class, Level.WARNING).capture(5); + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + public void generalLogin() throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + + User u1 = securityRealm.createAccount("user", "password"); + u1.setFullName("A User"); + u1.save(); + + // we should be using PBKDF2 hasher + String hashedPassword = u1.getProperty(Details.class).getPassword(); + assertThat(hashedPassword, startsWith("$PBKDF2$HMACSHA512:210000:")); + + WebClient wc = j.createWebClient(); + wc.login("user", "password"); + + assertThrows(FailingHttpStatusCodeException.class, () -> wc.login("user", "wrongPass")); + } + + @Test + public void userCreationWithHashedPasswords() throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + // "password" after it has gone through the KDF + securityRealm.createAccountWithHashedPassword("user_hashed", + "$PBKDF2$HMACSHA512:210000:ffbb207b847010af98cdd2b09c79392c$f67c3b985daf60db83a9088bc2439f7b77016d26c1439a9877c4f863c377272283ce346edda4578a5607ea620a4beb662d853b800f373297e6f596af797743a6"); + WebClient wc = j.createWebClient(); + + // login should succeed + wc.login("user_hashed", "password"); + + assertThrows(FailingHttpStatusCodeException.class, () -> wc.login("user_hashed", "password2")); + assertThat(lr, not(hasIncorrectHashingLogEntry())); + } + + @Test + public void userLoginAfterEnablingFIPS() throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + + User u1 = securityRealm.createAccount("user", "a"); + u1.setFullName("A User"); + // overwrite the password property using an password created using an incorrect algorithm + u1.addProperty(Details.fromHashedPassword(JBCRYPT_ENCODED_PASSWORD)); + + u1.save(); + assertThat(u1.getProperty(Details.class).getPassword(), is(JBCRYPT_ENCODED_PASSWORD)); + + try (WebClient wc = j.createWebClient()) { + assertThrows(FailingHttpStatusCodeException.class, () -> wc.login("user", "a")); + } + assertThat(lr, hasIncorrectHashingLogEntry()); + } + + @Test + public void userCreationWithJBCryptPasswords() throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> securityRealm.createAccountWithHashedPassword("user_hashed_incorrect_algorithm", JBCRYPT_ENCODED_PASSWORD)); + assertThat(illegalArgumentException.getMessage(), + is("The hashed password was hashed with an incorrect algorithm. Jenkins is expecting $PBKDF2")); + } + + private static Matcher hasIncorrectHashingLogEntry() { + return LoggerRule.recorded(is( + "A password appears to be stored (or is attempting to be stored) that was created with a different hashing/encryption algorithm, check the FIPS-140 state of the system has not changed inadvertently")); + } +} diff --git a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java index 9883b0680843..544715d2d2f1 100644 --- a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java +++ b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java @@ -41,6 +41,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionList; import hudson.model.User; +import hudson.security.HudsonPrivateSecurityRealm.Details; import hudson.security.pages.SignupPage; import java.lang.reflect.Field; import java.net.URL; @@ -50,11 +51,14 @@ import java.util.Base64; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import jenkins.security.ApiTokenProperty; import jenkins.security.SecurityListener; import jenkins.security.apitoken.ApiTokenPropertyConfiguration; import jenkins.security.seed.UserSeedProperty; import org.apache.commons.lang.StringUtils; +import org.hamcrest.Matcher; +import org.htmlunit.FailingHttpStatusCodeException; import org.htmlunit.HttpMethod; import org.htmlunit.WebRequest; import org.htmlunit.html.HtmlForm; @@ -70,15 +74,23 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; +import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.TestExtension; import org.mindrot.jbcrypt.BCrypt; @For({UserSeedProperty.class, HudsonPrivateSecurityRealm.class}) public class HudsonPrivateSecurityRealmTest { + // the PBKDF encoded form of "password" without the quotes + private static final String PBKDF_ENDOCED_PASSWORD = + "$PBKDF2$HMACSHA512:210000:ffbb207b847010af98cdd2b09c79392c$f67c3b985daf60db83a9088bc2439f7b77016d26c1439a9877c4f863c377272283ce346edda4578a5607ea620a4beb662d853b800f373297e6f596af797743a6"; + @Rule public JenkinsRule j = new JenkinsRule(); + @Rule + public LoggerRule lr = new LoggerRule().record(HudsonPrivateSecurityRealm.class, Level.WARNING).capture(5); + private SpySecurityListenerImpl spySecurityListener; @Before @@ -511,6 +523,7 @@ public void createAccountSupportsHashedPasswords() throws Exception { XmlPage w2 = (XmlPage) wc.goTo("whoAmI/api/xml", "application/xml"); assertThat(w2, hasXPath("//name", is("user_hashed"))); + assertThat(lr, not(hasIncorrectHashingLogEntry())); } @Test @@ -717,6 +730,40 @@ public void changingPassword_withSeedDisable_hasNoImpact() throws Exception { } } + @Test + public void userLoginAfterDisablingFIPS() throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + + User u1 = securityRealm.createAccount("user", "password"); + u1.setFullName("A User"); + // overwrite the password property using an password created using an incorrect algorithm + u1.addProperty(Details.fromHashedPassword(PBKDF_ENDOCED_PASSWORD)); + + u1.save(); + assertThat(u1.getProperty(Details.class).getPassword(), is(PBKDF_ENDOCED_PASSWORD)); + + try (WebClient wc = j.createWebClient()) { + assertThrows(FailingHttpStatusCodeException.class, () -> wc.login("user", "password")); + } + assertThat(lr, hasIncorrectHashingLogEntry()); + } + + @Test + public void userCreationWithPBKDFPasswords() throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> securityRealm.createAccountWithHashedPassword("user_hashed_incorrect_algorithm", PBKDF_ENDOCED_PASSWORD)); + assertThat(illegalArgumentException.getMessage(), + is("The hashed password was hashed with an incorrect algorithm. Jenkins is expecting #jbcrypt:")); + } + + private static Matcher hasIncorrectHashingLogEntry() { + return LoggerRule.recorded(is( + "A password appears to be stored (or is attempting to be stored) that was created with a different hashing/encryption algorithm, check the FIPS-140 state of the system has not changed inadvertently")); + } + private User prepareRealmAndAlice() throws Exception { j.jenkins.setDisableRememberMe(false); HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); From 270062ace3254a65fa29d37046b901d57b908fa5 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:04:01 +0200 Subject: [PATCH 172/305] [JENKINS-41516] Add script console listener (alternative/extended proposal) (#7056) * Set location for listener * Add ScriptListener * Remove debugging statements * Add listener for CLI * Add new listener for other sources * Simplify listener * Fix whitespace and comments * Update core/src/main/java/hudson/cli/GroovyCommand.java Co-authored-by: Wadeck Follonier * Add precise origins; Correctly log CLI input * Add LoggingGroovySh as suggested by @daniel-beck * Add ScriptListener Tests * Add basic default listener that just logs * WIP Script Listener * Cleaner ScriptListener API * Add license * Cleaner Binding stringification * Note TODO * Add Object feature parameters * Thanks Checkstyle * Thanks Spotbugs * Simplify test, make it pass again * Add since TODO * No mocking, use CLICommandInvoker for CLI commands * Minor cleanup * Fix imports * So far, #onScriptDefinition would be unused, so remove it * Checkstyle * Add test for DefaultScriptListener * Nicer message for the (hopefully rare) case of anon script execution * More thoroughly test logging * Remove obsolete TODO --------- Co-authored-by: Meiswinkel, Jan SF/HZA-ZC2S Co-authored-by: Jan Meiswinkel Co-authored-by: meiswjn <41284403+meiswjn@users.noreply.github.com> Co-authored-by: Daniel Beck Co-authored-by: Wadeck Follonier Co-authored-by: Basil Crow --- .../main/java/hudson/cli/GroovyCommand.java | 10 +- .../main/java/hudson/cli/GroovyshCommand.java | 30 ++- .../java/hudson/util/RemotingDiagnostics.java | 10 +- .../jenkins/util/DefaultScriptListener.java | 67 ++++++ .../java/jenkins/util/ScriptListener.java | 215 ++++++++++++++++++ .../jenkins/util/groovy/GroovyHookScript.java | 3 + .../jenkins/model/ScriptListenerTest.java | 182 +++++++++++++++ 7 files changed, 511 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/jenkins/util/DefaultScriptListener.java create mode 100644 core/src/main/java/jenkins/util/ScriptListener.java create mode 100644 test/src/test/java/jenkins/model/ScriptListenerTest.java diff --git a/core/src/main/java/hudson/cli/GroovyCommand.java b/core/src/main/java/hudson/cli/GroovyCommand.java index 0d7e45e6ca18..6f9233502591 100644 --- a/core/src/main/java/hudson/cli/GroovyCommand.java +++ b/core/src/main/java/hudson/cli/GroovyCommand.java @@ -27,12 +27,14 @@ import groovy.lang.Binding; import groovy.lang.GroovyShell; import hudson.Extension; +import hudson.model.User; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import jenkins.model.Jenkins; +import jenkins.util.ScriptListener; import org.apache.commons.io.IOUtils; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; @@ -63,14 +65,18 @@ protected int run() throws Exception { // this allows the caller to manipulate the JVM state, so require the execute script privilege. Jenkins.get().checkPermission(Jenkins.ADMINISTER); + final String scriptListenerCorrelationId = String.valueOf(System.identityHashCode(this)); + Binding binding = new Binding(); - binding.setProperty("out", new PrintWriter(new OutputStreamWriter(stdout, getClientCharset()), true)); + binding.setProperty("out", new ScriptListener.ListenerWriter(new PrintWriter(new OutputStreamWriter(stdout, getClientCharset()), true), GroovyCommand.class, null, scriptListenerCorrelationId, User.current())); binding.setProperty("stdin", stdin); binding.setProperty("stdout", stdout); binding.setProperty("stderr", stderr); GroovyShell groovy = new GroovyShell(Jenkins.get().getPluginManager().uberClassLoader, binding); - groovy.run(loadScript(), "RemoteClass", remaining.toArray(new String[0])); + String script = loadScript(); + ScriptListener.fireScriptExecution(script, binding, GroovyCommand.class, null, scriptListenerCorrelationId, User.current()); + groovy.run(script, "RemoteClass", remaining.toArray(new String[0])); return 0; } diff --git a/core/src/main/java/hudson/cli/GroovyshCommand.java b/core/src/main/java/hudson/cli/GroovyshCommand.java index 4f6c741c8a47..8bc4d522e254 100644 --- a/core/src/main/java/hudson/cli/GroovyshCommand.java +++ b/core/src/main/java/hudson/cli/GroovyshCommand.java @@ -28,6 +28,7 @@ import groovy.lang.Binding; import groovy.lang.Closure; import hudson.Extension; +import hudson.model.User; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -39,6 +40,7 @@ import java.util.ArrayList; import java.util.List; import jenkins.model.Jenkins; +import jenkins.util.ScriptListener; import jline.TerminalFactory; import jline.UnsupportedTerminal; import org.codehaus.groovy.tools.shell.Groovysh; @@ -54,6 +56,9 @@ */ @Extension public class GroovyshCommand extends CLICommand { + + private final String scriptListenerCorrelationId = String.valueOf(System.identityHashCode(this)); + @Override public String getShortDescription() { return Messages.GroovyshCommand_ShortDescription(); @@ -78,6 +83,8 @@ protected int run() { commandLine.append(arg); } + // TODO Add binding + ScriptListener.fireScriptExecution(null, null, GroovyshCommand.class, null, scriptListenerCorrelationId, User.current()); Groovysh shell = createShell(stdin, stdout, stderr); return shell.run(commandLine.toString()); } @@ -96,11 +103,14 @@ protected Groovysh createShell(InputStream stdin, PrintStream stdout, } catch (InterruptedException e) { throw new RuntimeException(e); } - binding.setProperty("out", new PrintWriter(new OutputStreamWriter(stdout, charset), true)); + + binding.setProperty("out", new PrintWriter(new OutputStreamWriter(new ScriptListener.ListenerOutputStream(stdout, charset, GroovyshCommand.class, null, scriptListenerCorrelationId, User.current()), charset), true)); binding.setProperty("hudson", Jenkins.get()); // backward compatibility binding.setProperty("jenkins", Jenkins.get()); - IO io = new IO(new BufferedInputStream(stdin), stdout, stderr); + IO io = new IO(new BufferedInputStream(stdin), + new ScriptListener.ListenerOutputStream(stdout, charset, GroovyshCommand.class, null, scriptListenerCorrelationId, User.current()), + new ScriptListener.ListenerOutputStream(stderr, charset, GroovyshCommand.class, null, scriptListenerCorrelationId, User.current())); final ClassLoader cl = Jenkins.get().pluginManager.uberClassLoader; Closure registrar = new Closure(null, null) { @@ -119,9 +129,23 @@ public Object doCall(Object[] args) { return null; } }; - Groovysh shell = new Groovysh(cl, binding, io, registrar); + Groovysh shell = new LoggingGroovySh(cl, binding, io, registrar); shell.getImports().add("hudson.model.*"); return shell; } + private class LoggingGroovySh extends Groovysh { + private final Binding binding; + + LoggingGroovySh(ClassLoader cl, Binding binding, IO io, Closure registrar) { + super(cl, binding, io, registrar); + this.binding = binding; + } + + @Override + protected void maybeRecordInput(String line) { + ScriptListener.fireScriptExecution(line, binding, GroovyshCommand.class, null, scriptListenerCorrelationId, User.current()); + super.maybeRecordInput(line); + } + } } diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index 57b339cb9a00..61d97f91cbd7 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -31,6 +31,7 @@ import hudson.FilePath; import hudson.Functions; import hudson.Util; +import hudson.model.User; import hudson.remoting.AsyncFutureImpl; import hudson.remoting.DelegatingCallable; import hudson.remoting.Future; @@ -46,11 +47,13 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; +import java.util.UUID; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; +import jenkins.util.ScriptListener; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.kohsuke.stapler.StaplerRequest; @@ -112,7 +115,12 @@ public Map call() { * Executes Groovy script remotely. */ public static String executeGroovy(String script, @NonNull VirtualChannel channel) throws IOException, InterruptedException { - return channel.call(new Script(script)); + final String correlationId = UUID.randomUUID().toString(); + final String context = channel.toString(); + ScriptListener.fireScriptExecution(script, new Binding(), RemotingDiagnostics.class, context, correlationId, User.current()); + final String output = channel.call(new Script(script)); + ScriptListener.fireScriptOutput(output, RemotingDiagnostics.class, context, correlationId, User.current()); + return output; } private static final class Script extends MasterToSlaveCallable implements DelegatingCallable { diff --git a/core/src/main/java/jenkins/util/DefaultScriptListener.java b/core/src/main/java/jenkins/util/DefaultScriptListener.java new file mode 100644 index 000000000000..3f3c966a8fd6 --- /dev/null +++ b/core/src/main/java/jenkins/util/DefaultScriptListener.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright (c) 2022 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.util; + +import edu.umd.cs.findbugs.annotations.NonNull; +import groovy.lang.Binding; +import hudson.Extension; +import hudson.model.User; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Basic default implementation of {@link jenkins.util.ScriptListener} that just logs. + * + * @since TODO + */ +@Extension +@Restricted(NoExternalUse.class) +public class DefaultScriptListener implements ScriptListener { + public static final Logger LOGGER = Logger.getLogger(DefaultScriptListener.class.getName()); + + @Override + public void onScriptExecution(String script, Binding binding, @NonNull Object feature, Object context, @NonNull String correlationId, User user) { + String userFragment = user == null ? " (no user)" : " by user: '" + user + "'"; + LOGGER.log(Level.FINE, LOGGER.isLoggable(Level.FINEST) ? new Exception() : null, + () -> "Execution of script: '" + script + "' with binding: '" + stringifyBinding(binding) + "' in feature: '" + feature + "' and context: '" + context + "' with correlation: '" + correlationId + "'" + userFragment); + } + + @Override + public void onScriptOutput(String output, @NonNull Object feature, Object context, @NonNull String correlationId, User user) { + String userFragment = user == null ? " (no user)" : " for user: '" + user + "'"; + LOGGER.log(Level.FINER, LOGGER.isLoggable(Level.FINEST) ? new Exception() : null, + () -> "Script output: '" + output + "' in feature: '" + feature + "' and context: '" + context + "' with correlation: '" + correlationId + "'" + userFragment); + } + + private static String stringifyBinding(Binding binding) { + if (binding == null) { + return null; + } + return InvokerHelper.toString(binding.getVariables()); + } +} diff --git a/core/src/main/java/jenkins/util/ScriptListener.java b/core/src/main/java/jenkins/util/ScriptListener.java new file mode 100644 index 000000000000..60a6c77bd747 --- /dev/null +++ b/core/src/main/java/jenkins/util/ScriptListener.java @@ -0,0 +1,215 @@ +/* + * The MIT License + * + * Copyright (c) 2022 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.util; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import groovy.lang.Binding; +import hudson.ExtensionPoint; +import hudson.model.User; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import org.kohsuke.stapler.StaplerRequest; + +/** + * A listener to track in-process script use. + * + *

Note that (unsandboxed) script execution can easily result in logging configuration being changed, so if you rely + * on complete logging of scripting actions, make sure to set up logging to remote systems.

+ * + * @see jenkins.model.Jenkins#_doScript(StaplerRequest, org.kohsuke.stapler.StaplerResponse, javax.servlet.RequestDispatcher, hudson.remoting.VirtualChannel, hudson.security.ACL) + * @see hudson.cli.GroovyCommand + * @see hudson.cli.GroovyshCommand + * @see jenkins.util.groovy.GroovyHookScript + * + * @since TODO + */ +public interface ScriptListener extends ExtensionPoint { + + /** + * Called just before scripts are executed. + * + * Examples include: + *
    + *
  • Groovy script console script execution
  • + *
  • {@code groovy} CLI command
  • + *
  • Start and end of a {@code groovysh} CLI command session, as well as individual commands submitted
  • + *
  • Execution of scripts integrating with Script Security Plugin
  • + *
+ * + * @param script The script to be executed or {@code null} if no script is available yet (e.g. a shell has just been opened). + * @param binding The script binding, or {@code null} if unavailable/inapplicable. + * @param feature The feature that triggered this event. Usually a fixed string or even a {@link java.lang.Class} + * if that's unambiguously describing the feature (e.g., {@link hudson.cli.GroovyshCommand#getClass()}). + * @param context Object representing the script definition context (e.g. {@link hudson.model.Run}). + * Can be {@code null} if not applicable (e.g., CLI commands not acting on jobs/builds). + * @param correlationId This value is used to correlate this script event to other, related script events. + * Callers are expected to provide values that allow receivers to associate script execution + * and output. Related events should have identical values. + * @param user If available, the user who executed the script. Can be {@code null}. + */ + default void onScriptExecution(@CheckForNull String script, @CheckForNull Binding binding, @NonNull Object feature, @CheckForNull Object context, @NonNull String correlationId, @CheckForNull User user) { + } + + /** + * Called when a script produces output. This can include error output. + * + * @param output The output of the script. + * @param feature The feature that triggered this event. Usually a fixed string or even a {@link java.lang.Class} + * if that's unambiguously describing the feature (e.g., {@link hudson.cli.GroovyshCommand#getClass()}). + * @param context Object representing the script definition context (e.g. {@link hudson.model.Run}). + * Can be {@code null} if not applicable (e.g., CLI commands not acting on jobs/builds). + * @param correlationId This value is used to correlate this script event to other, related script events. + * Callers are expected to provide values that allow receivers to associate script execution + * and output. Related events should have identical values. + * @param user If available, the user for which the output was created. Can be {@code null}. + */ + default void onScriptOutput(@CheckForNull String output, @NonNull Object feature, @CheckForNull Object context, @NonNull String correlationId, @CheckForNull User user) { + } + + /** + * Fires the {@link #onScriptExecution(String, Binding, Object, Object, String, hudson.model.User)} event. + * + * @param script The script to be executed or {@code null} if no script is available yet (e.g. a shell has just been opened). + * @param binding The script binding, or {@code null} if unavailable/inapplicable. + * @param feature The feature that triggered this event. Usually a fixed string or even a {@link java.lang.Class} + * if that's unambiguously describing the feature (e.g., {@link hudson.cli.GroovyshCommand#getClass()}). + * @param context Object representing the script definition context (e.g. {@link hudson.model.Run}). + * Can be {@code null} if not applicable (e.g., CLI commands not acting on jobs/builds). + * @param correlationId This value is used to correlate this script event to other, related script events. + * Callers are expected to provide values that allow receivers to associate script execution + * and output. Related events should have identical values. + * @param user If available, the user who caused this event. Can be {@code null}. + */ + // TODO Should null script be allowed? Do we care about e.g. someone starting groovysh but not actually executing a command (yet)? + static void fireScriptExecution(@CheckForNull String script, @CheckForNull Binding binding, @NonNull Object feature, @CheckForNull Object context, @NonNull String correlationId, @CheckForNull User user) { + Listeners.notify(ScriptListener.class, true, listener -> listener.onScriptExecution(script, binding, feature, context, correlationId, user)); + } + + /** + * Fires the {@link #onScriptOutput(String, Object, Object, String, hudson.model.User)} event. + * + * @param output The output of the script. + * @param context Object representing the script definition context (e.g. {@link hudson.model.Run}). + * Can be {@code null} if not applicable (e.g., CLI commands not acting on jobs/builds). + * @param correlationId This value is used to correlate this script event to other, related script events. + * Callers are expected to provide values that allow receivers to associate script execution + * and output. Related events should have identical values. + * @param user If available, the user for which the output was created. Can be {@code null}. + */ + static void fireScriptOutput(@CheckForNull String output, @NonNull Object feature, @CheckForNull Object context, @NonNull String correlationId, @CheckForNull User user) { + Listeners.notify(ScriptListener.class, true, listener -> listener.onScriptOutput(output, feature, context, correlationId, user)); + } + + /** + * {@link java.io.Writer} that calls {@link #fireScriptOutput(String, Object, Object, String, hudson.model.User)} with the + * output it writes to the wrapped {@link java.io.Writer}, and otherwise just forwards {@link #flush()} and {@link #close()}. + */ + class ListenerWriter extends Writer { + + private final Writer writer; + private final Object feature; + private final Object context; + private final String correlationId; + private final User user; + + @SuppressFBWarnings("EI_EXPOSE_REP2") + public ListenerWriter(Writer writer, Object feature, Object context, String correlationId, User user) { + this.writer = writer; + this.feature = feature; + this.context = context; + this.correlationId = correlationId; + this.user = user; + } + + @Override + public void write(@NonNull char[] cbuf, int off, int len) throws IOException { + ScriptListener.fireScriptOutput(String.copyValueOf(cbuf, off, len), feature, context, correlationId, user); + writer.write(cbuf, off, len); + } + + @Override + public void flush() throws IOException { + writer.flush(); + } + + @Override + public void close() throws IOException { + writer.close(); + } + } + + /** + * {@link java.io.OutputStream} that calls{@link #fireScriptOutput(String, Object, Object, String, hudson.model.User)} with + * the output it writes to the wrapped {@link java.io.OutputStream}, and otherwise just forwards {@link #flush()} + * and {@link #close()}. + */ + class ListenerOutputStream extends OutputStream { + + private final OutputStream os; + private final Charset charset; + private final Object feature; + private final Object context; + private final String correlationId; + private final User user; + + @SuppressFBWarnings("EI_EXPOSE_REP2") + public ListenerOutputStream(OutputStream os, Charset charset, Object feature, Object context, String correlationId, User user) { + this.os = os; + this.charset = charset; + this.feature = feature; + this.context = context; + this.correlationId = correlationId; + this.user = user; + } + + @Override + public void write(int b) throws IOException { + // Let's hope for verbosity's sake that nobody calls this directly, #write(byte[], int, int) should take care of regular calls. + ScriptListener.fireScriptOutput(new String(new byte[] { (byte) b }, charset), feature, context, correlationId, user); + os.write(b); + } + + @Override + public void write(@NonNull byte[] b, int off, int len) throws IOException { + final String writtenString = new String(b, charset).substring(off, len - off); + ScriptListener.fireScriptOutput(writtenString, feature, context, correlationId, user); + os.write(b, off, len); + } + + @Override + public void flush() throws IOException { + os.flush(); + } + + @Override + public void close() throws IOException { + os.close(); + } + } +} diff --git a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java index 61b13e1d090d..eeb816f31ffd 100644 --- a/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java +++ b/core/src/main/java/jenkins/util/groovy/GroovyHookScript.java @@ -7,6 +7,7 @@ import groovy.lang.Binding; import groovy.lang.GroovyCodeSource; import groovy.lang.GroovyShell; +import hudson.model.User; import java.io.File; import java.io.IOException; import java.net.URL; @@ -16,6 +17,7 @@ import java.util.logging.Logger; import javax.servlet.ServletContext; import jenkins.model.Jenkins; +import jenkins.util.ScriptListener; import jenkins.util.SystemProperties; /** @@ -133,6 +135,7 @@ protected void execute(File f) { @SuppressFBWarnings(value = "GROOVY_SHELL", justification = "Groovy hook scripts are a feature, not a bug") protected void execute(GroovyCodeSource s) { try { + ScriptListener.fireScriptExecution(s.getScriptText(), bindings, this.getClass(), s.getFile(), this.getClass().getName() + ":" + hook, User.current()); createShell().evaluate(s); } catch (RuntimeException x) { LOGGER.log(WARNING, "Failed to run script " + s.getName(), x); diff --git a/test/src/test/java/jenkins/model/ScriptListenerTest.java b/test/src/test/java/jenkins/model/ScriptListenerTest.java new file mode 100644 index 000000000000..2b8fcee441be --- /dev/null +++ b/test/src/test/java/jenkins/model/ScriptListenerTest.java @@ -0,0 +1,182 @@ +package jenkins.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasSize; + +import edu.umd.cs.findbugs.annotations.NonNull; +import groovy.lang.Binding; +import hudson.ExtensionList; +import hudson.cli.CLICommandInvoker; +import hudson.cli.GroovyCommand; +import hudson.cli.GroovyshCommand; +import hudson.model.User; +import hudson.util.RemotingDiagnostics; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.logging.Level; +import jenkins.util.DefaultScriptListener; +import jenkins.util.ScriptListener; +import org.htmlunit.HttpMethod; +import org.htmlunit.WebRequest; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.LoggerRule; +import org.jvnet.hudson.test.TestExtension; + +public class ScriptListenerTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Rule + public LoggerRule logging = new LoggerRule(); + + @Test + public void consoleUsageIsLogged() throws IOException { + final String output = "hello from script console"; + final String script = "println '" + output + "'"; + + logging.record(DefaultScriptListener.class.getName(), Level.FINEST).capture(100); + + try (JenkinsRule.WebClient wc = j.createWebClient()) { + final WebRequest request = new WebRequest(new URL(wc.getContextPath() + "scriptText?script=" + script), HttpMethod.POST); + wc.getPage(wc.addCrumb(request)); + } + + { // DefaultScriptListener + final List messages = logging.getMessages(); + assertThat(messages, hasSize(2)); + + assertThat(messages.get(0), containsString("Execution of script: '" + script + "' with binding: '[:]' in feature: 'class hudson.util.RemotingDiagnostics' and context: 'hudson.remoting.LocalChannel@")); + assertThat(messages.get(0), containsString("' with correlation: '")); + assertThat(messages.get(0), containsString("' (no user)")); + + assertThat(messages.get(1), containsString("Script output: 'hello from script console\n' in feature: 'class hudson.util.RemotingDiagnostics' and context: 'hudson.remoting.LocalChannel@")); + assertThat(messages.get(1), containsString("' with correlation: '")); + assertThat(messages.get(1), containsString("' (no user)")); + } + + { // DummyScriptUsageListener + final DummyScriptUsageListener listener = ExtensionList.lookupSingleton(DummyScriptUsageListener.class); + String execution = listener.getExecutionString(); + + assertThat(execution, containsString(RemotingDiagnostics.class.getName())); + assertThat(execution, containsString(script)); + assertThat(listener.getOutput(), containsString(output)); + } + } + + @Test + public void groovyCliUsageIsLogged() { + final String output = "hello from groovy CLI"; + final String script = "println '" + output + "'"; + + logging.record(DefaultScriptListener.class.getName(), Level.FINEST).capture(100); + + InputStream scriptStream = new ByteArrayInputStream(script.getBytes()); + new CLICommandInvoker(j, "groovy").withArgs("=").withStdin(scriptStream).invoke(); + + { // DefaultScriptListener + final List messages = logging.getMessages(); + assertThat(messages, hasSize(3)); + + assertThat(messages.get(0), containsString("Execution of script: '" + script + "' with binding: '[")); + assertThat(messages.get(0), containsString("]' in feature: 'class hudson.cli.GroovyCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(0), containsString("' (no user)")); + + assertThat(messages.get(1), containsString("Script output: 'hello from groovy CLI' in feature: 'class hudson.cli.GroovyCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(1), containsString("' (no user)")); + + assertThat(messages.get(2), containsString("Script output: '\n' in feature: 'class hudson.cli.GroovyCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(2), containsString("' (no user)")); + } + + { // DummyScriptUsageListener + final DummyScriptUsageListener listener = ExtensionList.lookupSingleton(DummyScriptUsageListener.class); + String execution = listener.getExecutionString(); + + assertThat(execution, containsString(GroovyCommand.class.getName())); + assertThat(execution, containsString(script)); + assertThat(listener.getOutput(), containsString(output)); + } + } + + @Test + public void groovyShCliUsageIsLogged() { + final String output = "hello from groovysh CLI"; + final String script = "println '" + output + "'"; + + logging.record(DefaultScriptListener.class.getName(), Level.FINEST).capture(100); + + InputStream scriptStream = new ByteArrayInputStream(script.getBytes()); + new CLICommandInvoker(j, "groovysh").withStdin(scriptStream).invoke(); + + { // DefaultScriptListener + final List messages = logging.getMessages(); + assertThat(messages, hasSize(9)); + + assertThat(messages.get(0), containsString("Execution of script: 'null' with binding: 'null' in feature: 'class hudson.cli.GroovyshCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(0), containsString("' (no user)")); + + // Only match short substrings to not have to deal with color escape codes in the output + assertThat(messages.get(1), containsString("Groovy Shell")); // Groovy Shell (2.4.21, JVM: 11.0.15) + assertThat(messages.get(2), containsString(":help")); // Type ':help' or ':h' for help. + assertThat(messages.get(3), containsString("Script output: '-------------------")); + assertThat(messages.get(4), containsString("000")); // groovy:000> + + assertThat(messages.get(5), containsString("Execution of script: '" + script + "' with binding: '[")); + assertThat(messages.get(5), containsString("]' in feature: 'class hudson.cli.GroovyshCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(5), containsString("' (no user)")); + + assertThat(messages.get(6), containsString("Script output: 'hello from groovysh CLI\n' in feature: 'class hudson.cli.GroovyshCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(6), containsString("' (no user)")); + + // Only match short substrings to not have to deal with color escape codes in the output + assertThat(messages.get(7), containsString("===>")); // ===> null + assertThat(messages.get(8), containsString("000")); // groovy:000> + } + + { // DummyScriptUsageListener + final DummyScriptUsageListener listener = ExtensionList.lookupSingleton(DummyScriptUsageListener.class); + String execution = listener.getExecutionString(); + + assertThat(execution, containsString(GroovyshCommand.class.getName())); + assertThat(execution, containsString(script)); + assertThat(listener.getOutput(), containsString(output)); + } + } + + @TestExtension + public static class DummyScriptUsageListener implements ScriptListener { + private final StringBuilder script = new StringBuilder(); + private final StringBuilder output = new StringBuilder(); + + @Override + public void onScriptExecution(String script, Binding binding, @NonNull Object feature, Object context, @NonNull String correlationId, User u) { + String username = "null"; + if (u != null) { + username = u.getFullName(); + } + String expectedOutFormat = "Script: '%s' in '%s' with '%s' by '%s'"; + this.script.append(String.format(expectedOutFormat, script, feature, context, correlationId, username)).append("\n"); + } + + @Override + public void onScriptOutput(String output, @NonNull Object feature, Object context, @NonNull String correlationId, User user) { + this.output.append(output); + } + + String getExecutionString() { + return script.toString(); + } + + String getOutput() { + return output.toString(); + } + } +} From a51932fc1bc0326b9eaa9dd9b7abf9ebdbbb4e5c Mon Sep 17 00:00:00 2001 From: Kevin Guerroudj <91883215+Kevin-CB@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:04:52 +0200 Subject: [PATCH 173/305] Merge back test for 2023-09-20 release (#8563) --- .../hudson/PluginManagerSecurity3072Test.java | 107 ------------------ .../test/java/hudson/PluginManagerTest.java | 62 ++++++++++ .../model/JenkinsSecurity3073Test.java | 77 ------------- .../test/java/jenkins/model/JenkinsTest.java | 59 ++++++++++ 4 files changed, 121 insertions(+), 184 deletions(-) delete mode 100644 test/src/test/java/hudson/PluginManagerSecurity3072Test.java delete mode 100644 test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java diff --git a/test/src/test/java/hudson/PluginManagerSecurity3072Test.java b/test/src/test/java/hudson/PluginManagerSecurity3072Test.java deleted file mode 100644 index 774d6b5ce1c6..000000000000 --- a/test/src/test/java/hudson/PluginManagerSecurity3072Test.java +++ /dev/null @@ -1,107 +0,0 @@ -package hudson; - -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; -import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; -import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; - -import hudson.model.RootAction; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.attribute.PosixFilePermission; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import javax.servlet.ServletException; -import jenkins.model.Jenkins; -import org.htmlunit.html.HtmlForm; -import org.htmlunit.html.HtmlPage; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.TestExtension; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -public class PluginManagerSecurity3072Test { - - @Rule - public JenkinsRule r = PluginManagerUtil.newJenkinsRule(); - - @Rule - public TemporaryFolder tmp = new TemporaryFolder(); - - @Test - @Issue("SECURITY-3072") - public void verifyUploadedPluginFromURLPermission() throws Exception { - assumeFalse(Functions.isWindows()); - - HtmlPage page = r.createWebClient().goTo("pluginManager/advanced"); - HtmlForm f = page.getFormByName("uploadPlugin"); - f.getInputByName("pluginUrl").setValue(Jenkins.get().getRootUrl() + "pluginManagerGetPlugin/htmlpublisher.jpi"); - r.submit(f); - - File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); - File filesTmpDir = filesRef.getParentFile(); - filesRef.deleteOnExit(); - - final Set[] filesPermission = new Set[]{new HashSet<>()}; - await().pollInterval(250, TimeUnit.MILLISECONDS) - .atMost(10, TimeUnit.SECONDS) - .until(() -> { - Optional lastUploadedPluginDir = Arrays.stream(Objects.requireNonNull( - filesTmpDir.listFiles((file, fileName) -> - fileName.startsWith("uploadDir")))). - max(Comparator.comparingLong(File::lastModified)); - if (lastUploadedPluginDir.isPresent()) { - filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPluginDir.get().toPath(), LinkOption.NOFOLLOW_LINKS); - Optional pluginFile = Arrays.stream(Objects.requireNonNull( - lastUploadedPluginDir.get().listFiles((file, fileName) -> - fileName.startsWith("uploaded")))). - max(Comparator.comparingLong(File::lastModified)); - assertTrue(pluginFile.isPresent()); - return true; - } else { - return false; - } - }); - assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); - } - - @TestExtension("verifyUploadedPluginFromURLPermission") - public static final class ReturnPluginJpiAction implements RootAction { - - @Override - public String getIconFileName() { - return "gear2.png"; - } - - @Override - public String getDisplayName() { - return "URL to retrieve a plugin jpi"; - } - - @Override - public String getUrlName() { - return "pluginManagerGetPlugin"; - } - - public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResponse) throws ServletException, IOException { - staplerResponse.setContentType("application/octet-stream"); - staplerResponse.setStatus(200); - staplerResponse.serveFile(staplerRequest, PluginManagerTest.class.getClassLoader().getResource("plugins/htmlpublisher.jpi")); - } - } -} diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 507354b8e1b8..dddd9239d97a 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -804,6 +804,43 @@ public void noInjectionOnAvailablePluginsPage() throws Exception { } } + @Test + @Issue("SECURITY-3072") + public void verifyUploadedPluginFromURLPermission() throws Exception { + assumeFalse(Functions.isWindows()); + + HtmlPage page = r.createWebClient().goTo("pluginManager/advanced"); + HtmlForm f = page.getFormByName("uploadPlugin"); + f.getInputByName("pluginUrl").setValue(Jenkins.get().getRootUrl() + "pluginManagerGetPlugin/htmlpublisher.jpi"); + r.submit(f); + + File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); + File filesTmpDir = filesRef.getParentFile(); + filesRef.deleteOnExit(); + + final Set[] filesPermission = new Set[]{new HashSet<>()}; + await().pollInterval(250, TimeUnit.MILLISECONDS) + .atMost(10, TimeUnit.SECONDS) + .until(() -> { + Optional lastUploadedPluginDir = Arrays.stream(Objects.requireNonNull( + filesTmpDir.listFiles((file, fileName) -> + fileName.startsWith("uploadDir")))). + max(Comparator.comparingLong(File::lastModified)); + if (lastUploadedPluginDir.isPresent()) { + filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPluginDir.get().toPath(), LinkOption.NOFOLLOW_LINKS); + Optional pluginFile = Arrays.stream(Objects.requireNonNull( + lastUploadedPluginDir.get().listFiles((file, fileName) -> + fileName.startsWith("uploaded")))). + max(Comparator.comparingLong(File::lastModified)); + assertTrue(pluginFile.isPresent()); + return true; + } else { + return false; + } + }); + assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); + } + static class AlertHandlerImpl implements AlertHandler { List messages = Collections.synchronizedList(new ArrayList<>()); @@ -838,4 +875,29 @@ public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResp } } + @TestExtension("verifyUploadedPluginFromURLPermission") + public static final class Security3072JpiAction implements RootAction { + + @Override + public String getIconFileName() { + return "gear2.png"; + } + + @Override + public String getDisplayName() { + return "URL to retrieve a plugin jpi"; + } + + @Override + public String getUrlName() { + return "pluginManagerGetPlugin"; + } + + public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResponse) throws ServletException, IOException { + staplerResponse.setContentType("application/octet-stream"); + staplerResponse.setStatus(200); + staplerResponse.serveFile(staplerRequest, PluginManagerTest.class.getClassLoader().getResource("plugins/htmlpublisher.jpi")); + } + } + } diff --git a/test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java b/test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java deleted file mode 100644 index 96c3fb3c634a..000000000000 --- a/test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java +++ /dev/null @@ -1,77 +0,0 @@ -package jenkins.model; - -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; -import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; -import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeFalse; - -import hudson.Functions; -import java.io.File; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.attribute.PosixFilePermission; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import org.apache.commons.io.FileUtils; -import org.htmlunit.html.HtmlForm; -import org.htmlunit.html.HtmlPage; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; - -public class JenkinsSecurity3073Test { - - - @Rule - public JenkinsRule j = new JenkinsRule(); - - @Rule - public TemporaryFolder tmp = new TemporaryFolder(); - - @Test - @Issue("SECURITY-3073") - public void verifyUploadedFingerprintFilePermission() throws Exception { - assumeFalse(Functions.isWindows()); - - HtmlPage page = j.createWebClient().goTo("fingerprintCheck"); - // The form doesn't have a name, the page contain the search form and the one we're interested in - HtmlForm form = page.getForms().get(1); - File dir = tmp.newFolder(); - File plugin = new File(dir, "htmlpublisher.jpi"); - // We're using a plugin to have a file above DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD - FileUtils.copyURLToFile(Objects.requireNonNull(getClass().getClassLoader().getResource("plugins/htmlpublisher.jpi")), plugin); - form.getInputByName("name").setValueAttribute(plugin.getAbsolutePath()); - j.submit(form); - - File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); - File filesTmpDir = filesRef.getParentFile(); - filesRef.deleteOnExit(); - - final Set[] filesPermission = new Set[]{new HashSet<>()}; - await().pollInterval(250, TimeUnit.MILLISECONDS) - .atMost(10, TimeUnit.SECONDS) - .until(() -> { - Optional lastUploadedPlugin = Arrays.stream(Objects.requireNonNull( - filesTmpDir.listFiles((file, fileName) -> - fileName.startsWith("jenkins-multipart-uploads")))). - max(Comparator.comparingLong(File::lastModified)); - if (lastUploadedPlugin.isPresent()) { - filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPlugin.get().toPath(), LinkOption.NOFOLLOW_LINKS); - return true; - } else { - return false; - } - }); - assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); - } -} diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index 5c0c9acedbb2..6750eabbd3f1 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -24,6 +24,10 @@ package jenkins.model; +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.containsString; @@ -38,9 +42,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.ExtensionList; +import hudson.Functions; import hudson.XmlFile; import hudson.init.InitMilestone; import hudson.init.Initializer; @@ -67,27 +73,40 @@ import hudson.util.FormValidation; import hudson.util.HttpResponses; import hudson.util.VersionNumber; +import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import jenkins.AgentProtocol; +import org.apache.commons.io.FileUtils; import org.htmlunit.FailingHttpStatusCodeException; import org.htmlunit.HttpMethod; import org.htmlunit.Page; import org.htmlunit.TextPage; import org.htmlunit.WebRequest; import org.htmlunit.WebResponse; +import org.htmlunit.html.HtmlForm; import org.htmlunit.html.HtmlPage; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; @@ -110,6 +129,46 @@ public class JenkinsTest { @Rule public JenkinsRule j = new JenkinsRule(); + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Test + @Issue("SECURITY-3073") + public void verifyUploadedFingerprintFilePermission() throws Exception { + assumeFalse(Functions.isWindows()); + + HtmlPage page = j.createWebClient().goTo("fingerprintCheck"); + // The form doesn't have a name, the page contain the search form and the one we're interested in + HtmlForm form = page.getForms().get(1); + File dir = tmp.newFolder(); + File plugin = new File(dir, "htmlpublisher.jpi"); + // We're using a plugin to have a file above DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD + FileUtils.copyURLToFile(Objects.requireNonNull(getClass().getClassLoader().getResource("plugins/htmlpublisher.jpi")), plugin); + form.getInputByName("name").setValueAttribute(plugin.getAbsolutePath()); + j.submit(form); + + File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); + File filesTmpDir = filesRef.getParentFile(); + filesRef.deleteOnExit(); + + final Set[] filesPermission = new Set[]{new HashSet<>()}; + await().pollInterval(250, TimeUnit.MILLISECONDS) + .atMost(10, TimeUnit.SECONDS) + .until(() -> { + Optional lastUploadedPlugin = Arrays.stream(Objects.requireNonNull( + filesTmpDir.listFiles((file, fileName) -> + fileName.startsWith("jenkins-multipart-uploads")))). + max(Comparator.comparingLong(File::lastModified)); + if (lastUploadedPlugin.isPresent()) { + filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPlugin.get().toPath(), LinkOption.NOFOLLOW_LINKS); + return true; + } else { + return false; + } + }); + assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); + } + @Issue("SECURITY-406") @Test public void testUserCreationFromUrlForAdmins() throws Exception { From 778b8b62de4e6f42da87cd78dbe8d731a6888cf6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:18:31 +0200 Subject: [PATCH 174/305] Update dependency org.apache.maven:maven-core to v3.9.5 (#8566) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .gitpod/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod/Dockerfile b/.gitpod/Dockerfile index 9f5ab9c02d91..01d953e840e7 100644 --- a/.gitpod/Dockerfile +++ b/.gitpod/Dockerfile @@ -1,6 +1,6 @@ FROM gitpod/workspace-full -ARG MAVEN_VERSION=3.9.4 +ARG MAVEN_VERSION=3.9.5 RUN brew install gh && \ bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh && sdk install maven ${MAVEN_VERSION} && sdk default maven ${MAVEN_VERSION}" From e740d1b6095c6ef63e3fc8df5dc155118424da4f Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:14:51 +0200 Subject: [PATCH 175/305] Fix tests that the PR build did not run on Windows (#8574) Fix tests that the CI build did not run on Windows Co-authored-by: Daniel Beck --- test/src/test/java/jenkins/model/ScriptListenerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/src/test/java/jenkins/model/ScriptListenerTest.java b/test/src/test/java/jenkins/model/ScriptListenerTest.java index 2b8fcee441be..0eecfee6127c 100644 --- a/test/src/test/java/jenkins/model/ScriptListenerTest.java +++ b/test/src/test/java/jenkins/model/ScriptListenerTest.java @@ -56,7 +56,7 @@ public void consoleUsageIsLogged() throws IOException { assertThat(messages.get(0), containsString("' with correlation: '")); assertThat(messages.get(0), containsString("' (no user)")); - assertThat(messages.get(1), containsString("Script output: 'hello from script console\n' in feature: 'class hudson.util.RemotingDiagnostics' and context: 'hudson.remoting.LocalChannel@")); + assertThat(messages.get(1), containsString("Script output: 'hello from script console" + System.lineSeparator() + "' in feature: 'class hudson.util.RemotingDiagnostics' and context: 'hudson.remoting.LocalChannel@")); assertThat(messages.get(1), containsString("' with correlation: '")); assertThat(messages.get(1), containsString("' (no user)")); } @@ -92,7 +92,7 @@ public void groovyCliUsageIsLogged() { assertThat(messages.get(1), containsString("Script output: 'hello from groovy CLI' in feature: 'class hudson.cli.GroovyCommand' and context: 'null' with correlation: '")); assertThat(messages.get(1), containsString("' (no user)")); - assertThat(messages.get(2), containsString("Script output: '\n' in feature: 'class hudson.cli.GroovyCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(2), containsString("Script output: '" + System.lineSeparator() + "' in feature: 'class hudson.cli.GroovyCommand' and context: 'null' with correlation: '")); assertThat(messages.get(2), containsString("' (no user)")); } @@ -133,7 +133,7 @@ public void groovyShCliUsageIsLogged() { assertThat(messages.get(5), containsString("]' in feature: 'class hudson.cli.GroovyshCommand' and context: 'null' with correlation: '")); assertThat(messages.get(5), containsString("' (no user)")); - assertThat(messages.get(6), containsString("Script output: 'hello from groovysh CLI\n' in feature: 'class hudson.cli.GroovyshCommand' and context: 'null' with correlation: '")); + assertThat(messages.get(6), containsString("Script output: 'hello from groovysh CLI" + System.lineSeparator() + "' in feature: 'class hudson.cli.GroovyshCommand' and context: 'null' with correlation: '")); assertThat(messages.get(6), containsString("' (no user)")); // Only match short substrings to not have to deal with color escape codes in the output From b29b457ac26798d1113e31536cf801b30f6a1a1e Mon Sep 17 00:00:00 2001 From: Nico Date: Fri, 6 Oct 2023 14:15:52 +0200 Subject: [PATCH 176/305] Actually adjust copy button logic to new API (#8554) * Actually adjust copy button logic to new API Remove the obsolete textarea creation and correctly handle other permission errors * Remove obsolete check if running as test HTMLUnit now supports `isSecureContext` --------- Co-authored-by: Mark Waite --- .../lib/layout/copyButton/copyButton.js | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/core/src/main/resources/lib/layout/copyButton/copyButton.js b/core/src/main/resources/lib/layout/copyButton/copyButton.js index 334d18e54179..738b038c8f6c 100644 --- a/core/src/main/resources/lib/layout/copyButton/copyButton.js +++ b/core/src/main/resources/lib/layout/copyButton/copyButton.js @@ -4,31 +4,20 @@ Behaviour.specify( 0, function (copyButton) { copyButton.addEventListener("click", () => { - // HTMLUnit 2.70.0 does not recognize isSecureContext - // https://issues.jenkins.io/browse/JENKINS-70895 - if (!window.isRunAsTest && isSecureContext) { - // Make an invisible textarea element containing the text - const fakeInput = document.createElement("textarea"); - fakeInput.value = copyButton.getAttribute("text"); - fakeInput.style.width = "1px"; - fakeInput.style.height = "1px"; - fakeInput.style.border = "none"; - fakeInput.style.padding = "0px"; - fakeInput.style.position = "absolute"; - fakeInput.style.top = "-99999px"; - fakeInput.style.left = "-99999px"; - fakeInput.setAttribute("tabindex", "-1"); - document.body.appendChild(fakeInput); - - // Select the text and copy it to the clipboard - fakeInput.select(); - navigator.clipboard.writeText(fakeInput.value); - - // Remove the textarea element - document.body.removeChild(fakeInput); - - // Show the completion message - hoverNotification(copyButton.getAttribute("message"), copyButton); + if (isSecureContext) { + // Copy the text to the clipboard + navigator.clipboard + .writeText(copyButton.getAttribute("text")) + .then(() => { + // Show the completion message + hoverNotification(copyButton.getAttribute("message"), copyButton); + }) + .catch(() => { + hoverNotification( + "Could not get permission to write to clipboard", + copyButton, + ); + }); } else { hoverNotification( "Copy is only supported with a secure (HTTPS) connection", From 6fdfdd01c6c551777b9ff7d7abd6d86536b281b0 Mon Sep 17 00:00:00 2001 From: Vincent Latombe Date: Fri, 6 Oct 2023 14:16:25 +0200 Subject: [PATCH 177/305] [JENKINS-71937] Fix deprecated `Slave` constructor (#8564) [JENKINS-71937] Fix deprecated Slave constructor Got broken in https://github.com/jenkinsci/jenkins/pull/8395 Co-authored-by: Basil Crow --- core/src/main/java/hudson/model/Slave.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index fe69bac0af93..507752378996 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -196,7 +196,7 @@ protected Slave(@NonNull String name, String nodeDescription, String remoteFS, i this.numExecutors = numExecutors; this.mode = mode; this.remoteFS = Util.fixNull(remoteFS).trim(); - this.labelAtomSet = Collections.unmodifiableSet(Label.parse(labelString)); + _setLabelString(labelString); this.launcher = launcher; this.retentionStrategy = retentionStrategy; getAssignedLabels(); // compute labels now From 0ca08a6476da4ab3088fef5190e2c9b87dd20ca2 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 7 Oct 2023 06:40:17 +0200 Subject: [PATCH 178/305] Update Node.js from 18.x to 20.x (#8571) --- .github/renovate.json | 2 +- CONTRIBUTING.md | 2 +- war/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index b6f6d7e65090..8c3c4ad17cea 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -16,7 +16,7 @@ }, { "matchPackageNames": ["node"], - "allowedVersions": "/18.[0-9]+.[0-9]+(.[0-9]+)?$/" + "allowedVersions": "/20.[0-9]+.[0-9]+(.[0-9]+)?$/" } ], "regexManagers": [ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d99b1eaeac6..0e6dbffdda9d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ This page provides information about contributing code to the Jenkins core codeb - Apache Maven 3.8.1 or above. You can [download Maven here](https://maven.apache.org/download.cgi). In the Jenkins project we usually use the most recent Maven release. - Any IDE which supports importing Maven projects. - - Install [Node.js 18.x](https://nodejs.org/en/). **Note:** only needed to work on the frontend assets found in the `war` module. + - Install [Node.js 20.x](https://nodejs.org/en/). **Note:** only needed to work on the frontend assets found in the `war` module. - Frontend tasks are run using [yarn](https://yarnpkg.com/). Run `npm install -g yarn` to install it. 4. Set up your development environment as described in [Preparing for Plugin Development](https://www.jenkins.io/doc/developer/tutorial/prepare/) diff --git a/war/pom.xml b/war/pom.xml index ccd156a88138..294c0457ec21 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -46,7 +46,7 @@ THE SOFTWARE. localhost 8080 - 18.18.0 + 20.8.0 1.22.19 From d1b847366fab775219af3418bf29e6dc0add928d Mon Sep 17 00:00:00 2001 From: Vincent Latombe Date: Sat, 7 Oct 2023 06:40:56 +0200 Subject: [PATCH 179/305] [JENKINS-72114] Fix serialization of `HudsonPrivateSecurityRealm.UserDetailsImpl` (#8557) [JENKINS-72114] Fix Serialization of HudsonPrivateSecurityRealm.UserDetailsImpl --- .../security/HudsonPrivateSecurityRealm.java | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index 5d78c8887c8f..0331884821d6 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -728,44 +728,62 @@ public boolean isEnabled() { } UserDetails asUserDetails() { - return new UserDetailsImpl(); - } - - private final class UserDetailsImpl implements UserDetails { + return new UserDetailsImpl(getAuthorities2(), getPassword(), getUsername(), isAccountNonExpired(), isAccountNonLocked(), isCredentialsNonExpired(), isEnabled()); + } + + private static final class UserDetailsImpl implements UserDetails { + private static final long serialVersionUID = 1L; + private final Collection authorities; + private final String password; + private final String username; + private final boolean accountNonExpired; + private final boolean accountNonLocked; + private final boolean credentialsNonExpired; + private final boolean enabled; + + UserDetailsImpl(Collection authorities, String password, String username, boolean accountNonExpired, boolean accountNonLocked, boolean credentialsNonExpired, boolean enabled) { + this.authorities = authorities; + this.password = password; + this.username = username; + this.accountNonExpired = accountNonExpired; + this.accountNonLocked = accountNonLocked; + this.credentialsNonExpired = credentialsNonExpired; + this.enabled = enabled; + } @Override public Collection getAuthorities() { - return Details.this.getAuthorities2(); + return authorities; } @Override public String getPassword() { - return Details.this.getPassword(); + return password; } @Override public String getUsername() { - return Details.this.getUsername(); + return username; } @Override public boolean isAccountNonExpired() { - return Details.this.isAccountNonExpired(); + return accountNonExpired; } @Override public boolean isAccountNonLocked() { - return Details.this.isAccountNonLocked(); + return accountNonLocked; } @Override public boolean isCredentialsNonExpired() { - return Details.this.isCredentialsNonExpired(); + return credentialsNonExpired; } @Override public boolean isEnabled() { - return Details.this.isEnabled(); + return enabled; } @Override From 929cc398270d183fc03ff956b5ba420c9dfb6687 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 08:34:14 -0600 Subject: [PATCH 180/305] Update Yarn to v3.6.4 (#8575) * Update Yarn to v3.6.4 * Update yarn in 'war/pom.xml' --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Alexander Brandes --- war/package.json | 2 +- war/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/war/package.json b/war/package.json index 51f217c8cd86..ede6f5376d81 100644 --- a/war/package.json +++ b/war/package.json @@ -62,5 +62,5 @@ "defaults", "not IE 11" ], - "packageManager": "yarn@3.6.3" + "packageManager": "yarn@3.6.4" } diff --git a/war/pom.xml b/war/pom.xml index 294c0457ec21..01b5059e26c6 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -50,8 +50,8 @@ THE SOFTWARE. 1.22.19 - 3.6.3 - 08ead1821a257416e6f217e89365425bf4b6d2430c3279318bedcec1a245fff5 + 3.6.4 + 7f7d51b38db0d94adf25c512e3f3d3b47d23c97922eecc540f7440f116bdb99a From 9fd645a2013c6c95458ea1f4455ec1945423f2e5 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:35:12 +0200 Subject: [PATCH 181/305] Add note that general usage statistics submission is disabled with FIPS (#8573) Co-authored-by: Daniel Beck --- .../main/resources/hudson/model/UsageStatistics/global.groovy | 4 +++- .../resources/hudson/model/UsageStatistics/global.properties | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/model/UsageStatistics/global.groovy b/core/src/main/resources/hudson/model/UsageStatistics/global.groovy index 901f0ead5448..5506a37a67d5 100644 --- a/core/src/main/resources/hudson/model/UsageStatistics/global.groovy +++ b/core/src/main/resources/hudson/model/UsageStatistics/global.groovy @@ -12,7 +12,9 @@ f.section(title: _("Usage Statistics")) { raw(_("disabledBySystemProperty")) } } else if (FIPS140.useCompliantAlgorithms()) { - f.optionalBlock(field: "usageStatisticsCollected", checked: app.usageStatisticsCollected, title: _("statsBlurbFIPS")) + f.optionalBlock(field: "usageStatisticsCollected", checked: app.usageStatisticsCollected, title: _("statsBlurbFIPS")) { + f.description(_("statsDescriptionFIPS")) + } } else { f.optionalBlock(field: "usageStatisticsCollected", checked: app.usageStatisticsCollected, title: _("statsBlurb")) } diff --git a/core/src/main/resources/hudson/model/UsageStatistics/global.properties b/core/src/main/resources/hudson/model/UsageStatistics/global.properties index db939f2a7515..5334747104a4 100644 --- a/core/src/main/resources/hudson/model/UsageStatistics/global.properties +++ b/core/src/main/resources/hudson/model/UsageStatistics/global.properties @@ -24,6 +24,8 @@ statsBlurb=\ Help make Jenkins better by sending anonymous usage statistics and crash reports to the Jenkins project statsBlurbFIPS=\ Help make Jenkins better by sending telemetry to the Jenkins project +statsDescriptionFIPS=\ + General usage statistics are not submitted when FIPS-140 compliance is requested. disabledBySystemProperty=\ The option to send anonymous usage statistics, crash reports and telemetry to the Jenkins project is disabled by a System Property. From 8d870c5a52ce975d8edaa5a0ffb78834fefd10f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 08:51:17 +0100 Subject: [PATCH 182/305] Update dependency sass to v1.69.0 (#8579) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- war/package.json | 2 +- war/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/war/package.json b/war/package.json index ede6f5376d81..492dd60351d7 100644 --- a/war/package.json +++ b/war/package.json @@ -39,7 +39,7 @@ "postcss-preset-env": "9.1.4", "postcss-scss": "4.0.9", "prettier": "3.0.3", - "sass": "1.68.0", + "sass": "1.69.0", "sass-loader": "13.3.2", "style-loader": "3.3.3", "stylelint": "15.10.2", diff --git a/war/yarn.lock b/war/yarn.lock index 3f8ab7996662..b6c936a6a341 100644 --- a/war/yarn.lock +++ b/war/yarn.lock @@ -4375,7 +4375,7 @@ __metadata: postcss-preset-env: 9.1.4 postcss-scss: 4.0.9 prettier: 3.0.3 - sass: 1.68.0 + sass: 1.69.0 sass-loader: 13.3.2 sortablejs: 1.15.0 style-loader: 3.3.3 @@ -6402,16 +6402,16 @@ __metadata: languageName: node linkType: hard -"sass@npm:1.68.0": - version: 1.68.0 - resolution: "sass@npm:1.68.0" +"sass@npm:1.69.0": + version: 1.69.0 + resolution: "sass@npm:1.69.0" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 65ccede83c96768beeb8dcaf67957b7c76b12ff1276bfd2849d7be151d46ba1400048a67717e6e5e4969bc75e87348e5530f5f272833f2e60a891c21a33d8ab0 + checksum: eabea31ea3b1dd529c7eff345c8b6468afe6ab8011bd4f95caa2cffb8fb115cc055ea21de425be6197f7ed22516f5652eccb98d315d592ea152ada553f964b8a languageName: node linkType: hard From 7d1217ba0bdde5380dfa7d5297a1a943bf211c26 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:48:51 +0200 Subject: [PATCH 183/305] Link to jenkins.io/redirect URL, not directly into docs (#8572) --- .../resources/hudson/model/UsageStatistics/global.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/model/UsageStatistics/global.properties b/core/src/main/resources/hudson/model/UsageStatistics/global.properties index 5334747104a4..0eb5e2772031 100644 --- a/core/src/main/resources/hudson/model/UsageStatistics/global.properties +++ b/core/src/main/resources/hudson/model/UsageStatistics/global.properties @@ -27,5 +27,5 @@ statsBlurbFIPS=\ statsDescriptionFIPS=\ General usage statistics are not submitted when FIPS-140 compliance is requested. disabledBySystemProperty=\ - The option to send anonymous usage statistics, crash reports and telemetry to the Jenkins project is disabled by a System Property. + The option to send anonymous usage statistics, crash reports and telemetry to the Jenkins project is disabled by a Java system property. From 566176d5fe80c1a72d0d5c119960fb3bd51b2afd Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:47:23 +0000 Subject: [PATCH 184/305] [maven-release-plugin] prepare release jenkins-2.427 --- bom/pom.xml | 2 +- cli/pom.xml | 2 +- core/pom.xml | 2 +- coverage/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- websocket/jetty10/pom.xml | 2 +- websocket/spi/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index f9c1142a4ab5..1585e1c23152 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index cd79b0518036..ab738cea5ded 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 cli diff --git a/core/pom.xml b/core/pom.xml index f51d46ef586f..83673f9a87db 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 jenkins-core diff --git a/coverage/pom.xml b/coverage/pom.xml index 9f27c8d11cf0..936340402fbc 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 jenkins-coverage diff --git a/pom.xml b/pom.xml index 048c20ac4927..282e42d69646 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 pom Jenkins main module @@ -63,7 +63,7 @@ THE SOFTWARE. scm:git:https://github.com/jenkinsci/jenkins.git scm:git:git@github.com:jenkinsci/jenkins.git - ${scmTag} + jenkins-2.427 https://github.com/jenkinsci/jenkins diff --git a/test/pom.xml b/test/pom.xml index dd7febbbbd33..6b4a7d79f5a2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 01b5059e26c6..003d0c831092 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 jenkins-war diff --git a/websocket/jetty10/pom.xml b/websocket/jetty10/pom.xml index eb4a169a59d1..625400592b17 100644 --- a/websocket/jetty10/pom.xml +++ b/websocket/jetty10/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 ../.. diff --git a/websocket/spi/pom.xml b/websocket/spi/pom.xml index 77abcc3b7248..57d731a8275f 100644 --- a/websocket/spi/pom.xml +++ b/websocket/spi/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - ${revision}${changelist} + 2.427 ../.. From 6674d6f4b19ae6bba3a0e631f6b74208d53de2d3 Mon Sep 17 00:00:00 2001 From: Jenkins Release Bot <66998184+jenkins-release-bot@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:47:35 +0000 Subject: [PATCH 185/305] [maven-release-plugin] prepare for next development iteration --- bom/pom.xml | 2 +- cli/pom.xml | 2 +- core/pom.xml | 2 +- coverage/pom.xml | 2 +- pom.xml | 6 +++--- test/pom.xml | 2 +- war/pom.xml | 2 +- websocket/jetty10/pom.xml | 2 +- websocket/spi/pom.xml | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 1585e1c23152..f9c1142a4ab5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} jenkins-bom diff --git a/cli/pom.xml b/cli/pom.xml index ab738cea5ded..cd79b0518036 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} cli diff --git a/core/pom.xml b/core/pom.xml index 83673f9a87db..f51d46ef586f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} jenkins-core diff --git a/coverage/pom.xml b/coverage/pom.xml index 936340402fbc..9f27c8d11cf0 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} jenkins-coverage diff --git a/pom.xml b/pom.xml index 282e42d69646..e59780092e5e 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} pom Jenkins main module @@ -63,7 +63,7 @@ THE SOFTWARE. scm:git:https://github.com/jenkinsci/jenkins.git scm:git:git@github.com:jenkinsci/jenkins.git - jenkins-2.427 + ${scmTag} https://github.com/jenkinsci/jenkins @@ -73,7 +73,7 @@ THE SOFTWARE. - 2.427 + 2.428 -SNAPSHOT diff --git a/test/pom.xml b/test/pom.xml index 6b4a7d79f5a2..dd7febbbbd33 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} jenkins-test diff --git a/war/pom.xml b/war/pom.xml index 003d0c831092..01b5059e26c6 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} jenkins-war diff --git a/websocket/jetty10/pom.xml b/websocket/jetty10/pom.xml index 625400592b17..eb4a169a59d1 100644 --- a/websocket/jetty10/pom.xml +++ b/websocket/jetty10/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} ../.. diff --git a/websocket/spi/pom.xml b/websocket/spi/pom.xml index 57d731a8275f..77abcc3b7248 100644 --- a/websocket/spi/pom.xml +++ b/websocket/spi/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main jenkins-parent - 2.427 + ${revision}${changelist} ../.. From b0b3aca3b5bb3a2aa5988f0b39386e708bdb6029 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:46:41 +0200 Subject: [PATCH 186/305] Update dependency eslint to v8.51.0 (#8584) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- war/package.json | 2 +- war/yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/war/package.json b/war/package.json index 492dd60351d7..8830b96cd71c 100644 --- a/war/package.json +++ b/war/package.json @@ -30,7 +30,7 @@ "clean-webpack-plugin": "4.0.0", "css-loader": "6.8.1", "css-minimizer-webpack-plugin": "5.0.1", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-config-prettier": "9.0.0", "handlebars-loader": "1.7.3", "mini-css-extract-plugin": "2.7.6", diff --git a/war/yarn.lock b/war/yarn.lock index b6c936a6a341..df1a3959f95e 100644 --- a/war/yarn.lock +++ b/war/yarn.lock @@ -1759,10 +1759,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.50.0": - version: 8.50.0 - resolution: "@eslint/js@npm:8.50.0" - checksum: 302478f2acaaa7228729ec6a04f56641590185e1d8cd1c836a6db8a6b8009f80a57349341be9fbb9aa1721a7a569d1be3ffc598a33300d22816f11832095386c +"@eslint/js@npm:8.51.0": + version: 8.51.0 + resolution: "@eslint/js@npm:8.51.0" + checksum: 0228bf1e1e0414843e56d9ff362a2a72d579c078f93174666f29315690e9e30a8633ad72c923297f7fd7182381b5a476805ff04dac8debe638953eb1ded3ac73 languageName: node linkType: hard @@ -3503,14 +3503,14 @@ __metadata: languageName: node linkType: hard -"eslint@npm:8.50.0": - version: 8.50.0 - resolution: "eslint@npm:8.50.0" +"eslint@npm:8.51.0": + version: 8.51.0 + resolution: "eslint@npm:8.51.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 "@eslint/eslintrc": ^2.1.2 - "@eslint/js": 8.50.0 + "@eslint/js": 8.51.0 "@humanwhocodes/config-array": ^0.11.11 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 @@ -3546,7 +3546,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 9ebfe5615dc84700000d218e32ddfdcfc227ca600f65f18e5541ec34f8902a00356a9a8804d9468fd6c8637a5ef6a3897291dad91ba6579d5b32ffeae5e31768 + checksum: 214fa5d1fcb67af1b8992ce9584ccd85e1aa7a482f8b8ea5b96edc28fa838a18a3b69456db45fc1ed3ef95f1e9efa9714f737292dc681e572d471d02fda9649c languageName: node linkType: hard @@ -4362,7 +4362,7 @@ __metadata: clean-webpack-plugin: 4.0.0 css-loader: 6.8.1 css-minimizer-webpack-plugin: 5.0.1 - eslint: 8.50.0 + eslint: 8.51.0 eslint-config-prettier: 9.0.0 handlebars: 4.7.8 handlebars-loader: 1.7.3 From 16debcb972c4d38c421667b633acebfc68855158 Mon Sep 17 00:00:00 2001 From: Abhishek Maity Date: Tue, 10 Oct 2023 23:03:33 +0530 Subject: [PATCH 187/305] [JENKINS-72028] at ` test/src/test/java/hudson/` replace deprecated `URL()` (#8569) * missing 'alt' attribute added * missing 'alt' attribute added * replaced 'new URL' to 'new URI' per Java 11 target codebase * Revert "replaced 'new URL' to 'new URI' per Java 11 target codebase" This reverts commit aeef4c1e4bb57a4832dbe368920aa0716a3f68e7. * at test classes replaced deprecated URL() with URI#.toURL() * Update test/src/test/java/hudson/model/UpdateSiteTest.java as per review Co-authored-by: James Nord * add missing quote * Spotless --------- Co-authored-by: James Nord Co-authored-by: Alexander Brandes Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com> --- .../test/java/hudson/TcpSlaveAgentListenerTest.java | 6 +++--- .../diagnosis/HudsonHomeDiskUsageMonitorTest.java | 8 ++++---- test/src/test/java/hudson/jobs/CreateItemTest.java | 13 +++++++------ .../java/hudson/markup/MarkupFormatterTest.java | 4 ++-- .../test/java/hudson/model/AbstractProjectTest.java | 5 +++-- .../hudson/model/DirectlyModifiableViewTest.java | 4 ++-- test/src/test/java/hudson/model/HudsonTest.java | 3 ++- test/src/test/java/hudson/model/ItemsTest.java | 10 +++++----- test/src/test/java/hudson/model/ProjectTest.java | 4 ++-- test/src/test/java/hudson/model/QueueTest.java | 4 ++-- .../test/java/hudson/model/UpdateCenterTest.java | 3 ++- test/src/test/java/hudson/model/UpdateSiteTest.java | 3 ++- .../src/test/java/hudson/model/UserRestartTest.java | 6 +++--- .../security/HudsonPrivateSecurityRealmTest.java | 4 ++-- .../security/csrf/DefaultCrumbIssuerTest.java | 4 ++-- .../hudson/util/RobustReflectionConverterTest.java | 6 +++--- 16 files changed, 46 insertions(+), 41 deletions(-) diff --git a/test/src/test/java/hudson/TcpSlaveAgentListenerTest.java b/test/src/test/java/hudson/TcpSlaveAgentListenerTest.java index 8d6f13d9807a..c589b1a46d55 100644 --- a/test/src/test/java/hudson/TcpSlaveAgentListenerTest.java +++ b/test/src/test/java/hudson/TcpSlaveAgentListenerTest.java @@ -6,7 +6,7 @@ import static org.junit.Assert.assertEquals; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import jenkins.model.Jenkins; import org.htmlunit.Page; import org.htmlunit.TextPage; @@ -40,12 +40,12 @@ public void diagnostics() throws Exception { int p = r.jenkins.getTcpSlaveAgentListener().getPort(); WebClient wc = r.createWebClient(); - TextPage text = wc.getPage(new URL("http://localhost:" + p + "/")); + TextPage text = wc.getPage(new URI("http://localhost:" + p + "/").toURL()); String c = text.getContent(); assertThat(c, containsString(Jenkins.VERSION)); wc.setThrowExceptionOnFailingStatusCode(false); - Page page = wc.getPage(new URL("http://localhost:" + p + "/xxx")); + Page page = wc.getPage(new URI("http://localhost:" + p + "/xxx").toURL()); assertEquals(HttpURLConnection.HTTP_NOT_FOUND, page.getWebResponse().getStatusCode()); } } diff --git a/test/src/test/java/hudson/diagnosis/HudsonHomeDiskUsageMonitorTest.java b/test/src/test/java/hudson/diagnosis/HudsonHomeDiskUsageMonitorTest.java index f21f5088128d..0e57e336b49f 100644 --- a/test/src/test/java/hudson/diagnosis/HudsonHomeDiskUsageMonitorTest.java +++ b/test/src/test/java/hudson/diagnosis/HudsonHomeDiskUsageMonitorTest.java @@ -9,7 +9,7 @@ import hudson.security.GlobalMatrixAuthorizationStrategy; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.util.List; import jenkins.model.Jenkins; import org.htmlunit.ElementNotFoundException; @@ -71,7 +71,7 @@ public void noAccessForNonAdmin() throws Exception { User bob = User.getById("bob", true); User administrator = User.getById("administrator", true); - WebRequest request = new WebRequest(new URL(wc.getContextPath() + "administrativeMonitor/hudsonHomeIsFull/act"), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(wc.getContextPath() + "administrativeMonitor/hudsonHomeIsFull/act").toURL(), HttpMethod.POST); NameValuePair param = new NameValuePair("no", "true"); request.setRequestParameters(List.of(param)); @@ -83,12 +83,12 @@ public void noAccessForNonAdmin() throws Exception { assertTrue(mon.isEnabled()); - WebRequest requestReadOnly = new WebRequest(new URL(wc.getContextPath() + "administrativeMonitor/hudsonHomeIsFull"), HttpMethod.GET); + WebRequest requestReadOnly = new WebRequest(new URI(wc.getContextPath() + "administrativeMonitor/hudsonHomeIsFull").toURL(), HttpMethod.GET); p = wc.getPage(requestReadOnly); assertEquals(HttpURLConnection.HTTP_FORBIDDEN, p.getWebResponse().getStatusCode()); wc.withBasicApiToken(administrator); - request = new WebRequest(new URL(wc.getContextPath() + "administrativeMonitor/hudsonHomeIsFull/act"), HttpMethod.POST); + request = new WebRequest(new URI(wc.getContextPath() + "administrativeMonitor/hudsonHomeIsFull/act").toURL(), HttpMethod.POST); request.setRequestParameters(List.of(param)); p = wc.getPage(request); assertEquals(HttpURLConnection.HTTP_OK, p.getWebResponse().getStatusCode()); diff --git a/test/src/test/java/hudson/jobs/CreateItemTest.java b/test/src/test/java/hudson/jobs/CreateItemTest.java index c8d511ce168d..df8c577e30dc 100644 --- a/test/src/test/java/hudson/jobs/CreateItemTest.java +++ b/test/src/test/java/hudson/jobs/CreateItemTest.java @@ -35,6 +35,7 @@ import hudson.model.ItemGroup; import hudson.model.listeners.ItemListener; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import java.text.MessageFormat; import org.htmlunit.HttpMethod; @@ -71,9 +72,9 @@ public void testCreateItemFromCopy() throws Exception { rule.createFreeStyleProject(sourceJobName); String newJobName = "newJob"; - URL apiURL = new URL(MessageFormat.format( + URL apiURL = new URI(MessageFormat.format( "{0}createItem?mode=copy&from={1}&name={2}", - rule.getURL().toString(), sourceJobName, newJobName)); + rule.getURL().toString(), sourceJobName, newJobName)).toURL(); WebRequest request = new WebRequest(apiURL, HttpMethod.POST); deleteContentTypeHeader(request); @@ -95,9 +96,9 @@ public void vetoCreateItemFromCopy() throws Exception { rule.createFreeStyleProject(sourceJobName); String newJobName = "newJob"; - URL apiURL = new URL(MessageFormat.format( + URL apiURL = new URI(MessageFormat.format( "{0}createItem?mode=copy&from={1}&name={2}", - rule.getURL().toString(), sourceJobName, newJobName)); + rule.getURL().toString(), sourceJobName, newJobName)).toURL(); WebRequest request = new WebRequest(apiURL, HttpMethod.POST); deleteContentTypeHeader(request); @@ -124,10 +125,10 @@ public void createWithFolderPaths() throws Exception { JenkinsRule.WebClient wc = rule.createWebClient(); - wc.getPage(new WebRequest(new URL(d2.getAbsoluteUrl() + "createItem?mode=copy&name=p2&from=../d1/p"), HttpMethod.POST)); + wc.getPage(new WebRequest(new URI(d2.getAbsoluteUrl() + "createItem?mode=copy&name=p2&from=../d1/p").toURL(), HttpMethod.POST)); assertNotNull(d2.getItem("p2")); - wc.getPage(new WebRequest(new URL(d2.getAbsoluteUrl() + "createItem?mode=copy&name=p3&from=/d1/p"), HttpMethod.POST)); + wc.getPage(new WebRequest(new URI(d2.getAbsoluteUrl() + "createItem?mode=copy&name=p3&from=/d1/p").toURL(), HttpMethod.POST)); assertNotNull(d2.getItem("p3")); } diff --git a/test/src/test/java/hudson/markup/MarkupFormatterTest.java b/test/src/test/java/hudson/markup/MarkupFormatterTest.java index f5e0e05095e5..a9e1283fb53e 100644 --- a/test/src/test/java/hudson/markup/MarkupFormatterTest.java +++ b/test/src/test/java/hudson/markup/MarkupFormatterTest.java @@ -33,7 +33,7 @@ import hudson.security.HudsonPrivateSecurityRealm; import java.io.IOException; import java.io.Writer; -import java.net.URL; +import java.net.URI; import org.htmlunit.HttpMethod; import org.htmlunit.Page; import org.htmlunit.WebRequest; @@ -104,7 +104,7 @@ public void security2153RequiresPOST() throws Exception { @Issue("SECURITY-2153") public void security2153SetsCSP() throws Exception { final JenkinsRule.WebClient wc = j.createWebClient(); - final Page htmlPage = wc.getPage(wc.addCrumb(new WebRequest(new URL(j.jenkins.getRootUrl() + "/markupFormatter/previewDescription?text=lolwut"), HttpMethod.POST))); + final Page htmlPage = wc.getPage(wc.addCrumb(new WebRequest(new URI(j.jenkins.getRootUrl() + "/markupFormatter/previewDescription?text=lolwut").toURL(), HttpMethod.POST))); final WebResponse response = htmlPage.getWebResponse(); assertEquals(200, response.getStatusCode()); assertThat(response.getContentAsString(), containsString("lolwut")); diff --git a/test/src/test/java/hudson/model/AbstractProjectTest.java b/test/src/test/java/hudson/model/AbstractProjectTest.java index 2da8fc430448..d4108e3a4c93 100644 --- a/test/src/test/java/hudson/model/AbstractProjectTest.java +++ b/test/src/test/java/hudson/model/AbstractProjectTest.java @@ -56,6 +56,7 @@ import hudson.util.StreamTaskListener; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -140,7 +141,7 @@ public void wipeWorkspaceProtected() throws Exception { // make sure that the action link is protected JenkinsRule.WebClient wc = j.createWebClient() .withThrowExceptionOnFailingStatusCode(false); - Page page = wc.getPage(new WebRequest(new URL(wc.getContextPath() + project.getUrl() + "doWipeOutWorkspace"), HttpMethod.POST)); + Page page = wc.getPage(new WebRequest(new URI(wc.getContextPath() + project.getUrl() + "doWipeOutWorkspace").toURL(), HttpMethod.POST)); assertEquals(HttpURLConnection.HTTP_FORBIDDEN, page.getWebResponse().getStatusCode()); } @@ -323,7 +324,7 @@ public void deleteRedirect() throws Exception { private String deleteRedirectTarget(String job) throws Exception { JenkinsRule.WebClient wc = j.createWebClient(); String base = wc.getContextPath(); - String loc = wc.getPage(wc.addCrumb(new WebRequest(new URL(base + job + "/doDelete"), HttpMethod.POST))).getUrl().toString(); + String loc = wc.getPage(wc.addCrumb(new WebRequest(new URI(base + job + "/doDelete").toURL(), HttpMethod.POST))).getUrl().toString(); assert loc.startsWith(base) : loc; return loc.substring(base.length()); } diff --git a/test/src/test/java/hudson/model/DirectlyModifiableViewTest.java b/test/src/test/java/hudson/model/DirectlyModifiableViewTest.java index aab9da2cfe65..52f46ca8f57e 100644 --- a/test/src/test/java/hudson/model/DirectlyModifiableViewTest.java +++ b/test/src/test/java/hudson/model/DirectlyModifiableViewTest.java @@ -29,7 +29,7 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; -import java.net.URL; +import java.net.URI; import org.hamcrest.Matchers; import org.htmlunit.HttpMethod; import org.htmlunit.Page; @@ -188,7 +188,7 @@ private Page doPost(View view, String path) throws Exception { WebClient wc = j.createWebClient() .withThrowExceptionOnFailingStatusCode(false); WebRequest req = new WebRequest( - new URL(j.jenkins.getRootUrl() + view.getUrl() + path), + new URI(j.jenkins.getRootUrl() + view.getUrl() + path).toURL(), HttpMethod.POST ); diff --git a/test/src/test/java/hudson/model/HudsonTest.java b/test/src/test/java/hudson/model/HudsonTest.java index a4fff0108458..822fd28eaf04 100644 --- a/test/src/test/java/hudson/model/HudsonTest.java +++ b/test/src/test/java/hudson/model/HudsonTest.java @@ -39,6 +39,7 @@ import hudson.tasks.BuildStep; import java.lang.reflect.Field; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import java.util.List; import jenkins.model.Jenkins; @@ -177,7 +178,7 @@ public void deleteHudsonComputer() throws Exception { wc.setThrowExceptionOnFailingStatusCode(false); // try to delete it by hitting the final URL directly - WebRequest req = new WebRequest(new URL(wc.getContextPath() + "computer/(built-in)/doDelete"), HttpMethod.POST); + WebRequest req = new WebRequest(new URI(wc.getContextPath() + "computer/(built-in)/doDelete").toURL(), HttpMethod.POST); page = wc.getPage(wc.addCrumb(req)); assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, page.getWebResponse().getStatusCode()); diff --git a/test/src/test/java/hudson/model/ItemsTest.java b/test/src/test/java/hudson/model/ItemsTest.java index 37c0befc3081..b41e9d48bdcd 100644 --- a/test/src/test/java/hudson/model/ItemsTest.java +++ b/test/src/test/java/hudson/model/ItemsTest.java @@ -41,7 +41,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Arrays; import jenkins.model.Jenkins; @@ -228,7 +228,7 @@ private enum OverwriteTactic { // redirect perversely counts as a failure .withRedirectEnabled(false) .withThrowExceptionOnFailingStatusCode(false); - WebResponse webResponse = wc.getPage(new WebRequest(new URL(wc.getContextPath() + "createItem?name=" + target + "&mode=hudson.model.FreeStyleProject"), HttpMethod.POST)).getWebResponse(); + WebResponse webResponse = wc.getPage(new WebRequest(new URI(wc.getContextPath() + "createItem?name=" + target + "&mode=hudson.model.FreeStyleProject").toURL(), HttpMethod.POST)).getWebResponse(); if (webResponse.getStatusCode() != HttpURLConnection.HTTP_MOVED_TEMP) { throw new FailingHttpStatusCodeException(webResponse); } @@ -241,7 +241,7 @@ private enum OverwriteTactic { JenkinsRule.WebClient wc = wc(r) .withRedirectEnabled(false) .withThrowExceptionOnFailingStatusCode(false); - WebResponse webResponse = wc.getPage(new WebRequest(new URL(wc.getContextPath() + "createItem?name=" + target + "&mode=copy&from=dupe"), HttpMethod.POST)).getWebResponse(); + WebResponse webResponse = wc.getPage(new WebRequest(new URI(wc.getContextPath() + "createItem?name=" + target + "&mode=copy&from=dupe").toURL(), HttpMethod.POST)).getWebResponse(); r.jenkins.getItem("dupe").delete(); if (webResponse.getStatusCode() != HttpURLConnection.HTTP_MOVED_TEMP) { throw new FailingHttpStatusCodeException(webResponse); @@ -252,7 +252,7 @@ private enum OverwriteTactic { REST_CREATE { @Override void run(JenkinsRule r, String target) throws Exception { JenkinsRule.WebClient wc = wc(r); - WebRequest req = new WebRequest(new URL(wc.getContextPath() + "createItem?name=" + target), HttpMethod.POST); + WebRequest req = new WebRequest(new URI(wc.getContextPath() + "createItem?name=" + target).toURL(), HttpMethod.POST); req.setAdditionalHeader("Content-Type", "application/xml"); req.setRequestBody(""); wc.getPage(req); @@ -265,7 +265,7 @@ private enum OverwriteTactic { JenkinsRule.WebClient wc = wc(r) .withRedirectEnabled(false) .withThrowExceptionOnFailingStatusCode(false); - WebResponse webResponse = wc.getPage(new WebRequest(new URL(wc.getContextPath() + "job/dupe/doRename?newName=" + target), HttpMethod.POST)).getWebResponse(); + WebResponse webResponse = wc.getPage(new WebRequest(new URI(wc.getContextPath() + "job/dupe/doRename?newName=" + target).toURL(), HttpMethod.POST)).getWebResponse(); if (webResponse.getStatusCode() != HttpURLConnection.HTTP_MOVED_TEMP) { r.jenkins.getItem("dupe").delete(); throw new FailingHttpStatusCodeException(webResponse); diff --git a/test/src/test/java/hudson/model/ProjectTest.java b/test/src/test/java/hudson/model/ProjectTest.java index 8d2f5e6bc7cc..82d9266e0e62 100644 --- a/test/src/test/java/hudson/model/ProjectTest.java +++ b/test/src/test/java/hudson/model/ProjectTest.java @@ -73,7 +73,7 @@ import java.io.IOException; import java.io.Serializable; import java.io.UncheckedIOException; -import java.net.URL; +import java.net.URI; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.ArrayList; @@ -618,7 +618,7 @@ public void testDoDoWipeOutWorkspace() throws Exception { JenkinsRule.WebClient wc = j.createWebClient(); wc.withBasicCredentials(user.getId(), "password"); - WebRequest request = new WebRequest(new URL(wc.getContextPath() + project.getUrl() + "doWipeOutWorkspace"), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(wc.getContextPath() + project.getUrl() + "doWipeOutWorkspace").toURL(), HttpMethod.POST); HtmlPage p = wc.getPage(request); assertEquals(200, p.getWebResponse().getStatusCode()); diff --git a/test/src/test/java/hudson/model/QueueTest.java b/test/src/test/java/hudson/model/QueueTest.java index e31e2acdd1c9..bdacb2f72a63 100644 --- a/test/src/test/java/hudson/model/QueueTest.java +++ b/test/src/test/java/hudson/model/QueueTest.java @@ -83,7 +83,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; @@ -1199,7 +1199,7 @@ private void checkCancelOperationUsingUrl(Function urlProvid Queue.Item currentOne = items[0]; assertFalse(currentOne.getFuture().isCancelled()); - WebRequest request = new WebRequest(new URL(r.getURL() + urlProvider.apply(currentOne)), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(r.getURL() + urlProvider.apply(currentOne)).toURL(), HttpMethod.POST); { // user without right cannot cancel JenkinsRule.WebClient wc = r.createWebClient() diff --git a/test/src/test/java/hudson/model/UpdateCenterTest.java b/test/src/test/java/hudson/model/UpdateCenterTest.java index 998e9badd67e..57ecf1d2d8bd 100644 --- a/test/src/test/java/hudson/model/UpdateCenterTest.java +++ b/test/src/test/java/hudson/model/UpdateCenterTest.java @@ -31,6 +31,7 @@ import static org.junit.Assume.assumeNoException; import java.io.ByteArrayInputStream; +import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateFactory; @@ -58,7 +59,7 @@ public class UpdateCenterTest { } private void doData(String location) throws Exception { - URL url = new URL(location); + URL url = new URI(location).toURL(); String jsonp = DownloadService.loadJSON(url); JSONObject json = JSONObject.fromObject(jsonp); diff --git a/test/src/test/java/hudson/model/UpdateSiteTest.java b/test/src/test/java/hudson/model/UpdateSiteTest.java index e355aec53399..5902387c2e83 100644 --- a/test/src/test/java/hudson/model/UpdateSiteTest.java +++ b/test/src/test/java/hudson/model/UpdateSiteTest.java @@ -38,6 +38,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -130,7 +131,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques } }); server.start(); - baseUrl = new URL("http", "localhost", connector.getLocalPort(), RELATIVE_BASE); + baseUrl = new URI("http", null, "localhost", connector.getLocalPort(), RELATIVE_BASE, null, null).toURL(); } @After diff --git a/test/src/test/java/hudson/model/UserRestartTest.java b/test/src/test/java/hudson/model/UserRestartTest.java index 95641933beed..d5b84b352029 100644 --- a/test/src/test/java/hudson/model/UserRestartTest.java +++ b/test/src/test/java/hudson/model/UserRestartTest.java @@ -33,7 +33,7 @@ import hudson.FilePath; import hudson.tasks.Mailer; -import java.net.URL; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Base64; import org.htmlunit.WebRequest; @@ -115,7 +115,7 @@ public void legacyConfigMoveCannotEscapeUserFolder() throws Throwable { JenkinsRule.WebClient wc = r.createWebClient() .withThrowExceptionOnFailingStatusCode(false); - WebRequest request = new WebRequest(new URL(r.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(r.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("..", "any-password")); wc.getPage(request); } @@ -123,7 +123,7 @@ public void legacyConfigMoveCannotEscapeUserFolder() throws Throwable { JenkinsRule.WebClient wc = r.createWebClient() .withThrowExceptionOnFailingStatusCode(false); - WebRequest request = new WebRequest(new URL(r.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(r.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("../users/..", "any-password")); wc.getPage(request); } diff --git a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java index 544715d2d2f1..b172e0e72e3b 100644 --- a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java +++ b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmTest.java @@ -44,7 +44,7 @@ import hudson.security.HudsonPrivateSecurityRealm.Details; import hudson.security.pages.SignupPage; import java.lang.reflect.Field; -import java.net.URL; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -356,7 +356,7 @@ private void createFirstAccount(String login) throws Exception { info.password2 = login; info.fullname = StringUtils.capitalize(login); - WebRequest request = new WebRequest(new URL(wc.getContextPath() + "securityRealm/createFirstAccount"), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(wc.getContextPath() + "securityRealm/createFirstAccount").toURL(), HttpMethod.POST); request.setRequestParameters(Arrays.asList( new NameValuePair("username", login), new NameValuePair("password1", login), diff --git a/test/src/test/java/hudson/security/csrf/DefaultCrumbIssuerTest.java b/test/src/test/java/hudson/security/csrf/DefaultCrumbIssuerTest.java index b6b417f8dc6f..2904af69fc4d 100644 --- a/test/src/test/java/hudson/security/csrf/DefaultCrumbIssuerTest.java +++ b/test/src/test/java/hudson/security/csrf/DefaultCrumbIssuerTest.java @@ -17,7 +17,7 @@ import hudson.model.User; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import javax.servlet.http.HttpServletResponse; import jenkins.model.Jenkins; import net.sf.json.JSONObject; @@ -303,7 +303,7 @@ public void twoRequestsWithoutSessionGetDifferentCrumbs() throws Exception { } private WebRequest createRequestForJobCreation(String jobName) throws Exception { - WebRequest req = new WebRequest(new URL(r.getURL() + "createItem?name=" + jobName), HttpMethod.POST); + WebRequest req = new WebRequest(new URI(r.getURL() + "createItem?name=" + jobName).toURL(), HttpMethod.POST); req.setAdditionalHeader("Content-Type", "application/xml"); req.setRequestBody(""); return req; diff --git a/test/src/test/java/hudson/util/RobustReflectionConverterTest.java b/test/src/test/java/hudson/util/RobustReflectionConverterTest.java index da667a80d802..ff0b255114e1 100644 --- a/test/src/test/java/hudson/util/RobustReflectionConverterTest.java +++ b/test/src/test/java/hudson/util/RobustReflectionConverterTest.java @@ -44,7 +44,7 @@ import hudson.security.ACL; import java.io.ByteArrayInputStream; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import java.nio.charset.Charset; import java.util.Map; import java.util.Set; @@ -206,7 +206,7 @@ public void testRestInterfaceFailure() throws Exception { r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); WebClient wc = r.createWebClient(); wc.withBasicApiToken("test"); - WebRequest req = new WebRequest(new URL(wc.getContextPath() + String.format("%s/config.xml", p.getUrl())), HttpMethod.POST); + WebRequest req = new WebRequest(new URI(wc.getContextPath() + String.format("%s/config.xml", p.getUrl())).toURL(), HttpMethod.POST); req.setEncodingType(null); req.setRequestBody(String.format(CONFIGURATION_TEMPLATE, "badvalue", AcceptOnlySpecificKeyword.ACCEPT_KEYWORD)); wc.getPage(req); @@ -237,7 +237,7 @@ public void testRestInterfaceFailure() throws Exception { WebClient wc = r.createWebClient() .withThrowExceptionOnFailingStatusCode(false); wc.withBasicApiToken("test"); - WebRequest req = new WebRequest(new URL(wc.getContextPath() + String.format("%s/config.xml", p.getUrl())), HttpMethod.POST); + WebRequest req = new WebRequest(new URI(wc.getContextPath() + String.format("%s/config.xml", p.getUrl())).toURL(), HttpMethod.POST); req.setEncodingType(null); req.setRequestBody(String.format(CONFIGURATION_TEMPLATE, AcceptOnlySpecificKeyword.ACCEPT_KEYWORD, "badvalue")); From 38a61a36fafab4e331b6216e7402839c8fe41780 Mon Sep 17 00:00:00 2001 From: Abhishek Maity Date: Tue, 10 Oct 2023 23:04:21 +0530 Subject: [PATCH 188/305] [JENKINS-72028] at `test/src/test/java/jenkins/` replace deprecated `URL()` (#8570) * missing 'alt' attribute added * missing 'alt' attribute added * replaced 'new URL' to 'new URI' per Java 11 target codebase * Revert "replaced 'new URL' to 'new URI' per Java 11 target codebase" This reverts commit aeef4c1e4bb57a4832dbe368920aa0716a3f68e7. * at test classes replaced deprecated URL() with URI#.toURL() * Spotless --------- Co-authored-by: James Nord Co-authored-by: Alexander Brandes --- .../java/jenkins/model/AssetManagerTest.java | 3 ++- .../test/java/jenkins/model/JenkinsTest.java | 3 ++- .../jenkins/security/ApiTokenPropertyTest.java | 3 ++- .../BasicHeaderApiTokenAuthenticatorTest.java | 14 +++++++------- .../jenkins/security/ResourceDomainTest.java | 5 +++-- .../jenkins/security/Security2761Test.java | 4 ++-- .../java/jenkins/security/Security637Test.java | 13 +++++++------ .../apitoken/ApiTokenStatsRestartTest.java | 3 ++- .../security/apitoken/ApiTokenStatsTest.java | 3 ++- .../seed/UserSeedChangeListenerTest.java | 4 ++-- .../seed/UserSeedPropertyRestartTest.java | 4 ++-- .../security/seed/UserSeedPropertyTest.java | 4 ++-- .../security/stapler/Security400Test.java | 18 +++++++++--------- .../security/stapler/Security914Test.java | 6 +++--- 14 files changed, 47 insertions(+), 40 deletions(-) diff --git a/test/src/test/java/jenkins/model/AssetManagerTest.java b/test/src/test/java/jenkins/model/AssetManagerTest.java index d00541d4a554..9683d6479bfd 100644 --- a/test/src/test/java/jenkins/model/AssetManagerTest.java +++ b/test/src/test/java/jenkins/model/AssetManagerTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import org.junit.Rule; import org.junit.Test; @@ -41,7 +42,7 @@ public class AssetManagerTest { @Test @Issue("JENKINS-58736") public void emptyAssetDoesNotThrowError() throws Exception { - URL url = new URL(j.getURL() + "assets"); + URL url = new URI(j.getURL() + "assets").toURL(); HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); assertEquals(HttpURLConnection.HTTP_NOT_FOUND, httpCon.getResponseCode()); } diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index 6750eabbd3f1..a8d846c5d79b 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -77,6 +77,7 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.Socket; +import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.LinkOption; @@ -439,7 +440,7 @@ public void testDoEval() throws Exception { } private Page eval(WebClient wc) throws Exception { - WebRequest req = new WebRequest(new URL(wc.getContextPath() + "eval"), HttpMethod.POST); + WebRequest req = new WebRequest(new URI(wc.getContextPath() + "eval").toURL(), HttpMethod.POST); req.setEncodingType(null); req.setRequestBody("${1+2}"); return wc.getPage(req); diff --git a/test/src/test/java/jenkins/security/ApiTokenPropertyTest.java b/test/src/test/java/jenkins/security/ApiTokenPropertyTest.java index 4f9080b4ac9e..30361ed8658c 100644 --- a/test/src/test/java/jenkins/security/ApiTokenPropertyTest.java +++ b/test/src/test/java/jenkins/security/ApiTokenPropertyTest.java @@ -22,6 +22,7 @@ import hudson.security.ACL; import hudson.security.ACLContext; import java.net.HttpURLConnection; +import java.net.URI; import java.net.URL; import java.util.Collection; import java.util.List; @@ -151,7 +152,7 @@ public void adminsShouldBeUnableToChangeTokensByDefault() throws Exception { requirePOST.getWebResponse().getStatusCode()); wc.setThrowExceptionOnFailingStatusCode(true); - WebRequest request = new WebRequest(new URL(j.getURL().toString() + foo.getUrl() + "/" + descriptor.getDescriptorUrl() + "/changeToken"), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(j.getURL().toString() + foo.getUrl() + "/" + descriptor.getDescriptorUrl() + "/changeToken").toURL(), HttpMethod.POST); HtmlPage res = wc.getPage(request); // TODO This nicer alternative requires https://github.com/jenkinsci/jenkins/pull/2268 or similar to work diff --git a/test/src/test/java/jenkins/security/BasicHeaderApiTokenAuthenticatorTest.java b/test/src/test/java/jenkins/security/BasicHeaderApiTokenAuthenticatorTest.java index 9ca39bb05f7f..873194dfb089 100644 --- a/test/src/test/java/jenkins/security/BasicHeaderApiTokenAuthenticatorTest.java +++ b/test/src/test/java/jenkins/security/BasicHeaderApiTokenAuthenticatorTest.java @@ -33,7 +33,7 @@ import hudson.ExtensionComponent; import hudson.model.User; -import java.net.URL; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.concurrent.atomic.AtomicReference; @@ -83,12 +83,12 @@ public void legacyToken_regularCase() throws Throwable { wc.getOptions().setThrowExceptionOnFailingStatusCode(false); { // for invalid token, no effect - WebRequest request = new WebRequest(new URL(j.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(j.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("user1", "invalid-token")); assertThat(wc.getPage(request).getWebResponse().getStatusCode(), equalTo(401)); } { // for invalid user, no effect - WebRequest request = new WebRequest(new URL(j.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(j.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("user-not-valid", token.get())); assertThat(wc.getPage(request).getWebResponse().getStatusCode(), equalTo(401)); } @@ -96,7 +96,7 @@ public void legacyToken_regularCase() throws Throwable { assertNull(User.getById("user-not-valid", false)); { // valid user with valid token, ok - WebRequest request = new WebRequest(new URL(j.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(j.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("user1", token.get())); XmlPage xmlPage = wc.getPage(request); assertThat(xmlPage, hasXPath("//name", is("user1"))); @@ -131,12 +131,12 @@ public void legacyToken_withoutLastGrantedAuthorities() throws Throwable { wc.getOptions().setThrowExceptionOnFailingStatusCode(false); { // for invalid token, no effect - WebRequest request = new WebRequest(new URL(j.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(j.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("user1", "invalid-token")); assertThat(wc.getPage(request).getWebResponse().getStatusCode(), equalTo(401)); } { // for invalid user, no effect - WebRequest request = new WebRequest(new URL(j.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(j.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("user-not-valid", token.get())); assertThat(wc.getPage(request).getWebResponse().getStatusCode(), equalTo(401)); } @@ -145,7 +145,7 @@ public void legacyToken_withoutLastGrantedAuthorities() throws Throwable { assertNull(User.getById("user-not-valid", false)); { // valid user with valid token, ok - WebRequest request = new WebRequest(new URL(j.jenkins.getRootUrl() + "whoAmI/api/xml")); + WebRequest request = new WebRequest(new URI(j.jenkins.getRootUrl() + "whoAmI/api/xml").toURL()); request.setAdditionalHeader("Authorization", base64("user1", token.get())); XmlPage xmlPage = wc.getPage(request); assertThat(xmlPage, hasXPath("//name", is("user1"))); diff --git a/test/src/test/java/jenkins/security/ResourceDomainTest.java b/test/src/test/java/jenkins/security/ResourceDomainTest.java index 9a6f88a2a035..feeed98b5f68 100644 --- a/test/src/test/java/jenkins/security/ResourceDomainTest.java +++ b/test/src/test/java/jenkins/security/ResourceDomainTest.java @@ -12,6 +12,7 @@ import hudson.model.FreeStyleProject; import hudson.model.Item; import hudson.model.UnprotectedRootAction; +import java.net.URI; import java.net.URL; import java.time.Instant; import java.util.UUID; @@ -352,13 +353,13 @@ public void testMoreUrlEncoding() throws Exception { URL url = page.getUrl(); Assert.assertTrue("page is served by resource domain", url.toString().contains("/static-files/")); - URL dirUrl = new URL(url.toString().replace("%20100%25%20evil%20content%20.html", "")); + URL dirUrl = new URI(url.toString().replace("%20100%25%20evil%20content%20.html", "")).toURL(); Page dirPage = webClient.getPage(dirUrl); Assert.assertEquals("page is found", 200, dirPage.getWebResponse().getStatusCode()); Assert.assertTrue("page content is HTML", dirPage.getWebResponse().getContentAsString().contains("href")); Assert.assertTrue("page content references file", dirPage.getWebResponse().getContentAsString().contains("evil content")); - URL topDirUrl = new URL(url.toString().replace("%20100%25%20evil%20dir%20name%20%20%20/%20100%25%20evil%20content%20.html", "")); + URL topDirUrl = new URI(url.toString().replace("%20100%25%20evil%20dir%20name%20%20%20/%20100%25%20evil%20content%20.html", "")).toURL(); Page topDirPage = webClient.getPage(topDirUrl); Assert.assertEquals("page is found", 200, topDirPage.getWebResponse().getStatusCode()); Assert.assertTrue("page content is HTML", topDirPage.getWebResponse().getContentAsString().contains("href")); diff --git a/test/src/test/java/jenkins/security/Security2761Test.java b/test/src/test/java/jenkins/security/Security2761Test.java index f1c75d9e7647..87f51fa2f517 100644 --- a/test/src/test/java/jenkins/security/Security2761Test.java +++ b/test/src/test/java/jenkins/security/Security2761Test.java @@ -7,7 +7,7 @@ import hudson.model.InvisibleAction; import hudson.model.UnprotectedRootAction; -import java.net.URL; +import java.net.URI; import java.util.concurrent.atomic.AtomicBoolean; import org.htmlunit.html.HtmlPage; import org.junit.Rule; @@ -29,7 +29,7 @@ public void symbolIconAltIsEscaped() throws Exception { JenkinsRule.WebClient wc = j.createWebClient(); wc.setAlertHandler((page, s) -> alerted.set(true)); - HtmlPage page = wc.getPage(new URL(wc.getContextPath() + ACTION_URL)); + HtmlPage page = wc.getPage(new URI(wc.getContextPath() + ACTION_URL).toURL()); String responseContent = page.getWebResponse().getContentAsString(); wc.waitForBackgroundJavaScript(5000); diff --git a/test/src/test/java/jenkins/security/Security637Test.java b/test/src/test/java/jenkins/security/Security637Test.java index 824a06d56001..71550b642324 100644 --- a/test/src/test/java/jenkins/security/Security637Test.java +++ b/test/src/test/java/jenkins/security/Security637Test.java @@ -42,6 +42,7 @@ import hudson.slaves.DumbSlave; import java.io.IOException; import java.lang.reflect.Field; +import java.net.URI; import java.net.URL; import java.net.URLStreamHandler; import java.util.Collections; @@ -95,8 +96,8 @@ public void urlDnsEquivalence() throws Throwable { sessions.then(j -> { // due to the DNS resolution they are equal assertEquals( - new URL("https://jenkins.io"), - new URL("https://www.jenkins.io") + new URI("https://jenkins.io").toURL(), + new URI("https://www.jenkins.io").toURL() ); }); } @@ -127,7 +128,7 @@ private static class URLBuilderCallable extends MasterToSlaveCallable futureBuild = p.scheduleBuild2(0); futureBuild.waitForStart(); - WebRequest request = new WebRequest(new URL(j.getURL() + "computers/0/executors/0/stopBuild"), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(j.getURL() + "computers/0/executors/0/stopBuild").toURL(), HttpMethod.POST); Page page = wc.getPage(request); assertEquals(404, page.getWebResponse().getStatusCode()); assertRequestWasNotBlocked(); @@ -340,7 +340,7 @@ public void ensureDoStopBuildWorks() throws Exception { FreeStyleBuild build = futureBuild.waitForStart(); String runExtId = URLEncoder.encode(build.getExternalizableId(), StandardCharsets.UTF_8); - WebRequest request = new WebRequest(new URL(j.getURL() + "computers/0/executors/0/stopBuild?runExtId=" + runExtId), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(j.getURL() + "computers/0/executors/0/stopBuild?runExtId=" + runExtId).toURL(), HttpMethod.POST); Page page = wc.getPage(request); assertEquals(404, page.getWebResponse().getStatusCode()); assertRequestWasNotBlocked(); @@ -360,7 +360,7 @@ public void ensureDoStopBuildWorks() throws Exception { QueueTaskFuture futureBuild = p.scheduleBuild2(0); futureBuild.waitForStart(); - WebRequest request = new WebRequest(new URL(j.getURL() + "computers/0/executors/0/stopBuild?runExtId=whatever"), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(j.getURL() + "computers/0/executors/0/stopBuild?runExtId=whatever").toURL(), HttpMethod.POST); Page page = wc.getPage(request); assertEquals(404, page.getWebResponse().getStatusCode()); assertRequestWasNotBlocked(); @@ -564,7 +564,7 @@ public void ensureLogRecordManagerAccessibleOnlyByAdmin() throws Exception { assertEquals(404, wc.goTo("log/" + logNameForAdmin + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); assertRequestWasNotBlocked(); - WebRequest request = new WebRequest(new URL(j.getURL() + "log/newLogRecorder/?name=" + logNameForAdmin), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(j.getURL() + "log/newLogRecorder/?name=" + logNameForAdmin).toURL(), HttpMethod.POST); wc.getOptions().setRedirectEnabled(false); Page page = wc.getPage(request); @@ -588,7 +588,7 @@ public void ensureLogRecordManagerAccessibleOnlyByAdmin() throws Exception { assertEquals(403, wc.goTo("log/" + logNameForUser + "/autoCompleteLoggerName/?value=a", null).getWebResponse().getStatusCode()); assertRequestWasNotBlocked(); - WebRequest request = new WebRequest(new URL(j.getURL() + "log/newLogRecorder/?name=" + logNameForUser), HttpMethod.POST); + WebRequest request = new WebRequest(new URI(j.getURL() + "log/newLogRecorder/?name=" + logNameForUser).toURL(), HttpMethod.POST); wc.getOptions().setRedirectEnabled(false); Page page = wc.getPage(request); diff --git a/test/src/test/java/jenkins/security/stapler/Security914Test.java b/test/src/test/java/jenkins/security/stapler/Security914Test.java index 5ade00a7678f..8a722c0109ac 100644 --- a/test/src/test/java/jenkins/security/stapler/Security914Test.java +++ b/test/src/test/java/jenkins/security/stapler/Security914Test.java @@ -31,7 +31,7 @@ import hudson.Functions; import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; import org.htmlunit.Page; import org.htmlunit.WebRequest; import org.junit.Rule; @@ -54,7 +54,7 @@ public void cannotUseInvalidLocale_toTraverseFolder() throws Exception { JenkinsRule.WebClient wc = j.createWebClient() .withThrowExceptionOnFailingStatusCode(false); - WebRequest request = new WebRequest(new URL(j.getURL() + "plugin/credentials/.xml")); + WebRequest request = new WebRequest(new URI(j.getURL() + "plugin/credentials/.xml").toURL()); // plugin deployed in: test\target\jenkins7375296945862059919tmp // rootDir is in : test\target\jenkinsTests.tmp\jenkins1274934531848159942test // j.jenkins.getRootDir().getName() = jenkins1274934531848159942test @@ -74,7 +74,7 @@ public void cannotUseInvalidLocale_toAnyFileInSystem() throws Exception { JenkinsRule.WebClient wc = j.createWebClient() .withThrowExceptionOnFailingStatusCode(false); - WebRequest request = new WebRequest(new URL(j.getURL() + "plugin/credentials/.ini")); + WebRequest request = new WebRequest(new URI(j.getURL() + "plugin/credentials/.ini").toURL()); // ../ can be multiply to infinity, no impact, we just need to have enough to reach the root request.setAdditionalHeader("Accept-Language", "../../../../../../../../../../../../windows/win"); From f7bcc00ff08f3786f525880afb4764e39f326f39 Mon Sep 17 00:00:00 2001 From: Julien Greffe <36693362+jgreffe@users.noreply.github.com> Date: Tue, 10 Oct 2023 19:35:17 +0200 Subject: [PATCH 189/305] [JENKINS-72141] French translation (#8577) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [JENKINS-72141] French translation * [JENKINS-72141] French translation * [JENKINS-72141] Apply suggestions * Apply suggestions from code review Co-authored-by: Hervé Le Meur <91831478+lemeurherve@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Hervé Le Meur <91831478+lemeurherve@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Hervé Le Meur <91831478+lemeurherve@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Hervé Le Meur <91831478+lemeurherve@users.noreply.github.com> --------- Co-authored-by: Hervé Le Meur <91831478+lemeurherve@users.noreply.github.com> Co-authored-by: Alexander Brandes --- .../hudson/model/Messages_fr.properties | 286 ++++++++++++++++-- .../jenkins/model/Jenkins/login.jelly | 2 +- .../jenkins/model/Jenkins/login_fr.properties | 1 + .../jenkins/model/Messages_fr.properties | 51 +++- .../lib/hudson/executors_fr.properties | 2 +- 5 files changed, 300 insertions(+), 42 deletions(-) diff --git a/core/src/main/resources/hudson/model/Messages_fr.properties b/core/src/main/resources/hudson/model/Messages_fr.properties index 14600f22b3f7..a7b94dbac835 100644 --- a/core/src/main/resources/hudson/model/Messages_fr.properties +++ b/core/src/main/resources/hudson/model/Messages_fr.properties @@ -1,6 +1,6 @@ # The MIT License # -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant, Damien Finck +# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant, Damien Finck, Julien Greffe # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -21,15 +21,35 @@ # THE SOFTWARE. AbstractBuild.BuildingRemotely=Construction à distance sur {0} -AbstractBuild.KeptBecause=conservé à cause de {0} +AbstractBuild.BuildingOnMaster=Construction sur le nœud contrôleur +AbstractBuild_Building=En cours de construction +AbstractBuild.BuildingInWorkspace=\ dans le répertoire de travail {0} +AbstractBuild.KeptBecause=Conservé à cause de {0} -AbstractProject.NewBuildForWorkspace=Demande d''un nouveau build afin d''avoir un répertoire de travail +AbstractItem.NoSuchJobExists=Job ‘{0}’ inexistant. Vouliez-vous ‘{1}’? +AbstractItem.NoSuchJobExistsWithoutSuggestion=Job ‘{0}’ inexistant. +AbstractItem.Pronoun=Item +AbstractItem.TaskNoun=Build +AbstractItem.BeingDeleted={0} en cours de suppression +AbstractItem.FailureToStopBuilds=Echec de l''interruption et de l''arrêt de {0,choice,1#{0,number,integer} build|1<{0,number,integer} \ + builds} de {1} +AbstractItem.NewNameInUse=Le nom “{0}†est déjà utilisé. +AbstractItem.NewNameUnchanged=Le nouveau nom est identique au nom courant. +AbstractProject.NewBuildForWorkspace=Demande d''un nouveau build afin d''avoir un répertoire de travail. +AbstractProject.AwaitingBuildForWorkspace=En attente du build afin d''avoir un répertoire de travail. +AbstractProject.AwaitingWorkspaceToComeOnline=Obligation de demande d''un nouveau build afin d''avoir un répertoire de travail, attente de {0}ms dans l''espoir d''en avoir un disponible prochainement AbstractProject.Pronoun=Projet AbstractProject.Aborted=Annulé +AbstractProject.UpstreamBuildInProgress=Projet en amont ‘{0}’ déjà en cours de construction +AbstractProject.DownstreamBuildInProgress=Projet en aval ‘{0}’ déjà en cours de construction AbstractProject.Disabled=Build désactivé +AbstractProject.NoBuilds=Aucun build existant. Planification d''un nouveau. AbstractProject.NoSCM=Pas d''outil de gestion de version AbstractProject.NoWorkspace=Pas répertoire de travail disponible, donc impossible de récupérer les mises à jour. +AbstractProject.WorkspaceTitle=Répertoire de travail de {0} +AbstractProject.WorkspaceTitleOnComputer=Répertoire de travail de {0} sur {1} AbstractProject.PollingABorted=Scrutation de l''outil de gestion de version annulée +AbstractProject.PollingVetoed=Scrutation de l''outil de gestion de version en contradiction avec {0} AbstractProject.ScmAborted=Récupération des mises à jour à partir de l''outil de gestion de version annulée AbstractProject.WorkspaceOffline=Le répertoire de travail est déconnecté. AbstractProject.BuildPermission.Description=\ @@ -37,9 +57,23 @@ AbstractProject.BuildPermission.Description=\ AbstractProject.WorkspacePermission.Description=\ Ce droit permet d''obtenir le contenu d''un répertoire de travail récupéré par Jenkins pour réaliser des builds. \ Si vous ne voulez pas qu''un utilisateur ait accès au code source, vous pouvez retirer ce droit. +AbstractProject.ExtendedReadPermission.Description=\ + Ce droit permet de lire les configurations de projets. \ + Notez que les informations sensibles dans les builds, comme les mots de passe, seront exposées à une plus grande audience en accordant ce droit. +AbstractProject.DiscoverPermission.Description=\ + Ce droit permet de découvrir les jobs.\ + Accès restreint comparé au droit de lecture, il permet de rediriger les utilisateurs anonymes vers la page d''identification quand ils essayent d''accéder à l''url d''un job. \ + Sans ce droit, les utilisateurs obtiendraient une erreur 404 et ne pourraient pas découvrir les noms de projets. +# WipeOutPermission is only visible in the security configuration if the system property +# hudson.security.WipeOutPermission is set to true +AbstractProject.WipeOutPermission.Description=\ + Ce droit permet de réinitialiser le contenu d''un répertoire de travail. +AbstractProject.CancelPermission.Description=\ + Ce droit permet d''annuler un build planifié, or d''arrêter un build en cours de construction. +AbstractProject.CustomWorkspaceEmpty=Le répertoire de travail personnalisé est vide. Api.MultipleMatch=Le XPath "{0}" correspond à {1} noeud(s). \ - Merci de fournir un XPath qui ne correspond qu''à un seul noeud, ou utilisez le paramètre de requète "wrapper" pour les encapsuler tous dans un élément racine. + Merci de fournir un XPath qui ne correspond qu''à un seul nœud, ou utilisez le paramètre de requête "wrapper" pour tous les encapsuler dans un élément racine. Api.NoXPathMatch=Pas de correspondance avec le XPath {0} Api.WrapperParamInvalid=Le paramètre wrapper ne doit contenir que des caractères alphanumériques ainsi que des points / tirets bas / traits d''union, et finalement le premier caractère doit être une lettre ou un trait d''union. @@ -47,36 +81,67 @@ BallColor.Aborted=Annulé BallColor.Disabled=Désactivé BallColor.Failed=En échec BallColor.InProgress=En cours +BallColor.NotBuilt=Non construit BallColor.Pending=En attente BallColor.Success=Succès BallColor.Unstable=Instable +Build.post_build_steps_failed=Les étapes après build ont échoué +CLI.clear-queue.shortDescription=Efface la file d''attente de build. +CLI.online-node.shortDescription=Reprise avec un nœud pour effectuer un build, afin d''annuler la commande "nœud hors ligne" précédente. -Executor.NotAvailable=N/A +Computer.Caption=Agent {0} +Computer.NoSuchSlaveExists=Agent "{0}" inexistant. Vouliez-vous "{1}"? +Computer.NoSuchSlaveExistsWithoutAdvice=Agent "{0}" inexistant. +Computer.Permissions.Title=Agent +Computer.ExtendedReadPermission.Description=Ce droit autorise les utilisateurs à lire la configuration de l''agent. +Computer.ConfigurePermission.Description=Ce droit autorise les utilisateurs à configurer les agents. +Computer.DeletePermission.Description=Ce droit autorise les utilisateurs à supprimer les agents existants. +Computer.CreatePermission.Description=Ce droit autorise les utilisateurs à créer des agents. +Computer.ConnectPermission.Description=Ce droit autorise les utilisateurs à connecter les agents ou les indiquer en ligne. +Computer.DisconnectPermission.Description=Ce droit autorise les utilisateurs à déconnecter les agents ou à les indiquer temporairement hors ligne. +Computer.BuildPermission.Description=Ce droit autorise les utilisateurs à construire des jobs en utilisant des agents. +Computer.BadChannel=Nœud agent hors ligne ou non accessible à distance (comme le nœud principal). + +ComputerSet.NoSuchSlave=Aucun agent: {0} +ComputerSet.SlaveAlreadyExists=L''agent ‘{0}’ existe déjà +ComputerSet.SpecifySlaveToCopy=Quel agent à copier +ComputerSet.DisplayName=Nœuds +Descriptor.From=(de {0}) + +Executor.NotAvailable=N/A FreeStyleProject.DisplayName=Construire un projet free-style -FreeStyleProject.Description=Ceci est la fonction principale de Jenkins qui sert à builder (construire) votre projet. Vous pouvez intégrer tous les outils de gestion de version avec tous les systèmes de build. Il est même possible d''utiliser Jenkins pour tout autre chose qu''un build logiciel. +FreeStyleProject.Description=Ceci est la fonction principale de Jenkins qui sert à builder (construire) votre projet. Vous pouvez intégrer tous les outils de gestion de version avec tous les systèmes de build. Il est même possible d''utiliser Jenkins pour toute autre chose qu''un build logiciel. + +HealthReport.EmptyString= Hudson.BadPortNumber=Numéro de port incorrect {0} -Hudson.Computer.Caption=Maître -Hudson.Computer.DisplayName=maître +Hudson.Computer.Caption=Contrôleur +Hudson.Computer.DisplayName=contrôleur Hudson.ControlCodeNotAllowed=Code de contrôle non autorisé Hudson.DisplayName=Jenkins Hudson.JobAlreadyExists=Un job existe déjà avec le nom ''{0}'' -Hudson.NoJavaInPath=java n''est pas dans votre PATH. Peut-être avez-vous besoin de configurer les JDKs? -Hudson.NoName=Aucune nom n''est spécifié -Hudson.NoSuchDirectory=Le répertoire n''existe pas: {0} +Hudson.NoJavaInPath=java n''est pas dans votre PATH. Peut-être avez-vous besoin de configurer les JDKs? +Hudson.NoName=Aucun nom n''est spécifié +Hudson.NoSuchDirectory=Le répertoire n''existe pas : {0} +Hudson.NodeBeingRemoved=Nœud en cours de suppression Hudson.NotADirectory={0} n''est pas un répertoire Hudson.NotAPlugin={0} n''est pas un plugin Jenkins Hudson.NotJDKDir={0} ne semble pas être un répertoire contenant un JDK Hudson.Permissions.Title=Global -Hudson.UnsafeChar=''{0}'' est un caractère dangereux +Hudson.USER_CONTENT_README=Les fichiers dans ce dossier seront inclus via http://yourjenkins/userContent/ +Hudson.UnsafeChar=’{0}’ est un caractère dangereux +Hudson.TrailingDot=Un nom ne peut pas se terminer par ‘.’ Hudson.ViewAlreadyExists=Une vue existe déjà avec le nom "{0}" Hudson.ViewName=Tous Hudson.NotANumber=Ceci n''est pas un nombre Hudson.NotAPositiveNumber=Ceci n''est pas un nombre positif +Hudson.NotANonNegativeNumber=Ceci n''est peut-être pas un nombre négatif Hudson.NotANegativeNumber=Ceci n''est pas un nombre négatif +Hudson.MustBeAtLeast=Doit être égal ou supérieur à {0} +Hudson.MustBeAtMost=Doit être égal ou inférieur à {0} Hudson.NotUsesUTF8ToDecodeURL=\ Votre conteneur n''utilise pas UTF-8 pour décoder les URLs. Si vous utilisez des caractères non-ASCII \ dans le nom d''un job ou autre, cela causera des problèmes. \ @@ -91,45 +156,119 @@ Hudson.ReadPermission.Description=\ Ce droit est utile quand vous ne voulez pas que les utilisateurs non authentifiés puissent voir les pages Jenkins \ — retirez ce droit à l''utilisateur anonymous, puis \ ajoutez le pseudo-utilisateur "authenticated" et accordez-lui le droit en lecture. +Hudson.RunScriptsPermission.Description=\ + Déprécié - veuillez utiliser le droit "Overall/Administer" à la place +Hudson.NodeDescription=le nœud principal du contrôleur Jenkins Item.Permissions.Title=Job - +Item.CREATE.description=Crée un nouveau job. +Item.DELETE.description=Supprime un job. +Item.CONFIGURE.description=Modifie la configuration d''un job. +Item.READ.description=Lire un job. (Vous pouvez interdire ce droit, mais autoriser "Découvrir" pour forcer un utilisateur anonyme à s''identifier et voir le job.) +Item.RENAME.description=Renommer un job. +ItemGroupMixIn.may_not_copy_as_it_contains_secrets_and_=Copie impossible de {0} car il contient des secrets et {1} possède {2}/{3} mais pas /{4} +JDK.DisplayName=JDK Job.AllRecentBuildFailed=Tous les builds récents ont échoué. -Job.BuildStability=Stabilité du build: {0} +Job.BuildStability=Stabilité du build : {0} Job.NOfMFailed={0} des {1} derniers builds ont échoué. Job.NoRecentBuildFailed=Aucun build récent n''a échoué. Job.Pronoun=job Job.minutes=mn - -Queue.AllNodesOffline=Tous les noeuds avec le libellé ''{0}'' sont hors ligne -Queue.BlockedBy=Bloqué par {0} +Job.NoRenameWhileBuilding=Impossible de renommer un job en cours de construction. +Job.you_must_use_the_save_button_if_you_wish=Vous devez utiliser le bouton Sauvegarder si vous voulez renommer un job. +Label.GroupOf=groupe de {0} +Label.InvalidLabel=libellé invalide +Label.ProvisionedFrom=Provisionné depuis {0} +LabelExpression.InvalidBooleanExpression=Expression booléenne invalide : {0} +LabelExpression_ObsoleteMasterLabel=Cette expression contient le libellé master qui n''est plus utilisé pour le nœud principal. Utilisez built-in à la place. \ + Voir plus. +LabelExpression.LabelLink=Label {1} respecte {3,choice,0#no nodes|1#1 node|1<{3} nodes}{4,choice,0#|1# et 1 cloud|1< et {4} clouds}. \ + Les droits ou autres restrictions apportées par des plugins peuvent réduire cette liste. +LabelExpression.NoMatch=Aucun agent/cloud ne correspond à ce libellé. +LabelExpression.NoMatch_DidYouMean=Aucun agent/cloud ne correspond à ce libellé. Vouliez-vous ‘{1}’ au lieu de ‘{0}’? +ManageJenkinsAction.DisplayName=Administrer Jenkins +MultiStageTimeSeries.EMPTY_STRING= +ParametersDefinitionProperty.BuildButtonText=Build +Queue.AllNodesOffline=Tous les nœuds avec le libellé "{0}" sont hors ligne +Queue.LabelHasNoNodes=Aucun nœud avec le libellé ‘{0}’ +Queue.BlockedBy=Bloqué par ‘{0}‘ Queue.HudsonIsAboutToShutDown=Jenkins est sur le point de se fermer Queue.InProgress=Un build est déjà en cours Queue.InQuietPeriod=En période d''attente. Expire dans {0} -Queue.NodeOffline={0} est hors ligne +Queue.NodeOffline=‘{0}‘ est hors ligne Queue.Unknown=??? - +Queue.FinishedWaiting=Attente terminée Queue.WaitingForNextAvailableExecutor=En attente du prochain exécuteur disponible -Queue.WaitingForNextAvailableExecutorOn=En attente du prochain exécuteur disponible sur {0} +Queue.WaitingForNextAvailableExecutorOn=En attente du prochain exécuteur disponible sur ‘{0}‘ +Queue.init=Restoration de la file de builds +Queue.node_has_been_removed_from_configuration={0} a été supprimé de la configuration +Queue.executor_slot_already_in_use=Emplacement d''exécuteur déjà utilisé +Queue.ExceptionCanTake=Exception lors de l''évaluation d''un nœud disponible pour la tâche +Queue.ExceptionCanRun=Exception lors de l''évaluation d''un nœud pouvant exécuter la tâche + +ResultTrend.Aborted=Annulé +ResultTrend.Failure=Échec +ResultTrend.Fixed=Résolu +ResultTrend.NotBuilt=Non exécuté +ResultTrend.NowUnstable=Maintenant instable +ResultTrend.StillFailing=Toujours en échec +ResultTrend.StillUnstable=Toujours instable +ResultTrend.Success=Succès +ResultTrend.Unstable=Instable +Run._is_waiting_for_a_checkpoint_on_={0} en attente d''un point de contrôle sur {1} Run.BuildAborted=Le build a été annulé -Run.MarkedExplicitly=marqué explicitement pour conservé l''enregistrement +Run.MarkedExplicitly=marqué explicitement pour conserver l''enregistrement Run.Permissions.Title=Historique des builds -Run.UnableToDelete=Impossible de supprimer {0}: {1} +Run.running_as_=Exécution en tant que {0} +Run.running_as_SYSTEM=Exécution en tant que SYSTEM +Run.UnableToDelete=Impossible de supprimer {0} : {1} Run.DeletePermission.Description=\ Cette option permet aux utilisateurs de supprimer manuellement des builds spécifiques dans l''historique de build. Run.UpdatePermission.Description=\ Cette option permet aux utilisateurs de mettre à jour la description et d''autres propriétés d''un build, \ par exemple pour laisser des notes sur la cause d''échec d''un build. +Run.ArtifactsPermission.Description=\ + Ce droit permet de récupérer les artefacts produits par les builds. \ + Si un utilisateur ne doit pas accéder aux artefacts, vous pouvez l''en empêcher en désactivant ce droit. +Run.InProgressDuration={0} et décompte +Run.NotStartedYet=Pas encore commencé +Run.ArtifactsBrowserTitle=Artefacts de {0} {1} + +Run.Summary.Stable=stable +Run.Summary.Unstable=instable +Run.Summary.Aborted=annulé +Run.Summary.NotBuilt=non construit +Run.Summary.BackToNormal=de retour à la normale +Run.Summary.BrokenForALongTime=en échec depuis longtemps +Run.Summary.BrokenSinceThisBuild=en échec depuis ce build +Run.Summary.BrokenSince=en échec depuis le build {0} +Run.Summary.Unknown=? +Slave.InvalidConfig.Executors=Configuration invalide de l''agent {0}. Nombre invalide d''exécuteurs. +Slave.InvalidConfig.NoName=Configuration invalide de l''agent. Le nom est vide. +Slave.Network.Mounted.File.System.Warning=Êtes-vous sûr de vouloir utiliser un système de fichier monté à travers le réseau pour le système de fichier root? Notez que ce dossier n''a pas besoin d''être visible par le contrôleur. +Slave.Remote.Director.Mandatory=Le dossier distant est obligatoire +Slave.Terminated=L''agent {0} a été arrêté +Slave.Remote.Relative.Path.Warning=Êtes-vous sûr de vouloire utiliser un chemin relatif pour le système de fichier root? Notez que les chemins relatifs nécessitent que l''éxecuteur fournisse un répertoire de travail constant. \ + Il est fortement conseillé d''utiliser un chemin absolu. +Slave.UnixSlave=Ceci est un agent Unix +Slave.WindowsSlave=Ceci est un agent Windows +TopLevelItemDescriptor.NotApplicableIn={0} éléments ne sont pas applicables pour {1} + +UpdateCenter.DownloadButNotActivated=Téléchargé avec succès. Sera activé lors du prochain démarrage +UpdateCenter.n_a=N/A View.Permissions.Title=Vues View.CreatePermission.Description=\ - Ce droit permet aux utilisateurs de créer des nouvelles vues. + Ce droit permet aux utilisateurs de créer de nouvelles vues. View.DeletePermission.Description=\ Ce droit permet aux utilisateurs de supprimer des vues existantes. View.ConfigurePermission.Description=\ Ce droit permet aux utilisateurs de changer la configuration des vues. - +View.MissingMode=Aucun type de vue n''est spécifié +View.DisplayNameNotUniqueWarning=Le nom d''affichage "{0}" est déjà utilisé dans une autre vue et \ + peut engender des erreurs et des délais. +UpdateCenter.DisplayName=Centre de mise à jour UpdateCenter.Status.CheckingInternet=Vérification de la connexion à internet UpdateCenter.Status.CheckingJavaNet=Vérification de la connexion à jenkins-ci.org UpdateCenter.Status.Success=Succès @@ -139,6 +278,50 @@ UpdateCenter.Status.UnknownHostException=\ UpdateCenter.Status.ConnectionFailed=\ Echec lors de la connexion à {0}. \ Peut-être devez-vous configurer le proxy HTTP. +UpdateCenter.init=Initialisation du centre de mise à jour +UpdateCenter.CoreUpdateMonitor.DisplayName=Notification de mise à jour de Jenkins + +UpdateCenter.PluginCategory.android=Développement Android +UpdateCenter.PluginCategory.builder=Outils de build +UpdateCenter.PluginCategory.buildwrapper=Enveloppes de build +UpdateCenter.PluginCategory.cli=Interface en ligne de commande +UpdateCenter.PluginCategory.cloud=Fournisseurs de service Cloud +UpdateCenter.PluginCategory.cluster=Gestion de cluster +UpdateCenter.PluginCategory.database=Base de données +UpdateCenter.PluginCategory.deployment=Déploiement +UpdateCenter.PluginCategory.devops=DevOps +UpdateCenter.PluginCategory.devsecops=DevSecOps +UpdateCenter.PluginCategory.dotnet=Développement .NET +UpdateCenter.PluginCategory.external=Site externe/Intégration d''outils +UpdateCenter.PluginCategory.groovy-related=Lié à Groovy +UpdateCenter.PluginCategory.ios=Développement iOS +UpdateCenter.PluginCategory.api-plugin=Librairie de plugins (utilisable par d''autres plugins) +UpdateCenter.PluginCategory.listview-column=Liste de vues en colonne +UpdateCenter.PluginCategory.maven=Maven +UpdateCenter.PluginCategory.misc=Divers +UpdateCenter.PluginCategory.notifier=Avertisseur de build +UpdateCenter.PluginCategory.orchestration=Orchestration +UpdateCenter.PluginCategory.page-decorator=Décorateurs de page +UpdateCenter.PluginCategory.parameter=Paramètres de build +UpdateCenter.PluginCategory.post-build=Autres actions après build +UpdateCenter.PluginCategory.python=Développement Python +UpdateCenter.PluginCategory.report=Rapports de build +UpdateCenter.PluginCategory.ruby=Développement Ruby +UpdateCenter.PluginCategory.runcondition=RunConditions utilisées par le plugin Run Condition +UpdateCenter.PluginCategory.scala=Développement Scala +UpdateCenter.PluginCategory.scm=Gestion du code source +UpdateCenter.PluginCategory.scm-related=Lié à la gestion de code source +UpdateCenter.PluginCategory.security=Securité +UpdateCenter.PluginCategory.agent=Gestion d''agent +UpdateCenter.PluginCategory.test=Test +UpdateCenter.PluginCategory.theme=Thèmes UI +UpdateCenter.PluginCategory.trigger=Déclencheurs de build +UpdateCenter.PluginCategory.ui=Interface utilisateur +UpdateCenter.PluginCategory.upload=Téléverseurs d''artefact +UpdateCenter.PluginCategory.user=Identification et gestion d''utilisateur +UpdateCenter.PluginCategory.view=Vues +UpdateCenter.PluginCategory.must-be-labeled=Non catégorisé +UpdateCenter.PluginCategory.unrecognized=Autres ({0}) Permalink.LastBuild=Dernier build Permalink.LastStableBuild=Dernier build stable @@ -146,8 +329,10 @@ Permalink.LastUnstableBuild=Dernier build instable Permalink.LastUnsuccessfulBuild=Dernier build non réussi Permalink.LastSuccessfulBuild=Dernier build avec succès Permalink.LastFailedBuild=Dernier build en échec +Permalink.LastCompletedBuild=Dernier build complété ParameterAction.DisplayName=Paramètres +ParametersDefinitionProperty.DisplayName=Ce build a des paramètres StringParameterDefinition.DisplayName=Paramètre String TextParameterDefinition.DisplayName=Paramètre texte FileParameterDefinition.DisplayName=Paramètre fichier @@ -157,28 +342,69 @@ ChoiceParameterDefinition.MissingChoices=Choix requis. RunParameterDefinition.DisplayName=Paramètre d''exécution PasswordParameterDefinition.DisplayName=Paramètre "Mot de passe" -Node.Mode.NORMAL=Utiliser ce noeud autant que possible -Node.Mode.EXCLUSIVE=Réserver ce noeud uniquement pour les jobs qui lui sont attachés +Node.BecauseNodeIsReserved=‘{0}’ est réservé pour les jobs avec une expression de libellé correspondant +Node.BecauseNodeIsNotAcceptingTasks=‘{0}’ n''accepte pas de tâche +Node.LabelMissing=‘{0}’ n''a pas le libellé ‘{1}’ +Node.LackingBuildPermission=‘{0}’ ne possède pas le droit de s''exécuter sur ‘{1}’ +Node.Mode.NORMAL=Utiliser ce nœud autant que possible +Node.Mode.EXCLUSIVE=Réserver ce nœud uniquement pour les jobs qui lui sont attachés ListView.DisplayName=Vue liste MyView.DisplayName=Ma vue -LoadStatistics.Legends.TotalExecutors=Nombre total d''exécuteurs +LoadStatistics.Legends.DefinedExecutors=Éxecuteurs déclarés LoadStatistics.Legends.OnlineExecutors=Nombre d''exécuteurs en ligne +LoadStatistics.Legends.ConnectingExecutors=Nombre d''éxecuteurs en cours de connexion +LoadStatistics.Legends.TotalExecutors=Nombre total d''exécuteurs LoadStatistics.Legends.BusyExecutors=Nombre d''exécuteurs occupés +LoadStatistics.Legends.IdleExecutors=Nombre d''exécuteurs en attente LoadStatistics.Legends.AvailableExecutors=Nombre d''exécuteurs disponibles LoadStatistics.Legends.QueueLength=Longueur de la file d''attente Cause.LegacyCodeCause.ShortDescription=Ce job a été lancé par du code legacy. Pas d''information sur les causes Cause.UpstreamCause.ShortDescription=Démarré par le projet amont "{0}" de numéro de build {1} +Cause.UpstreamCause.CausedBy=Causé à l''origine par : Cause.UserCause.ShortDescription=Démarré par l''utilisateur {0} Cause.UserIdCause.ShortDescription=Démarré par l''utilisateur {0} -Cause.RemoteCause.ShortDescription=Démarré à distance par {0} +Cause.RemoteCause.ShortDescription=Démarré à distance par l''hôte {0} +Cause.RemoteCause.ShortDescriptionWithNote=Demarré à distance par l''hôte {0} avec le commentaire : {1} + +ProxyView.NoSuchViewExists=La vue globale {0} n''existe pas +ProxyView.DisplayName=Inclure une vue globale MyViewsProperty.DisplayName=Mes vues MyViewsProperty.GlobalAction.DisplayName=Mes vues +MyViewsProperty.ViewExistsCheck.NotExist=La vue avec le nom {0} n''existe pas +MyViewsProperty.ViewExistsCheck.AlreadyExists=La vue avec le nom {0} existe déjà -ManageJenkinsAction.DisplayName=Administrer Jenkins -ParametersDefinitionProperty.DisplayName=Ce build a des paramètres +CLI.restart.shortDescription=Redémarrer Jenkins +CLI.safe-restart.shortDescription=Redémarrer Jenkins en sûreté +CLI.keep-build.shortDescription=Conserver cette construction sans limite de temps. + +BuildAuthorizationToken.InvalidTokenProvided=Le token fourni est invalide. + +Jenkins.CheckDisplayName.NameNotUniqueWarning=Le nom affiché "{0}" est utilisé comme nom de job et peut causer des résultats de recherche erronés. +Jenkins.CheckDisplayName.DisplayNameNotUniqueWarning=Le nom affiché "{0}" est déjà utilisé par un autre job et peut engender des erreurs et des délais. + +Jenkins.NotAllowedName=“{0}†n''est pas un nom autorisé +Jenkins.IsRestarting=Jenkins est en cours de redémarrage + +User.IllegalUsername="{0}" est interdit comme nom d''utilisateur pour des raisons de sécurité. +User.IllegalFullname="{0}" est interdit comme nom complet pour des raisons de sécurité. + +TimeZoneProperty.DisplayName=Fuseau horaire défini par l''utilisateur +TimeZoneProperty.DisplayDefaultTimeZone=Défaut +TimeZoneProperty.current_time_in_=Heure actuelle dans le fuseau horaire {0} : {1} +TimeZoneProperty.current_time_on_server_in_in_proposed_di=Heure actuelle sur le serveur dans le fuseau horaire {0} : {1}; dans le fuseau horaire proposé : {2} + +ManagementLink.Category.CONFIGURATION=Configuration du système +ManagementLink.Category.SECURITY=Sécurité +ManagementLink.Category.STATUS=Information du statut +ManagementLink.Category.TROUBLESHOOTING=Dépannage +ManagementLink.Category.TOOLS=Outils et actions +ManagementLink.Category.MISC=Autre +ManagementLink.Category.UNCATEGORIZED=Non catégorisé + +FileParameterValue.IndexTitle=Paramètres de fichier diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly index c9071d5308cd..083566a62ac0 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -70,7 +70,7 @@ THE SOFTWARE.