ForcePilot/backend/package/yuxi/channels/adapters/synologychat/security_audit.py
Kris 3ad218e537 feat(synologychat): 新增群晖Chat适配器完整实现
新增群晖Chat渠道适配器的全套实现,包括:
1. 基础适配器与导出接口定义
2. DSM API认证、探测与会话管理
3. 轮询与Webhook两种消息接收方式
4. 消息去重、格式化与规范化处理
5. 多账号支持与权限安全策略
6. 目录用户/群组发现功能
7. 审批配对与流量控制机制
8. 安全审计与配置检查功能
2026-05-12 00:49:16 +08:00

98 lines
3.2 KiB
Python

"""Security audit module for Synology Chat channel.
Performs security configuration checks and identifies potential risks
in the Synology Chat integration.
"""
from __future__ import annotations
from typing import Any
def audit_config(config: dict[str, Any]) -> list[dict[str, Any]]:
findings: list[dict[str, Any]] = []
if not config.get("verify_ssl", True):
findings.append(
{
"id": "ssl_disabled",
"severity": "high",
"message": "SSL verification is disabled, connections may be vulnerable to MITM attacks",
"remediation": "Set verify_ssl to true or ensure the NAS certificate is trusted",
}
)
if not config.get("password") and not config.get("password_file"):
findings.append(
{
"id": "no_credential",
"severity": "critical",
"message": "No DSM password or password_file configured",
"remediation": "Set password or password_file in config or DSM_PASSWORD env var",
}
)
sec = config.get("security", {})
if not isinstance(sec, dict):
sec = {}
dm_policy = sec.get("dm_policy", "open")
if dm_policy == "open":
findings.append(
{
"id": "open_dm_policy",
"severity": "medium",
"message": "DM policy is 'open', any user can interact with the bot",
"remediation": "Set dm_policy to 'allowlist' or 'pairing' and configure allow_from",
}
)
if dm_policy == "pairing" and not sec.get("allow_from"):
findings.append(
{
"id": "pairing_no_initial_allowlist",
"severity": "low",
"message": "Pairing mode enabled but initial allow_from is empty",
"remediation": "Add at least one trusted user ID to allow_from for operational access",
}
)
rate_limit = config.get("rate_limit", {}).get("max_per_minute", 30)
if isinstance(rate_limit, (int, float)) and rate_limit > 60:
findings.append(
{
"id": "high_rate_limit",
"severity": "low",
"message": f"Rate limit is set to {rate_limit}/min, which may be high for a NAS device",
"remediation": "Consider lowering to 30-60 requests per minute",
}
)
return findings
def audit_connection(dsm_url: str, verify_ssl: bool) -> list[dict[str, Any]]:
findings: list[dict[str, Any]] = []
if dsm_url.startswith("http://"):
findings.append(
{
"id": "cleartext_dsm_url",
"severity": "high",
"message": "DSM URL uses HTTP (cleartext), credentials may be transmitted in plaintext",
"remediation": "Use HTTPS for the DSM URL",
}
)
if not dsm_url:
findings.append(
{
"id": "empty_dsm_url",
"severity": "critical",
"message": "DSM URL is empty",
"remediation": "Configure a valid DSM URL",
}
)
return findings