Plugin Security Architecture in OmegaT¶
This section explains the plugin security architecture in OmegaT, fundamental for understanding and working with its plugin system.
Understanding ClassLoaders and Security¶
What is a ClassLoader?¶
A ClassLoader in Java is responsible for loading classes into memory. Think of it as a gatekeeper that controls which classes your code can see and use. Each ClassLoader creates an isolated namespace—classes loaded by one ClassLoader cannot directly access classes loaded by another unless explicitly permitted.
Why ClassLoaders Matter for Security¶
Consider the following scenarios:
Single Application ClassLoader:
Application ClassLoader
├── Plugin A classes
├── Plugin B classes
└── Plugin C classes
In this setup,
Plugin Acan access and interfere withPlugin B’s classes.
Per-Plugin ClassLoaders:
Application ClassLoader
├── Plugin A ClassLoader
│ └── Plugin A classes
├── Plugin B ClassLoader
│ └── Plugin B classes
└── Plugin C ClassLoader
└── Plugin C classes
Here, each plugin is isolated, preventing interference.
Current Implementation¶
OmegaT Design Approach¶
Code snippet:
EnumMap<PluginType, MainClassLoader> MAINCLASSLOADERS;
Assigns one ClassLoader per plugin type:
Theme plugins share a ClassLoader.
Language plugins share a ClassLoader.
Filter plugins share a ClassLoader.
…and so on.
For the formal decision and architectural background on this approach, see ADR: Plugin Security Architecture and ClassLoader Isolation.
Security Implications¶
Intra-Type Interference: Plugins of the same type (e.g., themes) can access each other’s classes.
Dependency Conflicts: Different plugins using incompatible library versions can cause runtime conflicts.
Fault Propagation: A bug or memory leak in one plugin can affect all plugins of the same type.
Theoretical Best Practice: Per-Plugin ClassLoader Isolation¶
Industry Standard Approach¶
There is a best practice of the software industry standard. It is “Per-Plugin” classloader isolation.
Here is a code snippet to explain.
public class PluginUtils {
Map<String, ClassLoader> pluginClassLoaders = new ConcurrentHashMap<>();
public void loadPlugins() {
// ... several initializations
// During plugin loading:
for (PluginManifest manifest : manifests) {
ClassLoader pluginClassLoader = new URLClassLoader(
manifest.getClasspathUrls(), getClass().getClassLoader()
);
pluginClassLoaders.put(manifest.getPluginId(), pluginClassLoader);
}
}
}
Benefits¶
Complete Isolation: Plugins cannot interfere with one another.
Dependency Safety: Plugins can use distinct library versions.
Fault Containment: Failures in one plugin don’t affect others.
Security Boundaries: Malicious plugins are isolated.
Resource Management: Memory leaks are contained per ClassLoader.
Industry Best Practices¶
OSGi Framework: Each bundle (plugin) gets its own ClassLoader
Eclipse Plugin System: Plugins are isolated by default
IntelliJ IDEA: Plugins run in separate ClassLoaders
NetBeans: Module system provides ClassLoader isolation
These systems all choose security over simplicity because plugin isolation is critical for stability and security.
Why Per-Plugin ClassLoaders Won’t Work for OmegaT¶
OmegaT has specific requirements that make per-plugin ClassLoader isolation impractical.
Constraint 1: Language Module Resource Sharing¶
Problem: Language modules must share resources like dictionaries with the core library.
Issue with Isolation:
Resources in
Plugin A’s ClassLoader are invisible to the core or other plugins.
Required Solution:
A shared ClassLoader for language plugins ensures resource accessibility.
Constraint 2: Swing UIManager Registration¶
Problem: Theme plugins must register Look-and-Feel (LaF) classes with
UIManager.Issue with Isolation:
UIManagerrequires global visibility to LaF classes, which isolated ClassLoaders cannot provide.
Required Solution:
A shared ClassLoader for theme plugins allows LnF classes to be registered and used globally.
Conclusion¶
The current design represents the optimal solution for OmegaT’s constraints. It enhances security within reasonable functional limits while maintaining a clean code structure and flexibility for future development.