Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
9 changes: 0 additions & 9 deletions .bumpversion.cfg

This file was deleted.

9 changes: 6 additions & 3 deletions .github/workflows/pypi-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
branches:
- main

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
build-n-publish:
name: Build and publish Python
Expand All @@ -13,13 +16,13 @@ jobs:
max-parallel: 1
matrix:
python-version:
- 3.8
- "3.8"
os:
- ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v1
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install tox
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/pypi-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
branches-ignore:
- main

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
build-n-publish:
name: Build and publish Python
Expand All @@ -13,13 +16,13 @@ jobs:
max-parallel: 1
matrix:
python-version:
- 3.8
- "3.8"
os:
- ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v1
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install tox
Expand Down
15 changes: 12 additions & 3 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
build:
name: Execute Unit Tests
Expand All @@ -16,15 +19,21 @@ jobs:
max-parallel: 1
matrix:
python-version:
- 3.8
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
os:
- ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v1
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install modules
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ ipython_config.py
# install all needed dependencies.
#Pipfile.lock

# uv
uv.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

Expand Down Expand Up @@ -134,4 +137,5 @@ dmypy.json
gremlin-python.iml

_*.json
_t.py
_t.py
ENDPOINTS.md
20 changes: 5 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,11 @@

Package version control conforms to semantic versioning.

This package uses [bumpversion](https://github.com/peritus/bumpversion) to manage the version
The version is defined statically in `pyproject.toml`:

Quick usage:

Major version update:
```bash
bumpversion --new-version 1.0.0 major
```

Minor version update:
```bash
bumpversion --new-version 0.4.0 minor
```toml
[project]
version = "0.19.2"
```

Patch version update:
```bash
bumpversion --new-version 0.3.13 patch
```
To cut a release, update the `version` field manually and open a PR against `main`.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
prune tools
50 changes: 0 additions & 50 deletions Makefile

This file was deleted.

19 changes: 11 additions & 8 deletions gremlinapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,24 @@
class SecretsFilter(logging.Filter):
def filter(self, record):
secret_length = 5
if len(GremlinAPIConfig.api_key) >= secret_length:
api_key = GremlinAPIConfig.api_key or ""
bearer_token = GremlinAPIConfig.bearer_token or ""
password = GremlinAPIConfig.password or ""
if len(api_key) >= secret_length:
record.msg = re.sub(
rf"{GremlinAPIConfig.api_key}[\'\s]?",
"..." + GremlinAPIConfig.api_key[-4:],
rf"{re.escape(api_key)}[\'\s]?",
"..." + api_key[-4:],
record.msg,
)
if len(GremlinAPIConfig.bearer_token) >= secret_length:
if len(bearer_token) >= secret_length:
record.msg = re.sub(
rf"{GremlinAPIConfig.bearer_token}[\'\s]?",
"..." + GremlinAPIConfig.bearer_token[-4:],
rf"{re.escape(bearer_token)}[\'\s]?",
"..." + bearer_token[-4:],
record.msg,
)
if len(GremlinAPIConfig.password) >= secret_length:
if len(password) >= secret_length:
record.msg = re.sub(
rf"{GremlinAPIConfig.password}[\'\s]?",
rf"{re.escape(password)}[\'\s]?",
"[PASSWORD REDACTED]",
record.msg,
)
Expand Down
55 changes: 40 additions & 15 deletions gremlinapi/attacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,27 @@ def list_active_attacks(
https_client: Type[GremlinAPIHttpClient] = get_gremlin_httpclient(),
*args: tuple,
**kwargs: dict,
) -> dict:
) -> list:
method: str = "GET"
endpoint: str = cls._list_endpoint("/attacks/active", **kwargs)
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
return body
source: str = kwargs.get("source", None)
page_size = kwargs.get("pageSize", None)
page_token: str = None
all_items: list = []
while True:
endpoint = cls._optional_team_endpoint("/attacks/active/paged", **kwargs)
if source and source.lower() in ("adhoc", "scenario"):
endpoint = cls._add_query_param(endpoint, "source", source)
if page_size:
endpoint = cls._add_query_param(endpoint, "pageSize", str(page_size))
if page_token:
endpoint = cls._add_query_param(endpoint, "pageToken", page_token)
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
all_items.extend(body.get("items", []))
page_token = body.get("pageToken") or None
if not page_token:
Comment thread
sewhyte marked this conversation as resolved.
break
return all_items

@classmethod
@register_cli_action("list_attacks", ("",), ("source", "pageSize", "teamId"))
Expand Down Expand Up @@ -110,17 +125,27 @@ def list_completed_attacks(
https_client: Type[GremlinAPIHttpClient] = get_gremlin_httpclient(),
*args: tuple,
**kwargs: dict,
) -> dict:
"""
:param https_client:
:param kwargs: { source(adhoc or scenario, query), pageSize(int32, query), teamId(string, query) }
:return:
"""
) -> list:
method: str = "GET"
endpoint: str = cls._list_endpoint("/attacks/completed", **kwargs)
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
return body
source: str = kwargs.get("source", None)
page_size = kwargs.get("pageSize", None)
page_token: str = None
all_items: list = []
while True:
endpoint = cls._optional_team_endpoint("/attacks/completed/paged", **kwargs)
if source and source.lower() in ("adhoc", "scenario"):
endpoint = cls._add_query_param(endpoint, "source", source)
if page_size:
endpoint = cls._add_query_param(endpoint, "pageSize", str(page_size))
if page_token:
endpoint = cls._add_query_param(endpoint, "pageToken", page_token)
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
all_items.extend(body.get("items", []))
page_token = body.get("pageToken") or None
if not page_token:
Comment on lines +145 to +146
break
return all_items

@classmethod
@register_cli_action("get_attack", ("guid",), ("teamId",))
Expand Down
24 changes: 18 additions & 6 deletions gremlinapi/companies.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,30 @@ def update_company_saml_props(
return body

@classmethod
@register_cli_action("list_company_users", ("identifier",), ("",))
@register_cli_action("list_company_users", ("identifier",), ("pageSize",))
def list_company_users(
cls,
https_client: Type[GremlinAPIHttpClient] = get_gremlin_httpclient(),
**kwargs: dict,
) -> dict:
) -> list:
method: str = "GET"
identifier: str = cls._error_if_not_param("identifier", **kwargs)
endpoint: str = f"/companies/{identifier}/users"
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
return body
page_size = kwargs.get("pageSize", None)
page_token: str = None
all_items: list = []
while True:
endpoint: str = f"/companies/{identifier}/users/paged"
if page_size:
endpoint = cls._add_query_param(endpoint, "pageSize", str(page_size))
if page_token:
endpoint = cls._add_query_param(endpoint, "pageToken", page_token)
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
all_items.extend(body.get("items", []))
page_token = body.get("page_token") or None
Comment thread
sewhyte marked this conversation as resolved.
if not page_token:
break
return all_items

@classmethod
@register_cli_action(
Expand Down
31 changes: 22 additions & 9 deletions gremlinapi/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,34 @@ def report_pricing(
return body

@classmethod
@register_cli_action("report_teams", ("",), ("start", "end", "period", "teamId"))
@register_cli_action("report_teams", ("",), ("start", "end", "period", "teamId", "pageSize"))
def report_teams(
cls,
https_client: Type[GremlinAPIHttpClient] = get_gremlin_httpclient(),
*args: tuple,
**kwargs: dict,
) -> dict:
) -> list:
method: str = "GET"
params: list = ["startDate", "endDate"]
endpoint: str = cls._build_query_string_option_team_endpoint(
"/reports/teams", params, **kwargs
)
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
return body
start_date: str = cls._error_if_not_param("startDate", **kwargs)
end_date: str = cls._error_if_not_param("endDate", **kwargs)
page_size = kwargs.get("pageSize", None)
Comment thread
sewhyte marked this conversation as resolved.
page_token: str = None
all_items: list = []
while True:
endpoint = cls._add_query_param("/reports/teams/paged", "startDate", start_date)
Comment thread
sewhyte marked this conversation as resolved.
endpoint = cls._add_query_param(endpoint, "endDate", end_date)
endpoint = cls._optional_team_endpoint(endpoint, **kwargs)
if page_size:
endpoint = cls._add_query_param(endpoint, "pageSize", str(page_size))
if page_token:
endpoint = cls._add_query_param(endpoint, "pageToken", page_token)
Comment thread
sewhyte marked this conversation as resolved.
Comment thread
sewhyte marked this conversation as resolved.
payload: dict = cls._payload(**{"headers": https_client.header()})
(resp, body) = https_client.api_call(method, endpoint, **payload)
all_items.extend(body.get("items", []))
page_token = body.get("pageToken") or None
if not page_token:
break
return all_items

@classmethod
@register_cli_action("report_users", ("",), ("start", "end", "period", "teamId"))
Expand Down
Loading
Loading