Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 4

[*.{json,yaml,yml,md}]
indent_size = 2

[Makefile]
indent_style = tab
38 changes: 33 additions & 5 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,50 @@ jobs:
matrix:
python-version: ["3.13"]
poetry-version: ["2.3.1"]
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install GNU sed on macOS
if: runner.os == 'macOS'
run: brew install gnu-sed
- name: Install poetry
run: pip install poetry==${{ matrix.poetry-version}}
- name: Install deps
run: make install
- name: Run tests
run: make test
- name: Check formatting
run: make check-format
- name: Run linter
run: make lint
- name: Security audit
run: make audit

ci-windows:
strategy:
fail-fast: false
matrix:
python-version: ["3.13"]
poetry-version: ["2.3.1"]
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
run: pip install poetry==${{ matrix.poetry-version}}
- name: View poetry --help
run: poetry --help
- name: Install deps
run: poetry install
- name: Run tests
run: poetry run pytest tests
- name: Run black
run: poetry run black leakix/*.py tests/*.py example/*.py --check
- name: Check formatting
run: poetry run ruff format --check leakix/ tests/ example/ executable/
- name: Run linter
run: poetry run ruff check leakix/ tests/ example/ executable/
- name: Security audit
run: poetry run pip-audit
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.10] - 2024-12-XX

### Changed

- Updated to Python 3.13+
([65c5121](https://github.com/LeakIX/LeakIXClient-Python/commit/65c5121))
- Updated l9format requirement from 1.3.1a3 to 1.3.2
([0975c1c](https://github.com/LeakIX/LeakIXClient-Python/commit/0975c1c))
- Updated fire requirement from ^0.5.0 to >=0.5,<0.8
([7cb5dae](https://github.com/LeakIX/LeakIXClient-Python/commit/7cb5dae))
- Bumped actions/setup-python from 5 to 6
([b1bc0da](https://github.com/LeakIX/LeakIXClient-Python/commit/b1bc0da))
- Bumped actions/checkout from 4 to 6
([6777ad9](https://github.com/LeakIX/LeakIXClient-Python/commit/6777ad9))

### Infrastructure

- Added pip-audit security scanning to CI
([62550bc](https://github.com/LeakIX/LeakIXClient-Python/commit/62550bc))
- Added Dependabot configuration for Python and GitHub Actions
([4dd4948](https://github.com/LeakIX/LeakIXClient-Python/commit/4dd4948))

## [0.1.9] - Previous Release

### Added

- Initial documented release
- Python client for LeakIX API
- Support for service and leak queries
- Host lookup by IPv4
- Plugin listing for authenticated users
- Subdomain queries
- Bulk export functionality
- Query building with MustQuery, MustNotQuery, ShouldQuery
- Field filters: TimeField, PluginField, IPField, PortField, CountryField

[unreleased]: https://github.com/LeakIX/LeakIXClient-Python/compare/v0.1.10...HEAD
[0.1.10]: https://github.com/LeakIX/LeakIXClient-Python/compare/v0.1.9...v0.1.10
[0.1.9]: https://github.com/LeakIX/LeakIXClient-Python/releases/tag/v0.1.9
107 changes: 107 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# LeakIXClient-Python Makefile

.PHONY: help
help: ## Ask for help!
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
awk 'BEGIN {FS = ":.*?## "}; \
{printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: install
install: ## Install dependencies
poetry install

.PHONY: build
build: ## Build the package
poetry build

.PHONY: test
test: ## Run tests
poetry run pytest

.PHONY: test-cov
test-cov: ## Run tests with coverage
poetry run pytest --cov=leakix --cov-report=term-missing

.PHONY: format
format: ## Format code with ruff
poetry run ruff format leakix/ tests/ example/ executable/

.PHONY: check-format
check-format: ## Check code formatting
poetry run ruff format --check leakix/ tests/ example/ executable/

.PHONY: lint
lint: ## Run ruff linter
poetry run ruff check leakix/ tests/ example/ executable/

.PHONY: lint-fix
lint-fix: ## Run ruff linter with auto-fix
poetry run ruff check --fix leakix/ tests/ example/ executable/

.PHONY: typecheck
typecheck: ## Run mypy type checker
poetry run mypy leakix/

.PHONY: audit
audit: ## Run security audit
poetry run pip-audit

.PHONY: check
check: check-format lint typecheck test ## Run all checks

.PHONY: check-outdated
check-outdated: ## Check for outdated dependencies
poetry show --outdated || true

.PHONY: clean
clean: ## Clean build artifacts
rm -rf dist/ build/ *.egg-info/ .pytest_cache/ .mypy_cache/ .ruff_cache/
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete 2>/dev/null || true

# Trailing whitespace targets
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SED := $(shell command -v gsed 2>/dev/null)
ifeq ($(SED),)
$(error GNU sed (gsed) not found on macOS. Install with: brew install gnu-sed)
endif
else
SED := sed
endif

.PHONY: fix-trailing-whitespace
fix-trailing-whitespace: ## Remove trailing whitespaces from all files
@echo "Removing trailing whitespaces from all files..."
@find . -type f \( \
-name "*.py" -o -name "*.toml" -o -name "*.md" -o -name "*.yaml" \
-o -name "*.yml" -o -name "*.json" \) \
-not -path "./.git/*" \
-not -path "./.mypy_cache/*" \
-not -path "./.pytest_cache/*" \
-not -path "./.ruff_cache/*" \
-exec sh -c \
'$(SED) -i -e "s/[[:space:]]*$$//" "$$1"' \
_ {} \; && \
echo "Trailing whitespaces removed."

.PHONY: check-trailing-whitespace
check-trailing-whitespace: ## Check for trailing whitespaces in source files
@echo "Checking for trailing whitespaces..."
@files_with_trailing_ws=$$(find . -type f \( \
-name "*.py" -o -name "*.toml" -o -name "*.md" -o -name "*.yaml" \
-o -name "*.yml" -o -name "*.json" \) \
-not -path "./.git/*" \
-not -path "./.mypy_cache/*" \
-not -path "./.pytest_cache/*" \
-not -path "./.ruff_cache/*" \
-exec grep -l '[[:space:]]$$' {} + 2>/dev/null || true); \
if [ -n "$$files_with_trailing_ws" ]; then \
echo "Files with trailing whitespaces found:"; \
echo "$$files_with_trailing_ws" | sed 's/^/ /'; \
echo ""; \
echo "Run 'make fix-trailing-whitespace' to fix automatically."; \
exit 1; \
else \
echo "No trailing whitespaces found."; \
fi
29 changes: 11 additions & 18 deletions example/example_client.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import datetime, timedelta

import decouple

from leakix import Client
from leakix.query import MustQuery, MustNotQuery, RawQuery
from leakix.field import PluginField, CountryField, TimeField, Operator
from leakix.field import CountryField, Operator, PluginField, TimeField
from leakix.plugin import Plugin
from datetime import datetime, timedelta
from leakix.query import MustNotQuery, MustQuery, RawQuery

API_KEY = decouple.config("API_KEY")
CLIENT = Client(api_key=API_KEY)
Expand All @@ -23,7 +25,7 @@ def example_get_service_filter_plugin():
response = CLIENT.get_service(queries=[query_http_ntlm])
assert response.status_code() == 200, response.status_code()
# check we only get NTML related services
assert all((i.tags == ["ntlm"] for i in response.json()))
assert all(i.tags == ["ntlm"] for i in response.json())


def example_get_service_filter_plugin_with_pagination():
Expand All @@ -36,7 +38,7 @@ def example_get_service_filter_plugin_with_pagination():
response = CLIENT.get_service(queries=[query_http_ntlm], page=1)
assert response.status_code() == 200
# check we only get NTML related services
assert all((i.tags == ["ntlm"] for i in response.json()))
assert all(i.tags == ["ntlm"] for i in response.json())


def example_get_leaks_filter_multiple_plugins():
Expand All @@ -45,10 +47,7 @@ def example_get_leaks_filter_multiple_plugins():
response = CLIENT.get_leak(queries=[query_http_ntlm, query_country])
assert response.status_code() == 200, response.status_code()
assert all(
(
i.geoip.country_name == "France" and i.tags == ["ntlm"]
for i in response.json()
)
i.geoip.country_name == "France" and i.tags == ["ntlm"] for i in response.json()
)


Expand All @@ -58,10 +57,7 @@ def example_get_leaks_multiple_filter_plugins_must_not():
response = CLIENT.get_leak(queries=[query_http_ntlm, query_country])
assert response.status_code() == 200, response.status_code()
assert all(
(
i.geoip.country_name != "France" and i.tags == ["ntlm"]
for i in response.json()
)
i.geoip.country_name != "France" and i.tags == ["ntlm"] for i in response.json()
)


Expand All @@ -71,10 +67,7 @@ def example_get_leak_raw_query():
response = CLIENT.get_leak(queries=[query])
assert response.status_code() == 200, response.status_code()
assert all(
(
i.geoip.country_name == "France" and i.tags == ["ntlm"]
for i in response.json()
)
i.geoip.country_name == "France" and i.tags == ["ntlm"] for i in response.json()
)


Expand Down Expand Up @@ -137,4 +130,4 @@ def example_get_subdomains():
example_bulk_export()
example_bulk_service()
example_bulk_export_last_event()
example_get_subdomain()
example_get_subdomains()
14 changes: 7 additions & 7 deletions executable/cli.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from leakix import Client
import json
from leakix.query import RawQuery, MustQuery
from datetime import datetime

import fire
from decouple import config
from leakix.field import TimeField, Operator, PluginField, UpdateDateField
from typing import Optional
from datetime import datetime

from leakix import Client
from leakix.field import Operator, UpdateDateField
from leakix.query import MustQuery, RawQuery

API_KEY = config("API_KEY")
DATETIME_FORMAT = "%Y-%m-%d"
Expand All @@ -17,8 +17,8 @@ def bulk_export_to_json(
self,
query: str,
filename: str,
before: Optional[str] = None,
after: Optional[str] = None,
before: str | None = None,
after: str | None = None,
):
before_dt = datetime.strptime(before, DATETIME_FORMAT)
after_dt = datetime.strptime(after, DATETIME_FORMAT)
Expand Down
Loading