GraalVM Polyglot API for multi-language runtime environments with host-guest interoperability and security controls.
—
GraalVM Polyglot API provides comprehensive security mechanisms to control access between host and guest languages. This includes host access policies, polyglot evaluation restrictions, sandboxing levels, and environment access controls.
HostAccess controls how guest languages can interact with host (Java) objects, providing fine-grained security policies from completely open access to strict sandboxing.
public final class HostAccess {
// Annotation-based access - only @Export members accessible
public static final HostAccess EXPLICIT;
// Scoped access with automatic resource cleanup
public static final HostAccess SCOPED;
// Full unrestricted access to all host objects
public static final HostAccess ALL;
// No host access allowed
public static final HostAccess NONE;
// Limited access suitable for constrained environments
public static final HostAccess CONSTRAINED;
// Method scoping enabled for enhanced isolation
public static final HostAccess ISOLATED;
// Strict restrictions for untrusted code execution
public static final HostAccess UNTRUSTED;
}Predefined Policy Examples:
// EXPLICIT: Only annotated members accessible
Context explicitContext = Context.newBuilder("js")
.allowHostAccess(HostAccess.EXPLICIT)
.build();
public class RestrictedAPI {
@HostAccess.Export
public String publicMethod() {
return "This is accessible";
}
public String privateMethod() {
return "This is NOT accessible";
}
}
explicitContext.getBindings("js").putMember("api", new RestrictedAPI());
explicitContext.eval("js", "console.log(api.publicMethod())"); // Works
// explicitContext.eval("js", "api.privateMethod()"); // Would throw exception
// ALL: Full access (use with caution)
Context openContext = Context.newBuilder("js")
.allowHostAccess(HostAccess.ALL)
.build();
// CONSTRAINED: Limited access for sandboxed execution
Context constrainedContext = Context.newBuilder("js")
.allowHostAccess(HostAccess.CONSTRAINED)
.sandbox(SandboxPolicy.CONSTRAINED)
.build();// Factory methods for custom configuration
public static HostAccess.Builder newBuilder();
public static HostAccess.Builder newBuilder(HostAccess conf);The HostAccess.Builder provides extensive customization options:
public static final class HostAccess.Builder {
// Access control methods
public HostAccess.Builder allowPublicAccess(boolean enabled);
public HostAccess.Builder allowAllImplementations(boolean enabled);
public HostAccess.Builder allowAllClassImplementations(boolean enabled);
public HostAccess.Builder allowArrayAccess(boolean enabled);
public HostAccess.Builder allowListAccess(boolean enabled);
public HostAccess.Builder allowBufferAccess(boolean enabled);
public HostAccess.Builder allowIterableAccess(boolean enabled);
public HostAccess.Builder allowIteratorAccess(boolean enabled);
public HostAccess.Builder allowMapAccess(boolean enabled);
// Annotation-based access
public HostAccess.Builder allowAccessAnnotatedBy(Class<? extends Annotation> annotation);
public HostAccess.Builder allowImplementationsAnnotatedBy(Class<? extends Annotation> annotation);
// Method scoping and inheritance
public HostAccess.Builder methodScoping(boolean enabled);
public HostAccess.Builder allowAccessInheritance(boolean enabled);
// Target type mappings
public HostAccess.Builder targetTypeMapping(Class<?> sourceType, Class<?> targetType, Predicate<Object> accepts, Function<Object, Object> converter);
public HostAccess.Builder targetTypeMapping(Class<?> sourceType, Class<?> targetType, Predicate<Object> accepts, Function<Object, Object> converter, HostAccess.TargetMappingPrecedence precedence);
// Build the configuration
public HostAccess build();
}Custom HostAccess Example:
// Custom access policy
HostAccess customAccess = HostAccess.newBuilder()
.allowPublicAccess(true)
.allowArrayAccess(true)
.allowListAccess(true)
.allowMapAccess(false) // Disable map access
.allowIterableAccess(true)
.methodScoping(true) // Enable method scoping for security
.allowAccessAnnotatedBy(HostAccess.Export.class)
.targetTypeMapping(String.class, Integer.class,
s -> s.matches("\\d+"),
s -> Integer.parseInt((String) s))
.build();
Context context = Context.newBuilder("js")
.allowHostAccess(customAccess)
.build();
// String to Integer conversion will work automatically
Value jsBindings = context.getBindings("js");
jsBindings.putMember("data", Arrays.asList("123", "456", "789"));
Value sum = context.eval("js", "data.reduce((a, b) => a + b, 0)"); // Auto-converts strings to ints
System.out.println(sum.asInt()); // 1368Marks fields, methods, and constructors as accessible from guest languages:
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Export {
}Marks interfaces that guest languages can implement:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Implementable {
}Disables method scoping for specific elements:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisableMethodScoping {
}Annotation Examples:
public class SecureAPI {
@HostAccess.Export
public String version = "1.0.0";
@HostAccess.Export
public String getInfo() {
return "Secure API Information";
}
@HostAccess.Export
@HostAccess.DisableMethodScoping
public void unscopedMethod() {
// This method won't be scoped even if method scoping is enabled
}
// This method is not accessible from guest languages
public String internalMethod() {
return "Internal use only";
}
}
@HostAccess.Implementable
public interface EventListener {
void onEvent(String eventType, Object data);
}
// Usage
Context context = Context.newBuilder("js")
.allowHostAccess(HostAccess.EXPLICIT)
.build();
context.getBindings("js").putMember("api", new SecureAPI());
context.getBindings("js").putMember("EventListener", EventListener.class);
context.eval("js", """
console.log(api.version); // Works - exported field
console.log(api.getInfo()); // Works - exported method
// Implement Java interface in JavaScript
let listener = new EventListener({
onEvent: function(type, data) {
console.log(`Event: ${type}, Data: ${data}`);
}
});
""");Target type mappings enable automatic conversion between types:
public enum TargetMappingPrecedence {
HIGHEST, HIGH, NORMAL, LOW, LOWEST
}
public enum MutableTargetMapping {
ENABLED, DISABLED
}Type Mapping Example:
HostAccess customMapping = HostAccess.newBuilder()
.allowPublicAccess(true)
// Convert JavaScript objects to Java Maps
.targetTypeMapping(Value.class, Map.class,
Value::hasMembers,
v -> {
Map<String, Object> map = new HashMap<>();
for (String key : v.getMemberKeys()) {
map.put(key, v.getMember(key).as(Object.class));
}
return map;
})
// Convert Java LocalDate to JavaScript Date-like object
.targetTypeMapping(LocalDate.class, Value.class,
Objects::nonNull,
date -> ProxyObject.fromMap(Map.of(
"year", date.getYear(),
"month", date.getMonthValue(),
"day", date.getDayOfMonth()
)))
.build();
Context context = Context.newBuilder("js")
.allowHostAccess(customMapping)
.build();PolyglotAccess controls cross-language evaluation and bindings access between different guest languages.
public final class PolyglotAccess {
// Full cross-language access
public static final PolyglotAccess ALL;
// No cross-language access
public static final PolyglotAccess NONE;
}// Factory method
public static PolyglotAccess.Builder newBuilder();The PolyglotAccess.Builder provides granular control over language interactions:
public static final class PolyglotAccess.Builder {
// Allow evaluation from one language to another
public PolyglotAccess.Builder allowEval(String from, String to);
// Allow mutual evaluation between languages
public PolyglotAccess.Builder allowEvalBetween(String... languages);
// Allow access to polyglot bindings
public PolyglotAccess.Builder allowBindingsAccess(String language);
// Deny evaluation (override previous allows)
public PolyglotAccess.Builder denyEval(String from, String to);
public PolyglotAccess.Builder denyEvalBetween(String... languages);
public PolyglotAccess.Builder denyBindingsAccess(String language);
// Build the configuration
public PolyglotAccess build();
}PolyglotAccess Examples:
// Allow JavaScript to evaluate Python, but not vice versa
PolyglotAccess restrictedPolyglot = PolyglotAccess.newBuilder()
.allowEval("js", "python")
.allowBindingsAccess("js")
.build();
Context context = Context.newBuilder("js", "python")
.allowPolyglotAccess(restrictedPolyglot)
.build();
// This works - JavaScript can evaluate Python
context.eval("js", """
let pythonResult = Polyglot.eval('python', '2 + 2');
console.log(pythonResult); // 4
""");
// This would fail - Python cannot evaluate JavaScript
// context.eval("python", "polyglot.eval(language='js', string='1 + 1')");
// Selective language interaction
PolyglotAccess selectiveAccess = PolyglotAccess.newBuilder()
.allowEvalBetween("js", "python") // Mutual JS <-> Python
.allowEval("ruby", "js") // Ruby -> JS only
.allowBindingsAccess("js") // Only JS can access bindings
.denyEval("python", "ruby") // Explicitly deny Python -> Ruby
.build();SandboxPolicy defines different levels of security isolation for polyglot contexts.
public enum SandboxPolicy {
/**
* No restrictions - full trust (default)
*/
TRUSTED,
/**
* Basic restrictions for potentially unsafe operations
*/
CONSTRAINED,
/**
* Enhanced isolation with method scoping enabled
*/
ISOLATED,
/**
* Maximum restrictions for untrusted code execution
*/
UNTRUSTED;
// Comparison methods
public boolean isStricterThan(SandboxPolicy other);
public boolean isStricterOrEqual(SandboxPolicy other);
}Sandbox Policy Examples:
// Trusted environment (default) - no restrictions
Context trustedContext = Context.newBuilder("js")
.sandbox(SandboxPolicy.TRUSTED)
.allowAllAccess(true)
.build();
// Constrained environment - basic restrictions
Context constrainedContext = Context.newBuilder("js")
.sandbox(SandboxPolicy.CONSTRAINED)
.allowHostAccess(HostAccess.CONSTRAINED)
.allowIO(IOAccess.NONE)
.build();
// Isolated environment - enhanced security
Context isolatedContext = Context.newBuilder("js")
.sandbox(SandboxPolicy.ISOLATED)
.allowHostAccess(HostAccess.ISOLATED)
.allowPolyglotAccess(PolyglotAccess.NONE)
.allowCreateThread(false)
.build();
// Untrusted environment - maximum restrictions
Context untrustedContext = Context.newBuilder("js")
.sandbox(SandboxPolicy.UNTRUSTED)
.allowHostAccess(HostAccess.UNTRUSTED)
.allowPolyglotAccess(PolyglotAccess.NONE)
.allowIO(IOAccess.NONE)
.allowCreateThread(false)
.allowCreateProcess(false)
.allowNativeAccess(false)
.build();
// Check sandbox policy strictness
SandboxPolicy current = SandboxPolicy.CONSTRAINED;
System.out.println(current.isStricterThan(SandboxPolicy.TRUSTED)); // true
System.out.println(current.isStricterThan(SandboxPolicy.UNTRUSTED)); // falseEnvironmentAccess controls access to environment variables and system properties.
public final class EnvironmentAccess {
// Inherit environment variables from the host process
public static final EnvironmentAccess INHERIT;
// No access to environment variables
public static final EnvironmentAccess NONE;
}// Factory method
public static EnvironmentAccess.Builder newBuilder();EnvironmentAccess Examples:
// Inherit host environment
Context inheritContext = Context.newBuilder("js")
.allowEnvironmentAccess(EnvironmentAccess.INHERIT)
.build();
// No environment access
Context restrictedContext = Context.newBuilder("js")
.allowEnvironmentAccess(EnvironmentAccess.NONE)
.build();
// Custom environment configuration
EnvironmentAccess customEnv = EnvironmentAccess.newBuilder()
.allowEnvironmentAccess(Map.of(
"APP_NAME", "MyApplication",
"APP_VERSION", "1.0.0"
))
.build();
Context customContext = Context.newBuilder("js")
.allowEnvironmentAccess(customEnv)
.build();Here's a comprehensive example showing how to configure a secure polyglot context:
public class SecurePolyglotSetup {
public static Context createSecureContext() {
// Custom host access with specific permissions
HostAccess secureHostAccess = HostAccess.newBuilder()
.allowPublicAccess(false) // Disable public access
.allowAccessAnnotatedBy(HostAccess.Export.class) // Only @Export members
.allowArrayAccess(true) // Allow array operations
.allowListAccess(true) // Allow list operations
.allowMapAccess(false) // Disable map access
.methodScoping(true) // Enable method scoping
.allowAccessInheritance(false) // Disable inheritance access
.build();
// Restricted polyglot access
PolyglotAccess polyglotAccess = PolyglotAccess.newBuilder()
.allowEval("js", "python") // JS can call Python
.allowBindingsAccess("js") // Only JS can use bindings
.build();
// Custom environment with minimal exposure
EnvironmentAccess envAccess = EnvironmentAccess.newBuilder()
.allowEnvironmentAccess(Map.of(
"NODE_ENV", "production",
"LOG_LEVEL", "info"
))
.build();
// Resource limits
ResourceLimits limits = ResourceLimits.newBuilder()
.statementLimit(100000, null) // Limit statement execution
.onLimit(event -> {
System.err.println("Resource limit exceeded: " + event.getLimitType());
})
.build();
// Build secure context
return Context.newBuilder("js", "python")
.sandbox(SandboxPolicy.CONSTRAINED) // Apply sandboxing
.allowHostAccess(secureHostAccess) // Restricted host access
.allowPolyglotAccess(polyglotAccess) // Limited polyglot access
.allowEnvironmentAccess(envAccess) // Custom environment
.allowIO(IOAccess.NONE) // No I/O access
.allowCreateThread(false) // No thread creation
.allowCreateProcess(false) // No process creation
.allowNativeAccess(false) // No native access
.resourceLimits(limits) // Apply resource limits
.build();
}
public static void main(String[] args) {
try (Context context = createSecureContext()) {
// Set up secure API
SecureAPI api = new SecureAPI();
context.getBindings("js").putMember("secureAPI", api);
// Execute user code safely
Value result = context.eval("js", """
let data = [1, 2, 3, 4, 5];
let sum = data.reduce((a, b) => a + b, 0);
secureAPI.processResult(sum);
""");
System.out.println("Execution completed safely: " + result);
} catch (PolyglotException e) {
if (e.isResourceExhausted()) {
System.err.println("Resource limits exceeded");
} else if (e.isHostException()) {
System.err.println("Host access violation: " + e.getMessage());
} else {
System.err.println("Execution error: " + e.getMessage());
}
}
}
}
class SecureAPI {
@HostAccess.Export
public String processResult(int value) {
if (value < 0 || value > 1000000) {
throw new IllegalArgumentException("Value out of allowed range");
}
return "Processed: " + value;
}
// This method is not accessible due to lack of @Export
public void dangerousOperation() {
System.exit(1); // This cannot be called from guest code
}
}// Start with most restrictive policy and add permissions as needed
Context context = Context.newBuilder("js")
.sandbox(SandboxPolicy.UNTRUSTED) // Most restrictive
.allowHostAccess(HostAccess.NONE) // No host access initially
.allowPolyglotAccess(PolyglotAccess.NONE) // No cross-language access
.allowIO(IOAccess.NONE) // No I/O access
.build();public class ValidatedAPI {
@HostAccess.Export
public String processUserInput(String input) {
if (input == null || input.length() > 1000) {
throw new IllegalArgumentException("Invalid input length");
}
// Sanitize input
String sanitized = input.replaceAll("[<>\"'&]", "");
return "Processed: " + sanitized;
}
}ResourceLimits limits = ResourceLimits.newBuilder()
.statementLimit(50000, source -> !source.isInternal())
.onLimit(event -> {
// Log security event
SecurityLogger.logResourceExhaustion(
event.getLimitType(),
event.getConsumed(),
event.getLimit()
);
// Take action (e.g., terminate context)
throw new SecurityException("Resource limit exceeded: " + event.getLimitType());
})
.build();Context context = Context.create("js");
CompletableFuture<Value> future = CompletableFuture.supplyAsync(() -> {
return context.eval("js", userProvidedCode);
});
try {
Value result = future.get(5, TimeUnit.SECONDS); // 5 second timeout
} catch (TimeoutException e) {
context.close(true); // Force close context
throw new SecurityException("Code execution timeout");
}public class SecurityAuditLogger {
public static void logHostAccess(String member, Object target) {
// Log all host access attempts
System.err.printf("Host access: %s on %s%n", member, target.getClass());
}
public static void logPolyglotEval(String fromLang, String toLang, String code) {
// Log cross-language evaluations
System.err.printf("Polyglot eval: %s -> %s: %s%n", fromLang, toLang,
code.length() > 50 ? code.substring(0, 50) + "..." : code);
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-graalvm-polyglot--graalvm-sdk