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* '}'
The keywords security and module are reserved and may not be used as identifiers.
PolicyDeclaration:
EntitlementDeclaration
DenyDeclaration
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.
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 |
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 |
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.**");
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