Skip to content

Commit

Permalink
Fix security vulnerability for untrusted library (SECURITY-3466)
Browse files Browse the repository at this point in the history
  • Loading branch information
c3p0-maif committed Sep 26, 2024
1 parent 786074c commit 7402485
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.jenkinsci.plugins.workflow.libs.LibraryResolver;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.StaplerRequest;

/**
* A class that holds a list of overrides configurations for a folder
Expand Down Expand Up @@ -70,38 +69,132 @@ public String getDisplayName() {
}

/**
* Simulate a new LibraryResolver prior to global resolver
* Return all known LibraryConfigurations including Global Libraries
* @param group the context
* @return the known LibraryConfigurations
*/
public static Collection<LibraryConfiguration> getDefinedLibrariesForGroup(ItemGroup<?> group) {
return getDefinedLibrariesForGroup(group, true);

Check warning on line 77 in src/main/java/io/jenkins/plugins/shared_library_version_override/FolderConfigurations.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 77 is not covered by tests
}

/**
* Return all known LibraryConfigurations including or not Global Libraries
* @param group the context
* @param includeGlobalLibraries if true then Global Libraries are not returned
* @return the known LibraryConfigurations
*/
public static Collection<LibraryConfiguration> getDefinedLibrariesForGroup(
ItemGroup<?> group, boolean includeGlobalLibraries) {
List<LibraryConfiguration> libraries = new ArrayList<>();
// Get all global librairies
if (includeGlobalLibraries) {

Check warning on line 90 in src/main/java/io/jenkins/plugins/shared_library_version_override/FolderConfigurations.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 90 is only partially covered, one branch is missing
GlobalLibraries libs = ExtensionList.lookupSingleton(GlobalLibraries.class);
libraries.addAll(libs.getLibraries());

Check warning on line 92 in src/main/java/io/jenkins/plugins/shared_library_version_override/FolderConfigurations.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 92 is not covered by tests
}
// Get all folder local libraries
for (ItemGroup<?> g = group; g instanceof AbstractFolder; g = ((AbstractFolder<?>) g).getParent()) {
AbstractFolder<?> f = (AbstractFolder<?>) g;
FolderLibraries prop = f.getProperties().get(FolderLibraries.class);
if (prop != null) {
libraries.addAll(prop.getLibraries());
}
}
LOGGER.log(
Level.FINE,
"FolderConfigurations.getDefinedLibrariesForGroup {0}\n",
libraries.stream().map(LibraryConfiguration::getName).collect(Collectors.toList()));
return libraries;
}

/**
* Return a copy of a LibraryConfiguration with a new version, if allowed
* @param item the override configuration desired
* @param libs the LibraryConfigurations known for the current context
* @return the copy of the LibraryConfiguration with the new version or null if library don't allow version override
*/
public static LibraryConfiguration getLibraryConfiguration(
LibraryCustomConfiguration item, Collection<LibraryConfiguration> libs) {
LibraryConfiguration libConfig = null;
for (LibraryConfiguration lib : libs) {
if (lib.getName().equals(item.getName())) {

Check warning on line 119 in src/main/java/io/jenkins/plugins/shared_library_version_override/FolderConfigurations.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 119 is only partially covered, one branch is missing
// if original library don't allow version override, so don't take it
if (!lib.isAllowVersionOverride()) {
LOGGER.log(
Level.FINE,
"FolderConfigurations.getLibraryConfiguration {0} don't allow version override, don't take it.\n",
lib.getName());
continue;
}
libConfig = new LibraryConfiguration(lib.getName(), lib.getRetriever());
libConfig.setDefaultVersion(item.getVersion());
libConfig.setImplicit(lib.isImplicit());
libConfig.setAllowVersionOverride(lib.isAllowVersionOverride());
libConfig.setIncludeInChangesets(lib.getIncludeInChangesets());
libConfig.setCachingConfiguration(lib.getCachingConfiguration());
}
}
return libConfig;
}

/**
* Simulate a new LibraryResolver for Trusted Libraries (Global-level Libraries)
*/
@Extension(ordinal = 1000) // Priority over pipeline-groovy-lib
public static class CustomLibraryResolver extends LibraryResolver {
public static class CustomTrustedLibraryResolver extends LibraryResolver {

@Override
public boolean isTrusted() {
return true;
}

public static Collection<LibraryConfiguration> getDefinedLibrariesForGroup(@CheckForNull ItemGroup<?> group) {
// Get all global librairies
GlobalLibraries libs = ExtensionList.lookupSingleton(GlobalLibraries.class);

List<LibraryConfiguration> libraries = new ArrayList<>(libs.getLibraries());
// Get all folder local libraries
private Collection<LibraryConfiguration> forGroup(@CheckForNull ItemGroup<?> group, boolean checkPermission) {
// Get all global libraries
Collection<LibraryConfiguration> allLibs =
ExtensionList.lookupSingleton(GlobalLibraries.class).getLibraries();
List<LibraryConfiguration> libraries = new ArrayList<>();
for (ItemGroup<?> g = group; g instanceof AbstractFolder; g = ((AbstractFolder<?>) g).getParent()) {
AbstractFolder<?> f = (AbstractFolder<?>) g;
FolderLibraries prop = f.getProperties().get(FolderLibraries.class);
if (prop != null) {
libraries.addAll(prop.getLibraries());
if (!checkPermission || f.hasPermission(Item.CONFIGURE)) {
FolderConfigurations prop = f.getProperties().get(FolderConfigurations.class);
if (prop != null) {
for (LibraryCustomConfiguration item : prop.getOverrides()) {
LibraryConfiguration libConfig = getLibraryConfiguration(item, allLibs);
if (libConfig != null) {
libraries.add(libConfig);
}
}
}
}
}
LOGGER.log(
Level.FINE,
"CustomLibraryResolver.getDefinedLibrariesForGroup {0}\n",
"CustomFolderLibraryResolver.forGroup {0}\n",
libraries.stream().map(LibraryConfiguration::getName).collect(Collectors.toList()));
return libraries;
}

@NonNull
@Override
public Collection<LibraryConfiguration> forJob(
@NonNull Job<?, ?> job, @NonNull Map<String, String> libraryVersions) {
return forGroup(job.getParent(), false);
}
}

/**
* Simulate a new LibraryResolver for Untrusted libraries (Folder-level Libraries)
*/
@Extension(ordinal = 1000) // Priority over pipeline-groovy-lib
public static class CustomUntrustedLibraryResolver extends LibraryResolver {

@Override
public boolean isTrusted() {
return false;
}

private Collection<LibraryConfiguration> forGroup(@CheckForNull ItemGroup<?> group, boolean checkPermission) {
// Get all available libraries
Collection<LibraryConfiguration> allLibs = getDefinedLibrariesForGroup(group);
// Get all folder-level libraries
Collection<LibraryConfiguration> allLibs = getDefinedLibrariesForGroup(group, false);
List<LibraryConfiguration> libraries = new ArrayList<>();
for (ItemGroup<?> g = group; g instanceof AbstractFolder; g = ((AbstractFolder<?>) g).getParent()) {
AbstractFolder<?> f = (AbstractFolder<?>) g;
Expand All @@ -119,55 +212,16 @@ private Collection<LibraryConfiguration> forGroup(@CheckForNull ItemGroup<?> gro
}
LOGGER.log(
Level.FINE,
"CustomLibraryResolver.forGroup {0}\n",
"CustomUntrustedLibraryResolver.forGroup {0}\n",
libraries.stream().map(LibraryConfiguration::getName).collect(Collectors.toList()));
return libraries;
}

private static LibraryConfiguration getLibraryConfiguration(
LibraryCustomConfiguration item, Collection<LibraryConfiguration> libs) {
LibraryConfiguration libConfig = null;
for (LibraryConfiguration lib : libs) {
if (lib.getName().equals(item.getName())) {
// if original library don't allow version override, so don't take it
if (!lib.isAllowVersionOverride()) {
LOGGER.log(
Level.FINE,
"CustomLibraryResolver.getLibraryConfiguration {0} don't allow version override, don't take it.\n",
lib.getName());
continue;
}
libConfig = new LibraryConfiguration(lib.getName(), lib.getRetriever());
libConfig.setDefaultVersion(item.getVersion());
libConfig.setImplicit(lib.isImplicit());
libConfig.setAllowVersionOverride(lib.isAllowVersionOverride());
libConfig.setIncludeInChangesets(lib.getIncludeInChangesets());
libConfig.setCachingConfiguration(lib.getCachingConfiguration());
}
}
return libConfig;
}

@NonNull
@Override
public Collection<LibraryConfiguration> forJob(
@NonNull Job<?, ?> job, @NonNull Map<String, String> libraryVersions) {
LOGGER.log(Level.FINER, "CustomLibraryResolver.forJob({0})\n", job);
return forGroup(job.getParent(), false);
}

@NonNull
@Override
public Collection<LibraryConfiguration> fromConfiguration(@NonNull StaplerRequest request) {
LOGGER.log(Level.FINER, "CustomLibraryResolver.fromConfiguration({0})\n", request);
return forGroup(request.findAncestorObject(AbstractFolder.class), true);
}

@NonNull
@Override
public Collection<LibraryConfiguration> suggestedConfigurations(@NonNull ItemGroup<?> group) {
LOGGER.log(Level.FINER, "CustomLibraryResolver.suggestedConfigurations({0})\n", group);
return forGroup(group, false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ public ListBoxModel doFillNameItems(@AncestorInPath Item item) {

Set<String> libNames = new TreeSet<>();
ItemGroup<?> group = getItemGroupFromItem(item);
Collection<LibraryConfiguration> libs =
FolderConfigurations.CustomLibraryResolver.getDefinedLibrariesForGroup(group);
Collection<LibraryConfiguration> libs = FolderConfigurations.getDefinedLibrariesForGroup(group);
for (LibraryConfiguration lib : libs) {
libNames.add(lib.getName());
}
Expand All @@ -117,8 +116,7 @@ public FormValidation doValidate(
List<FormValidation> validations = new ArrayList<>();
// Check name existence and version override allowance
ItemGroup<?> group = getItemGroupFromItem(item);
Collection<LibraryConfiguration> libs =
FolderConfigurations.CustomLibraryResolver.getDefinedLibrariesForGroup(group);
Collection<LibraryConfiguration> libs = FolderConfigurations.getDefinedLibrariesForGroup(group);

Check warning on line 119 in src/main/java/io/jenkins/plugins/shared_library_version_override/LibraryCustomConfiguration.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 93-119 are not covered by tests
LibraryConfiguration lib = libs.stream()
.filter(l -> l.getName().equals(name))
.findFirst()
Expand Down
Loading

0 comments on commit 7402485

Please sign in to comment.