Skip to content

Commit 99e6b1a

Browse files
authored
Merge pull request #179 from intelowlproject/develop
4.3.0
2 parents 69b6ceb + 386f83a commit 99e6b1a

13 files changed

+483
-6
lines changed

.github/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## [4.3.0](https://github.com/intelowlproject/pyintelowl/releases/tag/4.3.0)
4+
- this version supports the new Playbooks feature released with IntelOwl v4.1.0
5+
36
## [4.2.0](https://github.com/intelowlproject/pyintelowl/releases/tag/4.2.0)
47

58
- this version is fully compatible with IntelOwl v4 (#165)

.github/release_template.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Checklist for creating a new release
22

33
- [ ] Update `CHANGELOG.md` for the new version
4-
- [ ] Change version number in `docs.config.py`, `pyintelowl/version.py`
4+
- [ ] Change version number in `docs/conf.py`, `pyintelowl/version.py`
55
- [ ] Verify CI Tests
66
- [ ] Merge the PR to the `master` branch. **Note:** Only use "Merge and commit" as the merge strategy and not "Squash and merge". Using "Squash and merge" makes history between branches misaligned.
77

docs/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import os
1414
import sys
1515

16-
VERSION = "4.2.0"
16+
VERSION = "4.3.0"
1717
GITHUB_URL = "https://github.com/intelowlproject/pyintelowl"
1818

1919
sys.path.append(os.path.abspath("../"))

docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ On successful installation, The ``pyintelowl`` entryscript should be directly in
3434
connector-healthcheck Send healthcheck request for a connector
3535
get-analyzer-config Get current state of `analyzer_config.json` from...
3636
get-connector-config Get current state of `connector_config.json` from...
37+
get-playbook-config Get current state of `playbook_config.json` from...
3738
jobs Manage Jobs
3839
tags Manage tags
3940

docs/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Sphinx==5.0.1
1+
Sphinx==5.1.1
22
sphinx-rtd-theme
33
sphinxcontrib.asciinema
44
sphinxcontrib-napoleon

pyintelowl/cli/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
connector_healthcheck,
55
get_analyzer_config,
66
get_connector_config,
7+
get_playbook_config,
78
)
89
from .config import config
910
from .jobs import jobs
@@ -20,6 +21,7 @@
2021
cmds = [
2122
get_analyzer_config,
2223
get_connector_config,
24+
get_playbook_config,
2325
analyzer_healthcheck,
2426
connector_healthcheck,
2527
]

pyintelowl/cli/_jobs_utils.py

+4
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ def _display_all_jobs(logger, rows):
103103
table.add_column(
104104
header="Connectors\nCalled", justify="center", header_style=header_style
105105
)
106+
table.add_column(
107+
header="Playbooks\nCalled", justify="center", header_style=header_style
108+
)
106109
table.add_column(
107110
header="Process\nTime(s)", justify="center", header_style=header_style
108111
)
@@ -118,6 +121,7 @@ def _display_all_jobs(logger, rows):
118121
", ".join([t["label"] for t in el["tags"]]),
119122
", ".join(el["analyzers_to_execute"]),
120123
", ".join(el["connectors_to_execute"]),
124+
", ".join(el["playbooks_to_execute"]),
121125
str(el["process_time"]),
122126
get_status_text(el["status"]),
123127
)

pyintelowl/cli/analyse.py

+92
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@
7474
),
7575
]
7676

77+
__playbook_analyse_options = __analyse_options.copy()
78+
# doing it twice to remove --analyzers-list and --connectors-list
79+
__playbook_analyse_options.pop(0)
80+
__playbook_analyse_options.pop(0)
81+
82+
__playbook_analyse_options.append(
83+
click.option(
84+
"-pl",
85+
"--playbooks-list",
86+
type=str,
87+
default="",
88+
help="""
89+
Comma separated list of playbook names to invoke.
90+
Defaults to all configured playbooks.
91+
""",
92+
),
93+
)
94+
7795

7896
@click.group("analyse")
7997
def analyse():
@@ -162,6 +180,80 @@ def file(
162180
ctx.obj.logger.fatal(str(e))
163181

164182

183+
@analyse.command(help="Send playbook analysis request for an observable")
184+
@click.argument("value")
185+
@add_options(__playbook_analyse_options)
186+
@click.pass_context
187+
def playbook_observable(
188+
ctx: ClickContext,
189+
value: str,
190+
playbooks_list: str,
191+
tags_list: str,
192+
tlp: str,
193+
check,
194+
check_minutes_ago: int,
195+
runtime_config,
196+
should_poll: bool,
197+
):
198+
playbooks_list = playbooks_list.split(",") if len(playbooks_list) else []
199+
tags_labels = tags_list.split(",") if len(tags_list) else []
200+
if runtime_config:
201+
runtime_config = get_json_data(runtime_config)
202+
else:
203+
runtime_config = {}
204+
try:
205+
ctx.obj._new_analysis_playbook_cli(
206+
value,
207+
"observable",
208+
check,
209+
tlp,
210+
playbooks_list,
211+
runtime_config,
212+
tags_labels,
213+
should_poll,
214+
check_minutes_ago,
215+
)
216+
except IntelOwlClientException as e:
217+
ctx.obj.logger.fatal(str(e))
218+
219+
220+
@analyse.command(help="Send playbook analysis request for an observable")
221+
@click.argument("filepath", type=click.Path(exists=True, resolve_path=True))
222+
@add_options(__playbook_analyse_options)
223+
@click.pass_context
224+
def playbook_file(
225+
ctx: ClickContext,
226+
filepath: str,
227+
playbooks_list: str,
228+
tags_list: str,
229+
tlp: str,
230+
check,
231+
check_minutes_ago: int,
232+
runtime_config,
233+
should_poll: bool,
234+
):
235+
playbooks_list = playbooks_list.split(",") if len(playbooks_list) else []
236+
tags_labels = tags_list.split(",") if len(tags_list) else []
237+
if runtime_config:
238+
runtime_config = get_json_data(runtime_config)
239+
else:
240+
runtime_config = {}
241+
try:
242+
ctx.obj._new_analysis_playbook_cli(
243+
filepath,
244+
"file",
245+
check,
246+
tlp,
247+
playbooks_list,
248+
runtime_config,
249+
tags_labels,
250+
should_poll,
251+
check_minutes_ago,
252+
)
253+
except IntelOwlClientException as e:
254+
ctx.obj.logger.fatal(str(e))
255+
256+
165257
@analyse.command(
166258
help="Send multiple analysis requests. Reads file (csv or json) for inputs."
167259
)

pyintelowl/cli/commands.py

+62
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,68 @@ def get_connector_config(
157157
console.print(table)
158158

159159

160+
@click.command(
161+
help="Get current state of `playbook_config.json` from the IntelOwl instance",
162+
)
163+
@click.option(
164+
"-m",
165+
"--re-match",
166+
help="RegEx Pattern to filter analyzer names against",
167+
)
168+
@add_options(json_flag_option)
169+
@click.option(
170+
"-t", "--text", "as_text", is_flag=True, help="Print playbook names as CSV"
171+
)
172+
@click.pass_context
173+
def get_playbook_config(ctx: ClickContext, re_match: str, as_json: bool, as_text: bool):
174+
console = Console()
175+
ctx.obj.logger.info("Requesting [italic blue]playbook_config.json[/]..")
176+
try:
177+
res = ctx.obj.get_playbook_configs()
178+
# filter resulset if a regex pattern was provided
179+
if re_match:
180+
pat = re.compile(re_match)
181+
res = {k: v for k, v in res.items() if pat.match(k) is not None}
182+
except IntelOwlClientException as e:
183+
ctx.obj.logger.fatal(str(e))
184+
ctx.exit(0)
185+
if as_json:
186+
with console.pager(styles=True):
187+
console.print(json.dumps(res, indent=4))
188+
elif as_text:
189+
click.echo(", ".join(res.keys()))
190+
else:
191+
# otherwise, print full table
192+
headers = [
193+
"Name",
194+
"Analyzers",
195+
"Connectors",
196+
"Description",
197+
"Supports",
198+
"Disabled",
199+
]
200+
header_style = "bold blue"
201+
table = Table(
202+
show_header=True,
203+
title="Playbook Configurations",
204+
box=box.DOUBLE_EDGE,
205+
show_lines=True,
206+
)
207+
for h in headers:
208+
table.add_column(h, header_style=header_style, justify="center")
209+
for name, obj in res.items():
210+
table.add_row(
211+
name,
212+
get_json_syntax(obj.get("analyzers", {})),
213+
get_json_syntax(obj.get("connectors", {})),
214+
obj.get("description", ""),
215+
get_json_syntax(obj.get("supports", [])),
216+
get_success_text(obj.get("disabled", False)),
217+
)
218+
with console.pager(styles=True):
219+
console.print(table)
220+
221+
160222
@click.command(help="Send healthcheck request for an analyzer (docker-based)")
161223
@click.argument("analyzer_name", type=str)
162224
@click.pass_context

0 commit comments

Comments
 (0)