ADR: Plugin Security Architecture and ClassLoader Isolation

Status

Implemented

Context

OmegaT’s plugin system requires a balance between security, stability, and functional requirements. Historically, Java applications have two main approaches to plugin loading:

  1. Single Application ClassLoader: All plugins share the same namespace, allowing them to interfere with each other and causing dependency conflicts.

  2. Per-Plugin ClassLoader Isolation: The industry standard (used by OSGi, Eclipse, IntelliJ, NetBeans), where each plugin has its own ClassLoader.

While per-plugin isolation is ideal for security (fault containment, dependency safety, and resource management), OmegaT faces specific constraints that make it impractical for all plugin types:

  • Language Module Resource Sharing: Language modules must share resources like dictionaries with the core library and other plugins.

  • Swing UIManager Registration: Theme plugins must register Look-and-Feel (LaF) classes with the global UIManager, which requires global visibility that isolated ClassLoaders do not provide.

Decision

We have decided to implement a Type-Based ClassLoader Isolation strategy using an EnumMap to manage ClassLoaders.

Key Architectural Changes

  • EnumMap-Based Management: Use EnumMap<PluginType, MainClassLoader> MAINCLASSLOADERS to assign one ClassLoader per plugin type.

  • Grouped Isolation:

    • Theme plugins share one ClassLoader.

    • Language plugins share one ClassLoader.

    • Filter plugins share one ClassLoader.

    • Other plugin types follow this pattern.

Implementation Details

The implementation maps each PluginType to a specific MainClassLoader. This ensures that plugins of the same type can share necessary resources and interact with global APIs where required by their functional role.

EnumMap<PluginType, MainClassLoader> MAINCLASSLOADERS;

Memory Impact

  • Per-Type ClassLoaders: ~200KB overhead regardless of the number of plugins.

  • Comparison: Individual ClassLoaders would consume ~1MB for 20 plugins, while a single shared ClassLoader (deprecated) would consume ~50KB.

Consequences

Benefits

  1. Functional Compatibility: Respects the need for resource sharing in language modules and LaF registration in themes.

  2. Reasonable Isolation: Provides better security and stability than a single ClassLoader by separating different plugin types.

  3. Maintainability: Provides a clean and flexible mapping between plugin types and their loading logic.

  4. Future Flexibility: Provides a path toward stronger isolation (per-plugin) for plugin types that do not have shared resource constraints.

Trade-Offs and Risks

  1. Intra-Type Interference: Plugins of the same type (e.g., two different themes) can still access each other’s classes and interfere.

  2. Dependency Conflicts: Using different versions of the same library within plugins of the same type remains a risk.

  3. Fault Propagation: A bug or memory leak in one plugin can still affect other plugins of the same type.

Mitigation Strategies

To address the remaining risks, we recommend:

  • Validating plugin manifests and signatures.

  • Manual plugin reviews for official extensions.

  • Future investigation into sandboxing or more granular isolation where possible.

Testing and Verification

The architecture was verified by:

  • Ensuring language modules can correctly share dictionaries.

  • Ensuring theme plugins can register and apply Look-and-Feel classes via UIManager.

  • Monitoring memory usage to ensure ClassLoader overhead remains within acceptable limits.

References


Date: 2025-07-01 Author: Hiroshi Miura Reviewers: OmegaT Development Team