Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'merge/JENKINS-34908'
Conflicts:
	src/main/java/com/michelin/cio/hudson/plugins/maskpasswords/MaskPasswordsOutputStream.java
  • Loading branch information
oleg-nenashev committed Nov 29, 2016
2 parents f8fb42b + 25947b0 commit 38bea3a
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
Expand Up @@ -26,12 +26,15 @@
package com.michelin.cio.hudson.plugins.maskpasswords;

import hudson.console.LineTransformationOutputStream;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.StringUtils;

/**
* Custom output stream which masks a predefined set of passwords.
Expand Down Expand Up @@ -67,6 +70,16 @@ public MaskPasswordsOutputStream(OutputStream logger, @CheckForNull Collection<S
if(StringUtils.isNotEmpty(password)) { // we must not handle empty passwords
regex.append(Pattern.quote(password));
regex.append('|');
try {
String encodedPassword = URLEncoder.encode(password, "UTF-8");
if (!encodedPassword.equals(password)) {
// add to masking regex
regex.append(Pattern.quote(encodedPassword));
regex.append('|');
}
} catch (UnsupportedEncodingException e) {
// ignore any encoding problem => status quo
}
nbMaskedPasswords++;
}
}
Expand Down
@@ -0,0 +1,101 @@
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* 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 com.michelin.cio.hudson.plugins.maskpasswords;

import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runners.model.Statement;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.RestartableJenkinsRule;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Set;
import java.util.TreeSet;

@Issue("JENKINS-34908")
public class MaskPasswordsURLEncodingTest {

public static final String THE_SECRET = "#s3cr3t";
@ClassRule
public static BuildWatcher buildWatcher = new BuildWatcher();
@Rule
public RestartableJenkinsRule story = new RestartableJenkinsRule();


@Test
public void passwordMaskedEncoded() throws Exception {
story.addStep(new Statement() {
@Override
public void evaluate() throws Throwable {
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("node {wrap([$class: 'MaskPasswordsBuildWrapper', varPasswordPairs: [[var: 'PASSWORD', password: '" + THE_SECRET + "']]]) {semaphore 'restarting'; echo 'printed unencoded " + THE_SECRET + " oops'; echo 'printed encoded " + URLEncoder.encode(THE_SECRET, "UTF-8" ) + " oops'}}", true));
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
SemaphoreStep.waitForStart("restarting/1", b);
}
});
story.addStep(new Statement() {
@Override
public void evaluate() throws Throwable {
WorkflowJob p = story.j.jenkins.getItemByFullName("p", WorkflowJob.class);
WorkflowRun b = p.getLastBuild();
SemaphoreStep.success("restarting/1", null);
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(b));
story.j.assertLogContains("printed unencoded ******** oops", b);
story.j.assertLogContains("printed encoded ******** oops", b);
}
});
}

// Copied from credentials-binding-plugin; perhaps belongs in JenkinsRule?
private static Set<String> grep(File dir, String text) throws IOException {
Set<String> matches = new TreeSet<String>();
grep(dir, text, "", matches);
return matches;
}
private static void grep(File dir, String text, String prefix, Set<String> matches) throws IOException {
File[] kids = dir.listFiles();
if (kids == null) {
return;
}
for (File kid : kids) {
String qualifiedName = prefix + kid.getName();
if (kid.isDirectory()) {
grep(kid, text, qualifiedName + "/", matches);
} else if (kid.isFile() && FileUtils.readFileToString(kid).contains(text)) {
matches.add(qualifiedName);
}
}
}

}

0 comments on commit 38bea3a

Please sign in to comment.