secure-sandbox 0.0.1__py3-none-any.whl
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.
- secure_sandbox/__init__.py +64 -0
- secure_sandbox/cli.py +231 -0
- secure_sandbox/core.py +657 -0
- secure_sandbox/exceptions.py +52 -0
- secure_sandbox/whitelist.py +251 -0
- secure_sandbox-0.0.1.dist-info/METADATA +350 -0
- secure_sandbox-0.0.1.dist-info/RECORD +11 -0
- secure_sandbox-0.0.1.dist-info/WHEEL +5 -0
- secure_sandbox-0.0.1.dist-info/entry_points.txt +2 -0
- secure_sandbox-0.0.1.dist-info/licenses/LICENSE +21 -0
- secure_sandbox-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Secure Sandbox - 高安全性Python沙箱库
|
|
3
|
+
|
|
4
|
+
用于安全执行不可信的第三方代码(如AI生成的代码)
|
|
5
|
+
|
|
6
|
+
核心特性:
|
|
7
|
+
- Gas机制防止CPU DoS攻击
|
|
8
|
+
- AST白名单防止危险操作
|
|
9
|
+
- 属性拦截防止沙箱逃逸
|
|
10
|
+
- Import白名单控制模块导入
|
|
11
|
+
|
|
12
|
+
作者: Python Security Architect
|
|
13
|
+
版本: 0.0.1
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from .core import (
|
|
17
|
+
SecureSandbox,
|
|
18
|
+
SecurityConfig,
|
|
19
|
+
safe_execute,
|
|
20
|
+
GasLimitExceeded,
|
|
21
|
+
SandboxSecurityError,
|
|
22
|
+
ASTValidationError,
|
|
23
|
+
SandboxException,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from .whitelist import (
|
|
27
|
+
AST_WHITELIST,
|
|
28
|
+
AST_BLACKLIST,
|
|
29
|
+
DANGEROUS_ATTRIBUTES,
|
|
30
|
+
SAFE_ATTRIBUTES,
|
|
31
|
+
SAFE_BUILTIN_TYPES,
|
|
32
|
+
DEFAULT_ALLOWED_MODULES,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__version__ = "0.0.1"
|
|
36
|
+
__author__ = "Python Security Architect"
|
|
37
|
+
__email__ = "security@example.com"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# 核心类
|
|
41
|
+
'SecureSandbox',
|
|
42
|
+
'SecurityConfig',
|
|
43
|
+
|
|
44
|
+
# 便捷函数
|
|
45
|
+
'safe_execute',
|
|
46
|
+
|
|
47
|
+
# 异常类
|
|
48
|
+
'GasLimitExceeded',
|
|
49
|
+
'SandboxSecurityError',
|
|
50
|
+
'ASTValidationError',
|
|
51
|
+
'SandboxException',
|
|
52
|
+
|
|
53
|
+
# 白名单配置
|
|
54
|
+
'AST_WHITELIST',
|
|
55
|
+
'AST_BLACKLIST',
|
|
56
|
+
'DANGEROUS_ATTRIBUTES',
|
|
57
|
+
'SAFE_ATTRIBUTES',
|
|
58
|
+
'SAFE_BUILTIN_TYPES',
|
|
59
|
+
'DEFAULT_ALLOWED_MODULES',
|
|
60
|
+
|
|
61
|
+
# 元数据
|
|
62
|
+
'__version__',
|
|
63
|
+
'__author__',
|
|
64
|
+
]
|
secure_sandbox/cli.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI工具 - 命令行接口
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import json
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .core import SecureSandbox, SecurityConfig, safe_execute
|
|
11
|
+
from .exceptions import (
|
|
12
|
+
GasLimitExceeded,
|
|
13
|
+
SandboxSecurityError,
|
|
14
|
+
ASTValidationError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main():
|
|
19
|
+
"""命令行主函数"""
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
description="Secure Sandbox - 安全执行Python代码",
|
|
22
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
23
|
+
epilog="""
|
|
24
|
+
示例:
|
|
25
|
+
# 从文件执行代码
|
|
26
|
+
secure-sandbox script.py --max-gas 1000
|
|
27
|
+
|
|
28
|
+
# 从命令行执行代码
|
|
29
|
+
secure-sandbox -c "print('Hello, World!')" --max-gas 50
|
|
30
|
+
|
|
31
|
+
# 使用自定义配置
|
|
32
|
+
secure-sandbox script.py --config config.json
|
|
33
|
+
|
|
34
|
+
# 显示执行结果
|
|
35
|
+
secure-sandbox script.py --verbose
|
|
36
|
+
"""
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
'file',
|
|
41
|
+
nargs='?',
|
|
42
|
+
help='要执行的Python脚本文件'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
'-c', '--code',
|
|
47
|
+
help='直接执行代码字符串'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
'--max-gas',
|
|
52
|
+
type=int,
|
|
53
|
+
default=10000,
|
|
54
|
+
help='最大Gas额度 (默认: 10000)'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
'--allow-imports',
|
|
59
|
+
action='store_true',
|
|
60
|
+
default=True,
|
|
61
|
+
help='允许导入模块 (默认: True)'
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
'--no-imports',
|
|
66
|
+
action='store_true',
|
|
67
|
+
help='禁止导入模块'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
'--modules',
|
|
72
|
+
nargs='+',
|
|
73
|
+
help='允许导入的模块列表(覆盖默认白名单)'
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
'--config',
|
|
78
|
+
help='配置文件路径 (JSON格式)'
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
'--verbose',
|
|
83
|
+
action='store_true',
|
|
84
|
+
help='显示详细执行信息'
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
'--output-json',
|
|
89
|
+
action='store_true',
|
|
90
|
+
help='以JSON格式输出结果'
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
'--version',
|
|
95
|
+
action='version',
|
|
96
|
+
version='Secure Sandbox v0.0.1'
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
args = parser.parse_args()
|
|
100
|
+
|
|
101
|
+
# 获取代码
|
|
102
|
+
code: Optional[str] = None
|
|
103
|
+
if args.code:
|
|
104
|
+
code = args.code
|
|
105
|
+
elif args.file:
|
|
106
|
+
try:
|
|
107
|
+
with open(args.file, 'r', encoding='utf-8') as f:
|
|
108
|
+
code = f.read()
|
|
109
|
+
except FileNotFoundError:
|
|
110
|
+
print(f"错误: 文件 '{args.file}' 不存在", file=sys.stderr)
|
|
111
|
+
sys.exit(1)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f"错误: 无法读取文件 '{args.file}': {e}", file=sys.stderr)
|
|
114
|
+
sys.exit(1)
|
|
115
|
+
else:
|
|
116
|
+
print("错误: 请提供要执行的代码(通过文件或-c参数)", file=sys.stderr)
|
|
117
|
+
parser.print_help()
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
|
|
120
|
+
# 创建配置
|
|
121
|
+
config = SecurityConfig(max_gas=args.max_gas)
|
|
122
|
+
|
|
123
|
+
# 加载配置文件
|
|
124
|
+
if args.config:
|
|
125
|
+
try:
|
|
126
|
+
with open(args.config, 'r', encoding='utf-8') as f:
|
|
127
|
+
config_data = json.load(f)
|
|
128
|
+
|
|
129
|
+
# 更新配置
|
|
130
|
+
for key, value in config_data.items():
|
|
131
|
+
if hasattr(config, key):
|
|
132
|
+
setattr(config, key, value)
|
|
133
|
+
except FileNotFoundError:
|
|
134
|
+
print(f"错误: 配置文件 '{args.config}' 不存在", file=sys.stderr)
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
except json.JSONDecodeError as e:
|
|
137
|
+
print(f"错误: 配置文件格式错误: {e}", file=sys.stderr)
|
|
138
|
+
sys.exit(1)
|
|
139
|
+
|
|
140
|
+
# 处理import参数
|
|
141
|
+
if args.no_imports:
|
|
142
|
+
config.allow_imports = False
|
|
143
|
+
|
|
144
|
+
if args.modules:
|
|
145
|
+
config.allowed_modules = set(args.modules)
|
|
146
|
+
|
|
147
|
+
# 执行代码
|
|
148
|
+
sandbox = SecureSandbox(config)
|
|
149
|
+
|
|
150
|
+
if not code:
|
|
151
|
+
print("错误: 无法获取代码内容", file=sys.stderr)
|
|
152
|
+
sys.exit(1)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
result = sandbox.safe_execute(code, max_gas=args.max_gas)
|
|
156
|
+
|
|
157
|
+
if args.output_json:
|
|
158
|
+
# JSON格式输出
|
|
159
|
+
output = {
|
|
160
|
+
'success': result['success'],
|
|
161
|
+
'remaining_gas': result['remaining_gas'],
|
|
162
|
+
'total_checks': result['total_checks'],
|
|
163
|
+
'locals': {k: str(v) for k, v in result['locals'].items()},
|
|
164
|
+
}
|
|
165
|
+
print(json.dumps(output, indent=2))
|
|
166
|
+
elif args.verbose:
|
|
167
|
+
# 详细输出
|
|
168
|
+
print("\n" + "="*60)
|
|
169
|
+
print("执行成功")
|
|
170
|
+
print("="*60)
|
|
171
|
+
print(f"剩余Gas: {result['remaining_gas']}")
|
|
172
|
+
print(f"总检查次数: {result['total_checks']}")
|
|
173
|
+
print(f"局部变量: {list(result['locals'].keys())}")
|
|
174
|
+
print("="*60)
|
|
175
|
+
else:
|
|
176
|
+
# 简洁输出
|
|
177
|
+
print(f"✅ 执行成功 | 剩余Gas: {result['remaining_gas']}")
|
|
178
|
+
|
|
179
|
+
sys.exit(0)
|
|
180
|
+
|
|
181
|
+
except GasLimitExceeded as e:
|
|
182
|
+
if args.output_json:
|
|
183
|
+
output = {
|
|
184
|
+
'success': False,
|
|
185
|
+
'error': 'GasLimitExceeded',
|
|
186
|
+
'message': str(e),
|
|
187
|
+
}
|
|
188
|
+
print(json.dumps(output, indent=2))
|
|
189
|
+
else:
|
|
190
|
+
print(f"❌ Gas额度耗尽: {e}", file=sys.stderr)
|
|
191
|
+
sys.exit(2)
|
|
192
|
+
|
|
193
|
+
except SandboxSecurityError as e:
|
|
194
|
+
if args.output_json:
|
|
195
|
+
output = {
|
|
196
|
+
'success': False,
|
|
197
|
+
'error': 'SandboxSecurityError',
|
|
198
|
+
'message': str(e),
|
|
199
|
+
}
|
|
200
|
+
print(json.dumps(output, indent=2))
|
|
201
|
+
else:
|
|
202
|
+
print(f"❌ 安全违规: {e}", file=sys.stderr)
|
|
203
|
+
sys.exit(3)
|
|
204
|
+
|
|
205
|
+
except ASTValidationError as e:
|
|
206
|
+
if args.output_json:
|
|
207
|
+
output = {
|
|
208
|
+
'success': False,
|
|
209
|
+
'error': 'ASTValidationError',
|
|
210
|
+
'message': str(e),
|
|
211
|
+
}
|
|
212
|
+
print(json.dumps(output, indent=2))
|
|
213
|
+
else:
|
|
214
|
+
print(f"❌ AST验证失败: {e}", file=sys.stderr)
|
|
215
|
+
sys.exit(4)
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
if args.output_json:
|
|
219
|
+
output = {
|
|
220
|
+
'success': False,
|
|
221
|
+
'error': type(e).__name__,
|
|
222
|
+
'message': str(e),
|
|
223
|
+
}
|
|
224
|
+
print(json.dumps(output, indent=2))
|
|
225
|
+
else:
|
|
226
|
+
print(f"❌ 未预期错误: {type(e).__name__}: {e}", file=sys.stderr)
|
|
227
|
+
sys.exit(5)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == '__main__':
|
|
231
|
+
main()
|