Skip to main content
Version: 0.2.0

Policy Reference

jGuard uses a declarative policy language to define what code is permitted to do. Policies are defined in module-info.jguard files and compiled to binary format for runtime enforcement.

Core Concepts

Capabilities, Not Permissions

Unlike traditional permission systems, jGuard uses capabilities:

  • Permission: "Does this code have permission X?"
  • Capability: "This code can do X, Y, and Z"

Capabilities are explicit grants. If a capability isn't granted, the operation is denied.

Deny by Default

jGuard operates on a deny-by-default model:

✗ Not entitled → BLOCKED
✓ Entitled → ALLOWED

This is the safest default posture. You must explicitly grant every capability your code needs.

Modules as Principals

jGuard uses JPMS module identity as the security boundary:

  • Each module has its own policy
  • Packages within a module can have different entitlements
  • Module identity is verified through signed JARs in production

Policy File Format

Policies are defined in module-info.jguard:

security module com.example.myapp {
entitle <subject> to <capability>;
entitle <subject> to <capability>;
...
}

Subject Patterns

PatternMatches
moduleEntire module
com.exampleExact package
com.example.*Direct subpackages only
com.example..Package and all descendants

Example Policy

security module com.example.webapp {
// Module-wide system property access
entitle module to system.property.read;

// Package-specific network access
entitle com.example.webapp.http.. to network.outbound;

// Server can bind ports
entitle com.example.webapp.server to network.listen("8080-8443");

// Workers can create threads
entitle com.example.webapp.worker.. to threads.create;
}

Supported Capabilities

jGuard v0.2.0 supports 9 capabilities across 6 categories.

Filesystem

fs.read(root, glob)

Read files matching a glob pattern under a root directory.

entitle module to fs.read("config", "**");        // Any file under config/
entitle module to fs.read("/data", "*.json"); // JSON files in /data
entitle module to fs.read(".", "*.txt"); // Text files in current dir

Instrumented APIs:

  • java.nio.file.Files: newInputStream, newBufferedReader, readAllBytes, readAllLines, readString, lines, list, walk, find
  • java.io.FileInputStream, java.io.FileReader
  • java.io.RandomAccessFile (read mode)
  • java.nio.channels.FileChannel.open()

fs.write(root, glob)

Write files matching a glob pattern under a root directory.

entitle module to fs.write("build", "**");        // Any file under build/
entitle module to fs.write("logs", "*.log"); // Log files only

Instrumented APIs:

  • java.nio.file.Files: newOutputStream, newBufferedWriter, write, writeString, copy, move, createFile, createDirectory, createDirectories, delete, deleteIfExists
  • java.io.FileOutputStream, java.io.FileWriter

Network

network.outbound(hostPattern?, portSpec?)

Make outbound network connections.

// Any host, any port
entitle module to network.outbound;

// Host pattern, any port
entitle module to network.outbound("*.example.com");

// Any host, specific port
entitle module to network.outbound("*", 443);

// Host pattern + port range
entitle module to network.outbound("*.example.com", "80-443");

Host Patterns:

PatternMatchesExample
*Exactly one DNS segment*.example.com matches api.example.com
**One or more DNS segments**.example.com matches a.b.c.example.com

Host matching is case-insensitive with IDN normalization.

Port Specs:

FormatExampleDescription
Integer443Single port
String range"80-443"Inclusive port range

Instrumented APIs:

  • java.net.Socket: constructors, connect()
  • java.nio.channels.SocketChannel.connect()

network.listen(portSpec?)

Bind server sockets to listen for connections.

entitle module to network.listen;              // Any port
entitle module to network.listen(8080); // Specific port
entitle module to network.listen("8080-8090"); // Port range

Instrumented APIs:

  • java.net.ServerSocket: constructors, bind()
  • java.nio.channels.ServerSocketChannel.bind()

Concurrency

threads.create

Create new threads.

entitle com.example.app.worker.. to threads.create;

Instrumented APIs:

  • java.lang.Thread.start()

Native Code

native.load(pattern?)

Load native libraries via JNI.

entitle module to native.load;           // Any library
entitle module to native.load("mylib*"); // Library name pattern

Instrumented APIs:

  • java.lang.System: loadLibrary(), load()
  • java.lang.Runtime: loadLibrary(), load()

Environment

env.read(pattern?)

Read environment variables.

entitle module to env.read;          // Any env var (including bulk access)
entitle module to env.read("HOME"); // Specific variable
entitle module to env.read("APP_*"); // Pattern match
Bulk API Gating

System.getenv() with no arguments requires the no-arg form or explicit * pattern.

Instrumented APIs:

  • java.lang.System: getenv(), getenv(String)

System Properties

system.property.read(pattern?)

Read system properties.

entitle module to system.property.read;              // Any property
entitle module to system.property.read("java.home"); // Specific property
entitle module to system.property.read("app.**"); // All descendants
entitle module to system.property.read("app.*"); // Direct children only
Bulk API Gating

System.getProperties() requires the no-arg form or explicit * pattern.

Instrumented APIs:

  • java.lang.System: getProperty(String), getProperty(String, String), getProperties()

system.property.write(pattern?)

Write system properties.

entitle module to system.property.write;              // Any property
entitle module to system.property.write("app.config"); // Specific property
entitle module to system.property.write("app.**"); // All descendants
Bulk API Gating

System.setProperties() requires the no-arg form or explicit * pattern.

Instrumented APIs:

  • java.lang.System: setProperty(), setProperties(), clearProperty()

Capability Summary Table

CapabilityArgumentsExample
fs.readroot, globfs.read("config", "**/*.json")
fs.writeroot, globfs.write("logs", "*.log")
network.outboundhost?, port?network.outbound("*.example.com", 443)
network.listenport?network.listen("8080-8090")
threads.createnonethreads.create
native.loadpattern?native.load("mylib*")
env.readpattern?env.read("HOME")
system.property.readpattern?system.property.read("java.*")
system.property.writepattern?system.property.write("app.**")

Enforcement Modes

ModeBehaviorUse Case
STRICT (default)Block denied access, block on errorsProduction - fail secure
PERMISSIVEBlock denied access, allow on errorsMigration - allow recovery
AUDITLog only, never blockPolicy development

Override at runtime:

./gradlew runWithAgent -Pjguard.mode=audit

Or via system property:

java -Djguard.mode=audit -javaagent:jguard-agent.jar -jar app.jar

Policy Lifecycle

┌─────────────────┐
│ module-info │
│ .jguard │ ← Source (human-readable)
└────────┬────────┘
│ jguardc / Gradle plugin

┌─────────────────┐
│ policy.bin │ ← Binary (runtime)
└────────┬────────┘
│ Embedded in JAR

┌─────────────────┐
│ META-INF/jguard │
│ /policy.bin │ ← Shipped with application
└─────────────────┘

Next Steps