Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev -> Stable 2.4.0 #2266

Open
wants to merge 108 commits into
base: stable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
16f12d0
update issue template
invalid-email-address Jan 31, 2025
9a133e2
Merge pull request #2236 from blacklanternsecurity/update-issue-template
TheTechromancer Jan 31, 2025
3c10b34
adding subwords capability to ffuf_shortnames, adding ignore_case opt…
liquidsec Feb 1, 2025
b5dac08
adjust log message
liquidsec Feb 1, 2025
16c765f
Update trufflehog
blsaccess Feb 1, 2025
a9e06e0
adding debug message
liquidsec Feb 1, 2025
ea9e736
better ntlk data handling
liquidsec Feb 1, 2025
bf4c0d7
ruff format
liquidsec Feb 1, 2025
958dad9
undoing error
liquidsec Feb 1, 2025
eee42f9
better status message
liquidsec Feb 1, 2025
2d9f75b
bug fix
liquidsec Feb 1, 2025
e4c0711
prevent ffuf_shortnames from trying to solve impossible URL_HINTs
liquidsec Feb 1, 2025
dd546f6
Merge pull request #2233 from blacklanternsecurity/update-trufflehog
TheTechromancer Feb 2, 2025
4714ac6
Added a gitdumper module
domwhewell-sage Feb 2, 2025
b65df1d
Remove unused module
domwhewell-sage Feb 2, 2025
227c565
Bump fastapi from 0.115.7 to 0.115.8
dependabot[bot] Feb 3, 2025
64b201b
adding optin description
liquidsec Feb 3, 2025
a73ef43
turning off cloud warning when strict-scope is on
liquidsec Feb 6, 2025
d83d7f1
more efficient
liquidsec Feb 6, 2025
61475a6
temporary change to islate generic ssrf test
liquidsec Feb 6, 2025
79ed370
temporary to troubleshoot generic_ssrf test
liquidsec Feb 6, 2025
3710e16
adding locks to mock interactsh to prevent race conditions
liquidsec Feb 6, 2025
fd3709a
reverting temp test changes
liquidsec Feb 6, 2025
62e7d5f
changing DNS events to FINDING, adding omit option
liquidsec Feb 6, 2025
5bb9030
increasing detection wait time
liquidsec Feb 6, 2025
4099990
update
ocervell Feb 7, 2025
5b7a4f5
Fixed downloading with directory listing enabled
domwhewell-sage Feb 8, 2025
3fcc24e
add tech detect preset
invalid-email-address Feb 8, 2025
4768fdb
Add recursive object function
domwhewell-sage Feb 9, 2025
5f54a19
Bump mkdocstrings from 0.27.0 to 0.28.0
dependabot[bot] Feb 10, 2025
9d69473
Bump mkdocs-material from 9.5.50 to 9.6.3
dependabot[bot] Feb 10, 2025
0970793
fixing telerik FP bug
liquidsec Feb 10, 2025
7d95239
unnecessary import remove
liquidsec Feb 10, 2025
872760e
Merge pull request #2254 from blacklanternsecurity/cloud-strict-scope
liquidsec Feb 10, 2025
ff94531
removing import
liquidsec Feb 10, 2025
7c5ef1f
ocd
liquidsec Feb 10, 2025
03833c0
Bump deepdiff from 8.1.1 to 8.2.0
dependabot[bot] Feb 10, 2025
f3aac43
Corrected errors when no dirlisting. Todo fix dirlisting, user messages
domwhewell-sage Feb 10, 2025
af399a6
Merge pull request #2273 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 10, 2025
00918e0
Merge pull request #2272 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 10, 2025
be3faa9
Merge pull request #2270 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 10, 2025
90028c0
Bump pytest-asyncio from 0.25.2 to 0.25.3
dependabot[bot] Feb 10, 2025
feedda2
increasing again to stop rare race condition
liquidsec Feb 11, 2025
96270df
Corrected git checkout command and missing index file from test
domwhewell-sage Feb 11, 2025
447c1b5
fix debug logs
invalid-email-address Feb 11, 2025
4283a84
make sure a minimal timeout is set if none specified
liquidsec Feb 11, 2025
ad1d088
verbose
liquidsec Feb 11, 2025
1e5c35d
fix parsing relative urls with fragments
liquidsec Feb 11, 2025
2bdbca3
Merge pull request #2282 from blacklanternsecurity/curl-backup-timeout
liquidsec Feb 11, 2025
a3af658
make mock_interaction async
liquidsec Feb 11, 2025
4d2c88a
ruff format
liquidsec Feb 11, 2025
208d635
Merge pull request #2283 from blacklanternsecurity/url-fragment-handling
liquidsec Feb 11, 2025
c4fbe42
Merge pull request #2275 from blacklanternsecurity/telerik-spellcheck…
liquidsec Feb 11, 2025
2d0c35c
Add sub to regex helper
domwhewell-sage Feb 11, 2025
d56e7c2
Add option to fuzz tags / branches and ensure we only download files …
domwhewell-sage Feb 11, 2025
406470a
why was this here?
liquidsec Feb 11, 2025
11bffa3
better mocking of poll function
liquidsec Feb 11, 2025
3690347
test debugging
liquidsec Feb 11, 2025
6ba057a
Set info, verbose and debug messages
domwhewell-sage Feb 11, 2025
f8e1d2d
Changed the download message to verbose
domwhewell-sage Feb 11, 2025
b9a926a
async queues <3
liquidsec Feb 11, 2025
4db19ca
fix test
liquidsec Feb 11, 2025
0a812de
more adjustments
liquidsec Feb 11, 2025
422c490
more adjustments
liquidsec Feb 12, 2025
61ec71f
fix github workflows
invalid-email-address Feb 12, 2025
0bb3510
fix errors
invalid-email-address Feb 12, 2025
bc71158
removing asyncio.run weirdness
liquidsec Feb 12, 2025
f61943d
reworking wordlist download, removing deps
liquidsec Feb 12, 2025
74839d5
ruff format
liquidsec Feb 12, 2025
c46c2ee
Merge pull request #2265 from blacklanternsecurity/tech-detect-preset
liquidsec Feb 12, 2025
b1e8d11
cleaner
liquidsec Feb 12, 2025
14d986b
handle slashes
invalid-email-address Feb 12, 2025
b70d1a0
removing unneeded imports
liquidsec Feb 12, 2025
b756df2
readding numpy
liquidsec Feb 12, 2025
57cab10
Merge pull request #2281 from blacklanternsecurity/fix-debug-logs
TheTechromancer Feb 13, 2025
1594d47
resolve poetry.lock conflict
invalid-email-address Feb 13, 2025
bd2e659
Merge pull request #2256 from blacklanternsecurity/generic-ssrf-rework
liquidsec Feb 13, 2025
b744ddc
Merge pull request #2237 from blacklanternsecurity/ffuf-shortnames-su…
liquidsec Feb 13, 2025
9953aaf
Merge pull request #2246 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 13, 2025
31242d9
Merge pull request #2243 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 13, 2025
e217006
Bump ruff from 0.9.3 to 0.9.6
dependabot[bot] Feb 13, 2025
267caf1
Bump pyzmq from 26.2.0 to 26.2.1
dependabot[bot] Feb 13, 2025
a1eaeb9
Merge pull request #2241 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 13, 2025
050975a
Merge pull request #2276 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 13, 2025
1c5fc99
Update nuclei
blsaccess Feb 14, 2025
41fbc0b
Update trufflehog
blsaccess Feb 15, 2025
63ad4cb
Change trufflehog status from info to verbose
domwhewell-sage Feb 15, 2025
666776a
Add the current target to the trufflehog status
domwhewell-sage Feb 15, 2025
4182214
Merge pull request #2288 from domwhewell-sage/shhhh_trufflehog
TheTechromancer Feb 16, 2025
6cf3943
Merge pull request #2286 from blacklanternsecurity/update-nuclei
TheTechromancer Feb 16, 2025
cb3c2a3
Merge pull request #2251 from blacklanternsecurity/update-trufflehog
TheTechromancer Feb 16, 2025
90e3e86
Bump lxml from 5.3.0 to 5.3.1
dependabot[bot] Feb 17, 2025
032b576
Bump mkdocs-material from 9.6.3 to 9.6.4
dependabot[bot] Feb 17, 2025
dc17189
Bump websockets from 14.2 to 15.0
dependabot[bot] Feb 17, 2025
4be278b
bump badsecrets 0.9
liquidsec Feb 17, 2025
191072b
Merge pull request #2292 from blacklanternsecurity/dependabot/pip/dev…
liquidsec Feb 17, 2025
609fa50
version
liquidsec Feb 17, 2025
b4f27e0
Merge pull request #2295 from blacklanternsecurity/bump-badsecrets-0-9
liquidsec Feb 18, 2025
fdab8e8
Merge pull request #2294 from blacklanternsecurity/dependabot/pip/dev…
TheTechromancer Feb 18, 2025
ad434cf
Merge pull request #2291 from blacklanternsecurity/dependabot/pip/dev…
TheTechromancer Feb 18, 2025
40ea06d
[create-pull-request] automated change
TheTechromancer Feb 19, 2025
63c9982
Merge pull request #2234 from blacklanternsecurity/update-docs
TheTechromancer Feb 19, 2025
1903dc9
request error handling, remove git dependency, add some branch names
invalid-email-address Feb 19, 2025
4764d3b
Merge pull request #2284 from domwhewell-sage/gitdumper
TheTechromancer Feb 19, 2025
9cf555a
resolve conflict
invalid-email-address Feb 20, 2025
22da607
log failed deps
invalid-email-address Feb 20, 2025
c12aa15
install ansible collection community.general
invalid-email-address Feb 21, 2025
ea34dc5
Merge pull request #2259 from ocervell/remove-ansible
TheTechromancer Feb 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,18 @@ assignees: ""
---

**Describe the bug**
What happened?

**Expected behavior**
What was supposed to happen?
What happened vs what was expected?

**BBOT Command**
Example: `bbot -m httpx -t evilcorp.com`

**OS, BBOT Installation Method + Version**
Example: `OS: Arch Linux, Installation method: pip, BBOT version: 1.0.3.545`
Note: You can get the bbot version with `bbot --version`
Note: Windows is **not** supported. We have successfully used BBOT on Docker Desktop in the past, however Windows is highly problematic so if you choose this path you are on your own.
Note: You can get the BBOT version with `bbot --version`
Note: BBOT is designed from the ground up to run on Linux. Windows and MacOS are not officially supported. If you are using one of these platforms, it's recommended to use Docker.

**BBOT Config**
Attach your BBOT config (`bbot --current-config`).

**Logs**
If possible, produce the bug while `--debug` is enabled, and attach the relevant parts of `~/.bbot/logs/bbot.debug.log`
Attach your full BBOT preset (to show it, add `--current-preset` to your BBOT command).

**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs/Screenshots**
If possible, produce the bug while `--debug` is enabled, and attach the relevant parts of the output.
6 changes: 4 additions & 2 deletions .github/workflows/distro_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:
python3.11 -m pipx ensurepath
pipx install poetry
"
- name: Set OS Environment Variable
run: echo "OS_NAME=${{ matrix.os }}" | sed 's|[:/]|_|g' >> $GITHUB_ENV
- name: Run tests
run: |
export PATH="$HOME/.local/bin:$PATH"
Expand All @@ -66,5 +68,5 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-debug-logs-${{ matrix.os }}
path: pytest_debug_${{ matrix.os }}.log
name: pytest-debug-logs-${{ env.OS_NAME }}
path: pytest_debug.log
8 changes: 5 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Tests (Python Versions)
name: Tests
on:
push:
branches:
Expand All @@ -24,6 +24,8 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Set Python Version Environment Variable
run: echo "PYTHON_VERSION=${{ matrix.python-version }}" | sed 's|[:/]|_|g' >> $GITHUB_ENV
- name: Install dependencies
run: |
pip install poetry
Expand All @@ -39,8 +41,8 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-debug-logs-${{ matrix.python-version }}
path: pytest_debug_${{ matrix.python-version }}.log
name: pytest-debug-logs-${{ env.PYTHON_VERSION }}
path: pytest_debug.log
- name: Upload Code Coverage
uses: codecov/codecov-action@v5
with:
Expand Down
22 changes: 13 additions & 9 deletions bbot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,11 @@ async def _main():
all_modules = list(preset.module_loader.preloaded())
scan.helpers.depsinstaller.force_deps = True
succeeded, failed = await scan.helpers.depsinstaller.install(*all_modules)
log.info("Finished installing module dependencies")
return False if failed else True
if failed:
log.hugewarning(f"Failed to install dependencies for the following modules: {', '.join(failed)}")
return False
log.hugesuccess(f"Successfully installed dependencies for the following modules: {', '.join(succeeded)}")
return True

scan_name = str(scan.name)

Expand All @@ -180,13 +183,14 @@ async def _main():

if sys.stdin.isatty():
# warn if any targets belong directly to a cloud provider
for event in scan.target.seeds.events:
if event.type == "DNS_NAME":
cloudcheck_result = scan.helpers.cloudcheck(event.host)
if cloudcheck_result:
scan.hugewarning(
f'YOUR TARGET CONTAINS A CLOUD DOMAIN: "{event.host}". You\'re in for a wild ride!'
)
if not scan.preset.strict_scope:
for event in scan.target.seeds.events:
if event.type == "DNS_NAME":
cloudcheck_result = scan.helpers.cloudcheck(event.host)
if cloudcheck_result:
scan.hugewarning(
f'YOUR TARGET CONTAINS A CLOUD DOMAIN: "{event.host}". You\'re in for a wild ride!'
)

if not options.yes:
log.hugesuccess(f"Scan ready. Press enter to execute {scan.name}")
Expand Down
19 changes: 17 additions & 2 deletions bbot/core/helpers/depsinstaller/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def __init__(self, parent_helper):
self.ensure_root_lock = Lock()

async def install(self, *modules):
self.install_core_deps()
await self.install_core_deps()
succeeded = []
failed = []
try:
Expand Down Expand Up @@ -386,20 +386,34 @@ def ensure_root(self, message=""):
else:
log.warning("Incorrect password")

def install_core_deps(self):
async def install_core_deps(self):
to_install = set()
to_install_friendly = set()
playbook = []
self._install_sudo_askpass()
# ensure tldextract data is cached
self.parent_helper.tldextract("evilcorp.co.uk")
# install any missing commands
for command, package_name_or_playbook in self.CORE_DEPS.items():
if not self.parent_helper.which(command):
to_install_friendly.add(command)
if isinstance(package_name_or_playbook, str):
to_install.add(package_name_or_playbook)
else:
playbook.extend(package_name_or_playbook)
# install ansible community.general collection
if not self.setup_status.get("ansible:community.general", False):
log.info("Installing Ansible Community General Collection")
try:
command = ["ansible-galaxy", "collection", "install", "community.general"]
await self.parent_helper.run(command, check=True)
self.setup_status["ansible:community.general"] = True
log.info("Successfully installed Ansible Community General Collection")
except CalledProcessError as err:
log.warning(
f"Failed to install Ansible Community.General Collection (return code {err.returncode}): {err.stderr}"
)
# construct ansible playbook
if to_install:
playbook.append(
{
Expand All @@ -408,6 +422,7 @@ def install_core_deps(self):
"become": True,
}
)
# run playbook
if playbook:
log.info(f"Installing core BBOT dependencies: {','.join(sorted(to_install_friendly))}")
self.ensure_root()
Expand Down
4 changes: 4 additions & 0 deletions bbot/core/helpers/regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ async def search(self, compiled_regex, *args, **kwargs):
self.ensure_compiled_regex(compiled_regex)
return await self.parent_helper.run_in_executor(compiled_regex.search, *args, **kwargs)

async def sub(self, compiled_regex, *args, **kwargs):
self.ensure_compiled_regex(compiled_regex)
return await self.parent_helper.run_in_executor(compiled_regex.sub, *args, **kwargs)

async def findall(self, compiled_regex, *args, **kwargs):
self.ensure_compiled_regex(compiled_regex)
return await self.parent_helper.run_in_executor(compiled_regex.findall, *args, **kwargs)
Expand Down
1 change: 1 addition & 0 deletions bbot/core/helpers/web/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def build_request(self, *args, **kwargs):
# TODO: re-enable this
if self._target.in_scope(str(request.url)):
for hk, hv in self._web_config.get("http_headers", {}).items():
hv = str(hv)
# don't clobber headers
if hk not in request.headers:
request.headers[hk] = hv
Expand Down
32 changes: 25 additions & 7 deletions bbot/core/helpers/web/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ async def download(self, url, **kwargs):
if success:
return filename

async def wordlist(self, path, lines=None, **kwargs):
async def wordlist(self, path, lines=None, zip=False, zip_filename=None, **kwargs):
"""
Asynchronous function for retrieving wordlists, either from a local path or a URL.
Allows for optional line-based truncation and caching. Returns the full path of the wordlist
Expand All @@ -242,6 +242,9 @@ async def wordlist(self, path, lines=None, **kwargs):
path (str): The local or remote path of the wordlist.
lines (int, optional): Number of lines to read from the wordlist.
If specified, will return a truncated wordlist with this many lines.
zip (bool, optional): Whether to unzip the file after downloading. Defaults to False.
zip_filename (str, optional): The name of the file to extract from the ZIP archive.
Required if zip is True.
cache_hrs (float, optional): Number of hours to cache the downloaded wordlist.
Defaults to 720 hours (30 days) for remote wordlists.
**kwargs: Additional keyword arguments to pass to the 'download' function for remote wordlists.
Expand All @@ -259,6 +262,8 @@ async def wordlist(self, path, lines=None, **kwargs):
Fetching and truncating to the first 100 lines
>>> wordlist_path = await self.helpers.wordlist("/root/rockyou.txt", lines=100)
"""
import zipfile

if not path:
raise WordlistError(f"Invalid wordlist: {path}")
if "cache_hrs" not in kwargs:
Expand All @@ -272,6 +277,18 @@ async def wordlist(self, path, lines=None, **kwargs):
if not filename.is_file():
raise WordlistError(f"Unable to find wordlist at {path}")

if zip:
if not zip_filename:
raise WordlistError("zip_filename must be specified when zip is True")
try:
with zipfile.ZipFile(filename, "r") as zip_ref:
if zip_filename not in zip_ref.namelist():
raise WordlistError(f"File {zip_filename} not found in the zip archive {filename}")
zip_ref.extract(zip_filename, filename.parent)
filename = filename.parent / zip_filename
except Exception as e:
raise WordlistError(f"Error unzipping file {filename}: {e}")

if lines is None:
return filename
else:
Expand Down Expand Up @@ -336,6 +353,7 @@ async def curl(self, *args, **kwargs):
ignore_bbot_global_settings = kwargs.get("ignore_bbot_global_settings", False)

if ignore_bbot_global_settings:
http_timeout = 20 # setting 20 as a worse-case setting
log.debug("ignore_bbot_global_settings enabled. Global settings will not be applied")
else:
http_timeout = self.parent_helper.web_config.get("http_timeout", 20)
Expand All @@ -349,12 +367,12 @@ async def curl(self, *args, **kwargs):
for hk, hv in self.web_config.get("http_headers", {}).items():
headers[hk] = hv

# add the timeout
if "timeout" not in kwargs:
timeout = http_timeout
# add the timeout
if "timeout" not in kwargs:
timeout = http_timeout

curl_command.append("-m")
curl_command.append(str(timeout))
curl_command.append("-m")
curl_command.append(str(timeout))

for k, v in headers.items():
if isinstance(v, list):
Expand Down Expand Up @@ -400,7 +418,7 @@ async def curl(self, *args, **kwargs):
if raw_body:
curl_command.append("-d")
curl_command.append(raw_body)

log.verbose(f"Running curl command: {curl_command}")
output = (await self.parent_helper.run(curl_command)).stdout
return output

Expand Down
2 changes: 1 addition & 1 deletion bbot/modules/badsecrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class badsecrets(BaseModule):
options_desc = {
"custom_secrets": "Include custom secrets loaded from a local file",
}
deps_pip = ["badsecrets~=0.6.21"]
deps_pip = ["badsecrets~=0.9.29"]

async def setup(self):
self.custom_secrets = None
Expand Down
18 changes: 12 additions & 6 deletions bbot/modules/deadly/ffuf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ class ffuf(BaseModule):
"lines": 5000,
"max_depth": 0,
"extensions": "",
"ignore_case": False,
}

options_desc = {
"wordlist": "Specify wordlist to use when finding directories",
"lines": "take only the first N lines from the wordlist when finding directories",
"max_depth": "the maximum directory depth to attempt to solve",
"extensions": "Optionally include a list of extensions to extend the keyword with (comma separated)",
"ignore_case": "Only put lowercase words into the wordlist",
}

deps_common = ["ffuf"]
Expand Down Expand Up @@ -301,11 +303,12 @@ async def execute_ffuf(
]
if len(pre_emit_temp_canary) == 0:
yield found_json

else:
self.warning(
"Baseline changed mid-scan. This is probably due to a WAF turning on a block against you."
self.verbose(
f"Would have reported URL [{found_json['url']}], but baseline check failed. This could be due to a WAF turning on mid-scan, or an unusual web server configuration."
)
self.warning(f"Aborting the current run against [{url}]")
self.verbose(f"Aborting the current run against [{url}]")
return

yield found_json
Expand All @@ -328,7 +331,8 @@ def generate_templist(self, prefix=None):
return self.helpers.tempfile(virtual_file, pipe=False), len(virtual_file)

def generate_wordlist(self, wordlist_file):
wordlist = []
wordlist_set = set() # Use a set to avoid duplicates
ignore_case = self.config.get("ignore_case", False) # Get the ignore_case option
for line in self.helpers.read_file(wordlist_file):
line = line.strip()
if not line:
Expand All @@ -339,5 +343,7 @@ def generate_wordlist(self, wordlist_file):
if any(x in line for x in self.banned_characters):
self.debug(f"Skipping adding [{line}] to wordlist because it has a banned character")
continue
wordlist.append(line)
return wordlist
if ignore_case:
line = line.lower() # Convert to lowercase if ignore_case is enabled
wordlist_set.add(line) # Add to set to handle duplicates
return list(wordlist_set) # Convert set back to list before returning
2 changes: 1 addition & 1 deletion bbot/modules/deadly/nuclei.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class nuclei(BaseModule):
}

options = {
"version": "3.3.8",
"version": "3.3.9",
"tags": "",
"templates": "",
"severity": "",
Expand Down
Loading
Loading