TinyRuleKit

Expression Security

Expression security is enabled by default when expressions are enabled. TinyRuleKit always applies a minimum baseline policy while security is active, then applies the configured default, custom policy, or custom validator.

This prevents a permissive project policy from accidentally allowing runtime execution, classloader access, file APIs, reflection-like access, imports, or package-qualified Java access.

This matters when rule JSON can come from external files, plugins, administration panels, or user-edited configuration. Without validation, expressions can become an injection surface for malicious code or accidental access to unsafe APIs.

Default Policy

Customize the policy when your project needs stricter rules:

JAVA
import org.tinyrulekit.api.expression.security.ExpressionSecurityPolicy;

ExpressionSecurityPolicy policy = ExpressionSecurityPolicy.defaults()
        .withMaxLength(200)
        .withAdditionalBlockedTokens(Set.of("delete", "saveFile"));

RuleEngine<GameContext> engine = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .expressionSecurityPolicy(policy)
        .buildFromJson(json);

Use withBlockedTokens only when you intentionally want to replace the project-level token set. TinyRuleKit's minimum baseline policy still remains active unless expressionSecurityDisabled() is called.

Public Security Tools

The public API exposes the same building blocks used by the default security layer:

JAVA
import org.tinyrulekit.api.expression.security.ExpressionSecurityPolicy;
import org.tinyrulekit.api.expression.security.ExpressionValidators;

ExpressionSecurityPolicy minimumPolicy = ExpressionSecurityPolicy.minimum();
Set<String> defaultTokens = ExpressionSecurityPolicy.defaultBlockedTokens();

ExpressionValidator validator = ExpressionValidators.chain(
        ExpressionValidators.fromPolicy(ExpressionSecurityPolicy.defaults()),
        expression -> {
            if (expression.contains("projectSecret")) {
                throw new IllegalArgumentException("Expression is not allowed");
            }
        }
);

Most applications do not need to call ExpressionValidators.withMinimum because the builder already applies the minimum validator while expression security is enabled. It is useful when the same validator is reused outside the TinyRuleKit builder.

Project-Owned Policy

For production projects, prefer creating a named policy owned by the application instead of configuring expression security inline everywhere.

JAVA
final class GameExpressionPolicies {
    private GameExpressionPolicies() {
    }

    static ExpressionSecurityPolicy rulesFromConfigFiles() {
        return ExpressionSecurityPolicy.defaults()
                .withMaxLength(180)
                .withAdditionalBlockedTokens(Set.of(
                        "Files",
                        "Path",
                        "ClassLoader",
                        "projectSecret"
                ));
    }
}

Use that policy when building the engine:

JAVA
RuleEngine<GameContext> engine = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .expressionSecurityPolicy(GameExpressionPolicies.rulesFromConfigFiles())
        .buildFromJson(json);

Custom Validator

Use expressionValidator when the security model is not a blocked-token policy or when another expression framework has its own validation rules:

JAVA
ExpressionValidator validator = expression -> {
    if (expression.contains("delete")) {
        throw new IllegalArgumentException("Expression is not allowed");
    }
};

RuleEngine<GameContext> engine = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .expressionValidator(validator)
        .buildFromJson(json);

The validator runs once per expression during engine construction, before the configured ExpressionEngine compiles it. While expression security is enabled, TinyRuleKit runs the minimum validator before this custom validator.

Trust Levels

Use different named policies for different trust levels:

  • bundled application resources can be less restrictive
  • external configuration directories should be stricter
  • administration panels should validate and review expressions
  • user-authored or network-loaded rule documents should use the strictest policy

Disabling Security

Security can be disabled only for fully trusted rule sources:

JAVA
RuleEngine<GameContext> engine = RuleEngine.builder(GameContext.class)
        .conditions(GameRules::condition)
        .actions(GameRules::action)
        .expressionSecurityDisabled()
        .buildFromJson(json);

Do not disable security for player-authored, user-authored, network-loaded, or plugin-loaded rule documents.

Continue with Expression Security Measures for the consumer-facing security components and the minimum-protection behavior.

Return to Expression Best Practices for design and maintenance recommendations, or Static Rule Best Practices for design and maintenance recommendations.