Back to projects
Apr 20, 2026 • Project

RedOps: Java audit module framework via reflection

Extensible Java framework for running security audit modules in a standardized way, built on a @Module annotation, dynamic reflection-based loading, and a monitoring GUI.

JavaReflectionOffensive SecuritySoftware Architecture
Context
Group coursework at ISEP on cybersecurity: a tool that chains audit modules against a target.
My role
I designed the whole framework and core: module discovery through an annotation, instantiation, orchestration, lifecycle.
Outcome
Adding a module means writing one class annotated with @Module, without touching the rest of the code.

The Goal

RedOps is group coursework at ISEP, around cybersecurity. My part: the whole framework and core that make the plugins usable. What interested me was the architecture: how to build a plugin system in Java without relying on a framework like Spring — just the JDK’s native reflection.

Concretely, the idea is that you write a new class, annotate it with @Module, and it gets discovered at runtime and executed automatically on startup. The modules shipped with the project (ping, basic port scan) are intentionally simple — the goal wasn’t to deliver a full pentest tool but to validate the extensibility pattern.

Architecture

The @Module annotation and reflection scan

Everything is built on a custom annotation:

@Module(name = "Simple Ping", order = 0)
public class Ping extends AbstractModule {
    @Override
    protected ExecutionStatus process(InetAddress address) throws InterruptedException {
        boolean reachable = address.isReachable(5000);
        return reachable ? ExecutionStatus.SUCCESS : ExecutionStatus.ERROR;
    }
}

At compile time, AnnotationConfigLoader scans the com.redops package (via the Reflections library) to find all classes carrying @Module, extracts their metadata (name, execution order), and builds the configuration. No XML or YAML to maintain: adding a module means writing a class and an annotation, and the framework handles discovery on its own.

The orchestration pipeline

ModuleManager is the entry point for orchestration and delegates to three single-responsibility components:

public class ModuleManager {
    public Stream<ModuleInfo> startModules() {
        List<ModuleInfo> modules = initializer.initializeModules();
        executor.executeModules(modules);
        return modules.stream();
    }
}

Very basic dependency injection

ModuleInstantiator inspects constructor parameters via reflection and, depending on their type, provides the matching instance — for example, the target IP address for the module. No DI container, no @Inject annotations: just an if/switch on the expected type and the right instance passed in by hand. Intentionally minimal — the framework resolves what a module needs before instantiating it, rather than letting the module go fetch it itself.

Module lifecycle

Each module extends AbstractModule, which wraps the status transitions around the process() call:

public abstract class AbstractModule {
    public ExecutionStatus status = ExecutionStatus.NOT_STARTED;

    public void run() throws UnknownHostException, InterruptedException {
        setStatus(ExecutionStatus.IN_PROGRESS);
        ExecutionStatus result = process(getTargetAddress());
        setStatus(result);
    }

    protected abstract ExecutionStatus process(InetAddress address)
        throws InterruptedException, IOException;
}

Statuses (SUCCESS, WARNING, ERROR, CRITICAL_ERROR…) are defined in a dedicated enum and pushed to the GUI in real time. A failing module doesn’t stop the others — ModuleExecutor logs the error and continues the sequence.

GUI

The UI (Swing + FlatLaf for a more modern look, JFreeChart for result graphs) runs on the Event Dispatch Thread, separate from the module execution thread. Synchronization between the two goes through SwingUtilities.invokeLater(), to avoid the classic concurrent access issues in Swing.

What I Learned

Understanding where reflection’s guarantees end: without a framework like Spring behind the scenes, I had to inspect constructor parameters by hand and decide what to provide based on their type. A larger mapping could have supported more dependencies, but wasn’t really useful here. The threading side (running modules off the EDT, pushing UI updates via invokeLater) was also a solid introduction to classic concurrency problems in a desktop app.

Disclaimer

RedOps is an educational project: the bundled modules are intentionally basic (ping, port scan), designed to demonstrate the extensibility pattern rather than for real pentest use. The code is MIT licensed, on the group’s GitHub repository.