centient 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,19 +2,22 @@
2
2
  """
3
3
  RLVR Python Sandbox Executor
4
4
 
5
- Executes Python code in a restricted environment.
6
- Uses RestrictedPython when available, falls back to restricted builtins.
5
+ Executes Python code in a restricted environment using RestrictedPython.
6
+ RestrictedPython is REQUIRED - no fallback mode for security reasons.
7
7
 
8
8
  Communication protocol:
9
9
  - Reads JSON from stdin: {"code": "...", "input": ...}
10
10
  - Writes JSON to stdout: {"status": "success|error|timeout", "returnValue": ..., "stdout": "...", "stderr": "..."}
11
11
 
12
12
  Security measures:
13
+ - RestrictedPython compiler (required, no fallback)
13
14
  - No file system access
14
15
  - No network access
15
16
  - No subprocess spawning
16
- - No module imports (except safe builtins)
17
- - Restricted builtins
17
+ - No dangerous module imports
18
+ - Guarded attribute access (blocks __class__.__bases__ escape)
19
+
20
+ Install: pip install RestrictedPython
18
21
  """
19
22
 
20
23
  import sys
@@ -24,130 +27,41 @@ import traceback
24
27
  from contextlib import redirect_stdout, redirect_stderr
25
28
  from typing import Any, Dict
26
29
 
27
- # Try to import RestrictedPython for enhanced security
30
+ # RestrictedPython is REQUIRED - no fallback mode
28
31
  try:
29
32
  from RestrictedPython import compile_restricted, safe_globals
30
33
  from RestrictedPython.Guards import safe_builtins, guarded_iter_unpack_sequence
31
34
  from RestrictedPython.Eval import default_guarded_getiter, default_guarded_getitem
32
- RESTRICTED_PYTHON_AVAILABLE = True
33
- except ImportError:
34
- RESTRICTED_PYTHON_AVAILABLE = False
35
-
36
-
37
- # Safe builtins for fallback mode
38
- SAFE_BUILTINS = {
39
- # Type constructors
40
- 'bool': bool,
41
- 'int': int,
42
- 'float': float,
43
- 'str': str,
44
- 'list': list,
45
- 'dict': dict,
46
- 'tuple': tuple,
47
- 'set': set,
48
- 'frozenset': frozenset,
49
- 'bytes': bytes,
50
- 'bytearray': bytearray,
51
-
52
- # Type checking
53
- 'type': type,
54
- 'isinstance': isinstance,
55
- 'issubclass': issubclass,
56
-
57
- # Math and numeric
58
- 'abs': abs,
59
- 'round': round,
60
- 'min': min,
61
- 'max': max,
62
- 'sum': sum,
63
- 'pow': pow,
64
- 'divmod': divmod,
65
-
66
- # Iterables
67
- 'len': len,
68
- 'range': range,
69
- 'enumerate': enumerate,
70
- 'zip': zip,
71
- 'map': map,
72
- 'filter': filter,
73
- 'reversed': reversed,
74
- 'sorted': sorted,
75
- 'all': all,
76
- 'any': any,
77
-
78
- # String/repr
79
- 'repr': repr,
80
- 'ascii': ascii,
81
- 'chr': chr,
82
- 'ord': ord,
83
- 'bin': bin,
84
- 'hex': hex,
85
- 'oct': oct,
86
- 'format': format,
87
-
88
- # Object inspection (safe subset)
89
- 'hasattr': hasattr,
90
- 'getattr': getattr,
91
- 'callable': callable,
92
- 'id': id,
93
- 'hash': hash,
94
-
95
- # Exceptions
96
- 'Exception': Exception,
97
- 'ValueError': ValueError,
98
- 'TypeError': TypeError,
99
- 'KeyError': KeyError,
100
- 'IndexError': IndexError,
101
- 'AttributeError': AttributeError,
102
- 'RuntimeError': RuntimeError,
103
- 'StopIteration': StopIteration,
104
- 'ZeroDivisionError': ZeroDivisionError,
105
-
106
- # Constants
107
- 'True': True,
108
- 'False': False,
109
- 'None': None,
110
-
111
- # Print (captured)
112
- 'print': print,
113
- }
114
-
115
- # Explicitly blocked - should NOT be available
116
- BLOCKED = {
117
- 'open', 'file', 'input', 'raw_input',
118
- 'exec', 'eval', 'compile',
119
- '__import__', 'import',
120
- 'globals', 'locals', 'vars', 'dir',
121
- 'setattr', 'delattr',
122
- 'exit', 'quit',
123
- 'help', 'license', 'credits', 'copyright',
124
- }
35
+ except ImportError as e:
36
+ # Fatal error - RestrictedPython is required for security
37
+ print(json.dumps({
38
+ 'status': 'error',
39
+ 'returnValue': None,
40
+ 'stdout': '',
41
+ 'stderr': '',
42
+ 'error': {
43
+ 'type': 'SecurityError',
44
+ 'message': 'RestrictedPython is required but not installed. '
45
+ 'Install with: pip install RestrictedPython',
46
+ },
47
+ }))
48
+ sys.exit(1)
125
49
 
126
50
 
127
51
  def create_safe_globals(input_value: Any) -> Dict[str, Any]:
128
- """Create a restricted global namespace for code execution."""
129
- if RESTRICTED_PYTHON_AVAILABLE:
130
- # Use RestrictedPython's safe globals
131
- restricted_globals = dict(safe_globals)
132
- restricted_globals['__builtins__'] = safe_builtins
133
- restricted_globals['_getiter_'] = default_guarded_getiter
134
- restricted_globals['_getitem_'] = default_guarded_getitem
135
- restricted_globals['_iter_unpack_sequence_'] = guarded_iter_unpack_sequence
136
- restricted_globals['__INPUT__'] = input_value
137
- restricted_globals['print'] = print # Allow print for output capture
138
- return restricted_globals
139
- else:
140
- # Use our restricted builtins
141
- return {
142
- '__builtins__': SAFE_BUILTINS,
143
- '__name__': '__restricted__',
144
- '__doc__': None,
145
- '__INPUT__': input_value,
146
- }
52
+ """Create a restricted global namespace for code execution using RestrictedPython."""
53
+ restricted_globals = dict(safe_globals)
54
+ restricted_globals['__builtins__'] = safe_builtins
55
+ restricted_globals['_getiter_'] = default_guarded_getiter
56
+ restricted_globals['_getitem_'] = default_guarded_getitem
57
+ restricted_globals['_iter_unpack_sequence_'] = guarded_iter_unpack_sequence
58
+ restricted_globals['__INPUT__'] = input_value
59
+ restricted_globals['print'] = print # Allow print for output capture
60
+ return restricted_globals
147
61
 
148
62
 
149
63
  def execute_code(code: str, input_value: Any) -> Dict[str, Any]:
150
- """Execute code in restricted environment and return results."""
64
+ """Execute code in restricted environment using RestrictedPython."""
151
65
  stdout_capture = io.StringIO()
152
66
  stderr_capture = io.StringIO()
153
67
 
@@ -157,34 +71,27 @@ def execute_code(code: str, input_value: Any) -> Dict[str, Any]:
157
71
  'stdout': '',
158
72
  'stderr': '',
159
73
  'error': None,
160
- 'restrictedMode': 'RestrictedPython' if RESTRICTED_PYTHON_AVAILABLE else 'fallback',
74
+ 'restrictedMode': 'RestrictedPython',
161
75
  }
162
76
 
163
77
  try:
164
78
  # Create safe globals
165
79
  safe_globals_dict = create_safe_globals(input_value)
166
- safe_locals = {}
167
-
168
- # Compile code
169
- if RESTRICTED_PYTHON_AVAILABLE:
170
- # Use RestrictedPython compiler
171
- byte_code = compile_restricted(
172
- code,
173
- filename='<pattern>',
174
- mode='exec'
175
- )
176
- if byte_code.errors:
177
- result['status'] = 'error'
178
- result['error'] = {
179
- 'type': 'CompilationError',
180
- 'message': '\n'.join(byte_code.errors),
181
- }
182
- return result
183
- compiled_code = byte_code.code
184
- else:
185
- # Standard compile with AST validation would go here
186
- # For now, just compile normally (relies on restricted builtins)
187
- compiled_code = compile(code, '<pattern>', 'exec')
80
+
81
+ # Compile code with RestrictedPython
82
+ byte_code = compile_restricted(
83
+ code,
84
+ filename='<pattern>',
85
+ mode='exec'
86
+ )
87
+ if byte_code.errors:
88
+ result['status'] = 'error'
89
+ result['error'] = {
90
+ 'type': 'CompilationError',
91
+ 'message': '\n'.join(byte_code.errors),
92
+ }
93
+ return result
94
+ compiled_code = byte_code.code
188
95
 
189
96
  # Execute with output capture
190
97
  # Use same dict for globals and locals to enable recursive functions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "centient",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "MCP server for context engineering operations with handoff protocol, health monitoring, session branching with visualization, and query-based artifact loading",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",