Know when a stranger shows up on your host.
ProcessWatcher learns the processes your machine normally runs, then raises DEFCON-graded events the moment an unknown, non-whitelisted process appears — a simplified, embeddable process IDS for Java services. No native libraries, no agents to install.
Two phases: learn what's normal, then flag what isn't.
Borderline security died last millennium. ProcessWatcher makes a service aware of the processes running around it — so it can react when its environment is infiltrated.
Record the baseline
For a configurable window — 20 minutes on a quiet server, 36 hours where daily cron jobs run —
every observed process is fingerprinted (user|command) and learned.
Interpreters like python and bash are
fingerprinted on their full command line, so a trusted shell can't smuggle an unknown payload past you.
Grade the strangers
After the window, any process whose fingerprint isn't in the baseline — and doesn't match a whitelist — raises a suspicious event carrying a naive DEFCON threat level. A listening socket, a raw/packet capture socket, or simply staying alive pushes the grade higher.
Armed in about a dozen lines.
Add the dependency, declare a learning window and a handler, and start. Everything else has a sane default.
- 01
Add the dependency
Maven or Gradle, from the Cantara repository.
- 02
Configure & register a handler
Set the learning window, whitelist the known-good, react to suspicious events.
- 03
Start watching
Discovery uses the JDK ProcessHandle API by default — no native libs.
pom.xml
<!-- repository: https://mvnrepo.cantara.no --> <dependency> <groupId>no.cantara.processwatcher</groupId> <artifactId>processwatcher</artifactId> <version>0.1-SNAPSHOT</version> </dependency>
Watcher.java
ProcessWatcher pw = ProcessWatcher.getInstance(); pw.setFingerprintingPeriod(20 * 60 * 1000); // learn for 20 minutes pw.whitelist(".*/logrotate"); // match on executable path pw.setFingerprintBaselineFile(Path.of("baseline.txt")); // survive restarts pw.registerSuspiciousProcessHandler(event -> { DefconEvent defcon = event.getDefconEvent(); log.warn("IDS signal: DEFCON {} — {}", defcon.getLevel(), event.getProcess()); }); pw.start();
A producer–consumer pipeline, straight out of the JDK.
Each scan snapshots the process table and diffs it against the last one. New processes are qualified against the baseline; the survivors get graded and handed to your handlers.
Snapshot
Every interval, list processes via ProcessHandle (native) or ps (fallback).
Started / ended
Compare against the last snapshot by pid + start time — recycled pids are re-qualified.
Known?
Learning, known, whitelisted — or suspicious. Only strangers move on.
DEFCON + escalate
Score the threat, escalate on sockets or longevity, invoke your handler.
Detection that resists the obvious evasions.
The naive core is deliberately simple. These signals harden it against the ways an intruder tries to look normal.
Learn & recognise
A user|command baseline. A known binary started by a new user is not treated as known.
Interpreter-aware
python3 -c '…' won't pass as known just because python ran during learning — interpreters fingerprint on their command line.
Listening probes
A suspicious process holding a listening TCP socket is escalated, and the event reports the ports.
Raw & packet capture
Raw IP and AF_PACKET sockets — the footprint of scanners and sniffers — escalate the grade.
Long-lived intruders
A stranger still alive after the escalation delay is re-reported one level higher.
Armed on restart
Persist the baseline and a restarted service skips re-learning — no window for an intruder to be learned as normal.
Spoof-resistant
Whitelisting matches the executable path by default; command-line matching is opt-in and clearly flagged as spoofable.
Honest about privilege
Socket signals need root or CAP_SYS_PTRACE; when unavailable the watcher warns instead of silently seeing nothing.
Pure JDK discovery
ProcessHandle by default, ps as fallback. No Sigar, no bundled .so files, no runtime downloads.
A naive DEFCON scale — an indication, not a verdict.
The grade is a simple, transparent heuristic you can reason about. Treat it as a signal to act on, not authoritative severity. Socket and longevity signals move a grade one level toward the top.
| Level | Raised when | Example |
|---|---|---|
| DEFCON 1 | A known probing tool running with elevated privileges. | nc -lvnp 4444 · root |
| DEFCON 2 | A probing tool, or an unknown process, running privileged. | /opt/x/daemon · root |
| DEFCON 3 | A known command with an unknown fingerprint — same binary, new user. | /usr/bin/java · nobody |
| DEFCON 4 | An unknown process under a regular user. | /tmp/./miner · app |
| DEFCON 5 | Normal readiness — nothing unexpected. | — |
What it's good at catching.
A reverse shell or bind listener
The headline use case: an unfamiliar process opens a listening socket on your host. ProcessWatcher flags it and hands you the ports it's on.
A trusted interpreter, an untrusted script
An attacker runs their payload through python or bash to blend in. Command-line fingerprinting means it still reads as a stranger.
A sniffer or port scanner drops in
Raw and packet-capture sockets betray tools that watch traffic or map the network — the grade escalates automatically.
Seeing is believing, in your own logs
Wire the events to your logging or alerting and give developers a felt sense of what actually runs beside their service in production.
An in-process signal, not a replacement for a HIDS.
ProcessWatcher's angle is that it lives inside your JVM service and needs nothing installed on the host. It is not trying to be a full host intrusion detection system — and the honest comparison says so.
| ProcessWatcher | Roll-your-own ps | Sigar-based (legacy) | auditd / osquery | |
|---|---|---|---|---|
| Runs inside the JVM as a library | ● | ◐ | ● | — |
| Zero native deps / nothing to install | ● | ● | — | — |
| Learns a baseline & grades strangers | ● | — | — | ◐ |
| App-level events & handlers | ● | ◐ | ◐ | — |
| Listening / raw / packet socket signals | ● | — | — | ● |
| Kernel-level syscall & file-integrity coverage | — | — | — | ● |
| Deployment weight | tiny | DIY | heavy | ops-managed |
Honest positioning: if you need kernel-level auditing, file-integrity monitoring, or fleet-wide telemetry, reach for auditd or osquery — they see things a userspace library never will. ProcessWatcher is the lightweight, embeddable complement: it gives a single service self-awareness with one dependency and no host agent. Use both.
What it can't do — stated plainly.
A best-effort notifier, not a hardened IDS. Plan around these, and it earns its keep.
Trust on first use. An intruder already resident when learning starts is learned as normal. Baseline on a known-clean host.
Privilege for socket signals. Reading another process's sockets needs root or a capability; otherwise escalation only covers the watcher's own user.
A polling blind spot. A process that starts and exits within one scan interval may go unseen. Lower the interval to narrow it.
The grade is coarse. DEFCON levels are a naive heuristic, including the privilege guess — a signal to act on, not authoritative severity.
The baseline file is trusted. Anyone who can write it can self-whitelist. Store it 0600, owned by the service user.
Poll mode is best-effort. The ps fallback filters injected phantom pids, but the native scanner is the default and more robust.
Give your service a sense of who's home.
The core two-phase flow is implemented and tested. Try it, wire it to your alerting, and tell us where it should go next.