holmesgpt 0.13.0__py3-none-any.whl → 0.13.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.

Potentially problematic release.


This version of holmesgpt might be problematic. Click here for more details.

Files changed (54) hide show
  1. holmes/__init__.py +1 -1
  2. holmes/common/env_vars.py +4 -0
  3. holmes/core/llm.py +3 -1
  4. holmes/core/tool_calling_llm.py +112 -11
  5. holmes/core/toolset_manager.py +1 -5
  6. holmes/core/tracing.py +1 -1
  7. holmes/main.py +7 -1
  8. holmes/plugins/prompts/_fetch_logs.jinja2 +4 -0
  9. holmes/plugins/runbooks/CLAUDE.md +85 -0
  10. holmes/plugins/runbooks/README.md +24 -0
  11. holmes/plugins/toolsets/bash/argocd/__init__.py +65 -0
  12. holmes/plugins/toolsets/bash/argocd/constants.py +120 -0
  13. holmes/plugins/toolsets/bash/aws/__init__.py +66 -0
  14. holmes/plugins/toolsets/bash/aws/constants.py +529 -0
  15. holmes/plugins/toolsets/bash/azure/__init__.py +56 -0
  16. holmes/plugins/toolsets/bash/azure/constants.py +339 -0
  17. holmes/plugins/toolsets/bash/bash_instructions.jinja2 +6 -7
  18. holmes/plugins/toolsets/bash/bash_toolset.py +47 -13
  19. holmes/plugins/toolsets/bash/common/bash_command.py +131 -0
  20. holmes/plugins/toolsets/bash/common/stringify.py +14 -1
  21. holmes/plugins/toolsets/bash/common/validators.py +91 -0
  22. holmes/plugins/toolsets/bash/docker/__init__.py +59 -0
  23. holmes/plugins/toolsets/bash/docker/constants.py +255 -0
  24. holmes/plugins/toolsets/bash/helm/__init__.py +61 -0
  25. holmes/plugins/toolsets/bash/helm/constants.py +92 -0
  26. holmes/plugins/toolsets/bash/kubectl/__init__.py +80 -79
  27. holmes/plugins/toolsets/bash/kubectl/constants.py +0 -14
  28. holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +38 -56
  29. holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +28 -76
  30. holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +39 -99
  31. holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +34 -15
  32. holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +1 -1
  33. holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +38 -77
  34. holmes/plugins/toolsets/bash/parse_command.py +106 -32
  35. holmes/plugins/toolsets/bash/utilities/__init__.py +0 -0
  36. holmes/plugins/toolsets/bash/utilities/base64_util.py +12 -0
  37. holmes/plugins/toolsets/bash/utilities/cut.py +12 -0
  38. holmes/plugins/toolsets/bash/utilities/grep/__init__.py +10 -0
  39. holmes/plugins/toolsets/bash/utilities/head.py +12 -0
  40. holmes/plugins/toolsets/bash/utilities/jq.py +79 -0
  41. holmes/plugins/toolsets/bash/utilities/sed.py +164 -0
  42. holmes/plugins/toolsets/bash/utilities/sort.py +15 -0
  43. holmes/plugins/toolsets/bash/utilities/tail.py +12 -0
  44. holmes/plugins/toolsets/bash/utilities/tr.py +57 -0
  45. holmes/plugins/toolsets/bash/utilities/uniq.py +12 -0
  46. holmes/plugins/toolsets/bash/utilities/wc.py +12 -0
  47. holmes/plugins/toolsets/prometheus/prometheus.py +42 -12
  48. holmes/utils/console/logging.py +6 -1
  49. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/METADATA +1 -1
  50. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/RECORD +53 -30
  51. holmes/plugins/toolsets/bash/grep/__init__.py +0 -52
  52. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/LICENSE.txt +0 -0
  53. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/WHEEL +0 -0
  54. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+ import fnmatch
2
3
  import re
3
4
  from typing import Union
4
5
 
@@ -22,3 +23,93 @@ def whitelist_validator(field_name: str, whitelisted_values: set[str]):
22
23
  return value
23
24
 
24
25
  return validate_value
26
+
27
+
28
+ def validate_command_and_operations(
29
+ command: str,
30
+ options: list[str],
31
+ allowed_commands: dict[str, dict],
32
+ denied_commands: dict[str, dict],
33
+ ) -> None:
34
+ """Validate that the command and operation combination is safe, with wildcard support."""
35
+
36
+ # Check denied commands first (including wildcards)
37
+ _check_denied_command_with_wildcards(
38
+ command=command, options=options, denied_commands=denied_commands
39
+ )
40
+
41
+ # Check allowed commands (including wildcards)
42
+ _check_allowed_command_with_wildcards(
43
+ command=command, options=options, allowed_commands=allowed_commands
44
+ )
45
+
46
+
47
+ def _check_options_against_denied_commands(
48
+ command: str, options: list[str], denied_commands: dict
49
+ ):
50
+ for idx, option in enumerate(options):
51
+ new_command = command + " " + option
52
+ for potential_command, children in denied_commands.items():
53
+ option_does_match = fnmatch.fnmatchcase(option, potential_command)
54
+ if option_does_match and children == {}:
55
+ raise ValueError(f"Command is blocked: {new_command}")
56
+ elif option_does_match:
57
+ _check_options_against_denied_commands(
58
+ command=new_command,
59
+ options=options[idx + 1 :],
60
+ denied_commands=children,
61
+ )
62
+
63
+
64
+ def _check_denied_command_with_wildcards(
65
+ command: str, options: list[str], denied_commands: dict[str, dict]
66
+ ) -> None:
67
+ # Check exact command match first
68
+ for potential_command, children in denied_commands.items():
69
+ command_does_match = fnmatch.fnmatchcase(command, potential_command)
70
+ if command_does_match and children == {}:
71
+ raise ValueError(f"Command is blocked: {command}")
72
+ elif command_does_match:
73
+ _check_options_against_denied_commands(
74
+ command=command, options=options, denied_commands=children
75
+ )
76
+
77
+
78
+ def _do_options_match_an_allowed_command(
79
+ command: str, options: list[str], allowed_commands: dict
80
+ ) -> bool:
81
+ for idx, option in enumerate(options):
82
+ new_command = command + " " + option
83
+ for potential_command, children in allowed_commands.items():
84
+ option_does_match = fnmatch.fnmatchcase(option, potential_command)
85
+ if option_does_match and children == {}:
86
+ return True
87
+ elif option_does_match:
88
+ is_allowed = _do_options_match_an_allowed_command(
89
+ command=new_command,
90
+ options=options[idx + 1 :],
91
+ allowed_commands=children,
92
+ )
93
+ if is_allowed:
94
+ return True
95
+
96
+ return False
97
+
98
+
99
+ def _check_allowed_command_with_wildcards(
100
+ command: str, options: list[str], allowed_commands: dict[str, dict]
101
+ ):
102
+ for potential_command, children in allowed_commands.items():
103
+ cursor_does_match = fnmatch.fnmatchcase(command, potential_command)
104
+ if cursor_does_match and children == {}:
105
+ return
106
+ elif cursor_does_match:
107
+ is_allowed = _do_options_match_an_allowed_command(
108
+ command=command, options=options, allowed_commands=children
109
+ )
110
+ if is_allowed:
111
+ return
112
+
113
+ raise ValueError(
114
+ f"Command is not in the allowlist: {command + ' ' + ' '.join(options)}"
115
+ )
@@ -0,0 +1,59 @@
1
+ import argparse
2
+ from typing import Any, Optional
3
+
4
+ from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
5
+ from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
6
+ from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
7
+ from holmes.plugins.toolsets.bash.common.validators import (
8
+ validate_command_and_operations,
9
+ )
10
+ from holmes.plugins.toolsets.bash.docker.constants import (
11
+ ALLOWED_DOCKER_COMMANDS,
12
+ DENIED_DOCKER_COMMANDS,
13
+ )
14
+
15
+
16
+ class DockerCommand(BashCommand):
17
+ def __init__(self):
18
+ super().__init__("docker")
19
+
20
+ def add_parser(self, parent_parser: Any):
21
+ docker_parser = parent_parser.add_parser(
22
+ "docker",
23
+ help="Docker Command Line Interface",
24
+ exit_on_error=False,
25
+ )
26
+
27
+ docker_parser.add_argument(
28
+ "command",
29
+ help="Docker command (e.g., ps, images, inspect)",
30
+ )
31
+
32
+ docker_parser.add_argument(
33
+ "options",
34
+ nargs=argparse.REMAINDER,
35
+ default=[],
36
+ help="Docker CLI subcommands, operations, and options",
37
+ )
38
+ return docker_parser
39
+
40
+ def validate_command(
41
+ self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
42
+ ) -> None:
43
+ if hasattr(command, "options"):
44
+ validate_command_and_operations(
45
+ command=command.command,
46
+ options=command.options,
47
+ allowed_commands=ALLOWED_DOCKER_COMMANDS,
48
+ denied_commands=DENIED_DOCKER_COMMANDS,
49
+ )
50
+
51
+ def stringify_command(
52
+ self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
53
+ ) -> str:
54
+ parts = ["docker", command.command]
55
+
56
+ if hasattr(command, "options") and command.options:
57
+ parts.extend(command.options)
58
+
59
+ return " ".join(escape_shell_args(parts))
@@ -0,0 +1,255 @@
1
+ ALLOWED_DOCKER_COMMANDS: dict[str, dict] = {
2
+ # Container management (read-only)
3
+ "ps": {},
4
+ "container": {
5
+ "ls": {},
6
+ "list": {},
7
+ "inspect": {},
8
+ "logs": {},
9
+ "stats": {},
10
+ "top": {},
11
+ "port": {},
12
+ "diff": {},
13
+ },
14
+ # Image management (read-only)
15
+ "images": {},
16
+ "image": {
17
+ "ls": {},
18
+ "list": {},
19
+ "inspect": {},
20
+ "history": {},
21
+ },
22
+ # System information
23
+ "version": {},
24
+ "info": {},
25
+ "system": {
26
+ "info": {},
27
+ "events": {},
28
+ "df": {},
29
+ },
30
+ # Network inspection
31
+ "network": {
32
+ "ls": {},
33
+ "list": {},
34
+ "inspect": {},
35
+ },
36
+ # Volume inspection
37
+ "volume": {
38
+ "ls": {},
39
+ "list": {},
40
+ "inspect": {},
41
+ },
42
+ # Registry operations (read-only)
43
+ "search": {},
44
+ # Plugin inspection
45
+ "plugin": {
46
+ "ls": {},
47
+ "list": {},
48
+ "inspect": {},
49
+ },
50
+ # Node information (Swarm read-only)
51
+ "node": {
52
+ "ls": {},
53
+ "list": {},
54
+ "inspect": {},
55
+ },
56
+ # Service information (Swarm read-only)
57
+ "service": {
58
+ "ls": {},
59
+ "list": {},
60
+ "inspect": {},
61
+ "logs": {},
62
+ "ps": {},
63
+ },
64
+ # Stack information (read-only)
65
+ "stack": {
66
+ "ls": {},
67
+ "list": {},
68
+ "ps": {},
69
+ "services": {},
70
+ },
71
+ # Secret inspection (read-only metadata)
72
+ "secret": {
73
+ "ls": {},
74
+ "list": {},
75
+ "inspect": {},
76
+ },
77
+ # Config inspection (read-only)
78
+ "config": {
79
+ "ls": {},
80
+ "list": {},
81
+ "inspect": {},
82
+ },
83
+ # Context information
84
+ "context": {
85
+ "ls": {},
86
+ "list": {},
87
+ "inspect": {},
88
+ "show": {},
89
+ },
90
+ }
91
+
92
+ # Blocked Docker operations (state-modifying or dangerous)
93
+ DENIED_DOCKER_COMMANDS: dict[str, dict] = {
94
+ # Container lifecycle operations
95
+ "run": {},
96
+ "create": {},
97
+ "start": {},
98
+ "stop": {},
99
+ "restart": {},
100
+ "pause": {},
101
+ "unpause": {},
102
+ "kill": {},
103
+ "remove": {},
104
+ "rm": {},
105
+ "exec": {},
106
+ "attach": {},
107
+ "cp": {},
108
+ "commit": {},
109
+ "update": {},
110
+ "rename": {},
111
+ "wait": {},
112
+ "container": {
113
+ "create": {},
114
+ "start": {},
115
+ "stop": {},
116
+ "restart": {},
117
+ "pause": {},
118
+ "unpause": {},
119
+ "kill": {},
120
+ "remove": {},
121
+ "rm": {},
122
+ "exec": {},
123
+ "attach": {},
124
+ "cp": {},
125
+ "commit": {},
126
+ "update": {},
127
+ "rename": {},
128
+ "wait": {},
129
+ "prune": {},
130
+ "export": {}, # Can exfiltrate full filesystem
131
+ },
132
+ # Image operations
133
+ "build": {},
134
+ "pull": {},
135
+ "push": {},
136
+ "tag": {},
137
+ "untag": {},
138
+ "rmi": {},
139
+ "load": {},
140
+ "import": {},
141
+ "save": {},
142
+ "image": {
143
+ "build": {},
144
+ "pull": {},
145
+ "push": {},
146
+ "tag": {},
147
+ "untag": {},
148
+ "rm": {},
149
+ "remove": {},
150
+ "load": {},
151
+ "import": {},
152
+ "save": {},
153
+ "prune": {},
154
+ },
155
+ # Network operations
156
+ "network": {
157
+ "create": {},
158
+ "rm": {},
159
+ "remove": {},
160
+ "connect": {},
161
+ "disconnect": {},
162
+ "prune": {},
163
+ },
164
+ # Volume operations
165
+ "volume": {
166
+ "create": {},
167
+ "rm": {},
168
+ "remove": {},
169
+ "prune": {},
170
+ },
171
+ # System operations
172
+ "system": {
173
+ "prune": {},
174
+ },
175
+ # Registry operations
176
+ "login": {},
177
+ "logout": {},
178
+ # Plugin operations
179
+ "plugin": {
180
+ "install": {},
181
+ "enable": {},
182
+ "disable": {},
183
+ "upgrade": {},
184
+ "rm": {},
185
+ "remove": {},
186
+ "push": {},
187
+ "create": {},
188
+ "set": {},
189
+ },
190
+ # Swarm operations
191
+ "swarm": {
192
+ "init": {},
193
+ "join": {},
194
+ "leave": {},
195
+ "update": {},
196
+ "join-token": {},
197
+ "unlock": {},
198
+ "unlock-key": {},
199
+ },
200
+ # Node operations
201
+ "node": {
202
+ "update": {},
203
+ "demote": {},
204
+ "promote": {},
205
+ "rm": {},
206
+ "remove": {},
207
+ },
208
+ # Service operations
209
+ "service": {
210
+ "create": {},
211
+ "update": {},
212
+ "scale": {},
213
+ "rm": {},
214
+ "remove": {},
215
+ "rollback": {},
216
+ },
217
+ # Stack operations
218
+ "stack": {
219
+ "deploy": {},
220
+ "rm": {},
221
+ "remove": {},
222
+ },
223
+ # Secret operations
224
+ "secret": {
225
+ "create": {},
226
+ "rm": {},
227
+ "remove": {},
228
+ },
229
+ # Config operations
230
+ "config": {
231
+ "create": {},
232
+ "rm": {},
233
+ "remove": {},
234
+ },
235
+ # Context operations
236
+ "context": {
237
+ "create": {},
238
+ "rm": {},
239
+ "remove": {},
240
+ "update": {},
241
+ "use": {},
242
+ "export": {},
243
+ "import": {},
244
+ },
245
+ # Checkpoint operations
246
+ "checkpoint": {},
247
+ # Buildx operations
248
+ "buildx": {},
249
+ # Compose operations
250
+ "compose": {},
251
+ # Trust operations
252
+ "trust": {},
253
+ # Manifest operations
254
+ "manifest": {},
255
+ }
@@ -0,0 +1,61 @@
1
+ import argparse
2
+ from typing import Any, Optional
3
+
4
+ from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
5
+ from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
6
+ from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
7
+ from holmes.plugins.toolsets.bash.common.validators import (
8
+ validate_command_and_operations,
9
+ )
10
+ from holmes.plugins.toolsets.bash.helm.constants import (
11
+ ALLOWED_HELM_COMMANDS,
12
+ DENIED_HELM_COMMANDS,
13
+ )
14
+
15
+
16
+ class HelmCommand(BashCommand):
17
+ def __init__(self):
18
+ super().__init__("helm")
19
+
20
+ def add_parser(self, parent_parser: Any):
21
+ """Create Helm CLI parser with safe command validation."""
22
+ helm_parser = parent_parser.add_parser(
23
+ "helm", help="Helm Package Manager for Kubernetes", exit_on_error=False
24
+ )
25
+
26
+ # Add command subparser
27
+ helm_parser.add_argument(
28
+ "command", help="Helm command (e.g., list, get, status, show)"
29
+ )
30
+
31
+ # Capture remaining arguments
32
+ helm_parser.add_argument(
33
+ "options",
34
+ nargs=argparse.REMAINDER,
35
+ default=[],
36
+ help="Helm CLI subcommands, operations, and options",
37
+ )
38
+ return helm_parser
39
+
40
+ def validate_command(
41
+ self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
42
+ ) -> None:
43
+ if hasattr(command, "options"):
44
+ validate_command_and_operations(
45
+ command=command.command,
46
+ options=command.options,
47
+ allowed_commands=ALLOWED_HELM_COMMANDS,
48
+ denied_commands=DENIED_HELM_COMMANDS,
49
+ )
50
+
51
+ def stringify_command(
52
+ self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
53
+ ) -> str:
54
+ """Convert parsed Helm command back to safe command string."""
55
+ # Build command parts
56
+ parts = ["helm", command.command]
57
+
58
+ if hasattr(command, "options") and command.options:
59
+ parts.extend(command.options)
60
+
61
+ return " ".join(escape_shell_args(parts))
@@ -0,0 +1,92 @@
1
+ ALLOWED_HELM_COMMANDS: dict[str, dict] = {
2
+ # Release management (read-only)
3
+ "list": {},
4
+ "ls": {},
5
+ "get": {
6
+ "all": {},
7
+ "hooks": {},
8
+ "manifest": {},
9
+ "notes": {},
10
+ "values": {},
11
+ "metadata": {},
12
+ },
13
+ "status": {},
14
+ "history": {},
15
+ "diff": {
16
+ "upgrade": {},
17
+ "rollback": {},
18
+ "revision": {},
19
+ "release": {},
20
+ "version": {},
21
+ },
22
+ # Chart operations (read-only)
23
+ "show": {
24
+ "all": {},
25
+ "chart": {},
26
+ "readme": {},
27
+ "values": {},
28
+ "crds": {},
29
+ },
30
+ "inspect": {},
31
+ "search": {
32
+ "repo": {},
33
+ },
34
+ "lint": {},
35
+ "verify": {},
36
+ "dependency": {"list": {}},
37
+ # Repository operations (read-only)
38
+ "repo": {"list": {}},
39
+ # Help and information
40
+ "help": {},
41
+ "version": {},
42
+ # Plugin operations (read-only)
43
+ "plugin": {
44
+ "list": {},
45
+ },
46
+ # Completion
47
+ "completion": {},
48
+ }
49
+
50
+ # Blocked Helm operations (state-modifying or dangerous)
51
+ DENIED_HELM_COMMANDS: dict[str, dict] = {
52
+ # Release lifecycle operations
53
+ "install": {},
54
+ "upgrade": {},
55
+ "uninstall": {},
56
+ "delete": {},
57
+ "rollback": {},
58
+ "test": {},
59
+ # Repository management
60
+ "repo": {
61
+ "add": {},
62
+ "remove": {},
63
+ "rm": {},
64
+ "update": {},
65
+ "index": {},
66
+ },
67
+ # Chart packaging and publishing
68
+ "create": {},
69
+ "package": {},
70
+ "push": {},
71
+ "pull": {},
72
+ "fetch": {},
73
+ "dependency": {
74
+ "update": {},
75
+ "build": {},
76
+ },
77
+ # Plugin management
78
+ "plugin": {
79
+ "install": {},
80
+ "uninstall": {},
81
+ "update": {},
82
+ },
83
+ # Registry operations
84
+ "registry": {},
85
+ # Environment modification
86
+ "env": {},
87
+ # Configuration modification
88
+ "config": {},
89
+ # Mapkubeapis operations
90
+ "mapkubeapis": {},
91
+ "template": {},
92
+ }