Skip to content

Commit

Permalink
Add validation to include/exlude jobs regular expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
nikita-tkachenko-datadog committed Sep 18, 2024
1 parent 007fb29 commit 3db94aa
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 43 deletions.
2 changes: 1 addition & 1 deletion docker/controller-node/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ RUN cat /tmp/jenkins-casc.yaml | envsubst > /var/jenkins_home/casc/jenkins-casc.

RUN mkdir -p /var/jenkins_home/shared
RUN mkdir -p /var/jenkins_home/plugins
ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5055"
ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false -XX:+PrintConcurrentLocks -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5055"
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,21 @@ of this software and associated documentation files (the "Software"), to deal
import org.datadog.jenkins.plugins.datadog.configuration.api.key.DatadogCredentialsApiKey;
import org.datadog.jenkins.plugins.datadog.configuration.api.key.DatadogTextApiKey;
import org.datadog.jenkins.plugins.datadog.util.SuppressFBWarnings;
import org.datadog.jenkins.plugins.datadog.util.conversion.PatternListConverter;
import org.datadog.jenkins.plugins.datadog.util.conversion.PolymorphicReflectionConverter;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.interceptor.RequirePOST;

import javax.annotation.Nonnull;
import java.io.File;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;

import static org.datadog.jenkins.plugins.datadog.configuration.DatadogAgentConfiguration.DatadogAgentConfigurationDescriptor.*;
Expand Down Expand Up @@ -129,10 +134,15 @@ public class DatadogGlobalConfiguration extends GlobalConfiguration {

@XStreamConverter(PolymorphicReflectionConverter.class)
private DatadogClientConfiguration datadogClientConfiguration;

@XStreamConverter(PatternListConverter.class)
private List<Pattern> excluded = null;

@XStreamConverter(PatternListConverter.class)
private List<Pattern> included = null;

private String ciInstanceName = DEFAULT_CI_INSTANCE_NAME;
private String hostname = null;
private String excluded = null;
private String included = null;
private String globalTagFile = null;
private String globalTags = null;
private String globalJobTags = null;
Expand Down Expand Up @@ -193,25 +203,25 @@ public void loadEnvVariables() {
}

String excludedEnvVar = System.getenv(EXCLUDED_PROPERTY);
if(StringUtils.isBlank(excludedEnvVar)){
if (StringUtils.isNotBlank(excludedEnvVar)) {
this.excluded = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile);
} else {
// backwards compatibility
excludedEnvVar = System.getenv(BLACKLIST_PROPERTY);
if(StringUtils.isNotBlank(excludedEnvVar)){
this.excluded = excludedEnvVar;
this.excluded = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile);
}
} else {
this.excluded = excludedEnvVar;
}

String includedEnvVar = System.getenv(INCLUDED_PROPERTY);
if(StringUtils.isBlank(includedEnvVar)){
if (StringUtils.isNotBlank(includedEnvVar)) {
this.included = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile);
} else {
// backwards compatibility
includedEnvVar = System.getenv(WHITELIST_PROPERTY);
if(StringUtils.isNotBlank(includedEnvVar)){
this.included = includedEnvVar;
this.included = DatadogUtilities.cstrToList(excludedEnvVar, Pattern::compile);
}
} else {
this.included = includedEnvVar;
}

String globalTagFileEnvVar = System.getenv(GLOBAL_TAG_FILE_PROPERTY);
Expand Down Expand Up @@ -334,6 +344,28 @@ public FormValidation doCheckCiInstanceName(@QueryParameter("ciInstanceName") fi
return FormValidation.ok();
}

@RequirePOST
public FormValidation doCheckIncluded(@QueryParameter("included") final String included) {

Check warning

Code scanning / Jenkins Security Scan

Stapler: Missing permission check Warning

Potential missing permission check in DatadogGlobalConfiguration#doCheckIncluded
return doCheckPatterns(included);
}

@RequirePOST
public FormValidation doCheckExcluded(@QueryParameter("excluded") final String excluded) {

Check warning

Code scanning / Jenkins Security Scan

Stapler: Missing permission check Warning

Potential missing permission check in DatadogGlobalConfiguration#doCheckExcluded
return doCheckPatterns(excluded);
}

private static FormValidation doCheckPatterns(String commaSeparatedPatterns) {
List<String> patterns = DatadogUtilities.cstrToList(commaSeparatedPatterns);
for (String pattern : patterns) {
try {
Pattern.compile(pattern);
} catch (PatternSyntaxException e) {
return FormValidation.error(pattern + " is not a valid regular expression");
}
}
return FormValidation.ok();
}

/**
* Indicates if this builder can be used with all kinds of project types.
*
Expand Down Expand Up @@ -403,8 +435,23 @@ public boolean configure(final StaplerRequest req, final JSONObject formData) th
}

setHostname(formData.getString("hostname"));
setExcluded(formData.getString("excluded"));
setIncluded(formData.getString("included"));

String excludedFormData = formData.getString("excluded");
FormValidation excludedValidation = doCheckExcluded(excludedFormData);
if (excludedValidation.kind == Kind.ERROR) {
throw new FormException(excludedValidation.getMessage(), "excluded");
} else {
setExcluded(excludedFormData);
}

String includedFormData = formData.getString("included");
FormValidation includedValidation = doCheckIncluded(includedFormData);
if (includedValidation.kind == Kind.ERROR) {
throw new FormException(includedValidation.getMessage(), "included");
} else {
setIncluded(includedFormData);
}

setGlobalTagFile(formData.getString("globalTagFile"));
setGlobalTags(formData.getString("globalTags"));
setGlobalJobTags(formData.getString("globalJobTags"));
Expand All @@ -418,7 +465,6 @@ public boolean configure(final StaplerRequest req, final JSONObject formData) th
String includeEvents = formData.getString("includeEvents");
String excludeEvents = formData.getString("excludeEvents");
FormValidation configStatus = validateEventFilteringConfig(emitSecurityEvents, emitSystemEvents, includeEvents, excludeEvents);

if (configStatus.kind == Kind.ERROR) {
String message = configStatus.getMessage();
String formField = !message.contains("included") ? "excludeEvents" : "includeEvents";
Expand Down Expand Up @@ -493,14 +539,27 @@ public void setHostname(final String hostname) {
this.hostname = hostname;
}

public boolean isJobExcluded(@Nonnull final String jobName) {
if (excluded == null || excluded.isEmpty()) {
return false;
}
for (Pattern pattern : excluded) {
Matcher matcher = pattern.matcher(jobName);
if (matcher.matches()) {
return true;
}
}
return false;
}

/**
* Getter function for the excluded global configuration, containing
* a comma-separated list of jobs to exclude from monitoring.
*
* @return a String array containing the excluded global configuration.
*/
public String getExcluded() {
return excluded;
return DatadogUtilities.listToCstr(excluded, Pattern::toString);
}

/**
Expand All @@ -510,7 +569,20 @@ public String getExcluded() {
* @param jobs - a comma-separated list of jobs to exclude from monitoring.
*/
public void setExcluded(final String jobs) {
this.excluded = jobs;
this.excluded = DatadogUtilities.cstrToList(jobs, Pattern::compile);
}

public boolean isJobIncluded(@Nonnull final String jobName) {
if (included == null || included.isEmpty()) {
return true;
}
for (Pattern pattern : included) {
Matcher matcher = pattern.matcher(jobName);
if (matcher.matches()) {
return true;
}
}
return false;
}

/**
Expand All @@ -520,7 +592,7 @@ public void setExcluded(final String jobs) {
* @return a String array containing the included global configuration.
*/
public String getIncluded() {
return included;
return DatadogUtilities.listToCstr(included, Pattern::toString);
}

/**
Expand All @@ -530,7 +602,7 @@ public String getIncluded() {
* @param jobs - a comma-separated list of jobs to include for monitoring.
*/
public void setIncluded(final String jobs) {
this.included = jobs;
this.included = DatadogUtilities.cstrToList(jobs, Pattern::compile);
}

/**
Expand Down Expand Up @@ -991,13 +1063,13 @@ public String getTraceServiceName() {
/** @deprecated use {@link #getExcluded()} */
@Deprecated
public String getBlacklist() {
return excluded;
return DatadogUtilities.listToCstr(excluded, Pattern::toString);
}

/** @deprecated use {@link #getIncluded()} */
@Deprecated
public String getWhitelist() {
return included;
return DatadogUtilities.listToCstr(included, Pattern::toString);
}

/** @deprecated use {@link #getEnableCiVisibility()} */
Expand Down Expand Up @@ -1138,10 +1210,10 @@ protected Object readResolve() {
this.ciInstanceName = this.traceServiceName;
}
if (this.blacklist != null) {
this.excluded = this.blacklist;
this.excluded = DatadogUtilities.cstrToList(this.blacklist, Pattern::compile);
}
if (this.whitelist != null) {
this.included = this.whitelist;
this.included = DatadogUtilities.cstrToList(this.whitelist, Pattern::compile);
}
if (DATADOG_AGENT_CLIENT_TYPE.equals(reportWith)) {
this.datadogClientConfiguration = new DatadogAgentConfiguration(this.targetHost, this.targetPort, this.targetLogCollectionPort, this.targetTraceCollectionPort);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,17 +359,7 @@ private static boolean isJobExcluded(@Nonnull final String jobName) {
if (datadogGlobalConfig == null) {
return false;
}
final String excludedProp = datadogGlobalConfig.getExcluded();
List<String> excluded = cstrToList(excludedProp);
for (String excludedJob : excluded) {
Pattern excludedJobPattern = Pattern.compile(excludedJob);
Matcher jobNameMatcher = excludedJobPattern.matcher(jobName);
if (jobNameMatcher.matches()) {
return true;
}
}
return false;

return datadogGlobalConfig.isJobExcluded(jobName);
}

/**
Expand All @@ -383,16 +373,7 @@ private static boolean isJobIncluded(@Nonnull final String jobName) {
if (datadogGlobalConfig == null) {
return true;
}
final String includedProp = datadogGlobalConfig.getIncluded();
final List<String> included = cstrToList(includedProp);
for (String includedJob : included) {
Pattern includedJobPattern = Pattern.compile(includedJob);
Matcher jobNameMatcher = includedJobPattern.matcher(jobName);
if (jobNameMatcher.matches()) {
return true;
}
}
return included.isEmpty();
return datadogGlobalConfig.isJobIncluded(jobName);
}

/**
Expand All @@ -405,6 +386,23 @@ public static List<String> cstrToList(final String str) {
return convertRegexStringToList(str, ",");
}

/**
* Converts a comma-separated string into a list
*
* @param str - A string containing a comma separated list of items.
* @param converter - Converter function that is applied to every list element
*/
public static <T> List<T> cstrToList(String str, Function<String, T> converter) {
return convertRegexStringToList(str, ",").stream().map(converter).collect(Collectors.toList());
}

/**
* Converts a list to a comma-separated string
*/
public static <T> String listToCstr(List<T> list, Function<T, String> converter) {
return list != null ? list.stream().map(converter).collect(Collectors.joining(",")) : null;
}

/**
* Converts a string List into a List Object
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.datadog.jenkins.plugins.datadog.util.conversion;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.datadog.jenkins.plugins.datadog.DatadogUtilities;

public class PatternListConverter implements Converter {

@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
String string = DatadogUtilities.listToCstr((List<Pattern>) source, Pattern::toString);
context.convertAnother(string);
}

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
String string = (String) context.convertAnother(null, String.class);
if (StringUtils.isBlank(string)) {
return null;
}
return DatadogUtilities.cstrToList(string, Pattern::compile);
}

@Override
public boolean canConvert(Class type) {
return List.class.isAssignableFrom(type);
}
}

0 comments on commit 3db94aa

Please sign in to comment.