What it does
OpenAEV is an open source adversarial exposure validation platform — a system for scheduling and executing cyber attack simulations against your own infrastructure to validate whether your defenses actually catch them. It handles everything from email phishing injects and manual scenarios to automated payload execution through EDR agents like CrowdStrike, SentinelOne, Tanium, and Palo Alto Cortex.
Why I starred it
Most "purple team" tooling falls into one of two categories: point-and-click commercial products with opaque internals, or raw scripts that handle execution but nothing else. OpenAEV sits in between — it is open source and self-hostable, but it has a real platform model: exercises, scenarios, teams, injects, expectations, and a scheduler that runs them on a timeline.
The integration depth is what makes it interesting. This is not just a test harness that sends some packets and calls it done. It connects to OpenCTI for threat intelligence, pulls actual adversary TTPs, and uses those to populate simulation scenarios. The expectation system then verifies whether your controls fired — did the EDR alert? Did the SOC respond? Did the phishing email get blocked? That closed-loop validation is harder to build than it looks.
How it works
The backend is a Spring Boot Java application in openaev-api/. The frontend is a React app in openaev-front/. Everything is wired together with a Quartz scheduler in openaev-api/src/main/java/io/openaev/scheduler/.
The core execution loop lives in InjectsExecutionJob.java. It runs on a Quartz trigger and is annotated @DisallowConcurrentExecution so you never get two scheduler ticks stepping on each other. The job pulls all pending injects, groups them by exercise, and then runs them in parallel — exercises run in parallel with each other, injects within an exercise run in parallel too:
byExercises.entrySet().parallelStream()
.forEach(entry -> {
entry.getValue().parallelStream()
.forEach(executableInject -> {
try {
this.executeInject(executableInject);
} catch (Exception e) {
injectStatusService.failInjectStatus(inject.getId(), e.getMessage());
}
});
});
One thing worth noting: the job handles inject dependencies. If inject B depends on inject A succeeding, the scheduler filters out B from the current batch if A is also in the same batch. This prevents race conditions where the parent hasn't executed yet when the child is evaluated. The condition evaluation uses Spring Expression Language (SpEL) at runtime — the dependency conditions are stored as JSON strings, rewritten into SpEL expressions, and evaluated against a map of expectation results.
The inject execution dispatches through ExecutionExecutorService.java, which does a fairly mechanical agent classification before running anything. It pulls the set of agents for an inject, separates out inactive agents, agents without executors, and then routes by EDR type — CrowdStrike, SentinelOne, Tanium, Cortex each get their own batch path. Everything that doesn't match an EDR gets executed through the default agent path:
Set<Agent> crowdstrikeAgents =
executorUtils.findAgentsByExecutorType(agents, CROWDSTRIKE_EXECUTOR_TYPE);
agents.removeAll(crowdstrikeAgents);
// ... repeat for other EDR types ...
launchBatchExecutorContextForAgent(
crowdstrikeAgents, CROWDSTRIKE_EXECUTOR_NAME, inject, injectStatus, atLeastOneExecution);
The integration layer in openaev-api/src/main/java/io/openaev/integration/ uses a factory pattern. Manager.java holds a map of ConnectorInstance → Integration, initializes each factory on startup, and exposes a typed request() method that searches across running integrations for a component matching a given criteria. It is a service locator at heart, but with explicit "started" state checks before dispatching. The ManagerFactory wraps the singleton Manager with a @Lock annotation to prevent concurrent initialization.
The email injector in openaev-api/src/main/java/io/openaev/injectors/email/ is one of the built-in injects. It supports both bulk sending (sendMulti) and per-user sending with optional PGP encryption (sendSingle). The IMAP integration is toggled by openaev.mail.imap.enabled — when enabled, it can monitor responses to detect whether targets acted on the phishing email.
Using it
Docker is the practical deployment path:
# Clone and start with Docker Compose
git clone https://github.com/OpenAEV-Platform/openaev
cd openaev
docker compose up -d
The platform exposes a web UI on port 8080. From there you create an exercise, add injects (email, SMS, manual challenges, payload execution), assign teams and assets, and set a start time. The scheduler picks it up and starts firing injects on schedule.
For payload execution against endpoints, you register an executor (CrowdStrike Falcon, SentinelOne, etc.) via the settings UI. The platform then dispatches payloads through those EDR's management APIs, targeting specific endpoint assets. Expectations are configured per inject — you define what outcome constitutes "success" (EDR alert, manual sign-off, challenge completion) and the platform tracks whether it was met.
Rough edges
The project is genuinely well-maintained — commits land daily, the CI pipeline runs through Drone and CircleCI, and there is real test coverage in openaev-api/src/test/. The test structure is thorough for a Java project of this size: dedicated packages for scheduler, rest, integration, executors, and service. The InjectsExecutionJob test dependencies are visible from @VisibleForTesting annotations, which is a good signal.
What is rough: the CE/EE split. A meaningful chunk of the more powerful features — MITRE ATT&CK coverage scoring, some executor integrations, enterprise SSO — live behind the Enterprise Edition license. The Ee class shows up as a constructor parameter in CrowdStrikeExecutorIntegration.java, which hints at capability gates inside the integration code. You will not know exactly where CE ends until you hit the wall.
The Java + Spring stack means the self-hosted footprint is substantial. The Docker Compose setup requires Elasticsearch, PostgreSQL, MinIO, and RabbitMQ in addition to the platform itself. That is a lot of infrastructure to keep healthy for what is essentially a simulation scheduler.
The frontend is React + Redux, which is functional but not particularly interesting. The backend is where the engineering is.
Bottom line
OpenAEV is the right tool if you are running a security team that needs repeatable, scheduled adversarial validation with real EDR integration — not just pen-test scripts but a platform that tracks exercises over time and closes the loop on expectation results. If you are already running OpenCTI for threat intelligence, this is the natural companion.
