sycommon-python-lib 0.2.8a4__py3-none-any.whl → 0.2.8a5__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.
@@ -971,6 +971,20 @@ def setup_sandbox_handler(app: FastAPI, config: dict = None):
971
971
  env["TEMP"] = os.path.join(workspace, "tmp")
972
972
  env["TMP"] = os.path.join(workspace, "tmp")
973
973
  env["SANDBOX_ROOT"] = workspace
974
+ # 签名型凭据豁免(企微文档机器人):WECOM_DOC_BOT_* 用于【本地 HMAC 签名】
975
+ # (sign=sha256(secret+bot_id+t+nonce)),签名必须在发起方算,网关无法代注
976
+ # ——这是收口「零密钥」的已知例外。流量仍走网关(URL 指网关+X-Upstream-Host,
977
+ # 受白名单/审计管制),只是 secret 必须到脚本手里。仅当凭据包里有才注入。
978
+ try:
979
+ from sycommon.middleware.sandbox_gateway import credential_store
980
+ _c = credential_store().any_user()
981
+ _creds = credential_store().get(_c) if _c else None
982
+ for _k in ("WECOM_DOC_BOT_ID", "WECOM_DOC_BOT_SECRET"):
983
+ _v = (_creds or {}).get(_k)
984
+ if _v:
985
+ env[_k] = str(_v)
986
+ except Exception:
987
+ pass
974
988
  # 出网网关:子进程 SYT_BASE_URL 指向网关(http://127.0.0.1:port),
975
989
  # HTTPS_PROXY 让 requests/httpx/curl/wget 自动走网关。node/裸 socket 不读
976
990
  # proxy 的运行时,由后续 seccomp 硬墙兜底(只放行网关 host:port)。
@@ -116,15 +116,22 @@ def _strip_cred_headers(headers) -> dict:
116
116
 
117
117
 
118
118
  def _apply_credentials(method: str, target_url: str, headers: dict,
119
- creds: dict) -> tuple[str, dict]:
119
+ creds: dict, upstream_host: str = "") -> tuple[str, dict]:
120
120
  """按目标 host/path 改写 URL(query 注入)+ 注入凭据头。返回 (url, headers)。
121
121
 
122
122
  网关→真上游的请求构造在此完成。host 路由表见模块 docstring。
123
+ upstream_host: 请求头 X-Upstream-Host 的值(网关 origin 模式下 URL host 是
124
+ 网关自身,真实目标在该头里)——凭据路由优先按它判定,其次按 URL host。
123
125
  """
124
126
  host = (urlparse(target_url).hostname or "").lower()
127
+ # 网关 origin 模式:URL host 是 127.0.0.1(网关),真实目标在 X-Upstream-Host;
128
+ # 凭据路由按真实目标判定(取两者中非网关地址的那个)。
129
+ route_host = host
130
+ if upstream_host:
131
+ route_host = (urlparse(f"https://{upstream_host}").hostname or "").lower()
125
132
 
126
133
  # --- SYT 网关:Bearer + X-Sy-Api-Authz + X-Host-Name ---
127
- if _match_syt_upstream(host, creds.get("SYT_BASE_URL", "")):
134
+ if _match_syt_upstream(route_host, creds.get("SYT_BASE_URL", "")):
128
135
  token = creds.get("SYT_TOKEN", "")
129
136
  if token:
130
137
  headers["Authorization"] = f"Bearer {token}"
@@ -137,7 +144,7 @@ def _apply_credentials(method: str, target_url: str, headers: dict,
137
144
  return target_url, headers
138
145
 
139
146
  # --- 企微 gettoken:corpid/corpsecret 在 URL query ---
140
- if _is_wecom(host):
147
+ if _is_wecom(route_host):
141
148
  corp_id = creds.get("WECOM_CORP_ID", "")
142
149
  corp_secret = creds.get("WECOM_CORP_SECRET", "")
143
150
  if corp_id and corp_secret and "/cgi-bin/gettoken" in target_url:
@@ -146,10 +153,12 @@ def _apply_credentials(method: str, target_url: str, headers: dict,
146
153
  q.setdefault("corpid", corp_id)
147
154
  q.setdefault("corpsecret", corp_secret)
148
155
  target_url = urlunparse(parsed._replace(query=urlencode(q)))
156
+ # 企微文档机器人(wecom.py 的 MCP 网关)用 DOC_BOT 凭据签名——不在此注入
157
+ # (wecom.py 本地算签名),只保证 gettoken 的 corp 凭据注入。
149
158
  return target_url, headers
150
159
 
151
160
  # --- OA MCP 后端:SYU-Token + SYU-Url(改写为真上游 base)---
152
- if _is_oa_backend(host):
161
+ if _is_oa_backend(route_host):
153
162
  token = creds.get("SYT_TOKEN", "")
154
163
  syt_base = (creds.get("SYT_BASE_URL") or "").rstrip("/")
155
164
  if token:
@@ -412,9 +421,23 @@ class EgressGateway:
412
421
  syt_base = (creds.get("SYT_BASE_URL") or "").rstrip("/")
413
422
  if upstream_host:
414
423
  # SSRF 防护:上游 host 禁止指向回环/本机/私有网段(本机服务含沙箱
415
- # 自身 API,免鉴权,打穿即跨用户任意执行)。非回环的 10.x/172.16-31.x/
416
- # 192.168.x 内网目标按 EgressPolicy 判定(directAllow 命中才放)。
417
- if _is_loopback_or_private(upstream_host):
424
+ # 自身 API,免鉴权,打穿即跨用户任意执行)。例外:EgressPolicy
425
+ # viaGateway/directAllow 显式登记的【信任内网上游】(如 OA MCP
426
+ # ai.syitservice.com→10.10.1.171)——它们是内网服务但有意经营,
427
+ # 不属于 SSRF 攻击面(策略由 _check_allowlist 继续管)。
428
+ policy = None
429
+ try:
430
+ from sycommon.config.EgressPolicyConfig import EgressPolicyConfig
431
+ policy = EgressPolicyConfig.from_config()
432
+ except Exception:
433
+ policy = None
434
+ trusted = []
435
+ if policy is not None:
436
+ trusted = list(policy.viaGateway) + list(policy.directAllow)
437
+ uh_hostname = upstream_host.split(":")[0] if upstream_host.count(":") <= 1 \
438
+ else upstream_host
439
+ if (_is_loopback_or_private(upstream_host)
440
+ and not EgressPolicyConfig._host_match(uh_hostname.lower(), trusted)):
418
441
  SYLogger.warning(
419
442
  f"[EgressGateway] 拒绝内网/回环上游(SSRF 防护): {upstream_host}")
420
443
  return web.Response(status=403, text="gateway: upstream host not allowed")
@@ -446,9 +469,11 @@ class EgressGateway:
446
469
  continue
447
470
  fwd_headers[k] = v
448
471
 
449
- # 注入凭据头 / 改写 URL(query 注入)
472
+ # 注入凭据头 / 改写 URL(query 注入)。
473
+ # upstream_host 透传给凭据路由:网关 origin 模式下 URL host 是网关自身,
474
+ # 真实目标在 X-Upstream-Host——OA(ai.sy)/企微按它判路由。
450
475
  target_url, fwd_headers = _apply_credentials(
451
- request.method, target_url, fwd_headers, creds)
476
+ request.method, target_url, fwd_headers, creds, upstream_host)
452
477
 
453
478
  # 读请求体(上传场景)
454
479
  body = await request.read() if request.can_read_body else None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.2.8a4
3
+ Version: 0.2.8a5
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -239,8 +239,8 @@ sycommon/middleware/exception.py,sha256=UAy0tKijI_2JoKjwT3h62aL-tybftP3IETvcr26N
239
239
  sycommon/middleware/middleware.py,sha256=1qA_0rT7pv1xTfuvA0kdYkxVOHYT6roSSNO7Cmf2wYg,1778
240
240
  sycommon/middleware/monitor_memory.py,sha256=pYRK-wRuDd6enSg9Pf8tQxPdYQS6S0AyjyXeKFRLKEs,628
241
241
  sycommon/middleware/mq.py,sha256=9X6KKtadFjBXKS5L3kEKujYio9wwGfWgXwWOAHO-HDg,254
242
- sycommon/middleware/sandbox.py,sha256=q4meNKKonqviamB4iDcX92pfSNZ7PLwjikfeCjdxr2Q,101543
243
- sycommon/middleware/sandbox_gateway.py,sha256=MdvzYz-LLicwX9YkwreZA7OUTR9WTwVi6VW4e20wQWw,23298
242
+ sycommon/middleware/sandbox.py,sha256=UMSFteEKhzbL-G_o0BypIc6MftmQd5ICzK8eWgJhhq8,102376
243
+ sycommon/middleware/sandbox_gateway.py,sha256=foVfLfco_UhtOT2z9ZGTKCa8WIoveJ0tLe8FAthDv-g,24967
244
244
  sycommon/middleware/timeout.py,sha256=KlxOPa8xl2dg6yuRi_EzkVJG8bX4stb5ueYxctzzGM8,1433
245
245
  sycommon/middleware/token_tracking.py,sha256=rEbgV1bgWMdzAERx4aq5XAvOIT6jTY_tK1P0xHJnL3o,6609
246
246
  sycommon/middleware/tool_result_truncation.py,sha256=xtwBf8q11Z8EPzmo3Ju4OfUbULOklSsWo_oystxCW0o,13640
@@ -333,8 +333,8 @@ sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
333
333
  sycommon/tools/user_id.py,sha256=o-zsubGSR5rSSZf8Pk8B7AhE21ahDTTIW5JZmJO6Flw,4761
334
334
  sycommon/xxljob/__init__.py,sha256=7eoBlQxv-B39IfRSCY2bkqdGYs1QRe1umAWd88VMEEM,86
335
335
  sycommon/xxljob/xxljob_service.py,sha256=1yifwIBNGsCIxLnQjHKiBlbsigc_zvPH-dMTZcNxe-Q,7649
336
- sycommon_python_lib-0.2.8a4.dist-info/METADATA,sha256=NZxc6nNAdgPwefWjpCfFT3wVBl8QhqlfcVGh7M_0Fb8,7996
337
- sycommon_python_lib-0.2.8a4.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
338
- sycommon_python_lib-0.2.8a4.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
339
- sycommon_python_lib-0.2.8a4.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
340
- sycommon_python_lib-0.2.8a4.dist-info/RECORD,,
336
+ sycommon_python_lib-0.2.8a5.dist-info/METADATA,sha256=c0sLx9ZQmytPyQI9X-dYDub5ezbOMmp9NBcU8vsA6J0,7996
337
+ sycommon_python_lib-0.2.8a5.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
338
+ sycommon_python_lib-0.2.8a5.dist-info/entry_points.txt,sha256=gsR4SssKxDWjRU8ggidzNcdMXDPRSKRS7UaGyNP84Qg,92
339
+ sycommon_python_lib-0.2.8a5.dist-info/top_level.txt,sha256=RgphKrg7nJyZ7irJqbxFr-5H2LUYTvI7ivoWZH2hcD0,29
340
+ sycommon_python_lib-0.2.8a5.dist-info/RECORD,,