Policy Language Specification
This document specifies the syntax and semantics of the jGuard policy descriptor, a declarative format used to define security entitlements for Java modules.
Overviewβ
A jGuard policy descriptor declares which capabilities are granted to a Java module and, optionally, to specific packages within that module.
A jGuard policy descriptor:
- is not executable code
- has no side effects
- is evaluated at build or installation time
- is enforced at runtime by the jGuard agent
Design Goalsβ
The policy descriptor is designed to:
- support least-privilege execution
- be deterministic and reviewable
- integrate naturally with the Java Platform Module System (JPMS)
- remain stable across Java versions
Lexical Structureβ
Whitespaceβ
Whitespace consists of spaces, tabs, carriage returns, line terminators, and comments. Whitespace may appear between any lexical tokens.
Commentsβ
// Line comment - extends to end of line
/* Block comment
can span multiple lines */
Comments are treated as whitespace.
Identifiersβ
An identifier consists of:
- a letter (
AβZ,aβz) or underscore (_) as the first character - followed by any number of letters, digits (
0β9), or underscores
Identifiers are case-sensitive.
String Literalsβ
A string literal is enclosed in double quotes (").
Escape sequences follow JSON-style conventions:
\",\\,\n,\t- Unicode escapes:
\uXXXX
String literals are fully resolved at policy compile time.
Grammar (EBNF)β
The grammar is expressed in Extended BackusβNaur Form (EBNF), compatible with the JLS lexical structure.
Policy Fileβ
A policy file consists of exactly one security module declaration.
PolicyFile:
SecurityModuleDeclaration
Security Module Declarationβ
SecurityModuleDeclaration:
'security' 'module' ModuleName '{' PolicyDeclaration* '}'
PolicyDeclaration:
EntitlementDeclaration
DenyDeclaration
TrustedDeclaration
Contextual Keywordsβ
All keywords in jGuard are contextual, meaning they are only treated as keywords in specific syntactic positions. This allows package names like io.example.security.module to be used without escaping.
Keywords: security, module, entitle, deny, to, trusted, defensive
These identifiers are parsed as keywords only where the grammar expects them. In all other positions (such as within package names), they are treated as ordinary identifiers.
Module Nameβ
ModuleName:
Identifier ( '.' Identifier )*
The module name identifies the Java module to which the policy applies.
Entitlement Declarationsβ
EntitlementDeclaration:
'entitle' Subject 'to' Capability ';'
Each entitlement declaration grants a capability to a subject.
Deny Declarationsβ
DenyDeclaration:
'deny' Subject 'to' Capability ';'
'deny' '(' 'defensive' ')' Subject 'to' Capability ';'
Each deny declaration removes a capability from a subject. Denials take precedence over grants.
The defensive modifier suppresses warnings when denying a capability that was never granted.
Trusted Declarationsβ
TrustedDeclaration:
'trusted' ';'
A trusted declaration marks the entire module as trusted, bypassing all capability checks. This is intended for native libraries that require unrestricted system access.
The trusted keyword is only permitted in external policy override files. It cannot appear in embedded policies (policies shipped inside JARs). This prevents malicious libraries from granting themselves unrestricted access.
// File: policies-src/ai.djl.pytorch.jguard (external override)
security module ai.djl.pytorch {
trusted;
}
Trusted modules also require the -Djguard.allow.trusted=true system property or allowTrusted = true in the Gradle plugin configuration.
Subjectβ
Subject:
'module'
PackagePattern
- The keyword
modulerefers to the entire module - A package pattern refers to code within matching packages
Package Patternsβ
PackagePattern:
PackageName
PackageName '.*'
PackageName '..'
PackageName:
Identifier ( '.' Identifier )*
| Pattern | Meaning |
|---|---|
p | Exactly package p |
p.* | Direct subpackages of p |
p.. | Package p and all descendants |
Capabilitiesβ
Capability:
CapabilityName
CapabilityName '(' CapabilityArguments? ')'
CapabilityName:
Identifier ( '.' Identifier )*
CapabilityArguments:
Argument ( ',' Argument )*
Argument:
Identifier
StringLiteral
IntegerLiteral
The meaning and validity of arguments depend on the capability.
Well-Formedness Rulesβ
A policy file is ill-formed if any of the following conditions hold.
Structural Constraintsβ
- The policy file contains zero or more than one security module declaration
- An entitlement declaration omits a subject, capability, or terminating semicolon
- An entitlement declaration appears outside a security module declaration
Module Constraintsβ
- The declared module name is not a syntactically valid Java module name
- The declared module name does not match the module to which the policy is applied
Package Constraintsβ
- A package pattern contains empty or malformed segments
- A package wildcard appears anywhere other than the end of the pattern
- The pattern
..or.*is used without a package name prefix
Capability Constraintsβ
- A capability name is unknown to the implementation
- A capability is invoked with an invalid number or type of arguments
- A capability argument fails validation (e.g., invalid glob syntax)
Static Semanticsβ
Capability Signaturesβ
Each capability has a fixed signature:
| Capability | Signature | Description |
|---|---|---|
fs.read | (root, glob) | Read files matching glob under root |
fs.write | (root, glob) | Write files matching glob under root |
fs.hardlink | (root, glob) | Create hard links under root |
network.outbound | (hostPattern?, portSpec?) | Open outbound connections |
network.listen | (portSpec?) | Bind server sockets |
threads.create | (no arguments) | Create new threads |
native.load | (pattern?) | Load native libraries |
env.read | (pattern?) | Read environment variables |
system.property.read | (pattern?) | Read system properties |
system.property.write | (pattern?) | Write system properties |
process.exec | (pattern?) | Execute external processes |
crypto.provider | (no arguments) | Modify JCE providers |
runtime.exit | (no arguments) | Terminate the JVM |
runtime.shutdown_hook | (no arguments) | Register shutdown hooks |
Argument Detailsβ
fs.read / fs.writeβ
rootβ base directory path (string)globβ glob pattern for matching files (string)
entitle module to fs.read("config", "**/*.json");
entitle module to fs.write("logs", "*.log");
network.outboundβ
hostPatternβ optional host glob pattern (string)portSpecβ optional port or port range (integer or string)
Host pattern syntax:
*β matches exactly one DNS segment**β matches one or more DNS segments- Literal segments for exact matching
Port spec:
- Integer:
443β specific port - String range:
"80-443"β inclusive range
entitle module to network.outbound; // Any
entitle module to network.outbound("*.example.com"); // Host only
entitle module to network.outbound("*", 443); // Port only
entitle module to network.outbound("*.example.com", "80-443"); // Both
network.listenβ
portSpecβ optional port or port range
entitle module to network.listen; // Any port
entitle module to network.listen(8080); // Specific port
entitle module to network.listen("8080-8090"); // Port range
env.readβ
patternβ optional variable name pattern
entitle module to env.read; // Any, including bulk access
entitle module to env.read("HOME"); // Specific variable
system.property.read / writeβ
patternβ optional property key pattern
Pattern syntax:
- No argument or
*β any property (grants bulk access) java.homeβ exact matchapp.**β matchesappand all descendantsapp.*β matches direct children only
entitle module to system.property.read;
entitle module to system.property.read("java.home");
entitle module to system.property.write("app.**");
process.execβ
patternβ optional command pattern
Pattern syntax:
- No argument β any process execution allowed
/usr/bin/javaβ exact command match/opt/app/bin/*β wildcard match
entitle module to process.exec;
entitle module to process.exec("/usr/bin/java");
entitle module to process.exec("/opt/app/bin/*");
crypto.providerβ
No arguments. Guards all JCE provider modification operations.
entitle com.example.security.. to crypto.provider;
runtime.exitβ
No arguments. Guards JVM termination operations.
entitle com.example.main to runtime.exit;
Guarded operations:
System.exit(int)Runtime.exit(int)Runtime.halt(int)
runtime.shutdown_hookβ
No arguments. Guards shutdown hook registration and removal.
entitle com.example.lifecycle.. to runtime.shutdown_hook;
Guarded operations:
Runtime.addShutdownHook(Thread)Runtime.removeShutdownHook(Thread)
Accumulation of Entitlementsβ
- Multiple entitlements for the same subject are cumulative
- Duplicate entitlements are permitted but deduplicated internally
Denial Semanticsβ
- Denials remove capabilities from the effective policy
- Denials always take precedence over grants
- A denial matches an entitlement if the capability is equal and the denial's subject encompasses the entitlement's subject
Subject encompassing rules:
| Denial Subject | Encompasses |
|---|---|
module | All subjects |
pkg.. | pkg, pkg.sub, pkg.sub.child, etc. |
pkg.* | Direct child packages |
pkg | Only exact package |
Defensive Denialsβ
When a denial targets a capability that was never granted:
- Without
defensive: A warning is emitted - With
defensive: No warning is emitted
Use deny(defensive) for proactive security policies.
Default Behaviorβ
If no entitlement grants a capability to a subject, that capability is denied.
Execution Semanticsβ
At runtime, when a guarded operation is attempted:
- The operation is classified as a capability
- The calling code is attributed to a
(module, package) - The policy is consulted to determine whether the capability is granted
- If granted, execution proceeds
- If not granted, execution fails with
SecurityException
An implementation fails deterministically when a capability is denied.
Error Handlingβ
Policy Violationsβ
When a policy violation occurs, the error includes:
- The attempted capability
- The calling module
- The calling package
- The reason for denial
SecurityException: Capability denied
Module: com.example.myapp
Package: com.example.myapp.cli
Attempted: network.outbound
Reason: not entitled (only com.example.myapp.http is entitled)
Parse Errorsβ
Policy parsing errors identify:
- The error location (line and column)
- The nature of the error
Error: module-info.jguard:5:12
Unknown capability: network.inbound
Did you mean: network.listen?
Versioningβ
This specification defines policy format version 1.
Future versions may:
- Introduce new capabilities
- Extend capability argument forms
- Add optional policy constructs
Future versions will not change the meaning of valid version-1 policies.
Relationship to Java Modulesβ
A jGuard policy descriptor is analogous to module-info.java:
- Both declare metadata about a module
- Both are non-executable
- Both define boundaries enforced by the runtime
However, a jGuard policy descriptor:
- Does not participate in Java compilation
- Does not affect module resolution
- Is enforced solely by the jGuard agent