xsoar-cli 1.0.3__tar.gz → 1.0.4__tar.gz

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 xsoar-cli might be problematic. Click here for more details.

Files changed (93) hide show
  1. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/.github/workflows/release.yml +2 -31
  2. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/PKG-INFO +1 -1
  3. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/__about__.py +1 -1
  4. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/case/commands.py +4 -4
  5. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/config/commands.py +12 -2
  6. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/manifest/commands.py +5 -5
  7. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/pack/commands.py +2 -2
  8. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/plugins/README.md +22 -22
  9. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/utilities.py +27 -1
  10. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/.github/workflows/pull-request-open.yml +0 -0
  11. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/.gitignore +0 -0
  12. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/CONTRIBUTING.md +0 -0
  13. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/LICENSE.txt +0 -0
  14. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/README.md +0 -0
  15. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/examples/README.md +0 -0
  16. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/examples/advanced_plugin.py +0 -0
  17. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/pyproject.toml +0 -0
  18. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/requirements.txt +0 -0
  19. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/requirements_dev.txt +0 -0
  20. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/__init__.py +0 -0
  21. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/case/README.md +0 -0
  22. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/case/__init__.py +0 -0
  23. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/cli.py +0 -0
  24. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/config/README.md +0 -0
  25. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/config/__init__.py +0 -0
  26. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/graph/README.md +0 -0
  27. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/graph/__init__.py +0 -0
  28. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/graph/commands.py +0 -0
  29. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/manifest/README.md +0 -0
  30. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/manifest/__init__.py +0 -0
  31. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/pack/README.md +0 -0
  32. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/pack/__init__.py +0 -0
  33. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/playbook/README.md +0 -0
  34. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/playbook/__init__.py +0 -0
  35. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/playbook/commands.py +0 -0
  36. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/plugins/__init__.py +0 -0
  37. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/plugins/commands.py +0 -0
  38. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/src/xsoar_cli/plugins/manager.py +0 -0
  39. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/__init__.py +0 -0
  40. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/conftest.py +0 -0
  41. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_base.py +0 -0
  42. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_case.py +0 -0
  43. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_config.py +0 -0
  44. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Download/playbook-empty.yml +0 -0
  45. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonPlaybooks/.pack-ignore +0 -0
  46. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonPlaybooks/.secrets-ignore +0 -0
  47. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonPlaybooks/Author_image.png +0 -0
  48. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonPlaybooks/Playbooks/GenericPlaybook.yml +0 -0
  49. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonPlaybooks/README.md +0 -0
  50. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonPlaybooks/pack_metadata.json +0 -0
  51. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/.pack-ignore +0 -0
  52. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/.secrets-ignore +0 -0
  53. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/Author_image.png +0 -0
  54. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/README.md +0 -0
  55. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/Scripts/GenericScript/GenericScript.py +0 -0
  56. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/Scripts/GenericScript/GenericScript.yml +0 -0
  57. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/Scripts/GenericScript/README.md +0 -0
  58. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_CommonScripts/pack_metadata.json +0 -0
  59. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/.pack-ignore +0 -0
  60. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/.secrets-ignore +0 -0
  61. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Author_image.png +0 -0
  62. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Playbooks/EDR_InitialTriage.yml +0 -0
  63. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/README.md +0 -0
  64. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/EDR_FetchFile.py +0 -0
  65. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/EDR_FetchFile.yml +0 -0
  66. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/README.md +0 -0
  67. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_Triage/EDR_Triage.py +0 -0
  68. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_Triage/EDR_Triage.yml +0 -0
  69. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_Triage/README.md +0 -0
  70. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/LegacyItem.py +0 -0
  71. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/LegacyItem.yml +0 -0
  72. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/README.md +0 -0
  73. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/test_data/basescript-dummy.json +0 -0
  74. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_EDR/pack_metadata.json +0 -0
  75. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_Layouts/.pack-ignore +0 -0
  76. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_Layouts/.secrets-ignore +0 -0
  77. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_Layouts/Author_image.png +0 -0
  78. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_Layouts/Layouts/layoutscontainer-GenericLayout.json +0 -0
  79. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_Layouts/README.md +0 -0
  80. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/MyOrg_Layouts/pack_metadata.json +0 -0
  81. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/Packs/Not_applicable/Playbooks/Empty.yml +0 -0
  82. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/manifest_base.json +0 -0
  83. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/manifest_invalid.json +0 -0
  84. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/manifest_with_pack_not_on_server.json +0 -0
  85. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/server_base_response.json +0 -0
  86. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/server_base_response_missing_one_pack.json +0 -0
  87. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/server_base_response_with_updates.json +0 -0
  88. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_data/server_base_response_with_updates_and_one_extra.json +0 -0
  89. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_graph.py +0 -0
  90. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_manifest.py +0 -0
  91. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_pack.py +0 -0
  92. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_playbook.py +0 -0
  93. {xsoar_cli-1.0.3 → xsoar_cli-1.0.4}/tests/test_plugins.py +0 -0
@@ -1,12 +1,8 @@
1
1
  name: release
2
2
 
3
3
  on:
4
- push:
5
- tags:
6
- - "[0-9]+.[0-9]+.[0-9]+"
7
- - "[0-9]+.[0-9]+.[0-9]+a[0-9]+"
8
- - "[0-9]+.[0-9]+.[0-9]+b[0-9]+"
9
- - "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
4
+ release:
5
+ types: [published]
10
6
 
11
7
  env:
12
8
  PACKAGE_NAME: "xsoar-cli"
@@ -117,28 +113,3 @@ jobs:
117
113
 
118
114
  - name: Publish distribution to PyPI
119
115
  uses: pypa/gh-action-pypi-publish@release/v1
120
-
121
- github_release:
122
- name: Create GitHub Release
123
- needs: [setup_and_build, details]
124
- runs-on: ubuntu-latest
125
- permissions:
126
- contents: write
127
- steps:
128
- - name: Checkout Code
129
- uses: actions/checkout@v3
130
- with:
131
- fetch-depth: 0
132
-
133
- - name: Download artifacts
134
- uses: actions/download-artifact@v4
135
- with:
136
- name: dist
137
- path: dist/
138
-
139
- - name: Create GitHub Release
140
- id: create_release
141
- env:
142
- GH_TOKEN: ${{ github.token }}
143
- run: |
144
- gh release create ${{ needs.details.outputs.tag_name }} dist/* --title ${{ needs.details.outputs.tag_name }} --generate-notes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xsoar-cli
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Project-URL: Documentation, https://github.com/tlium/xsoar-cli#readme
5
5
  Project-URL: Issues, https://github.com/tlium/xsoar-cli/issues
6
6
  Project-URL: Source, https://github.com/tlium/xsoar-cli
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Torbjørn Lium <torben@lium.org>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "1.0.3"
4
+ __version__ = "1.0.4"
@@ -20,7 +20,7 @@ def case() -> None:
20
20
  @click.pass_context
21
21
  @load_config
22
22
  def get(ctx: click.Context, casenumber: int, environment: str) -> None:
23
- xsoar_client: Client = ctx.obj["server_envs"][environment]
23
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
24
24
  response = xsoar_client.get_case(casenumber)
25
25
  if response["total"] == 0 and not response["data"]:
26
26
  click.echo(f"Cannot find case ID {casenumber}")
@@ -40,7 +40,7 @@ def clone(ctx: click.Context, casenumber: int, source: str, dest: str) -> None:
40
40
  if not valid_envs:
41
41
  click.echo(f"Error: cannot find environments {source} and/or {dest} in config")
42
42
  ctx.exit(1)
43
- xsoar_source_client: Client = ctx.obj["server_envs"][source]
43
+ xsoar_source_client: Client = ctx.obj["server_envs"][source]["xsoar_client"]
44
44
  results = xsoar_source_client.get_case(casenumber)
45
45
  data = results["data"][0]
46
46
  # Dbot mirror info is irrelevant. This will be added again if applicable by XSOAR after ticket creation in dev.
@@ -57,7 +57,7 @@ def clone(ctx: click.Context, casenumber: int, source: str, dest: str) -> None:
57
57
  # Ensure that playbooks run immediately when the case is created
58
58
  data["createInvestigation"] = True
59
59
 
60
- xsoar_dest_client: Client = ctx.obj["server_envs"][dest]
60
+ xsoar_dest_client: Client = ctx.obj["server_envs"][dest]["xsoar_client"]
61
61
  case_data = xsoar_dest_client.create_case(data=data)
62
62
  click.echo(json.dumps(case_data, indent=4))
63
63
 
@@ -71,7 +71,7 @@ def clone(ctx: click.Context, casenumber: int, source: str, dest: str) -> None:
71
71
  @load_config
72
72
  def create(ctx: click.Context, environment: str, casetype: str, name: str, details: str) -> None:
73
73
  """Creates a new case in XSOAR. If invalid case type is specified as a command option, XSOAR will default to using Unclassified."""
74
- xsoar_client: Client = ctx.obj["server_envs"][environment]
74
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
75
75
  if not casetype:
76
76
  casetype = ctx.obj["default_new_case_type"]
77
77
  data = {
@@ -8,7 +8,13 @@ if TYPE_CHECKING:
8
8
 
9
9
  import contextlib
10
10
 
11
- from xsoar_cli.utilities import get_config_file_contents, get_config_file_path, get_config_file_template_contents, load_config
11
+ from xsoar_cli.utilities import (
12
+ fail_if_no_artifacts_provider,
13
+ get_config_file_contents,
14
+ get_config_file_path,
15
+ get_config_file_template_contents,
16
+ load_config,
17
+ )
12
18
 
13
19
 
14
20
  @click.group(help="Create/validate etc")
@@ -35,6 +41,7 @@ def show(ctx: click.Context, masked: bool) -> None:
35
41
  @click.option("--only-test-environment", default=None, show_default=True, help="Environment as defined in config file")
36
42
  @click.pass_context
37
43
  @load_config
44
+ @fail_if_no_artifacts_provider
38
45
  def validate(ctx: click.Context, only_test_environment: str) -> None:
39
46
  """Validates that the configuration file is JSON and tests connectivity for each XSOAR Client environment defined."""
40
47
  return_code = 0
@@ -44,7 +51,7 @@ def validate(ctx: click.Context, only_test_environment: str) -> None:
44
51
  # what the user specified in option
45
52
  continue
46
53
  click.echo(f'Testing "{server_env}" environment...', nl=False)
47
- xsoar_client: Client = ctx.obj["server_envs"][server_env]
54
+ xsoar_client: Client = ctx.obj["server_envs"][server_env]["xsoar_client"]
48
55
  try:
49
56
  xsoar_client.test_connectivity()
50
57
  except ConnectionError as ex:
@@ -53,6 +60,9 @@ def validate(ctx: click.Context, only_test_environment: str) -> None:
53
60
  return_code = 1
54
61
  continue
55
62
  click.echo("OK")
63
+ if ctx.obj["default_environment"] not in ctx.obj["server_envs"]:
64
+ click.echo(f'Error: default environment "{ctx.obj["default_environment"]}" not found in server config.')
65
+ return_code = 1
56
66
  ctx.exit(return_code)
57
67
 
58
68
 
@@ -46,7 +46,7 @@ def manifest() -> None:
46
46
  @load_config
47
47
  def update(ctx: click.Context, environment: str, manifest: str) -> None:
48
48
  """Update manifest on disk with latest available content pack versions."""
49
- xsoar_client: Client = ctx.obj["server_envs"][environment]
49
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
50
50
  manifest_data = load_manifest(manifest)
51
51
  click.echo("Fetching outdated packs from XSOAR server. This may take a minute...", nl=False)
52
52
  results = xsoar_client.get_outdated_packs()
@@ -89,7 +89,7 @@ def update(ctx: click.Context, environment: str, manifest: str) -> None:
89
89
  def validate(ctx: click.Context, environment: str, manifest: str) -> None:
90
90
  """Validate manifest JSON and all pack availability. Validates upstream pack availability by doing HTTP CONNECT.
91
91
  Custom pack availability is implementation dependant."""
92
- xsoar_client: Client = ctx.obj["server_envs"][environment]
92
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
93
93
  manifest_data = load_manifest(manifest)
94
94
  click.echo("Manifest is valid JSON")
95
95
  keys = ["custom_packs", "marketplace_packs"]
@@ -129,7 +129,7 @@ def validate(ctx: click.Context, environment: str, manifest: str) -> None:
129
129
  def diff(ctx: click.Context, manifest: str, environment: str) -> None:
130
130
  """Prints out the differences (if any) between what is defined in the xsoar_config.json manifest and what is actually
131
131
  installed on the XSOAR server."""
132
- xsoar_client: Client = ctx.obj["server_envs"][environment]
132
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
133
133
  manifest_data = load_manifest(manifest)
134
134
  installed_packs = xsoar_client.get_installed_packs()
135
135
  all_good = True
@@ -170,7 +170,7 @@ def deploy(ctx: click.Context, environment: str, manifest: str, verbose: bool, y
170
170
  if not should_continue:
171
171
  ctx.exit()
172
172
 
173
- xsoar_client: Client = ctx.obj["server_envs"][environment]
173
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
174
174
  manifest_data = load_manifest(manifest)
175
175
  click.echo("Fetching installed packs...", err=True)
176
176
  installed_packs = xsoar_client.get_installed_packs()
@@ -191,7 +191,7 @@ def deploy(ctx: click.Context, environment: str, manifest: str, verbose: bool, y
191
191
  # Print message that install is skipped
192
192
 
193
193
  if none_installed:
194
- click.echo("No packs to install. XSOAR server is up to date with manifest.")
194
+ click.echo("No packs to install. All packs and versions in manifest is already installed on XSOAR server.")
195
195
 
196
196
 
197
197
  manifest.add_command(deploy)
@@ -22,7 +22,7 @@ def pack(ctx: click.Context) -> None:
22
22
  @load_config
23
23
  def delete(ctx: click.Context, environment: str, pack_id: str) -> None:
24
24
  """Deletes a content pack from the XSOAR server."""
25
- xsoar_client: Client = ctx.obj["server_envs"][environment]
25
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
26
26
  if not xsoar_client.is_installed(pack_id=pack_id):
27
27
  click.echo(f"Pack ID {pack_id} is not installed. Cannot delete.")
28
28
  sys.exit(1)
@@ -36,7 +36,7 @@ def delete(ctx: click.Context, environment: str, pack_id: str) -> None:
36
36
  @load_config
37
37
  def get_outdated(ctx: click.Context, environment: str) -> None:
38
38
  """Prints out a list of outdated content packs."""
39
- xsoar_client: Client = ctx.obj["server_envs"][environment]
39
+ xsoar_client: Client = ctx.obj["server_envs"][environment]["xsoar_client"]
40
40
  click.echo("Fetching outdated packs. This may take a little while...", err=True)
41
41
  outdated_packs = xsoar_client.get_outdated_packs()
42
42
  if not outdated_packs:
@@ -40,20 +40,20 @@ class MyPlugin(XSOARPlugin):
40
40
  @property
41
41
  def name(self) -> str:
42
42
  return "myplugin"
43
-
44
- @property
43
+
44
+ @property
45
45
  def version(self) -> str:
46
46
  return "1.0.0"
47
-
47
+
48
48
  @property
49
49
  def description(self) -> str:
50
50
  return "My custom plugin"
51
-
51
+
52
52
  def get_command(self) -> click.Command:
53
53
  @click.command(help="My custom command")
54
54
  def mycommand():
55
55
  click.echo("Hello from my plugin!")
56
-
56
+
57
57
  return mycommand
58
58
  ```
59
59
 
@@ -75,23 +75,23 @@ class MyCustomPlugin(XSOARPlugin):
75
75
  @property
76
76
  def name(self) -> str:
77
77
  return "mycustom"
78
-
78
+
79
79
  @property
80
80
  def version(self) -> str:
81
81
  return "1.0.0"
82
-
82
+
83
83
  @property
84
84
  def description(self) -> str:
85
85
  return "A custom plugin with multiple commands"
86
-
86
+
87
87
  def get_command(self) -> click.Command:
88
88
  """Return the main command group for this plugin."""
89
-
89
+
90
90
  @click.group(help="My custom commands")
91
91
  def mycustom():
92
92
  """Main command group for my custom plugin."""
93
93
  pass
94
-
94
+
95
95
  @click.command(help="Greet someone")
96
96
  @click.option("--name", default="World", help="Name to greet")
97
97
  @click.option("--times", default=1, help="Number of times to greet")
@@ -99,7 +99,7 @@ class MyCustomPlugin(XSOARPlugin):
99
99
  """Greet someone multiple times."""
100
100
  for i in range(times):
101
101
  click.echo(f"Hello, {name}!")
102
-
102
+
103
103
  @click.command(help="Show current status")
104
104
  @click.option("--verbose", "-v", is_flag=True, help="Verbose output")
105
105
  def status(verbose: bool):
@@ -108,7 +108,7 @@ class MyCustomPlugin(XSOARPlugin):
108
108
  if verbose:
109
109
  click.echo(f"Description: {self.description}")
110
110
  click.echo("Status: Active")
111
-
111
+
112
112
  @click.command(help="Process a file")
113
113
  @click.argument("filename", type=click.Path(exists=True))
114
114
  @click.option("--output", "-o", help="Output file")
@@ -118,18 +118,18 @@ class MyCustomPlugin(XSOARPlugin):
118
118
  if output:
119
119
  click.echo(f"Output will be saved to: {output}")
120
120
  # Your processing logic here
121
-
121
+
122
122
  # Add commands to the group
123
123
  mycustom.add_command(greet)
124
124
  mycustom.add_command(status)
125
125
  mycustom.add_command(process)
126
-
126
+
127
127
  return mycustom
128
-
128
+
129
129
  def initialize(self):
130
130
  """Initialize the plugin."""
131
131
  click.echo("My custom plugin initialized!")
132
-
132
+
133
133
  def cleanup(self):
134
134
  """Cleanup when the plugin is unloaded."""
135
135
  pass
@@ -176,7 +176,7 @@ from xsoar_cli.utilities import load_config
176
176
  @load_config
177
177
  def my_command(ctx: click.Context):
178
178
  # Access XSOAR client
179
- xsoar_client = ctx.obj["server_envs"]["dev"]
179
+ xsoar_client = ctx.obj["server_envs"]["dev"]["xsoar_client"]
180
180
  # Use the client...
181
181
  ```
182
182
 
@@ -284,11 +284,11 @@ class MyPlugin(XSOARPlugin):
284
284
  @click.group()
285
285
  def myplugin():
286
286
  pass
287
-
287
+
288
288
  @click.command()
289
289
  def case(): # ✅ Namespaced as 'myplugin case'
290
290
  click.echo("My case command")
291
-
291
+
292
292
  myplugin.add_command(case)
293
293
  return myplugin
294
294
  ```
@@ -297,7 +297,7 @@ class MyPlugin(XSOARPlugin):
297
297
 
298
298
  These command names are reserved by the core CLI:
299
299
  - `case` - Case/incident management
300
- - `config` - Configuration management
300
+ - `config` - Configuration management
301
301
  - `graph` - Dependency graphs
302
302
  - `manifest` - Manifest operations
303
303
  - `pack` - Content pack operations
@@ -330,7 +330,7 @@ def get_command(self) -> click.Command:
330
330
  except Exception as e:
331
331
  click.echo(f"Error: {e}", err=True)
332
332
  raise click.Abort()
333
-
333
+
334
334
  return mycommand
335
335
  ```
336
336
 
@@ -430,4 +430,4 @@ except ImportError:
430
430
 
431
431
  You can share plugins by simply sharing the Python file. Users can place it in their plugins directory and it will be automatically discovered.
432
432
 
433
- For more complex plugins, consider packaging them as proper Python packages that install the plugin file automatically.
433
+ For more complex plugins, consider packaging them as proper Python packages that install the plugin file automatically.
@@ -1,3 +1,4 @@
1
+ import contextlib
1
2
  import json
2
3
  from collections.abc import Callable
3
4
  from functools import update_wrapper
@@ -75,6 +76,29 @@ def load_config(f: Callable) -> Callable:
75
76
  return update_wrapper(wrapper, f)
76
77
 
77
78
 
79
+ def fail_if_no_artifacts_provider(f: Callable) -> Callable:
80
+ """
81
+ This function is only to be used as a decorator for various xsoar-cli subcommands, and only AFTER the load_config decorator has been called.
82
+ The intention is to fail gracefully if any subcommand is executed which requires an artifacts provider."
83
+ """
84
+
85
+ @click.pass_context
86
+ def wrapper(ctx: click.Context, *args, **kwargs) -> Callable: # noqa: ANN002, ANN003
87
+ if "environment" in ctx.params: # noqa: SIM108
88
+ key = ctx.params["environment"]
89
+ else:
90
+ key = ctx.obj["default_environment"]
91
+
92
+ with contextlib.suppress(KeyError):
93
+ location = ctx.obj["server_envs"][key].get("artifacts_location", None)
94
+ if not location:
95
+ click.echo("Command requires artifacts repository, but no artifacts_location defined in config.")
96
+ ctx.exit(1)
97
+ return ctx.invoke(f, *args, **kwargs)
98
+
99
+ return update_wrapper(wrapper, f)
100
+
101
+
78
102
  def parse_config(config: dict, ctx: click.Context) -> None:
79
103
  # Set the two XSOAR client objects in Click Context for use in later functions
80
104
  ctx.obj = {}
@@ -83,7 +107,8 @@ def parse_config(config: dict, ctx: click.Context) -> None:
83
107
  ctx.obj["default_new_case_type"] = config["default_new_case_type"]
84
108
  ctx.obj["server_envs"] = {}
85
109
  for key in config["server_config"]:
86
- ctx.obj["server_envs"][key] = Client(
110
+ ctx.obj["server_envs"][key] = {}
111
+ ctx.obj["server_envs"][key]["xsoar_client"] = Client(
87
112
  api_token=config["server_config"][key]["api_token"],
88
113
  server_url=config["server_config"][key]["base_url"],
89
114
  verify_ssl=config["server_config"][key]["verify_ssl"],
@@ -93,3 +118,4 @@ def parse_config(config: dict, ctx: click.Context) -> None:
93
118
  artifacts_location=config["server_config"][key].get("artifacts_location", None),
94
119
  s3_bucket_name=config["server_config"][key].get("s3_bucket_name", None),
95
120
  )
121
+ ctx.obj["server_envs"][key]["artifacts_location"] = config["server_config"][key].get("artifacts_location", None)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes