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
| Pattern | Matches |
|---|---|
module | Entire module |
com.example | Exact 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, findjava.io.FileInputStream,java.io.FileReaderjava.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, deleteIfExistsjava.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:
| Pattern | Matches | Example |
|---|---|---|
* | 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:
| Format | Example | Description |
|---|---|---|
| Integer | 443 | Single 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
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
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
System.setProperties() requires the no-arg form or explicit * pattern.
Instrumented APIs:
java.lang.System: setProperty(), setProperties(), clearProperty()
Capability Summary Table
| Capability | Arguments | Example |
|---|---|---|
fs.read | root, glob | fs.read("config", "**/*.json") |
fs.write | root, glob | fs.write("logs", "*.log") |
network.outbound | host?, port? | network.outbound("*.example.com", 443) |
network.listen | port? | network.listen("8080-8090") |
threads.create | none | threads.create |
native.load | pattern? | native.load("mylib*") |
env.read | pattern? | env.read("HOME") |
system.property.read | pattern? | system.property.read("java.*") |
system.property.write | pattern? | system.property.write("app.**") |
Enforcement Modes
| Mode | Behavior | Use Case |
|---|---|---|
| STRICT (default) | Block denied access, block on errors | Production - fail secure |
| PERMISSIVE | Block denied access, allow on errors | Migration - allow recovery |
| AUDIT | Log only, never block | Policy 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
- External Policies - Grant/deny semantics for third-party code
- Getting Started - Installation and quickstart
- Use Cases - Real-world examples