Process intrusion detection · JVM library

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.

Java 17+ Zero native dependencies Apache-2.0 Cantara open source
The idea

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.

PHASE 1 — FINGERPRINT

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.

PHASE 2 — DETECT

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.

Quickstart

Armed in about a dozen lines.

Add the dependency, declare a learning window and a handler, and start. Everything else has a sane default.

  1. 01

    Add the dependency

    Maven or Gradle, from the Cantara repository.

  2. 02

    Configure & register a handler

    Set the learning window, whitelist the known-good, react to suspicious events.

  3. 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();
How it works

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.

SCAN

Snapshot

Every interval, list processes via ProcessHandle (native) or ps (fallback).

DIFF

Started / ended

Compare against the last snapshot by pid + start time — recycled pids are re-qualified.

QUALIFY

Known?

Learning, known, whitelisted — or suspicious. Only strangers move on.

GRADE

DEFCON + escalate

Score the threat, escalate on sockets or longevity, invoke your handler.

Capabilities

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.

FINGERPRINT

Learn & recognise

A user|command baseline. A known binary started by a new user is not treated as known.

LOTL

Interpreter-aware

python3 -c '…' won't pass as known just because python ran during learning — interpreters fingerprint on their command line.

SOCKETS

Listening probes

A suspicious process holding a listening TCP socket is escalated, and the event reports the ports.

SNIFFERS

Raw & packet capture

Raw IP and AF_PACKET sockets — the footprint of scanners and sniffers — escalate the grade.

PERSISTENCE

Long-lived intruders

A stranger still alive after the escalation delay is re-reported one level higher.

BASELINE

Armed on restart

Persist the baseline and a restarted service skips re-learning — no window for an intruder to be learned as normal.

WHITELIST

Spoof-resistant

Whitelisting matches the executable path by default; command-line matching is opt-in and clearly flagged as spoofable.

FAIL-LOUD

Honest about privilege

Socket signals need root or CAP_SYS_PTRACE; when unavailable the watcher warns instead of silently seeing nothing.

ZERO DEPS

Pure JDK discovery

ProcessHandle by default, ps as fallback. No Sigar, no bundled .so files, no runtime downloads.

Threat grading

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.

LevelRaised whenExample
DEFCON 1A known probing tool running with elevated privileges.nc -lvnp 4444 · root
DEFCON 2A probing tool, or an unknown process, running privileged./opt/x/daemon · root
DEFCON 3A known command with an unknown fingerprint — same binary, new user./usr/bin/java · nobody
DEFCON 4An unknown process under a regular user./tmp/./miner · app
DEFCON 5Normal readiness — nothing unexpected.
Use cases

What it's good at catching.

Listening probes

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.

Living off the land

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.

Recon & capture

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.

Developer awareness

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.

Where it fits

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 weighttinyDIYheavyops-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.

Known limits

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.

Work in progress · collaboration wanted

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.