Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
009bad3
added unit test showing leaked password
Jul 14, 2020
52f6f4b
working PoC
Jul 24, 2020
75c7406
add listener to report errors to pipeline output
Jul 24, 2020
191f08e
PoC 2 for groovy interpolation interception. Does not require core mods
Jul 28, 2020
ccf9040
Wrap EnvironmentExpander and EnvVars together for parseArgs
Jul 31, 2020
5c53b94
use incrementals, revert jenkins version
Jul 31, 2020
c5ef8ad
code cleanup
Jul 31, 2020
1a7793d
catch null arguments
Jul 31, 2020
0829025
Make unit test windows friendly. Remove dollar sign from password
Jul 31, 2020
18653c0
update to use newer implementation of EnvironmentExpander
Aug 4, 2020
5ba9b2a
Use updated api in EnvironmentExpander
Aug 5, 2020
aeebf17
address review comments
Aug 27, 2020
02804b3
add factory method, add report action, and summary page
Sep 1, 2020
20d781d
change from table to list, change icon
Sep 2, 2020
b404458
update jelly formatting, update unit test
Sep 5, 2020
22c4ae3
Check for empty body
Sep 8, 2020
41984a6
Update body check, support legacy stage behavior
Sep 10, 2020
d4759b1
add check for empty args
Sep 10, 2020
4a98072
fix variable clashing
Sep 10, 2020
8bdc019
address review comments
Sep 10, 2020
e975453
Refactor Action name, generate action only when there are secrets exp…
Sep 10, 2020
d4765a4
update null environment variable test
Sep 10, 2020
341972a
check for "bat" args on windows
Sep 11, 2020
ade619f
avoid reflective API
Sep 11, 2020
79d255a
address review comments
Sep 11, 2020
0abb665
address review comments
Sep 15, 2020
7a5d0c7
update jelly file path, fix localization error with parenthesis
Sep 15, 2020
506d5b3
add placeholder explanation page for jelly
Sep 15, 2020
93b018b
more refactoring of envwatcher
Sep 15, 2020
065b129
update step-api dependency
Sep 15, 2020
4903d4e
support detecting interpolation in describables
Sep 16, 2020
cc5b3b0
Track groovy strings instead of using InterpolatedSecretsDetector.
Sep 18, 2020
8517f12
added InterpolatedUninstantiatedDescribable
Sep 21, 2020
f12f226
add null checks for environmentexpander and envars
Sep 21, 2020
9a31bce
Merge remote-tracking branch 'upstream/master' into interpolation-v2
Sep 22, 2020
6e8b5eb
Refactor ArgumentsActionImpl using EnvironmentExpander and removing s…
Sep 23, 2020
eaa4c90
set sensitiveVariables as field instead of recursively passing through
Sep 23, 2020
70c4c22
Move interpolatedStrings into parseArgs
Sep 23, 2020
8a799dc
Remove duplicate code
Sep 23, 2020
1afab59
no metaStep returns NamedArgsAndClosure
Sep 25, 2020
dbe7d8f
Update error logging
Sep 25, 2020
902ab29
add windows support for unit test
Sep 25, 2020
6a39c5c
report step name and arguments that log a warning
Sep 28, 2020
ec29d4b
Handle multiple sensitive variables in one argument
Sep 28, 2020
97b1535
fix windows tests
Sep 28, 2020
b34e5fd
Merge remote-tracking branch 'upstream/master' into interpolation-v2
Sep 29, 2020
b0d183b
update documentation
Sep 29, 2020
b846c44
refactoring
Oct 1, 2020
4f9997d
fix jelly output
Oct 1, 2020
8506214
fix unit tests, some clean up
Oct 1, 2020
e6284e9
add redirect to console warning
Oct 1, 2020
ef157e9
address review comments
Oct 8, 2020
00d220b
simplify parseArgs
Oct 8, 2020
a8df396
centralize parsing of NamedArgsAndClosure
Oct 12, 2020
7c8022b
Sort arguments in step signature
Oct 12, 2020
a890c43
Merge remote-tracking branch 'upstream/master' into interpolation-v2
Oct 12, 2020
36050b4
fix comments
Oct 13, 2020
163e7cb
make step arguments print out in order they were added
Oct 14, 2020
3740ed1
update workflow-step-api and credentials-binding to release versions
Oct 15, 2020
a20db3d
update bom
Oct 19, 2020
b14f8cf
bump bom to v15
Oct 19, 2020
d489f73
address review comments
Oct 19, 2020
11b12ba
make getStepSignature recursive
Oct 20, 2020
bd0dfd2
make recursion more generic, add unit test for getStepSignature()
Oct 20, 2020
86cf9f3
parse UninstantiatedDescribable in getStepSignature
Oct 20, 2020
a24ffe0
control warning behavior with system property
Oct 20, 2020
cd43425
update unit tests with new UninstantiatedDescribable output
Oct 20, 2020
ac2ec02
Merge remote-tracking branch 'upstream/master' into interpolation-v2
Oct 27, 2020
41e50e6
address review comments
Nov 2, 2020
3e54d36
make InterpolatedWarnings.run transient field
Nov 2, 2020
b52438d
update UninstantiatedDescribable $class toString
Nov 3, 2020
49695e0
update InterpolatedSecretesAction onLoad and onAttached
Nov 3, 2020
7d8672c
Update getStepSignature to better reflect pipeline input
Nov 5, 2020
611326c
Remove printing of step signature
Nov 5, 2020
96c2f30
remove setting the model for the Uninstantiated Describable
Nov 5, 2020
7811148
Make sure password parameters are masked in step arguments
dwnusbaum Nov 5, 2020
f7798af
Remove InterpolatedSecretsActionTest.java
dwnusbaum Nov 5, 2020
40eb64a
Align workflow-support tests jar with incremental version
dwnusbaum Nov 5, 2020
bc8d268
Update to latest workflow-support incremental
dwnusbaum Nov 5, 2020
e173261
Merge remote-tracking branch 'upstream/master' into interpolation-v2
Nov 5, 2020
4524d26
update pom, update changelog to prepare for release
Nov 6, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
## Changelog

### 2.85

Release date: 2020-11-05

* Improvement: Add warnings when secrets are used with Groovy String interpolation. ([JENKINS-63254](https://issues.jenkins-ci.org/browse/JENKINS-63254))
* Fix: Allow masking of secret variables that use the same name as system variables. ([JENKINS-47101](https://issues.jenkins-ci.org/browse/JENKINS-47101))
* Fix: Throw an error when a step that requires a body has no body. ([PR #370](https://github.com/jenkinsci/workflow-cps-plugin/pull/370))

### 2.84

Release date: 2020-10-30
Expand Down
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<java.level>8</java.level>
<no-test-jar>false</no-test-jar>
<groovy-cps.version>1.32</groovy-cps.version>
<workflow-support-plugin.version>3.6</workflow-support-plugin.version>
<node.version>12.19.0</node.version>
<npm.version>6.14.8</npm.version>
<frontend-version>1.10.0</frontend-version>
Expand Down Expand Up @@ -96,6 +97,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>${workflow-support-plugin.version}</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand All @@ -104,7 +106,6 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>script-security</artifactId>
<version>1.75</version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current version of the BOM makes this redundant.

</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down Expand Up @@ -135,6 +136,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>${workflow-support-plugin.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ private static final class DynamicContextQuery {
}
}

@FunctionalInterface
interface ThrowingSupplier<T> {
T get() throws IOException;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public class CpsStepContext extends DefaultStepContext { // TODO add XStream cla
private transient List<CpsBodyInvoker> bodyInvokers = new ArrayList<>();

/**
* While {@link CpsStepContext} has not received teh response, maintains the body closure.
* While {@link CpsStepContext} has not received the response, maintains the body closure.
*
* This is the implicit closure block passed to the step invocation.
*/
Expand Down
238 changes: 182 additions & 56 deletions src/main/java/org/jenkinsci/plugins/workflow/cps/DSL.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;


/**
* Implements {@link ArgumentsAction} by storing step arguments, with sanitization.
Expand All @@ -66,47 +65,39 @@ public class ArgumentsActionImpl extends ArgumentsAction {
@CheckForNull
private Map<String,Object> arguments;

private final Set<String> sensitiveVariables;

boolean isUnmodifiedBySanitization = true;

private static final Logger LOGGER = Logger.getLogger(ArgumentsActionImpl.class.getName());

public ArgumentsActionImpl(@Nonnull Map<String, Object> stepArguments, @CheckForNull EnvVars env) {
public ArgumentsActionImpl(@Nonnull Map<String, Object> stepArguments, @CheckForNull EnvVars env, @Nonnull Set<String> sensitiveVariables) {
this.sensitiveVariables = new HashSet<>(sensitiveVariables);
arguments = serializationCheck(sanitizeMapAndRecordMutation(stepArguments, env));
}

/** Create a step, sanitizing strings for secured content */
public ArgumentsActionImpl(@Nonnull Map<String, Object> stepArguments) {
this(stepArguments, new EnvVars());
this(stepArguments, new EnvVars(), Collections.emptySet());
}

/** For testing use only */
ArgumentsActionImpl(){
ArgumentsActionImpl(@Nonnull Set<String> sensitiveVariables){
this.isUnmodifiedBySanitization = false;
this.arguments = Collections.emptyMap();
this.sensitiveVariables = sensitiveVariables;
}

/** See if sensitive environment variable content is in a string */
public static boolean isStringSafe(@CheckForNull String input, @CheckForNull EnvVars variables, @Nonnull Set<String> safeEnvVariables) {
if (input == null || variables == null || variables.size() == 0) {
return true;
/** See if sensitive environment variable content is in a string and replace the content with its associated variable name, otherwise return string unmodified*/
public static String replaceSensitiveVariables(@Nonnull String input, @CheckForNull EnvVars variables, @Nonnull Set<String> sensitiveVariables) {
if (variables == null || variables.size() == 0 || sensitiveVariables.size() ==0) {
return input;
}
StringBuilder pattern = new StringBuilder();
int count = 0;
for (Map.Entry<String,String> ent : variables.entrySet()) {
String val = ent.getValue();
if (val == null || val.isEmpty() || safeEnvVariables.contains(ent.getKey())) { // Skip values that are safe
continue;
}
if (count > 0) {
pattern.append('|');
}
pattern.append(Pattern.quote(val));
count++;
String modded = input;
for (String sensitive : sensitiveVariables) {
modded = modded.replace(variables.get(sensitive), "${" + sensitive + "}");
}
return (count > 0)
? !Pattern.compile(pattern.toString()).matcher(input).find()
: true;
return modded;
}

/** Restrict stored arguments to a reasonable subset of types so we don't retain totally arbitrary objects
Expand Down Expand Up @@ -138,98 +129,6 @@ boolean isStorableType(Object ob) {
return c.isPrimitive() || (c.isArray() && !(c.getComponentType().isPrimitive())); // Primitive arrays are not legal here
}

/** Normal environment variables, as opposed to ones that might come from credentials bindings */
private static final HashSet<String> SAFE_ENVIRONMENT_VARIABLES = new HashSet<>(Arrays.asList(
// Pipeline/Jenkins variables in normal builds
"BRANCH_NAME",
"BUILD_DISPLAY_NAME",
"BUILD_ID",
"BUILD_NUMBER",
"BUILD_TAG",
"BUILD_URL",
"CHANGE_AUTHOR",
"CHANGE_AUTHOR_DISPLAY_NAME",
"CHANGE_AUTHOR_EMAIL",
"CHANGE_ID",
"CHANGE_TARGET",
"CHANGE_TITLE",
"CHANGE_URL",
"EXECUTOR_NUMBER",
"HUDSON_COOKIE",
"HUDSON_HOME",
"HUDSON_SERVER_COOKIE",
"HUDSON_URL",
"JENKINS_HOME",
"JENKINS_SERVER_COOKIE",
"JENKINS_URL",
"JOB_BASE_NAME",
"JOB_NAME",
"JOB_URL",
"NODE_LABELS",
"NODE_NAME",
"STAGE_NAME",
"WORKSPACE",

// Normal system variables for posix environments
"HOME",
"LANG",
"LOGNAME",
"MAIL",
"NLSPATH",
"PATH",
"PWD",
"SHELL",
"SHLVL",
"TERM",
"USER",
"XFILESEARCHPATH",

// Windows system variables
"ALLUSERSPROFILE",
"APPDATA",
"CD",
"ClientName",
"CMDEXTVERSION",
"CMDCMDLINE",
"CommonProgramFiles",
"COMPUTERNAME",
"COMSPEC",
"DATE",
"ERRORLEVEL",
"HighestNumaNodeNumber",
"HOMEDRIVE",
"HOMEPATH",
"LOCALAPPDATA",
"LOGONSERVER",
"NUMBER_OF_PROCESSORS",
"OS",
"PATHEXT",
"PROCESSOR_ARCHITECTURE",
"PROCESSOR_ARCHITEW6432",
"PROCESSOR_IDENTIFIER",
"PROCESSOR_LEVEL",
"PROCESSOR_REVISION",
"ProgramW6432",
"ProgramData",
"ProgramFiles",
"ProgramFiles (x86)",
"PROMPT",
"PSModulePath",
"Public",
"RANDOM",
"%SessionName%",
"SYSTEMDRIVE",
"SYSTEMROOT",
"TEMP", "TMP",
"TIME",
"UserDnsDomain",
"USERDOMAIN",
"USERDOMAIN_roamingprofile",
// "USERNAME", // Not whitelisted because this is a likely variable name for credentials binding
"USERPROFILE",
"WINDIR"
));

/**
* Sanitize a list recursively
*/
Expand Down Expand Up @@ -279,7 +178,7 @@ Object sanitizeArrayAndRecordMutation(@Nonnull Object[] objects, @CheckForNull E

/** Recursively sanitize a single object by:
* - Exploding {@link Step}s and {@link UninstantiatedDescribable}s into their Maps to sanitize
* - Removing unsafe strings using {@link #isStringSafe(String, EnvVars, Set)} and replace with {@link NotStoredReason#MASKED_VALUE}
* - Removing unsafe strings using {@link #replaceSensitiveVariables(String, EnvVars, Set)} and replace with the variable name
* - Removing oversized objects using {@link #isOversized(Object)} and replacing with {@link NotStoredReason#OVERSIZE_VALUE}
* While making an effort not to retain needless copies of objects and to re-use originals where possible
* (including the Step or UninstantiatedDescribable)
Expand Down Expand Up @@ -331,9 +230,12 @@ Object sanitizeObjectAndRecordMutation(@CheckForNull Object o, @CheckForNull Env
this.isUnmodifiedBySanitization = true;
return NotStoredReason.UNSERIALIZABLE;
}
} else if (modded instanceof String && vars != null && !vars.isEmpty() && !isStringSafe((String)modded, vars, SAFE_ENVIRONMENT_VARIABLES)) {
this.isUnmodifiedBySanitization = false;
return NotStoredReason.MASKED_VALUE;
} else if (modded instanceof String && vars != null && !vars.isEmpty()) {
String replaced = replaceSensitiveVariables((String)modded, vars, sensitiveVariables);
if (!replaced.equals(modded)) {
this.isUnmodifiedBySanitization = false;
}
return replaced;
}

if (modded != tempVal) {
Expand Down Expand Up @@ -391,12 +293,11 @@ Map<String, Object> serializationCheck(@Nonnull Map<String, Object> arguments) {
@Nonnull
Map<String,Object> sanitizeMapAndRecordMutation(@Nonnull Map<String, Object> mapContents, @CheckForNull EnvVars variables) {
// Package scoped so we can test it directly
HashMap<String, Object> output = Maps.newHashMapWithExpectedSize(mapContents.size());
LinkedHashMap<String, Object> output = new LinkedHashMap<>(mapContents.size());

boolean isMutated = false;
for (Map.Entry<String,?> param : mapContents.entrySet()) {
Object modded = sanitizeObjectAndRecordMutation(param.getValue(), variables);

if (modded != param.getValue()) {
// Sanitization stripped out some values, so we need to store the mutated object
output.put(param.getKey(), modded);
Expand Down
Loading