xsoar-cli 1.3.0__tar.gz → 1.3.2__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.
Files changed (103) hide show
  1. xsoar_cli-1.3.2/.github/workflows/main.yml +22 -0
  2. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/.github/workflows/pull-request-open.yml +18 -7
  3. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/CONTRIBUTING.md +38 -28
  4. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/PKG-INFO +29 -8
  5. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/README.md +28 -7
  6. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/_version.py +2 -2
  7. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/pyproject.toml +6 -0
  8. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/cli.py +4 -0
  9. xsoar_cli-1.3.2/src/xsoar_cli/integration/commands.py +55 -0
  10. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/playbook/commands.py +3 -1
  11. xsoar_cli-1.3.2/src/xsoar_cli/rbac/commands.py +72 -0
  12. xsoar_cli-1.3.2/tests/test_connection_errors.py +129 -0
  13. xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_Layouts/Author_image.png +0 -0
  14. xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_Layouts/README.md +0 -0
  15. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/uv.lock +243 -83
  16. xsoar_cli-1.3.0/requirements.txt +0 -4
  17. xsoar_cli-1.3.0/requirements_dev.txt +0 -4
  18. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/.github/workflows/release.yml +0 -0
  19. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/.gitignore +0 -0
  20. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/CLAUDE.md +0 -0
  21. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/LICENSE.txt +0 -0
  22. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/__about__.py +0 -0
  23. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/__init__.py +0 -0
  24. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/case/README.md +0 -0
  25. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/case/__init__.py +0 -0
  26. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/case/commands.py +0 -0
  27. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/config/README.md +0 -0
  28. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/config/__init__.py +0 -0
  29. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/config/commands.py +0 -0
  30. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/configuration.py +0 -0
  31. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/connection_errors.py +0 -0
  32. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/graph/README.md +0 -0
  33. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/graph/__init__.py +0 -0
  34. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/graph/commands.py +0 -0
  35. {xsoar_cli-1.3.0/src/xsoar_cli/manifest → xsoar_cli-1.3.2/src/xsoar_cli/integration}/__init__.py +0 -0
  36. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/log.py +0 -0
  37. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/manifest/README.md +0 -0
  38. {xsoar_cli-1.3.0/src/xsoar_cli/pack → xsoar_cli-1.3.2/src/xsoar_cli/manifest}/__init__.py +0 -0
  39. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/manifest/commands.py +0 -0
  40. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/pack/README.md +0 -0
  41. {xsoar_cli-1.3.0/src/xsoar_cli/playbook → xsoar_cli-1.3.2/src/xsoar_cli/pack}/__init__.py +0 -0
  42. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/pack/commands.py +0 -0
  43. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/playbook/README.md +0 -0
  44. /xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_CommonPlaybooks/.pack-ignore → /xsoar_cli-1.3.2/src/xsoar_cli/playbook/__init__.py +0 -0
  45. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/plugins/README.md +0 -0
  46. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/plugins/__init__.py +0 -0
  47. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/plugins/commands.py +0 -0
  48. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/plugins/manager.py +0 -0
  49. /xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_CommonPlaybooks/.secrets-ignore → /xsoar_cli-1.3.2/src/xsoar_cli/rbac/__init__.py +0 -0
  50. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/src/xsoar_cli/utilities.py +0 -0
  51. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/__init__.py +0 -0
  52. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/conftest.py +0 -0
  53. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_base.py +0 -0
  54. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_case.py +0 -0
  55. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_config.py +0 -0
  56. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Download/playbook-empty.yml +0 -0
  57. {xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_CommonScripts → xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_CommonPlaybooks}/.pack-ignore +0 -0
  58. {xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_CommonScripts → xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_CommonPlaybooks}/.secrets-ignore +0 -0
  59. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonPlaybooks/Author_image.png +0 -0
  60. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonPlaybooks/Playbooks/GenericPlaybook.yml +0 -0
  61. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonPlaybooks/README.md +0 -0
  62. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonPlaybooks/pack_metadata.json +0 -0
  63. {xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_EDR → xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_CommonScripts}/.pack-ignore +0 -0
  64. {xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_EDR → xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_CommonScripts}/.secrets-ignore +0 -0
  65. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonScripts/Author_image.png +0 -0
  66. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonScripts/README.md +0 -0
  67. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonScripts/Scripts/GenericScript/GenericScript.py +0 -0
  68. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonScripts/Scripts/GenericScript/GenericScript.yml +0 -0
  69. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonScripts/Scripts/GenericScript/README.md +0 -0
  70. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_CommonScripts/pack_metadata.json +0 -0
  71. {xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_Layouts → xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_EDR}/.pack-ignore +0 -0
  72. {xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_Layouts → xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_EDR}/.secrets-ignore +0 -0
  73. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Author_image.png +0 -0
  74. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Playbooks/EDR_InitialTriage.yml +0 -0
  75. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/README.md +0 -0
  76. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/EDR_FetchFile.py +0 -0
  77. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/EDR_FetchFile.yml +0 -0
  78. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/README.md +0 -0
  79. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_Triage/EDR_Triage.py +0 -0
  80. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_Triage/EDR_Triage.yml +0 -0
  81. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/EDR_Triage/README.md +0 -0
  82. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/LegacyItem.py +0 -0
  83. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/LegacyItem.yml +0 -0
  84. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/README.md +0 -0
  85. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/Scripts/LegacyItem/test_data/basescript-dummy.json +0 -0
  86. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_EDR/pack_metadata.json +0 -0
  87. /xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_Layouts/Author_image.png → /xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_Layouts/.pack-ignore +0 -0
  88. /xsoar_cli-1.3.0/tests/test_data/Packs/MyOrg_Layouts/README.md → /xsoar_cli-1.3.2/tests/test_data/Packs/MyOrg_Layouts/.secrets-ignore +0 -0
  89. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_Layouts/Layouts/layoutscontainer-GenericLayout.json +0 -0
  90. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/MyOrg_Layouts/pack_metadata.json +0 -0
  91. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/Packs/Not_applicable/Playbooks/Empty.yml +0 -0
  92. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/manifest_base.json +0 -0
  93. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/manifest_invalid.json +0 -0
  94. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/manifest_with_pack_not_on_server.json +0 -0
  95. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/server_base_response.json +0 -0
  96. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/server_base_response_missing_one_pack.json +0 -0
  97. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/server_base_response_with_updates.json +0 -0
  98. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_data/server_base_response_with_updates_and_one_extra.json +0 -0
  99. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_graph.py +0 -0
  100. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_manifest.py +0 -0
  101. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_pack.py +0 -0
  102. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_playbook.py +0 -0
  103. {xsoar_cli-1.3.0 → xsoar_cli-1.3.2}/tests/test_plugins.py +0 -0
@@ -0,0 +1,22 @@
1
+ on:
2
+ push:
3
+ branches:
4
+ - main
5
+
6
+ jobs:
7
+ coverage:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Checkout code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Install uv and set the Python version
14
+ uses: astral-sh/setup-uv@v7
15
+ with:
16
+ python-version: "3.14"
17
+
18
+ - name: Sync uv
19
+ run: uv sync --all-groups
20
+
21
+ - name: Run unit tests with coverage
22
+ run: uv run pytest --cov=src/xsoar_cli --cov-report=term-missing
@@ -23,13 +23,24 @@ jobs:
23
23
  python-version: ${{ matrix.python-version }}
24
24
 
25
25
  - name: Sync uv
26
- run: uv sync
27
-
28
- - name: Install requirements
29
- run: |
30
- uv pip install -r requirements.txt
31
- uv pip install -r requirements_dev.txt
32
- uv pip install -e .
26
+ run: uv sync --all-groups
33
27
 
34
28
  - name: Run unit tests
35
29
  run: uv run pytest
30
+
31
+ coverage:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - name: Checkout code
35
+ uses: actions/checkout@v4
36
+
37
+ - name: Install uv and set the Python version
38
+ uses: astral-sh/setup-uv@v7
39
+ with:
40
+ python-version: "3.14"
41
+
42
+ - name: Sync uv
43
+ run: uv sync --all-groups
44
+
45
+ - name: Run unit tests with coverage
46
+ run: uv run pytest --cov=src/xsoar_cli --cov-report=term-missing
@@ -32,29 +32,52 @@ We welcome contributions of all kinds! Whether you're fixing bugs, adding featur
32
32
  cd xsoar-cli
33
33
  ```
34
34
 
35
- ### 2. Set Up Development Environment
35
+ ### 2. Install uv
36
+
37
+ [uv](https://docs.astral.sh/uv/) is the recommended package manager for this project. Using legacy venv/pip workflows should also work, but uv is a great tool.
38
+
39
+ ```bash
40
+ curl -LsSf https://astral.sh/uv/install.sh | sh
41
+ ```
42
+
43
+ For other platforms and installation options, see the [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/).
44
+
45
+ ### 3. Set Up Development Environment
46
+
47
+ **Recommended: uv**
48
+
49
+ You may find that you will need to make modifications in [xsoar-client](https://github.com/tlium/xsoar-client) and/or [xsoar-dependency-graph](https://github.com/tlium/xsoar-dependency-graph) in order
50
+ to fix bugs or add new features to `xsoar-cli`. If you need to work on `xsoar-client` or `xsoar-dependency-graph`, install them in editable mode from your local Git clones:
51
+
36
52
  ```bash
37
- # Create virtual environment
53
+ uv pip install -e /path/to/your/xsoar-client
54
+ uv pip install -e /path/to/your/xsoar-dependency-graph
55
+ ```
56
+
57
+ ```bash
58
+ # Install dependencies and xsoar-cli in editable mode
59
+ uv sync
60
+ uv pip install -e .
61
+ ```
62
+ **Alternative: pip/venv**
63
+
64
+ ```bash
65
+ # Create and activate a virtual environment
38
66
  python -m venv venv
39
- source venv/bin/activate # On Windows: venv\Scripts\activate
67
+ source venv/bin/activate
40
68
 
41
69
  # Install development dependencies
42
70
  pip install -r requirements_dev.txt
43
71
 
44
- # Install the package in development mode
72
+ # Install xsoar-cli in editable mode
45
73
  pip install -e .
46
74
  ```
47
75
 
48
- ### 3. Verify Setup
49
- ```bash
50
- # Run tests to ensure everything works
51
- pytest
52
-
53
- # Check code style
54
- black --check src/ tests/
76
+ If you need to work on the supporting libraries, install them in editable mode as well:
55
77
 
56
- # Run the CLI to verify installation
57
- xsoar-cli --help
78
+ ```bash
79
+ pip install -e /path/to/your/xsoar-client
80
+ pip install -e /path/to/your/xsoar-dependency-graph
58
81
  ```
59
82
 
60
83
  ## Development Workflow
@@ -71,12 +94,6 @@ xsoar-cli --help
71
94
  ```bash
72
95
  # Run tests
73
96
  pytest
74
-
75
- # Format code
76
- black src/ tests/
77
-
78
- # Check for any issues
79
- python -m pytest --cov=src/xsoar_cli
80
97
  ```
81
98
 
82
99
  4. **Commit with clear messages**:
@@ -91,8 +108,7 @@ xsoar-cli --help
91
108
  ```
92
109
 
93
110
  ### Code Style
94
- - We use [Black](https://github.com/psf/black) for code formatting
95
- - Run `black src/ tests/` before committing
111
+ - We use [Ruff](https://docs.astral.sh/ruff/) for code formatting
96
112
  - Follow existing code patterns and conventions
97
113
  - Add type hints where appropriate
98
114
  - Write docstrings for new functions and classes
@@ -127,12 +143,6 @@ Documentation improvements are especially welcome! You can contribute to documen
127
143
 
128
144
  ## Submitting Pull Requests
129
145
 
130
- ### Before Submitting
131
- - [ ] Tests pass (`pytest`)
132
- - [ ] Code is formatted (`black src/ tests/`)
133
- - [ ] Documentation is updated if needed
134
- - [ ] Commit messages are clear and descriptive
135
-
136
146
  ### Pull Request Guidelines
137
147
  - **Describe your changes** clearly in the PR description
138
148
  - **Reference any related issues** (e.g., "Fixes #123")
@@ -194,4 +204,4 @@ Be respectful and constructive in all interactions. We want this to be a welcomi
194
204
 
195
205
  Contributors are recognized in release notes and we appreciate all contributions, no matter how small!
196
206
 
197
- Thank you for contributing!
207
+ Thank you for contributing!
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xsoar-cli
3
- Version: 1.3.0
3
+ Version: 1.3.2
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
@@ -44,7 +44,7 @@ Pull Requests are very welcome and appreciated! See [CONTRIBUTING.md](CONTRIBUTI
44
44
 
45
45
  ```bash
46
46
  # Install
47
- pip install xsoar-cli
47
+ uv tool install xsoar-cli
48
48
 
49
49
  # Create configuration file
50
50
  xsoar-cli config create
@@ -65,27 +65,47 @@ This CLI tool is made to be run from the root of a content repository. Some comm
65
65
 
66
66
  ### Core Requirements
67
67
  - XSOAR servers version 6 or 8
68
- - Python 3.9+ (only tested with Python 3.12, earlier versions may work but are not guaranteed)
68
+ - Python 3.10+ (tested with Python 3.10-3.14, earlier versions may work but are untested)
69
+ - [uv](https://docs.astral.sh/uv/) for installation
69
70
 
70
- ### Additional Requirements
71
- - **AWS SDK for Python (Boto3)** - Only required when working with custom content packs stored in S3.
72
- - **Azure SDK for Python**
71
+ ### Additional Requirements
72
+ - **demisto-sdk** - Required by several commands, most notably `xsoar-cli playbook download`. Should be installed separately.
73
73
 
74
74
 
75
75
  ## Installation
76
76
 
77
+ Installing xsoar-cli as a uv tool is recommended. This gives it an isolated environment with its own dependencies, avoiding version conflicts with other Python tools such as demisto-sdk.
78
+
79
+ ```bash
80
+ uv tool install xsoar-cli
81
+ ```
82
+
83
+ Alternatively, using pip:
84
+
77
85
  ```bash
78
86
  pip install xsoar-cli
79
87
  ```
80
88
 
81
89
  ## Upgrading
82
90
 
91
+ ```bash
92
+ uv tool upgrade xsoar-cli
93
+ ```
94
+
95
+ Or with pip:
96
+
83
97
  ```bash
84
98
  pip install --upgrade xsoar-cli
85
99
  ```
86
100
 
87
101
  ## Uninstalling
88
102
 
103
+ ```bash
104
+ uv tool uninstall xsoar-cli
105
+ ```
106
+
107
+ Or with pip:
108
+
89
109
  ```bash
90
110
  pip uninstall xsoar-cli
91
111
  ```
@@ -106,6 +126,7 @@ After creating the config file, edit it with your XSOAR server details:
106
126
  {
107
127
  "default_environment": "xsoar6",
108
128
  "default_new_case_type": "My case type",
129
+ "log_level": "INFO",
109
130
  "custom_pack_authors": ["My_Org"],
110
131
  "server_config": {
111
132
  "xsoar6": {
@@ -149,6 +170,7 @@ After creating the config file, edit it with your XSOAR server details:
149
170
  "default_environment": "dev",
150
171
  "default_new_case_type": "My case type",
151
172
  "custom_pack_authors": ["My_Org"],
173
+ "log_level": "DEBUG"
152
174
  "server_config": {
153
175
  "dev": {
154
176
  "base_url": "https://your.dev.domain",
@@ -241,8 +263,7 @@ xsoar-cli supports a plugin system that allows you to extend the CLI with custom
241
263
  - Use `xsoar-cli config validate` to verify your configuration
242
264
 
243
265
  **Python compatibility issues**
244
- - Ensure you're using Python 3.9 or later
245
- - Consider using Python 3.12 for best compatibility
266
+ - Ensure you're using Python 3.10 or later
246
267
 
247
268
  ## Contributing
248
269
 
@@ -19,7 +19,7 @@ Pull Requests are very welcome and appreciated! See [CONTRIBUTING.md](CONTRIBUTI
19
19
 
20
20
  ```bash
21
21
  # Install
22
- pip install xsoar-cli
22
+ uv tool install xsoar-cli
23
23
 
24
24
  # Create configuration file
25
25
  xsoar-cli config create
@@ -40,27 +40,47 @@ This CLI tool is made to be run from the root of a content repository. Some comm
40
40
 
41
41
  ### Core Requirements
42
42
  - XSOAR servers version 6 or 8
43
- - Python 3.9+ (only tested with Python 3.12, earlier versions may work but are not guaranteed)
43
+ - Python 3.10+ (tested with Python 3.10-3.14, earlier versions may work but are untested)
44
+ - [uv](https://docs.astral.sh/uv/) for installation
44
45
 
45
- ### Additional Requirements
46
- - **AWS SDK for Python (Boto3)** - Only required when working with custom content packs stored in S3.
47
- - **Azure SDK for Python**
46
+ ### Additional Requirements
47
+ - **demisto-sdk** - Required by several commands, most notably `xsoar-cli playbook download`. Should be installed separately.
48
48
 
49
49
 
50
50
  ## Installation
51
51
 
52
+ Installing xsoar-cli as a uv tool is recommended. This gives it an isolated environment with its own dependencies, avoiding version conflicts with other Python tools such as demisto-sdk.
53
+
54
+ ```bash
55
+ uv tool install xsoar-cli
56
+ ```
57
+
58
+ Alternatively, using pip:
59
+
52
60
  ```bash
53
61
  pip install xsoar-cli
54
62
  ```
55
63
 
56
64
  ## Upgrading
57
65
 
66
+ ```bash
67
+ uv tool upgrade xsoar-cli
68
+ ```
69
+
70
+ Or with pip:
71
+
58
72
  ```bash
59
73
  pip install --upgrade xsoar-cli
60
74
  ```
61
75
 
62
76
  ## Uninstalling
63
77
 
78
+ ```bash
79
+ uv tool uninstall xsoar-cli
80
+ ```
81
+
82
+ Or with pip:
83
+
64
84
  ```bash
65
85
  pip uninstall xsoar-cli
66
86
  ```
@@ -81,6 +101,7 @@ After creating the config file, edit it with your XSOAR server details:
81
101
  {
82
102
  "default_environment": "xsoar6",
83
103
  "default_new_case_type": "My case type",
104
+ "log_level": "INFO",
84
105
  "custom_pack_authors": ["My_Org"],
85
106
  "server_config": {
86
107
  "xsoar6": {
@@ -124,6 +145,7 @@ After creating the config file, edit it with your XSOAR server details:
124
145
  "default_environment": "dev",
125
146
  "default_new_case_type": "My case type",
126
147
  "custom_pack_authors": ["My_Org"],
148
+ "log_level": "DEBUG"
127
149
  "server_config": {
128
150
  "dev": {
129
151
  "base_url": "https://your.dev.domain",
@@ -216,8 +238,7 @@ xsoar-cli supports a plugin system that allows you to extend the CLI with custom
216
238
  - Use `xsoar-cli config validate` to verify your configuration
217
239
 
218
240
  **Python compatibility issues**
219
- - Ensure you're using Python 3.9 or later
220
- - Consider using Python 3.12 for best compatibility
241
+ - Ensure you're using Python 3.10 or later
221
242
 
222
243
  ## Contributing
223
244
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.3.0'
32
- __version_tuple__ = version_tuple = (1, 3, 0)
31
+ __version__ = version = '1.3.2'
32
+ __version_tuple__ = version_tuple = (1, 3, 2)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -60,3 +60,9 @@ tests = ["tests", "*/xsoar-cli/tests"]
60
60
 
61
61
  [tool.coverage.report]
62
62
  exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]
63
+
64
+ [dependency-groups]
65
+ dev = [
66
+ "pytest>=9.0.2",
67
+ "pytest-cov>=7.0.0",
68
+ ]
@@ -16,12 +16,14 @@ from .__about__ import __version__
16
16
  from .case import commands as case_commands
17
17
  from .config import commands as config_commands
18
18
  from .graph import commands as graph_commands
19
+ from .integration import commands as integration_commands
19
20
  from .log import LoggingSetup, setup_logging
20
21
  from .manifest import commands as manifest_commands
21
22
  from .pack import commands as pack_commands
22
23
  from .playbook import commands as playbook_commands
23
24
  from .plugins import commands as plugin_commands
24
25
  from .plugins.manager import PluginManager
26
+ from .rbac import commands as rbac_commands
25
27
  from .utilities import get_config_file_contents, get_config_file_path
26
28
 
27
29
 
@@ -64,10 +66,12 @@ def _register_commands() -> None:
64
66
  cli.add_command(config_commands.config)
65
67
  cli.add_command(case_commands.case)
66
68
  cli.add_command(pack_commands.pack)
69
+ cli.add_command(integration_commands.integration)
67
70
  cli.add_command(manifest_commands.manifest)
68
71
  cli.add_command(playbook_commands.playbook)
69
72
  cli.add_command(graph_commands.graph)
70
73
  cli.add_command(plugin_commands.plugins)
74
+ cli.add_command(rbac_commands.rbac)
71
75
 
72
76
 
73
77
  def _load_plugins() -> tuple[list[str], PluginManager]:
@@ -0,0 +1,55 @@
1
+ import json
2
+ import logging
3
+ from typing import TYPE_CHECKING
4
+
5
+ import click
6
+
7
+ from xsoar_cli.utilities import get_xsoar_config, load_config, validate_xsoar_connectivity
8
+
9
+ if TYPE_CHECKING:
10
+ from xsoar_client.xsoar_client import Client
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @click.group()
16
+ @click.pass_context
17
+ def integration(ctx: click.Context) -> None:
18
+ """(BETA) Save/load integration configuration for an integration instance."""
19
+
20
+
21
+ @click.option("--environment", default=None, help="Default environment set in config file.")
22
+ @click.command()
23
+ @click.argument("name", type=str)
24
+ @click.pass_context
25
+ @load_config
26
+ @validate_xsoar_connectivity()
27
+ def dumpconfig(ctx: click.Context, environment: str | None, name: str | None) -> None:
28
+ """Dumps integration config to JSON file."""
29
+ config = get_xsoar_config(ctx)
30
+ xsoar_client: Client = config.get_client(environment)
31
+ logger.debug("Dumping integration config (environment: '%s')", environment or config.default_environment)
32
+ response = xsoar_client.get_integrations()
33
+ integrations = json.loads(response)
34
+ integration_data = next((i for i in integrations if i["name"] == name), None)
35
+ logger.debug("Fetching config for integration name '%s'(environment: '%s')", name, environment or config.default_environment)
36
+ if not integration_data:
37
+ click.echo(f"Cannot find integration instance '{name}'")
38
+ ctx.exit(1)
39
+ click.echo(json.dumps(integration_data, sort_keys=True, indent=4) + "\n")
40
+
41
+
42
+ @click.option("--environment", default=None, help="Default environment set in config file.")
43
+ @click.command()
44
+ @click.argument("name", type=str)
45
+ @click.pass_context
46
+ @load_config
47
+ @validate_xsoar_connectivity()
48
+ def loadconfig(ctx: click.Context, environment: str | None, name: str, instance_name: str) -> None:
49
+ """Loads integration config from JSON file."""
50
+ logger.debug("integration loadconfig command not implemented")
51
+ click.echo("Command not implemented")
52
+
53
+
54
+ integration.add_command(dumpconfig)
55
+ integration.add_command(loadconfig)
@@ -72,9 +72,11 @@ def download(ctx: click.Context, environment: str | None, name: str) -> None:
72
72
  [
73
73
  "demisto-sdk",
74
74
  "format",
75
- "--assume-yes",
75
+ "--assume-no",
76
76
  "--no-validate",
77
77
  "--no-graph",
78
+ "--from-version",
79
+ "6.10.0",
78
80
  str(filepath),
79
81
  ],
80
82
  check=False,
@@ -0,0 +1,72 @@
1
+ import json
2
+ import logging
3
+ from typing import TYPE_CHECKING
4
+
5
+ import click
6
+
7
+ import xsoar_cli
8
+ from xsoar_cli.utilities import get_xsoar_config, load_config, validate_xsoar_connectivity
9
+
10
+ if TYPE_CHECKING:
11
+ from xsoar_client.xsoar_client import Client
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ @click.group()
17
+ @click.pass_context
18
+ def rbac(ctx: click.Context) -> None:
19
+ """Dump roles, users and user groups"""
20
+
21
+
22
+ @click.option("--environment", default=None, help="Default environment set in config file.")
23
+ @click.command()
24
+ @click.pass_context
25
+ @load_config
26
+ @validate_xsoar_connectivity()
27
+ def getroles(ctx: click.Context, environment: str | None) -> None:
28
+ """Dump all roles in your environment."""
29
+ config = get_xsoar_config(ctx)
30
+ xsoar_client: Client = config.get_client(environment)
31
+ results = xsoar_client.get_roles()
32
+ roles = json.loads(results)
33
+ click.echo(json.dumps(roles, sort_keys=True, indent=4) + "\n")
34
+
35
+
36
+ @click.option("--environment", default=None, help="Default environment set in config file.")
37
+ @click.command()
38
+ @click.pass_context
39
+ @load_config
40
+ @validate_xsoar_connectivity()
41
+ def getusers(ctx: click.Context, environment: str | None) -> None:
42
+ """Dump all users in your environment."""
43
+ config = get_xsoar_config(ctx)
44
+ xsoar_client: Client = config.get_client(environment)
45
+ results = xsoar_client.get_users()
46
+ users = json.loads(results)
47
+ click.echo(json.dumps(users, sort_keys=True, indent=4) + "\n")
48
+
49
+ config = get_xsoar_config(ctx)
50
+ xsoar_client: Client = config.get_client(environment)
51
+
52
+
53
+ @click.option("--environment", default=None, help="Default environment set in config file.")
54
+ @click.command()
55
+ @click.pass_context
56
+ @load_config
57
+ @validate_xsoar_connectivity()
58
+ def getusergroups(ctx: click.Context, environment: str | None) -> None:
59
+ """Dump all roles in your environment."""
60
+ config = get_xsoar_config(ctx)
61
+ xsoar_client: Client = config.get_client(environment)
62
+ if xsoar_client.config.server_version < 8:
63
+ click.echo("Errot: Command not supported for XSOAR server versions less than 8")
64
+ ctx.exit(1)
65
+ results = xsoar_client.get_user_groups()
66
+ user_groups = json.loads(results)
67
+ click.echo(json.dumps(user_groups, sort_keys=True, indent=4) + "\n")
68
+
69
+
70
+ rbac.add_command(getroles)
71
+ rbac.add_command(getusers)
72
+ rbac.add_command(getusergroups)
@@ -0,0 +1,129 @@
1
+ """Tests for ConnectionErrorHandler.
2
+
3
+ Covers get_message and _extract_hostname using a single parametrized test method.
4
+ Exception objects that require raise/except chains are built by module-level helpers
5
+ and passed directly as parameter values.
6
+ """
7
+
8
+ import socket
9
+ from unittest.mock import MagicMock
10
+
11
+ import pytest
12
+ from urllib3.exceptions import NameResolutionError
13
+
14
+ from xsoar_cli.connection_errors import ConnectionErrorHandler
15
+
16
+
17
+ def _make_name_resolution_error(hostname: str, reason: str) -> NameResolutionError:
18
+ """Build a NameResolutionError for the given hostname and reason string.
19
+
20
+ Uses a MagicMock for the conn argument, with __str__ fixed to 'conn' so the
21
+ formatted message is predictable across test runs.
22
+ """
23
+ conn = MagicMock()
24
+ conn.__str__ = lambda self: "conn"
25
+ return NameResolutionError(hostname, conn, socket.gaierror(reason))
26
+
27
+
28
+ def _chained_name_resolution_error() -> NameResolutionError:
29
+ """Return a NameResolutionError chained from an OSError root cause.
30
+
31
+ Represents the realistic urllib3 case where a NameResolutionError wraps a
32
+ lower-level socket error.
33
+ """
34
+ try:
35
+ try:
36
+ raise OSError("nodename nor servname provided, or not known")
37
+ except OSError as root:
38
+ raise _make_name_resolution_error("example.com", "nodename nor servname provided, or not known") from root
39
+ except NameResolutionError as exc:
40
+ return exc
41
+
42
+
43
+ def _chained_without_name_resolution_error() -> RuntimeError:
44
+ """Return a RuntimeError chained from an OSError root cause, with no NameResolutionError in the chain.
45
+
46
+ Used to verify that get_message falls back to the root cause string when no
47
+ hostname can be extracted.
48
+ """
49
+ try:
50
+ try:
51
+ raise OSError("connection refused")
52
+ except OSError as root:
53
+ raise RuntimeError("operation failed") from root
54
+ except RuntimeError as exc:
55
+ return exc
56
+
57
+
58
+ def _name_resolution_error_no_args() -> NameResolutionError:
59
+ """Return a NameResolutionError with an empty args tuple.
60
+
61
+ Bypasses the normal constructor to simulate a malformed exception object,
62
+ used to verify that _extract_hostname handles missing args gracefully.
63
+ """
64
+ exc = NameResolutionError.__new__(NameResolutionError)
65
+ exc.args = ()
66
+ return exc
67
+
68
+
69
+ def _name_resolution_error_with_message(message: str) -> NameResolutionError:
70
+ """Return a NameResolutionError with args set to the given message string.
71
+
72
+ Bypasses the normal constructor so arbitrary message content can be injected,
73
+ used to test _extract_hostname's message parsing edge cases.
74
+ """
75
+ exc = NameResolutionError.__new__(NameResolutionError)
76
+ exc.args = (message,)
77
+ return exc
78
+
79
+
80
+ handler = ConnectionErrorHandler()
81
+
82
+
83
+ class TestConnectionErrorHandler:
84
+ """Tests for ConnectionErrorHandler.
85
+
86
+ Both get_message and _extract_hostname are exercised through a single
87
+ parametrized method. The method column selects which handler method to call
88
+ via getattr, keeping all cases in one place.
89
+ """
90
+
91
+ @pytest.mark.parametrize(
92
+ ("method", "exception", "expected"),
93
+ [
94
+ # get_message: simple cases
95
+ ("get_message", None, "Unknown error"),
96
+ ("get_message", ValueError("something went wrong"), "something went wrong"),
97
+ # get_message: exception chain without NameResolutionError
98
+ ("get_message", _chained_without_name_resolution_error(), "connection refused"),
99
+ # get_message: NameResolutionError with chained root cause
100
+ (
101
+ "get_message",
102
+ _chained_name_resolution_error(),
103
+ "Failed to resolve 'example.com' (nodename nor servname provided, or not known)",
104
+ ),
105
+ # _extract_hostname: valid hostnames
106
+ (
107
+ "_extract_hostname",
108
+ _make_name_resolution_error("example.com", "nodename nor servname provided, or not known"),
109
+ "example.com",
110
+ ),
111
+ ("_extract_hostname", _make_name_resolution_error("my-host.internal", "Name or service not known"), "my-host.internal"),
112
+ # _extract_hostname: no args
113
+ ("_extract_hostname", _name_resolution_error_no_args(), None),
114
+ # _extract_hostname: message without "Failed to resolve"
115
+ ("_extract_hostname", _name_resolution_error_with_message("some unrelated error message"), None),
116
+ ("_extract_hostname", _name_resolution_error_with_message("conn: Could not connect to host"), None),
117
+ # _extract_hostname: malformed quotes
118
+ ("_extract_hostname", _name_resolution_error_with_message("conn: Failed to resolve 'example.com"), None),
119
+ ],
120
+ )
121
+ def test_connection_error_handler(self, method: str, exception: BaseException | None, expected: str | None) -> None:
122
+ """Invoke the named handler method with exception and assert the result matches expected.
123
+
124
+ method: name of the ConnectionErrorHandler method to call
125
+ exception: exception instance (or None) passed as the sole argument
126
+ expected: expected return value; None for _extract_hostname failure cases
127
+ """
128
+ result = getattr(handler, method)(exception)
129
+ assert result == expected