Skip to content

Security: Arinaranetwork/Variableplus

Security

docs/security.md

Security Model

Variable+ evaluates mathematical expressions using Python's eval() function. To prevent code injection and malicious operations, it implements a multi-layer security model.


Threat Model

What Variable+ Protects Against

Threat Protection Layer
Arbitrary code execution AST validation blocks imports, function/class defs Evaluator
System access __builtins__ removed from namespace Evaluator
Sandbox escape via __class__ Forbidden attribute access list Evaluator
Resource exhaustion (DoS) No range(), list(), enumerate() Evaluator
Data exfiltration No file I/O, network, or system calls Evaluator
Infinite loops No while/for/function definitions Evaluator

What Variable+ Does NOT Protect Against

Threat Reason
CPU-intensive expressions calc("9**9**9**9") will consume CPU (Python limitation)
Memory via large numbers calc("10**100000000") can exhaust memory
Floating-point errors Division by near-zero values may hang
Timing side-channels Expression evaluation time varies with complexity

Security Layers

Layer 1: Tokenization

The tokenizer only accepts characters from the defined symbol table (table.json). Unknown characters raise UnknownSymbolError before any evaluation occurs.

This means arbitrary Python syntax like __import__, exec, eval cannot even be tokenized through the standard calc() entry point.

Layer 2: AST Validation (SafeNodeVisitor)

After translation to Python, the expression is parsed into an AST (Abstract Syntax Tree) and validated by SafeNodeVisitor. This blocks:

Blocked AST Nodes

Node Type What It Blocks
ast.Import / ast.ImportFrom import os, from os import system
ast.FunctionDef / ast.AsyncFunctionDef def f(): ...
ast.ClassDef class X: ...
ast.Lambda lambda x: x
ast.Attribute (filtered) x.__class__, x.__builtins__
ast.Call (filtered) Only allows whitelisted function calls

Allowed Function Calls

Only these function calls pass AST validation:

Category Functions
math.* Any function from the math module
Built-in safe abs, round, min, max, sum, pow
Trig reciprocals sec, csc, cot
Other sign, cbrt, fourthrt

Any other function call raises SecurityError.

Forbidden Attributes

Access to these attribute names is always blocked:

__builtins__    __class__       __subclasses__
__bases__       __mro__         __globals__
__code__        __import__      __getattr__
__setattr__     __delattr__     __dict__

Layer 3: Namespace Restriction

The expression is evaluated with a namespace that has __builtins__ set to an empty dictionary. This means:

  • No print(), input(), open(), exec(), eval()
  • No dir(), vars(), globals(), locals()
  • No type(), isinstance(), getattr(), setattr()
  • No compile(), __import__()

Only the explicitly whitelisted names are available:

ALLOWED_NAMES = {
    "math": math,           # Full math module
    "abs": abs,             # Absolute value
    "round": round,         # Rounding
    "min": min,             # Minimum
    "max": max,             # Maximum
    "sum": sum,             # Summation
    "pow": pow,             # Power
    "sec": _sec,            # Secant
    "csc": _csc,            # Cosecant
    "cot": _cot,            # Cotangent
    "sign": _sign,          # Sign function
    "cbrt": _cbrt,          # Cube root
    "fourthrt": _fourthrt,  # Fourth root
    "e": math.e,            # Euler's number
    "j": 1j,                # Imaginary unit
    "i": 1j,                # Imaginary unit
    "True": True,           # Boolean
    "False": False,         # Boolean
}

Plus user-provided variables.


Attack Examples and Defenses

Import Attempts

calc("__import__('os').system('rm -rf /')")
# Blocked at tokenizer: __import__ is not a valid token
# If somehow bypassed: SafeNodeVisitor blocks ast.Import

calc("import os")
# SecurityError: Import statements are not allowed

Attribute Traversal

calc("().__class__.__bases__[0].__subclasses__()")
# SecurityError: Access to '__class__' is not allowed

Built-in Function Abuse

calc("exec('import os')")
# SecurityError: Function call 'exec' is not allowed
# (exec is not in the whitelist)

calc("eval('1+1')")
# SecurityError: Function call 'eval' is not allowed

Resource Exhaustion

# Previously possible (now blocked):
calc("list(range(999999999))")
# SecurityError: Function call 'list' is not allowed
# (list and range removed from ALLOWED_NAMES in v0.5)

Security Recommendations

For Library Users

  1. Validate input length — Limit expression string length to prevent very long expressions.
  2. Set timeouts — Wrap calc() in a timeout for untrusted input.
  3. Don't pass user data as variables blindly — Variable names from user input should be validated.
  4. Don't use evaluate() directly — Always go through calc() for full pipeline security.

For Library Developers

  1. Never add eval, exec, compile to ALLOWED_NAMES.
  2. Never add getattr, setattr, type to ALLOWED_NAMES.
  3. Never add list, dict, tuple with range — enables DoS.
  4. Test new functions against the SafeNodeVisitor whitelist.
  5. Review table.json changes for lambda injections or arbitrary code.

Example: Safe Usage with Untrusted Input

import signal

class TimeoutError(Exception):
    pass

def timeout_handler(signum, frame):
    raise TimeoutError("Calculation timed out")

def safe_calc(expression, max_length=500, timeout_seconds=5, **variables):
    """Calculate with safety limits for untrusted input."""
    # Limit expression length
    if len(expression) > max_length:
        raise ValueError(f"Expression too long: {len(expression)} > {max_length}")
    
    # Set timeout (Unix only)
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout_seconds)
    
    try:
        return calc(expression, **variables)
    finally:
        signal.alarm(0)  # Cancel timeout

Comparison with Other Approaches

Approach Security Functionality Variable+
Raw eval() None Full Python N/A
ast.literal_eval() High Literals only N/A
eval() with __builtins__={} Medium Easy to bypass Layer 3 only
Variable+ High Math expressions All 3 layers
SymPy .evalf() High Symbolic math Different scope

There aren't any published security advisories