diff --git a/build.gradle b/build.gradle index a41412bcec..65ad224725 100644 --- a/build.gradle +++ b/build.gradle @@ -321,6 +321,83 @@ tasks.register("installDefaultGems") { doLast { rake(projectDir, buildDir, 'plugin:install-default') } + finalizedBy "syncStdlibBouncyCastleJars" +} + +tasks.register("syncStdlibBouncyCastleJars") { + description = "Replace JRuby stdlib BouncyCastle jars with the version from the bundled jruby-openssl gem" + doLast { + def bundleDir = file("${projectDir}/vendor/bundle/jruby") + def stdlibDir = file("${projectDir}/vendor/jruby/lib/ruby/stdlib") + + // Find the bundled jruby-openssl gem (search all Ruby compat version dirs) + def gemDirs = [] + bundleDir.eachDirRecurse { dir -> + if (dir.name.matches("jruby-openssl-.*-java") && dir.parentFile.name == "gems") { + gemDirs.add(dir) + } + } + if (gemDirs.isEmpty()) { + throw new GradleException("Cannot find bundled jruby-openssl gem in ${bundleDir}") + } + def gemDir = gemDirs.sort().last() + + // Read BC version from bundled gem's version.rb + def versionFile = file("${gemDir}/lib/jopenssl/version.rb") + def versionContent = versionFile.text + def bcVersionMatcher = (versionContent =~ /BOUNCY_CASTLE_VERSION\s*=\s*'([^']+)'/) + if (!bcVersionMatcher.find()) { + throw new GradleException("Cannot parse BOUNCY_CASTLE_VERSION from ${versionFile}") + } + def bcVersion = bcVersionMatcher.group(1) + def gemVersion = (versionContent =~ /VERSION\s*=\s*'([^']+)'/)[0][1] + logger.lifecycle("Syncing stdlib BouncyCastle jars: using version ${bcVersion} from jruby-openssl ${gemVersion}") + + // Replace stdlib BC jars with those from the bundled gem. + // We avoid deleting old version jars because on Windows they may still be locked + // by the JRuby process that ran installDefaultGems. Instead, we copy the new version + // jars alongside any existing ones. The jopenssl/load.rb uses BOUNCY_CASTLE_VERSION + // from version.rb to select which jars to load, so old jars are simply ignored at runtime. + def stdlibBcDir = file("${stdlibDir}/org/bouncycastle") + stdlibBcDir.mkdirs() + + def gemBcDir = file("${gemDir}/lib/org/bouncycastle") + if (gemBcDir.exists()) { + def bcArtifacts = ["bcprov-jdk18on", "bcpkix-jdk18on", "bctls-jdk18on", "bcutil-jdk18on"] + bcArtifacts.each { artifact -> + def gemArtifactVersionDir = file("${gemBcDir}/${artifact}/${bcVersion}") + if (gemArtifactVersionDir.exists()) { + def stdlibArtifactDir = file("${stdlibBcDir}/${artifact}") + def targetDir = file("${stdlibArtifactDir}/${bcVersion}") + targetDir.mkdirs() + copy { + from gemArtifactVersionDir + into targetDir + } + // Best-effort cleanup of old version dirs (may fail on Windows due to file locks) + if (stdlibArtifactDir.exists()) { + stdlibArtifactDir.eachDir { versionDir -> + if (versionDir.name != bcVersion) { + try { + delete(versionDir) + } catch (Exception e) { + logger.warn("Could not remove old BC jars at ${versionDir} (file may be locked): ${e.message}") + } + } + } + } + } + } + } + + // Update stdlib's jopenssl/version.rb to match the bundled gem + def stdlibVersionFile = file("${stdlibDir}/jopenssl/version.rb") + if (stdlibVersionFile.exists()) { + stdlibVersionFile.text = versionContent + } + + logger.lifecycle("Stdlib BouncyCastle jars synced to version ${bcVersion}") + } } tasks.register("installDevelopmentGems") { diff --git a/lib/pluginmanager/util.rb b/lib/pluginmanager/util.rb index e6710039db..06f3d9acb9 100644 --- a/lib/pluginmanager/util.rb +++ b/lib/pluginmanager/util.rb @@ -17,6 +17,7 @@ require "rubygems/package" require "yaml" +require "digest" module LogStash::PluginManager def self.load_aliases_definitions(path = File.expand_path('plugin_aliases.yml', __dir__)) diff --git a/rakelib/artifacts.rake b/rakelib/artifacts.rake index 75dea58784..143dd6593f 100644 --- a/rakelib/artifacts.rake +++ b/rakelib/artifacts.rake @@ -119,6 +119,7 @@ namespace "artifact" do @exclude_paths << 'docker/data/logstash/env2yaml/**/*.java' @exclude_paths << 'docker/data/logstash/env2yaml/build.gradle' @exclude_paths << 'docker/data/logstash/env2yaml/settings.gradle' + @exclude_paths.freeze end