fix: back to explicit plugin activation#3578
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request refines the plugin management system to enforce explicit activation for plugins, moving away from implicit discovery for certain scenarios. It introduces checks against a list of configured plugins during both main class loader discovery and JAR-based loading, ensuring that only intended plugins are registered. Additionally, it improves robustness by preventing and logging warnings for duplicate plugin registrations. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
🧪 CI InsightsHere's what we observed from your CI run for d8c11a7. 🟢 All jobs passed!But CI Insights is watching 👀 |
There was a problem hiding this comment.
Code Review
This pull request refactors the plugin activation logic to explicitly check against configuredPlugins before registering a plugin, ensuring that only plugins specified in the configuration are activated. However, the current implementation has a critical security flaw where plugin classes are instantiated before the authorization check, allowing for Remote Code Execution (RCE) via malicious JAR files. Additionally, the authorization check can be bypassed by naming a malicious JAR file the same as an authorized plugin. Beyond these critical security concerns, there are also minor areas for improvement regarding code readability and efficiency.
| for (final ServerPlugin pluginInstance : serviceLoader) { | ||
| final String name = pluginInstance.getName(); | ||
| // Load the first plugin implementation (typically only one per JAR) | ||
| for (ServerPlugin pluginInstance : serviceLoader) { |
There was a problem hiding this comment.
The PluginManager uses ServiceLoader to discover and load ServerPlugin implementations from JAR files in the lib/plugins directory. The iteration over the ServiceLoader on line 143 triggers the instantiation of the plugin class (using its default constructor). This instantiation occurs before the check on line 157, which verifies if the plugin is explicitly authorized in the arcadedb.server.plugins configuration. An attacker who can place a malicious JAR file in the plugins directory can achieve arbitrary code execution by placing malicious logic in the static initializer or constructor of their plugin class, even if the plugin is not listed in the configuration.
To remediate this, use ServiceLoader.stream() (available in Java 9+) to inspect the plugin's class name or other metadata before instantiating it. Alternatively, manually parse the META-INF/services/com.arcadedb.server.ServerPlugin file within the JAR to verify the class name against the allowed list before loading and instantiating it.
for (ServiceLoader.Provider<ServerPlugin> provider : serviceLoader.stream().toList()) {
String className = provider.type().getName();
String simpleName = provider.type().getSimpleName();
if (configuredPlugins.contains(simpleName) || configuredPlugins.contains(pluginName) || configuredPlugins.contains(className)) {
ServerPlugin pluginInstance = provider.get();| if (configuredPlugins.contains(name) || configuredPlugins.contains(pluginName) || configuredPlugins.contains( | ||
| pluginInstance.getClass().getName())) { |
There was a problem hiding this comment.
The check for explicit plugin activation in loadPlugin includes a check against the JAR filename (pluginName). Specifically, line 157 allows a plugin to be loaded if configuredPlugins.contains(pluginName). Since the JAR filename is easily controlled by an attacker who can write to the plugins directory, they can bypass the explicit activation policy by naming their malicious JAR file the same as a legitimately authorized plugin.
To remediate this, remove the check against pluginName (the JAR filename) from the authorization logic. Only authorize plugins based on their full class name or their internal name (returned by getName()), ensuring that these are also verified against a trusted source.
if (configuredPlugins.contains(name) || configuredPlugins.contains(
pluginInstance.getClass().getName())) {
server/src/main/java/com/arcadedb/server/plugin/PluginManager.java
Outdated
Show resolved
Hide resolved
| final PluginDescriptor descriptor = new PluginDescriptor(pluginInstance.getName(), classLoader); | ||
| descriptor.setPluginInstance(pluginInstance); |
There was a problem hiding this comment.
The PluginDescriptor is created unconditionally at the beginning of the loop. If the plugin is already registered (lines 151-155) or not configured (the else branch of the if on line 157), this descriptor is created but not used, leading to unnecessary object allocation. Consider creating the PluginDescriptor only when the plugin is confirmed to be registered.
if (configuredPlugins.contains(name) || configuredPlugins.contains(pluginName) || configuredPlugins.contains(
pluginInstance.getClass().getName())) {
// Create plugin descriptor
final PluginDescriptor descriptor = new PluginDescriptor(pluginInstance.getName(), classLoader);
descriptor.setPluginInstance(pluginInstance);
// Register the plugin
Code ReviewThanks for the fix to enforce explicit plugin activation. The intent is clear, but I've spotted several issues that should be addressed before merging. 1. Indentation Bug in
|
| # | Severity | Issue |
|---|---|---|
| 1 | High | Indentation of if body in discoverPluginsOnMainClassLoader is wrong |
| 2 | Medium | final removed from variables contrary to project style |
| 3 | High | Existing tests likely broken by new filter (AutoBackupSchedulerPlugin no longer auto-discovered) |
| 4 | Low | PluginDescriptor created before duplicate check; getName() called twice |
| 5 | Low | continue → break semantic change undocumented |
| 6 | Medium | Inconsistent plugin identity key between the two discovery paths |
The checklist items in the PR description (build + unit tests) are both unchecked — please confirm the build passes and the tests are green with these changes.
Coverage summary from CodacySee diff coverage on Codacy
Coverage variation details
Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: Diff coverage details
Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: See your quality gate settings Change summary preferences |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3578 +/- ##
==========================================
- Coverage 65.70% 65.00% -0.71%
==========================================
Files 1514 1514
Lines 103646 103653 +7
Branches 21449 21452 +3
==========================================
- Hits 68101 67377 -724
- Misses 26302 27102 +800
+ Partials 9243 9174 -69 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…roperty -Darcadedb.server.plugins
Code ReviewThanks for the fix to enforce explicit plugin activation. The intent is clear, but I have spotted several issues that should be addressed before merging. 1. Dead Code in ArcadeDBServer.init() (Low)Line 740 reads the configuration value but discards the result. It is a pure no-op and should be removed: 2. Duplicate Detection Does Not Actually Skip Registration (High)In loadPlugin, when a duplicate is detected the code logs a warning but then falls through. The plugin is still registered, silently overwriting the existing entry: The warning says "skipping" but the code does not skip anything. A 3. Trailing Comma When SERVER_PLUGINS Is Empty (Low)If 4. Tests Rely on Implicit Side-Effect of ArcadeDBServer Constructor (Medium)The
This is fragile:
The explicit plugin list in the test setup should simply include 5. Commented-Out Code Left in Production (Low)Two These should either be reinstated (with the decision documented) or removed entirely. Summary
Issue #2 is a correctness bug: the duplicate-detection path logs "skipping" but does not skip. |
Code ReviewThe overall intent of this PR is good: enforcing explicit plugin activation rather than auto-discovering everything on the classpath is a safer approach. However, I found several issues worth addressing. 1. Dead code in
|
(cherry picked from commit b39992b)
What does this PR do?
A brief description of the change being made with this pull request.
Motivation
What inspired you to submit this pull request?
Related issues
A list of issues either fixed, containing architectural discussions, otherwise relevant
for this Pull Request.
Additional Notes
Anything else we should know when reviewing?
Checklist
mvn clean packagecommand