Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
10 changes: 6 additions & 4 deletions src/main/java/org/jenkinsci/plugins/workflow/cps/DSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ protected Object invokeStep(StepDescriptor d, String name, Object args) {
// TODO: generalize the notion of Step taking over the FlowNode creation.
boolean hack = d instanceof ParallelStep.DescriptorImpl || d instanceof LoadStep.DescriptorImpl;

FlowNode an = null;
FlowNode an;
CpsThread thread = CpsThread.current();
if (!d.takesImplicitBlockArgument() && !hack) {
an = new StepAtomNode(exec, d, thread.head.get());
Expand All @@ -232,7 +232,7 @@ protected Object invokeStep(StepDescriptor d, String name, Object args) {
}

CpsStepContext context = new CpsStepContext(d, thread, handle, an);
EnvironmentWatcher envWatcher = new EnvironmentWatcher(context);
EnvironmentWatcher envWatcher = EnvironmentWatcher.of(context, exec);
NamedArgsAndClosure ps = parseArgs(args, d, envWatcher);
context.setBody(ps.body, thread);
// Ensure ArgumentsAction is attached before we notify even synchronous listeners:
Expand Down Expand Up @@ -271,7 +271,9 @@ protected Object invokeStep(StepDescriptor d, String name, Object args) {
DescribableModel<? extends Step> stepModel = DescribableModel.of(d.clazz);
s = stepModel.instantiate(ps.namedArgs, listener);
}
envWatcher.logResults(listener);
if (envWatcher != null) {
envWatcher.logResults(listener);
}

// Persist the node - block start and end nodes do their own persistence.
CpsFlowExecution.maybeAutoPersistNode(an);
Expand Down Expand Up @@ -466,7 +468,7 @@ private NamedArgsAndClosure(Map<?,?> namedArgs, Closure body, @Nullable Environm

for (Map.Entry<?,?> entry : namedArgs.entrySet()) {
String k = entry.getKey().toString().intern(); // coerces GString and more
Object v = flattenGString(entry.getValue(), envWatcher);//expander, msgs);//envVars, msgs);
Object v = flattenGString(entry.getValue(), envWatcher);
this.namedArgs.put(k, v);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.jenkinsci.plugins.workflow.cps;

import hudson.EnvVars;
import hudson.model.Run;
import hudson.model.TaskListener;
import org.jenkinsci.plugins.workflow.cps.view.EnvironmentWatcherRunReport;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.steps.EnvironmentExpander;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
Expand All @@ -15,39 +17,49 @@
import java.util.stream.Collectors;

public class EnvironmentWatcher implements Serializable {
private EnvVars envVars = null;
private EnvironmentExpander expander = null;
private EnvVars envVars;
private Set<String> watchedVars;
private List<String> scanResults;
private static EnvironmentWatcherRunReport runReport;

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

public EnvironmentWatcher(CpsStepContext context) {
public static EnvironmentWatcher of(CpsStepContext context, CpsFlowExecution exec) {
try {
envVars = context.get(EnvVars.class);
expander = context.get(EnvironmentExpander.class);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "Error storing the arguments for step: " + context.getStepDescriptor().getFunctionName(), e);
}
if (expander != null) {
watchedVars = expander.getSensitiveVars();
EnvVars contextEnvVars = context.get(EnvVars.class);
EnvironmentExpander contextExpander = context.get(EnvironmentExpander.class);
if (contextEnvVars != null && contextExpander != null) {
if (runReport == null) {
FlowExecutionOwner owner = exec.getOwner();
if (owner != null && owner.getExecutable() instanceof Run) {
runReport = ((Run) owner.getExecutable()).getAction(EnvironmentWatcherRunReport.class);
}
}
if (runReport != null) {
return new EnvironmentWatcher(contextEnvVars, contextExpander);
}
}
} catch (InterruptedException | IOException e) {
LOGGER.log(Level.FINE, "Unable to create EnvironmentWatcher instance.\n" + e.getMessage());
}
return null;
}

public EnvironmentWatcher(@Nonnull EnvVars envVars, @Nonnull EnvironmentExpander expander) {
this.envVars = envVars;
watchedVars = expander.getSensitiveVars();
}

public void scan(String text) {
// TODO: would EnvVars ever be null?
if (watchedVars == null || envVars == null) {
scanResults = null;
} else {
scanResults = watchedVars.stream().filter(e -> text.contains(envVars.get(e))).collect(Collectors.toList());
}
scanResults = watchedVars.stream().filter(e -> text.contains(envVars.get(e))).collect(Collectors.toList());
}

public void logResults(TaskListener listener) {
if (scanResults != null && !scanResults.isEmpty()) {
listener.getLogger().println("The following Groovy string may be insecure. Use single quotes to prevent leaking secrets via Groovy interpolation. Affected variables: " + scanResults.toString());
runReport.record(scanResults);
}
}

private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.jenkinsci.plugins.workflow.cps.view;

import hudson.Extension;
import hudson.model.Run;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionListener;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.logging.Logger;

/**
* Listener to add action for UI report for each run
*/
@Extension
public class EnvironmentWatcherListener extends FlowExecutionListener {
private static final Logger LOGGER = Logger.getLogger(EnvironmentWatcherListener.class.getName());

@Override
public void onRunning(@Nonnull FlowExecution execution) {
FlowExecutionOwner owner = execution.getOwner();
try {
if (owner != null && owner.getExecutable() instanceof Run) {
((Run) owner.getExecutable()).addAction(new EnvironmentWatcherRunReport());
}
} catch (IOException e) {
LOGGER.warning(e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.jenkinsci.plugins.workflow.cps.view;

import hudson.model.Run;
import jenkins.model.RunAction2;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Action to generate the UI report for watched environment variables
*/
public class EnvironmentWatcherRunReport implements RunAction2, Serializable {

private Set<String> results;
private transient Run<?, ?> run;

public String getIconFileName() {
return null;
}

public String getDisplayName() {
return null;
}

public String getUrlName() {
return null;
}

public void record(List<String> stepResults) {
if (results == null) {
results = new HashSet<>();
}
results.addAll(stepResults);
}

public Set<String> getResults() {
return results;
}

public boolean getInProgress() {
return run.isBuilding();
}

@Override
public void onAttached(Run<?, ?> run) {
this.run = run;
}

@Override
public void onLoad(Run<?, ?> run) {
this.run = run;
}

private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright (c) 2020, 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.
-->

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:t="/lib/hudson">
<t:summary icon="warning.png">
Possible insecure use of sensitive variables:
<j:if test="${it.inProgress}">
(in progress)
</j:if>
<ul>
<j:forEach var="variable" items="${it.results}">
<li>${variable}</li>
</j:forEach>
</ul>
</t:summary>
</j:jelly>