cybersecured-agent-cli 2.0.0__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.
Files changed (45) hide show
  1. cybersecured_agent/__init__.py +12 -0
  2. cybersecured_agent/__main__.py +6 -0
  3. cybersecured_agent/agent_frameworks/__init__.py +23 -0
  4. cybersecured_agent/agent_frameworks/_types.py +17 -0
  5. cybersecured_agent/agent_frameworks/hermes.py +108 -0
  6. cybersecured_agent/agent_frameworks/kimi_cli.py +108 -0
  7. cybersecured_agent/agent_frameworks/openclaw.py +117 -0
  8. cybersecured_agent/agent_frameworks/opencode.py +111 -0
  9. cybersecured_agent/api_client.py +251 -0
  10. cybersecured_agent/cli.py +73 -0
  11. cybersecured_agent/commands/__init__.py +25 -0
  12. cybersecured_agent/commands/advisory.py +52 -0
  13. cybersecured_agent/commands/agent.py +395 -0
  14. cybersecured_agent/commands/application.py +100 -0
  15. cybersecured_agent/commands/assessment.py +82 -0
  16. cybersecured_agent/commands/auth.py +78 -0
  17. cybersecured_agent/commands/config.py +50 -0
  18. cybersecured_agent/commands/coverage.py +46 -0
  19. cybersecured_agent/commands/diagnose.py +24 -0
  20. cybersecured_agent/commands/incident.py +234 -0
  21. cybersecured_agent/commands/scan.py +164 -0
  22. cybersecured_agent/commands/status.py +33 -0
  23. cybersecured_agent/config.py +114 -0
  24. cybersecured_agent/constants.py +98 -0
  25. cybersecured_agent/exceptions.py +25 -0
  26. cybersecured_agent/fingerprint/__init__.py +20 -0
  27. cybersecured_agent/fingerprint/core.py +126 -0
  28. cybersecured_agent/fingerprint/dump.py +139 -0
  29. cybersecured_agent/fingerprint/hashing.py +55 -0
  30. cybersecured_agent/fingerprint/versions.py +9 -0
  31. cybersecured_agent/infra/__init__.py +4 -0
  32. cybersecured_agent/infra/cloud_detect.py +111 -0
  33. cybersecured_agent/infra/config_files.py +73 -0
  34. cybersecured_agent/infra/desensitize.py +163 -0
  35. cybersecured_agent/infra/machine_id.py +173 -0
  36. cybersecured_agent/infra/process_chain.py +165 -0
  37. cybersecured_agent/machine_id.py +13 -0
  38. cybersecured_agent/output.py +91 -0
  39. cybersecured_agent/risk_factors.py +43 -0
  40. cybersecured_agent/workspace.py +31 -0
  41. cybersecured_agent_cli-2.0.0.dist-info/METADATA +76 -0
  42. cybersecured_agent_cli-2.0.0.dist-info/RECORD +45 -0
  43. cybersecured_agent_cli-2.0.0.dist-info/WHEEL +4 -0
  44. cybersecured_agent_cli-2.0.0.dist-info/entry_points.txt +2 -0
  45. cybersecured_agent_cli-2.0.0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,12 @@
1
+ """Cybersecured (厚锋科技) Agent CLI - 龙行无忧 AI智能体风险管家服务命令行工具。
2
+
3
+ A cross-platform CLI tool for AI智能体 risk assessment and advisory services.
4
+ Supports Windows, Linux, macOS, cloud platforms, and Docker containers.
5
+
6
+ License: GPL-3.0-or-later
7
+ """
8
+
9
+ __version__ = "2.0.0"
10
+ __author__ = "Cybersecured Technology (厚锋科技)"
11
+ __email__ = "developer@cybersecured.cn"
12
+ __license__ = "GPL-3.0-or-later"
@@ -0,0 +1,6 @@
1
+ """Entry point for python -m cybersecured_agent."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,23 @@
1
+ """Agent framework modules registry.
2
+
3
+ Related design: docs/41.tools-designs/design-cybersecured-agent-cli.md
4
+ """
5
+
6
+ from . import openclaw, hermes, kimi_cli, opencode
7
+ from ..fingerprint.versions import FP_CURRENT_VERSION
8
+
9
+ # Order = detection priority (highest first)
10
+ SUPPORTED_FRAMEWORKS = (openclaw, hermes, kimi_cli, opencode)
11
+
12
+ _REQUIRED_ATTRS = ("NAME", "detect", "collect_environment", "STABLE_IDENTITY_BY_VERSION")
13
+
14
+ for _mod in SUPPORTED_FRAMEWORKS:
15
+ for _attr in _REQUIRED_ATTRS:
16
+ if not hasattr(_mod, _attr):
17
+ raise ImportError(
18
+ f"agent_frameworks/{_mod.__name__} missing required attribute '{_attr}'"
19
+ )
20
+ if FP_CURRENT_VERSION not in _mod.STABLE_IDENTITY_BY_VERSION:
21
+ raise ImportError(
22
+ f"{_mod.__name__} must implement fp version {FP_CURRENT_VERSION}"
23
+ )
@@ -0,0 +1,17 @@
1
+ """Shared types for agent framework modules.
2
+
3
+ Related design: docs/41.tools-designs/design-cybersecured-agent-cli.md
4
+ """
5
+
6
+ from typing import Any, Dict, NamedTuple
7
+
8
+
9
+ class DetectionResult(NamedTuple):
10
+ matched: bool
11
+ evidence: Dict[str, Any]
12
+
13
+
14
+ class ProcessInfo(NamedTuple):
15
+ pid: int
16
+ ppid: int
17
+ args: str
@@ -0,0 +1,108 @@
1
+ """Hermes framework detection and identity extraction.
2
+
3
+ Related design: docs/41.tools-designs/design-cybersecured-agent-cli.md
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any, Dict, Mapping
9
+
10
+ from ..infra.config_files import read_json, read_with_desensitize
11
+ from ._types import DetectionResult, ProcessInfo
12
+
13
+ NAME = "hermes"
14
+
15
+ _ENV_VARS = ["HERMES_HOME"]
16
+ _CONFIG_DIRS = [Path.home() / ".hermes"]
17
+ _CONFIG_FILENAMES = ["config.json"]
18
+
19
+
20
+ def detect(env: Mapping[str, str], proc_chain: list[ProcessInfo]) -> DetectionResult:
21
+ evidence: Dict[str, Any] = {}
22
+
23
+ for var in _ENV_VARS:
24
+ val = env.get(var)
25
+ evidence[f"env_var_{var}"] = "present" if val else "absent"
26
+ if val:
27
+ return DetectionResult(matched=True, evidence=evidence)
28
+
29
+ for info in proc_chain:
30
+ if "hermes" in info.args.lower():
31
+ evidence["process_match"] = "hermes-runtime"
32
+ return DetectionResult(matched=True, evidence=evidence)
33
+ evidence["process_match"] = "none"
34
+
35
+ # Config dir existence alone does NOT mean "currently running hermes".
36
+ for cfg_dir in _CONFIG_DIRS:
37
+ if cfg_dir.exists():
38
+ evidence["config_dir"] = str(cfg_dir)
39
+ break
40
+ else:
41
+ evidence["config_dir"] = "not found"
42
+
43
+ return DetectionResult(matched=False, evidence=evidence)
44
+
45
+
46
+ def collect_environment(env: Mapping[str, str],
47
+ proc_chain: list[ProcessInfo],
48
+ workspace: Path) -> Dict[str, Any]:
49
+ data: Dict[str, Any] = {
50
+ "env_vars": {},
51
+ "config_files": {},
52
+ "version": None,
53
+ }
54
+
55
+ for var in _ENV_VARS:
56
+ if var in env:
57
+ data["env_vars"][var] = env[var]
58
+
59
+ for cfg_dir in _CONFIG_DIRS:
60
+ if not cfg_dir.exists():
61
+ continue
62
+ for filename in _CONFIG_FILENAMES:
63
+ path = cfg_dir / filename
64
+ if path.exists():
65
+ data["config_files"][filename] = read_with_desensitize(path)
66
+ version_path = cfg_dir / "config.json"
67
+ if version_path.exists():
68
+ cfg = read_json(version_path)
69
+ if isinstance(cfg, dict):
70
+ data["version"] = cfg.get("version")
71
+ break
72
+
73
+ return data
74
+
75
+
76
+ def _resolve_config_path(env: Mapping[str, str], workspace: Path) -> str:
77
+ hermes_home = env.get("HERMES_HOME")
78
+ if hermes_home:
79
+ return str(Path(hermes_home).resolve()).replace("\\", "/")
80
+
81
+ for cfg_dir in _CONFIG_DIRS:
82
+ if cfg_dir.exists():
83
+ for filename in _CONFIG_FILENAMES:
84
+ path = cfg_dir / filename
85
+ if path.exists():
86
+ return str(path.resolve()).replace("\\", "/")
87
+ return str(cfg_dir.resolve()).replace("\\", "/")
88
+
89
+ return str(workspace.resolve()).replace("\\", "/")
90
+
91
+
92
+ def _identity_legacy(env: Mapping[str, str],
93
+ proc_chain: list[ProcessInfo],
94
+ workspace: Path) -> Dict[str, str]:
95
+ return {"config_path": _resolve_config_path(env, workspace)}
96
+
97
+
98
+ def _identity_v1(env: Mapping[str, str],
99
+ proc_chain: list[ProcessInfo],
100
+ workspace: Path) -> Dict[str, str]:
101
+ return _identity_legacy(env, proc_chain, workspace)
102
+
103
+
104
+ STABLE_IDENTITY_BY_VERSION = {
105
+ "sha256": _identity_legacy,
106
+ "v0": _identity_legacy,
107
+ "v1": _identity_v1,
108
+ }
@@ -0,0 +1,108 @@
1
+ """Kimi CLI framework detection and identity extraction.
2
+
3
+ Related design: docs/41.tools-designs/design-cybersecured-agent-cli.md
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any, Dict, Mapping
9
+
10
+ from ..infra.config_files import read_json, read_with_desensitize
11
+ from ._types import DetectionResult, ProcessInfo
12
+
13
+ NAME = "kimi-cli"
14
+
15
+ _ENV_VARS = ["KIMI_WORK_DIR", "KIMI_SHARE_DIR", "KIMI_CONFIG_PATH"]
16
+ _CONFIG_DIRS = [Path.home() / ".kimi"]
17
+ _CONFIG_FILENAMES = ["config.json", "config.toml"]
18
+
19
+
20
+ def detect(env: Mapping[str, str], proc_chain: list[ProcessInfo]) -> DetectionResult:
21
+ evidence: Dict[str, Any] = {}
22
+
23
+ for var in _ENV_VARS:
24
+ val = env.get(var)
25
+ evidence[f"env_var_{var}"] = "present" if val else "absent"
26
+ if val:
27
+ return DetectionResult(matched=True, evidence=evidence)
28
+
29
+ for info in proc_chain:
30
+ if "kimi" in info.args.lower():
31
+ evidence["process_match"] = "kimi-runtime"
32
+ return DetectionResult(matched=True, evidence=evidence)
33
+ evidence["process_match"] = "none"
34
+
35
+ # Config dir existence alone does NOT mean "currently running kimi-cli".
36
+ for cfg_dir in _CONFIG_DIRS:
37
+ if cfg_dir.exists():
38
+ evidence["config_dir"] = str(cfg_dir)
39
+ break
40
+ else:
41
+ evidence["config_dir"] = "not found"
42
+
43
+ return DetectionResult(matched=False, evidence=evidence)
44
+
45
+
46
+ def collect_environment(env: Mapping[str, str],
47
+ proc_chain: list[ProcessInfo],
48
+ workspace: Path) -> Dict[str, Any]:
49
+ data: Dict[str, Any] = {
50
+ "env_vars": {},
51
+ "config_files": {},
52
+ "version": None,
53
+ }
54
+
55
+ for var in _ENV_VARS:
56
+ if var in env:
57
+ data["env_vars"][var] = env[var]
58
+
59
+ for cfg_dir in _CONFIG_DIRS:
60
+ if not cfg_dir.exists():
61
+ continue
62
+ for filename in _CONFIG_FILENAMES:
63
+ path = cfg_dir / filename
64
+ if path.exists():
65
+ data["config_files"][filename] = read_with_desensitize(path)
66
+ version_path = cfg_dir / "config.json"
67
+ if version_path.exists():
68
+ cfg = read_json(version_path)
69
+ if isinstance(cfg, dict):
70
+ data["version"] = cfg.get("version")
71
+ break
72
+
73
+ return data
74
+
75
+
76
+ def _resolve_config_path(env: Mapping[str, str], workspace: Path) -> str:
77
+ config_path = env.get("KIMI_CONFIG_PATH")
78
+ if config_path:
79
+ return str(Path(config_path).resolve()).replace("\\", "/")
80
+
81
+ for cfg_dir in _CONFIG_DIRS:
82
+ if cfg_dir.exists():
83
+ for filename in _CONFIG_FILENAMES:
84
+ path = cfg_dir / filename
85
+ if path.exists():
86
+ return str(path.resolve()).replace("\\", "/")
87
+ return str(cfg_dir.resolve()).replace("\\", "/")
88
+
89
+ return str(workspace.resolve()).replace("\\", "/")
90
+
91
+
92
+ def _identity_legacy(env: Mapping[str, str],
93
+ proc_chain: list[ProcessInfo],
94
+ workspace: Path) -> Dict[str, str]:
95
+ return {"config_path": _resolve_config_path(env, workspace)}
96
+
97
+
98
+ def _identity_v1(env: Mapping[str, str],
99
+ proc_chain: list[ProcessInfo],
100
+ workspace: Path) -> Dict[str, str]:
101
+ return _identity_legacy(env, proc_chain, workspace)
102
+
103
+
104
+ STABLE_IDENTITY_BY_VERSION = {
105
+ "sha256": _identity_legacy,
106
+ "v0": _identity_legacy,
107
+ "v1": _identity_v1,
108
+ }
@@ -0,0 +1,117 @@
1
+ """OpenClaw framework detection and identity extraction.
2
+
3
+ Related design: docs/41.tools-designs/design-cybersecured-agent-cli.md
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any, Dict, Mapping
9
+
10
+ from ..infra.config_files import read_json, read_with_desensitize
11
+ from ._types import DetectionResult, ProcessInfo
12
+
13
+ NAME = "openclaw"
14
+
15
+ _ENV_VARS = ["OPENCLAW_WORKSPACE", "OPENCLAW_WORKSPACE_DIR"]
16
+ _CONFIG_DIRS = [Path.home() / ".openclaw"]
17
+ _CONFIG_FILENAMES = ["config.json", "agents.json"]
18
+
19
+
20
+ def detect(env: Mapping[str, str], proc_chain: list[ProcessInfo]) -> DetectionResult:
21
+ evidence: Dict[str, Any] = {}
22
+
23
+ for var in _ENV_VARS:
24
+ val = env.get(var)
25
+ evidence[f"env_var_{var}"] = "present" if val else "absent"
26
+ if val:
27
+ return DetectionResult(matched=True, evidence=evidence)
28
+
29
+ for info in proc_chain:
30
+ if "openclaw" in info.args.lower():
31
+ evidence["process_match"] = "openclaw-runtime"
32
+ return DetectionResult(matched=True, evidence=evidence)
33
+ evidence["process_match"] = "none"
34
+
35
+ # Config dir existence alone does NOT mean "currently running openclaw".
36
+ # It only indicates the user has installed it. Record for diagnostics only.
37
+ for cfg_dir in _CONFIG_DIRS:
38
+ if cfg_dir.exists():
39
+ evidence["config_dir"] = str(cfg_dir)
40
+ break
41
+ else:
42
+ evidence["config_dir"] = "not found"
43
+
44
+ return DetectionResult(matched=False, evidence=evidence)
45
+
46
+
47
+ def collect_environment(env: Mapping[str, str],
48
+ proc_chain: list[ProcessInfo],
49
+ workspace: Path) -> Dict[str, Any]:
50
+ data: Dict[str, Any] = {
51
+ "env_vars": {},
52
+ "config_files": {},
53
+ "version": None,
54
+ }
55
+
56
+ for var in _ENV_VARS:
57
+ if var in env:
58
+ data["env_vars"][var] = env[var]
59
+
60
+ for cfg_dir in _CONFIG_DIRS:
61
+ if not cfg_dir.exists():
62
+ continue
63
+ for filename in _CONFIG_FILENAMES:
64
+ path = cfg_dir / filename
65
+ if path.exists():
66
+ data["config_files"][filename] = read_with_desensitize(path)
67
+ # Try to extract version from config.json
68
+ version_path = cfg_dir / "config.json"
69
+ if version_path.exists():
70
+ cfg = read_json(version_path)
71
+ if isinstance(cfg, dict):
72
+ data["version"] = cfg.get("version")
73
+ break
74
+
75
+ return data
76
+
77
+
78
+ def _resolve_config_path(env: Mapping[str, str], workspace: Path) -> str:
79
+ """Resolve the stable config path for legacy fingerprint generation."""
80
+ config_path = env.get("OPENCLAW_CONFIG")
81
+ if config_path:
82
+ return str(Path(config_path).resolve()).replace("\\", "/")
83
+
84
+ for cfg_dir in _CONFIG_DIRS:
85
+ if cfg_dir.exists():
86
+ for filename in _CONFIG_FILENAMES:
87
+ path = cfg_dir / filename
88
+ if path.exists():
89
+ return str(path.resolve()).replace("\\", "/")
90
+ return str(cfg_dir.resolve()).replace("\\", "/")
91
+
92
+ return str(workspace.resolve()).replace("\\", "/")
93
+
94
+
95
+ def _identity_legacy(env: Mapping[str, str],
96
+ proc_chain: list[ProcessInfo],
97
+ workspace: Path) -> Dict[str, str]:
98
+ """Legacy fingerprint fields (sha256 + v0). Frozen — never change."""
99
+ return {"config_path": _resolve_config_path(env, workspace)}
100
+
101
+
102
+ def _identity_v1(env: Mapping[str, str],
103
+ proc_chain: list[ProcessInfo],
104
+ workspace: Path) -> Dict[str, str]:
105
+ """v1 fingerprint fields. Placeholder — will be refined in Phase B
106
+ after real environment sampling via diagnose.
107
+ """
108
+ # For Phase A, v1 uses the same stable field as legacy to avoid
109
+ # fp changes before we are ready to bump FP_CURRENT_VERSION.
110
+ return _identity_legacy(env, proc_chain, workspace)
111
+
112
+
113
+ STABLE_IDENTITY_BY_VERSION = {
114
+ "sha256": _identity_legacy,
115
+ "v0": _identity_legacy,
116
+ "v1": _identity_v1,
117
+ }
@@ -0,0 +1,111 @@
1
+ """Opencode framework detection and identity extraction.
2
+
3
+ Related design: docs/41.tools-designs/design-cybersecured-agent-cli.md
4
+
5
+ Detection signals (from diagnose sampling on macOS):
6
+ - Process chain contains "opencode" keyword
7
+ - Config dir ~/.config/opencode/ with opencode.json / oh-my-openagent.json
8
+ - No known env vars as of 2026-05-10
9
+ """
10
+
11
+ import os
12
+ from pathlib import Path
13
+ from typing import Any, Dict, Mapping
14
+
15
+ from ..infra.config_files import read_json, read_with_desensitize
16
+ from ._types import DetectionResult, ProcessInfo
17
+
18
+ NAME = "opencode"
19
+
20
+ _ENV_VARS: list[str] = ['OPENCODE', 'OPENCODE_RUN_ID', 'OPENCODE_PID', 'OPENCODE_PROCESS_ROLE']
21
+ _CONFIG_DIRS = [Path.home() / ".config" / "opencode"]
22
+ _CONFIG_FILENAMES = ["opencode.json"]
23
+
24
+
25
+ def detect(env: Mapping[str, str], proc_chain: list[ProcessInfo]) -> DetectionResult:
26
+ evidence: Dict[str, Any] = {}
27
+
28
+ for var in _ENV_VARS:
29
+ val = env.get(var)
30
+ evidence[f"env_var_{var}"] = "present" if val else "absent"
31
+ if val:
32
+ return DetectionResult(matched=True, evidence=evidence)
33
+
34
+ for info in proc_chain:
35
+ if "opencode" in info.args.lower():
36
+ evidence["process_match"] = "opencode-runtime"
37
+ return DetectionResult(matched=True, evidence=evidence)
38
+ evidence["process_match"] = "none"
39
+
40
+ # Config dir existence alone does NOT mean "currently running opencode".
41
+ for cfg_dir in _CONFIG_DIRS:
42
+ if cfg_dir.exists():
43
+ evidence["config_dir"] = str(cfg_dir)
44
+ break
45
+ else:
46
+ evidence["config_dir"] = "not found"
47
+
48
+ return DetectionResult(matched=False, evidence=evidence)
49
+
50
+
51
+ def collect_environment(env: Mapping[str, str],
52
+ proc_chain: list[ProcessInfo],
53
+ workspace: Path) -> Dict[str, Any]:
54
+ data: Dict[str, Any] = {
55
+ "env_vars": {},
56
+ "config_files": {},
57
+ "version": None,
58
+ }
59
+
60
+ for var in _ENV_VARS:
61
+ if var in env:
62
+ data["env_vars"][var] = env[var]
63
+
64
+ for cfg_dir in _CONFIG_DIRS:
65
+ if not cfg_dir.exists():
66
+ continue
67
+ for filename in _CONFIG_FILENAMES:
68
+ path = cfg_dir / filename
69
+ if path.exists():
70
+ data["config_files"][filename] = read_with_desensitize(path)
71
+ version_path = cfg_dir / "opencode.json"
72
+ if version_path.exists():
73
+ cfg = read_json(version_path)
74
+ if isinstance(cfg, dict):
75
+ data["version"] = cfg.get("version")
76
+ break
77
+
78
+ return data
79
+
80
+
81
+ def _resolve_config_path(env: Mapping[str, str], workspace: Path) -> str:
82
+ for cfg_dir in _CONFIG_DIRS:
83
+ if cfg_dir.exists():
84
+ for filename in _CONFIG_FILENAMES:
85
+ path = cfg_dir / filename
86
+ if path.exists():
87
+ return str(path.resolve()).replace("\\", "/")
88
+ return str(cfg_dir.resolve()).replace("\\", "/")
89
+
90
+ return str(workspace.resolve()).replace("\\", "/")
91
+
92
+
93
+ def _identity_legacy(env: Mapping[str, str],
94
+ proc_chain: list[ProcessInfo],
95
+ workspace: Path) -> Dict[str, str]:
96
+ """Legacy fingerprint fields (sha256 + v0). Frozen — never change."""
97
+ return {"config_path": _resolve_config_path(env, workspace)}
98
+
99
+
100
+ def _identity_v1(env: Mapping[str, str],
101
+ proc_chain: list[ProcessInfo],
102
+ workspace: Path) -> Dict[str, str]:
103
+ """v1 fingerprint fields. Placeholder — will be refined in Phase B."""
104
+ return _identity_legacy(env, proc_chain, workspace)
105
+
106
+
107
+ STABLE_IDENTITY_BY_VERSION = {
108
+ "sha256": _identity_legacy,
109
+ "v0": _identity_legacy,
110
+ "v1": _identity_v1,
111
+ }