threatzone 1.0.0__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.
- threatzone-1.0.0/.actrc +18 -0
- threatzone-1.0.0/.github/act-events/pull_request.json +21 -0
- threatzone-1.0.0/.github/act-events/push.json +24 -0
- threatzone-1.0.0/.github/act-events/release.json +15 -0
- threatzone-1.0.0/.github/act-events/workflow_dispatch.json +11 -0
- threatzone-1.0.0/.github/workflows/README.md +68 -0
- threatzone-1.0.0/.github/workflows/ci.yml +155 -0
- threatzone-1.0.0/.github/workflows/publish.yml +199 -0
- threatzone-1.0.0/.gitignore +82 -0
- threatzone-1.0.0/.python-version +1 -0
- threatzone-1.0.0/LICENSE +21 -0
- threatzone-1.0.0/PKG-INFO +213 -0
- threatzone-1.0.0/README.md +177 -0
- threatzone-1.0.0/docs/RECIPES.md +633 -0
- threatzone-1.0.0/docs/TYPES.md +244 -0
- threatzone-1.0.0/examples/README.md +39 -0
- threatzone-1.0.0/examples/recipe_01_submit_and_wait.py +58 -0
- threatzone-1.0.0/examples/recipe_02_url_analysis.py +59 -0
- threatzone-1.0.0/examples/recipe_03_get_malicious_indicators.py +65 -0
- threatzone-1.0.0/examples/recipe_04_download_all_artifacts.py +55 -0
- threatzone-1.0.0/examples/recipe_05_search_by_sha256.py +51 -0
- threatzone-1.0.0/examples/recipe_06_export_network_iocs.py +70 -0
- threatzone-1.0.0/examples/recipe_07_handle_report_unavailable.py +54 -0
- threatzone-1.0.0/examples/recipe_08_paginate_submissions.py +50 -0
- threatzone-1.0.0/examples/recipe_09_discriminate_errors_by_code.py +72 -0
- threatzone-1.0.0/examples/recipe_10_async_client.py +60 -0
- threatzone-1.0.0/examples/recipe_11_daily_summary.py +65 -0
- threatzone-1.0.0/examples/recipe_12_mitre_cross_reference.py +73 -0
- threatzone-1.0.0/pyproject.toml +90 -0
- threatzone-1.0.0/scripts/act-ci.sh +32 -0
- threatzone-1.0.0/scripts/act-publish.sh +39 -0
- threatzone-1.0.0/scripts/sdk-smoke-real-api.py +102 -0
- threatzone-1.0.0/src/threatzone/__init__.py +177 -0
- threatzone-1.0.0/src/threatzone/_async_client.py +1161 -0
- threatzone-1.0.0/src/threatzone/_client.py +391 -0
- threatzone-1.0.0/src/threatzone/_config.py +79 -0
- threatzone-1.0.0/src/threatzone/_constants.py +16 -0
- threatzone-1.0.0/src/threatzone/_exceptions.py +256 -0
- threatzone-1.0.0/src/threatzone/_streaming.py +209 -0
- threatzone-1.0.0/src/threatzone/_sync_client.py +1158 -0
- threatzone-1.0.0/src/threatzone/py.typed +0 -0
- threatzone-1.0.0/src/threatzone/testing/__init__.py +24 -0
- threatzone-1.0.0/src/threatzone/testing/_responses.py +833 -0
- threatzone-1.0.0/src/threatzone/testing/fake_api.py +1232 -0
- threatzone-1.0.0/src/threatzone/testing/routes.py +154 -0
- threatzone-1.0.0/src/threatzone/testing/scenarios.py +131 -0
- threatzone-1.0.0/src/threatzone/testing/state.py +129 -0
- threatzone-1.0.0/src/threatzone/types/__init__.py +244 -0
- threatzone-1.0.0/src/threatzone/types/behaviours.py +40 -0
- threatzone-1.0.0/src/threatzone/types/cdr.py +34 -0
- threatzone-1.0.0/src/threatzone/types/common.py +86 -0
- threatzone-1.0.0/src/threatzone/types/config.py +52 -0
- threatzone-1.0.0/src/threatzone/types/downloads.py +16 -0
- threatzone-1.0.0/src/threatzone/types/eml.py +56 -0
- threatzone-1.0.0/src/threatzone/types/errors.py +44 -0
- threatzone-1.0.0/src/threatzone/types/indicators.py +228 -0
- threatzone-1.0.0/src/threatzone/types/me.py +117 -0
- threatzone-1.0.0/src/threatzone/types/mitre.py +17 -0
- threatzone-1.0.0/src/threatzone/types/network.py +151 -0
- threatzone-1.0.0/src/threatzone/types/processes.py +94 -0
- threatzone-1.0.0/src/threatzone/types/signature_check.py +29 -0
- threatzone-1.0.0/src/threatzone/types/static_scan.py +34 -0
- threatzone-1.0.0/src/threatzone/types/submissions.py +124 -0
- threatzone-1.0.0/src/threatzone/types/syscalls.py +17 -0
- threatzone-1.0.0/src/threatzone/types/url_analysis.py +153 -0
- threatzone-1.0.0/tests/__init__.py +1 -0
- threatzone-1.0.0/tests/conftest.py +859 -0
- threatzone-1.0.0/tests/integration/__init__.py +6 -0
- threatzone-1.0.0/tests/integration/conftest.py +61 -0
- threatzone-1.0.0/tests/integration/flows/__init__.py +1 -0
- threatzone-1.0.0/tests/integration/flows/test_flow_access_control.py +70 -0
- threatzone-1.0.0/tests/integration/flows/test_flow_cdr_lifecycle.py +40 -0
- threatzone-1.0.0/tests/integration/flows/test_flow_dynamic_lifecycle.py +76 -0
- threatzone-1.0.0/tests/integration/flows/test_flow_static_only.py +54 -0
- threatzone-1.0.0/tests/integration/flows/test_flow_url_lifecycle.py +52 -0
- threatzone-1.0.0/tests/integration/recipes/__init__.py +1 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_01_submit_and_wait.py +80 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_02_url_analysis.py +63 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_03_malicious_indicators.py +71 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_04_download_artifacts.py +63 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_05_search_by_sha256.py +38 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_06_export_network_iocs.py +75 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_07_report_unavailable.py +76 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_08_pagination.py +63 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_09_error_discrimination.py +75 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_10_async_client.py +67 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_11_daily_summary.py +53 -0
- threatzone-1.0.0/tests/integration/recipes/test_recipe_12_mitre_cross_reference.py +64 -0
- threatzone-1.0.0/tests/integration/test_contract_pydantic.py +60 -0
- threatzone-1.0.0/tests/integration/test_examples.py +181 -0
- threatzone-1.0.0/tests/test_async_client.py +1474 -0
- threatzone-1.0.0/tests/test_client.py +517 -0
- threatzone-1.0.0/tests/test_exceptions.py +355 -0
- threatzone-1.0.0/tests/test_streaming.py +356 -0
- threatzone-1.0.0/tests/test_sync_client.py +1343 -0
- threatzone-1.0.0/tests/test_types.py +747 -0
- threatzone-1.0.0/uv.lock +708 -0
threatzone-1.0.0/.actrc
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# act defaults for threatzone-python-sdk
|
|
2
|
+
# Use catthehacker's medium image — ships with python, node, uv-compatible tooling.
|
|
3
|
+
# "micro" is too small; "full" is multi-gigabyte. Medium is the sweet spot.
|
|
4
|
+
-P ubuntu-latest=catthehacker/ubuntu:act-latest
|
|
5
|
+
-P ubuntu-22.04=catthehacker/ubuntu:act-22.04
|
|
6
|
+
-P ubuntu-24.04=catthehacker/ubuntu:act-24.04
|
|
7
|
+
|
|
8
|
+
# Reuse containers between runs for caching. Faster repeat runs, mirrors CI re-runs.
|
|
9
|
+
--reuse
|
|
10
|
+
|
|
11
|
+
# Default event context file (realistic push-to-main payload).
|
|
12
|
+
--eventpath .github/act-events/push.json
|
|
13
|
+
|
|
14
|
+
# Default workflow directory (same as github).
|
|
15
|
+
--workflows .github/workflows/
|
|
16
|
+
|
|
17
|
+
# Default branch context so github.ref resolves to main.
|
|
18
|
+
--defaultbranch main
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"action": "opened",
|
|
3
|
+
"pull_request": {
|
|
4
|
+
"number": 1,
|
|
5
|
+
"state": "open",
|
|
6
|
+
"title": "local act PR",
|
|
7
|
+
"base": {
|
|
8
|
+
"ref": "main",
|
|
9
|
+
"sha": "0000000000000000000000000000000000000000"
|
|
10
|
+
},
|
|
11
|
+
"head": {
|
|
12
|
+
"ref": "local-act-branch",
|
|
13
|
+
"sha": "0000000000000000000000000000000000000001"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"name": "threatzone-python-sdk",
|
|
18
|
+
"full_name": "Malwation/threatzone-python-sdk",
|
|
19
|
+
"default_branch": "main"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ref": "refs/heads/main",
|
|
3
|
+
"before": "0000000000000000000000000000000000000000",
|
|
4
|
+
"after": "0000000000000000000000000000000000000001",
|
|
5
|
+
"repository": {
|
|
6
|
+
"name": "threatzone-python-sdk",
|
|
7
|
+
"full_name": "Malwation/threatzone-python-sdk",
|
|
8
|
+
"default_branch": "main",
|
|
9
|
+
"master_branch": "main"
|
|
10
|
+
},
|
|
11
|
+
"pusher": {
|
|
12
|
+
"name": "local-act",
|
|
13
|
+
"email": "local-act@threatzone.test"
|
|
14
|
+
},
|
|
15
|
+
"commits": [],
|
|
16
|
+
"head_commit": {
|
|
17
|
+
"id": "0000000000000000000000000000000000000001",
|
|
18
|
+
"message": "local act push event",
|
|
19
|
+
"author": {
|
|
20
|
+
"name": "local-act",
|
|
21
|
+
"email": "local-act@threatzone.test"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"action": "published",
|
|
3
|
+
"ref": "refs/tags/v1.0.0",
|
|
4
|
+
"release": {
|
|
5
|
+
"tag_name": "v1.0.0",
|
|
6
|
+
"name": "local act release v1.0.0",
|
|
7
|
+
"draft": false,
|
|
8
|
+
"prerelease": false
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"name": "threatzone-python-sdk",
|
|
12
|
+
"full_name": "Malwation/threatzone-python-sdk",
|
|
13
|
+
"default_branch": "main"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Local workflow testing with `act`
|
|
2
|
+
|
|
3
|
+
This SDK ships two GitHub Actions workflows:
|
|
4
|
+
|
|
5
|
+
| File | Triggers | Jobs |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `ci.yml` | `push` / `pull_request` on `main`,`develop`; `workflow_dispatch` | `lint`, `type-check`, `test` (Python 3.10–3.13 matrix), `build` |
|
|
8
|
+
| `publish.yml` | `release: published`; `workflow_dispatch` (with `dry_run` input) | `test` (matrix), `lint`, `type-check`, `version-check`, `build`, `publish-pypi` |
|
|
9
|
+
|
|
10
|
+
Both workflows are fully runnable **locally** via [`act`](https://github.com/nektos/act) so you can iterate on CI without pushing dummy commits. Everything needed for local runs is committed alongside the workflows:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
.actrc # default image, --reuse, --defaultbranch main
|
|
14
|
+
.github/act-events/ # realistic event payloads (push, PR, release, workflow_dispatch)
|
|
15
|
+
scripts/act-ci.sh # wrapper around `act` for ci.yml
|
|
16
|
+
scripts/act-publish.sh # wrapper around `act` for publish.yml (dry-run by default)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
1. **Docker** running locally.
|
|
22
|
+
2. **`act` ≥ 0.2.87** on your PATH:
|
|
23
|
+
```bash
|
|
24
|
+
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
|
|
25
|
+
sudo mv ./bin/act /usr/local/bin/act
|
|
26
|
+
act --version
|
|
27
|
+
```
|
|
28
|
+
3. First invocation will pull `catthehacker/ubuntu:act-latest` (~1 GB). Subsequent runs reuse it.
|
|
29
|
+
|
|
30
|
+
## Running CI
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
./scripts/act-ci.sh # run every CI job (lint + type-check + 4-python matrix + build)
|
|
34
|
+
./scripts/act-ci.sh lint # ruff check + ruff format --check
|
|
35
|
+
./scripts/act-ci.sh type-check # mypy
|
|
36
|
+
./scripts/act-ci.sh test # pytest matrix (Python 3.10 → 3.13), 4 parallel containers
|
|
37
|
+
./scripts/act-ci.sh build # uv build, wheel import smoke, artifact upload
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `test` job is the slowest — act spins up one container per Python version. Use `./scripts/act-ci.sh test -j test --matrix python-version:3.12` if you only want to exercise a single version.
|
|
41
|
+
|
|
42
|
+
## Running Publish (dry-run)
|
|
43
|
+
|
|
44
|
+
`publish.yml` gates the real PyPI upload behind `github.event.inputs.dry_run == 'false'` or a `release` event. The wrapper defaults to `workflow_dispatch` with `dry_run=true`, which means:
|
|
45
|
+
|
|
46
|
+
- ✅ `lint`, `type-check`, `test` matrix, `build`, wheel-import smoke ALL run
|
|
47
|
+
- ⏭ `version-check` is skipped (only runs for real `release` events)
|
|
48
|
+
- ⏭ `publish-pypi` is skipped (dry-run gate)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
./scripts/act-publish.sh # dry-run: every job except publish-pypi
|
|
52
|
+
./scripts/act-publish.sh build # dry-run, single job
|
|
53
|
+
./scripts/act-publish.sh --release # simulate a real release event
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The `--release` mode will trigger `version-check` and attempt to reach `publish-pypi` but still fails safely because no real PyPI token is wired into the local act environment.
|
|
57
|
+
|
|
58
|
+
## Event payloads
|
|
59
|
+
|
|
60
|
+
Every fixture under `.github/act-events/` is a realistic-enough subset of the GitHub webhook payload to let the workflow expressions (`github.event_name`, `github.ref`, `github.event.inputs.dry_run`) resolve correctly. Edit the files if a new workflow needs additional fields.
|
|
61
|
+
|
|
62
|
+
## Known local-run caveats
|
|
63
|
+
|
|
64
|
+
| Step | Behavior under act | Why |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `actions/upload-artifact@v4` | Skipped via `if: ${{ !env.ACT }}` | v4 requires `ACTIONS_RUNTIME_TOKEN` which act cannot provide. The step runs normally on real GitHub. Consequence: `publish-pypi` under `--release` cannot find the artifact locally, but that path also can't reach PyPI without credentials so it's moot. |
|
|
67
|
+
| `codecov/codecov-action@v4` | Runs, upload silently fails | Gated behind `github.event_name == 'push' && github.ref == 'refs/heads/main'` — the push event payload satisfies this but no codecov token is wired in locally. Non-fatal. |
|
|
68
|
+
| `pypa/gh-action-pypi-publish@release/v1` | Never reached | Blocked by the dry-run gate; see above. |
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
# Cancel in-progress runs on the same ref when a new commit lands
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
lint:
|
|
20
|
+
name: Lint + format (ruff)
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v4
|
|
27
|
+
with:
|
|
28
|
+
version: latest
|
|
29
|
+
enable-cache: true
|
|
30
|
+
cache-dependency-glob: "uv.lock"
|
|
31
|
+
|
|
32
|
+
- name: Set up Python
|
|
33
|
+
run: uv python install 3.12
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: uv sync --all-extras --dev
|
|
37
|
+
|
|
38
|
+
- name: ruff check
|
|
39
|
+
run: uv run ruff check src/ tests/
|
|
40
|
+
|
|
41
|
+
- name: ruff format --check
|
|
42
|
+
run: uv run ruff format --check src/ tests/
|
|
43
|
+
|
|
44
|
+
type-check:
|
|
45
|
+
name: Type check (mypy)
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- name: Install uv
|
|
51
|
+
uses: astral-sh/setup-uv@v4
|
|
52
|
+
with:
|
|
53
|
+
version: latest
|
|
54
|
+
enable-cache: true
|
|
55
|
+
cache-dependency-glob: "uv.lock"
|
|
56
|
+
|
|
57
|
+
- name: Set up Python
|
|
58
|
+
run: uv python install 3.12
|
|
59
|
+
|
|
60
|
+
- name: Install dependencies
|
|
61
|
+
run: uv sync --all-extras --dev
|
|
62
|
+
|
|
63
|
+
- name: mypy
|
|
64
|
+
run: uv run mypy src/threatzone
|
|
65
|
+
|
|
66
|
+
test:
|
|
67
|
+
name: Unit tests (Python ${{ matrix.python-version }})
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
strategy:
|
|
70
|
+
fail-fast: false
|
|
71
|
+
matrix:
|
|
72
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
73
|
+
env:
|
|
74
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
75
|
+
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
|
|
79
|
+
- name: Install uv
|
|
80
|
+
uses: astral-sh/setup-uv@v4
|
|
81
|
+
with:
|
|
82
|
+
version: latest
|
|
83
|
+
enable-cache: true
|
|
84
|
+
cache-dependency-glob: "uv.lock"
|
|
85
|
+
|
|
86
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
87
|
+
run: uv python install ${{ matrix.python-version }}
|
|
88
|
+
|
|
89
|
+
- name: Install dependencies
|
|
90
|
+
run: uv sync --all-extras --dev --python ${{ matrix.python-version }}
|
|
91
|
+
|
|
92
|
+
- name: Run tests
|
|
93
|
+
run: uv run pytest tests/ -v --tb=short
|
|
94
|
+
|
|
95
|
+
- name: Run tests with coverage
|
|
96
|
+
if: matrix.python-version == '3.12'
|
|
97
|
+
run: |
|
|
98
|
+
uv run pytest tests/ \
|
|
99
|
+
--cov=src/threatzone \
|
|
100
|
+
--cov-report=xml \
|
|
101
|
+
--cov-report=term-missing \
|
|
102
|
+
--cov-fail-under=75
|
|
103
|
+
|
|
104
|
+
- name: Upload coverage to Codecov
|
|
105
|
+
if: matrix.python-version == '3.12' && github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
106
|
+
uses: codecov/codecov-action@v4
|
|
107
|
+
with:
|
|
108
|
+
file: ./coverage.xml
|
|
109
|
+
fail_ci_if_error: false
|
|
110
|
+
|
|
111
|
+
build:
|
|
112
|
+
name: Build distribution
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
needs: [lint, type-check, test]
|
|
115
|
+
steps:
|
|
116
|
+
- uses: actions/checkout@v4
|
|
117
|
+
|
|
118
|
+
- name: Install uv
|
|
119
|
+
uses: astral-sh/setup-uv@v4
|
|
120
|
+
with:
|
|
121
|
+
version: latest
|
|
122
|
+
enable-cache: true
|
|
123
|
+
cache-dependency-glob: "uv.lock"
|
|
124
|
+
|
|
125
|
+
- name: Set up Python
|
|
126
|
+
run: uv python install 3.12
|
|
127
|
+
|
|
128
|
+
- name: Install dependencies
|
|
129
|
+
run: uv sync --all-extras --dev
|
|
130
|
+
|
|
131
|
+
- name: Build wheel and sdist
|
|
132
|
+
run: uv build
|
|
133
|
+
|
|
134
|
+
- name: Check distribution contents
|
|
135
|
+
run: |
|
|
136
|
+
ls -la dist/
|
|
137
|
+
uv run python -m zipfile -l dist/*.whl | head -30
|
|
138
|
+
|
|
139
|
+
- name: Verify wheel imports cleanly
|
|
140
|
+
run: |
|
|
141
|
+
uv run --with ./dist/*.whl python -c "
|
|
142
|
+
import threatzone
|
|
143
|
+
print(f'threatzone version: {threatzone.__version__}')
|
|
144
|
+
assert threatzone.__version__, 'version missing'
|
|
145
|
+
"
|
|
146
|
+
|
|
147
|
+
- name: Upload distribution artifacts
|
|
148
|
+
# Skip under nektos/act — upload-artifact@v4 requires ACTIONS_RUNTIME_TOKEN
|
|
149
|
+
# which act cannot provide(learned it after countless errors dear readers...). Runs normally on real GitHub.
|
|
150
|
+
if: ${{ !env.ACT }}
|
|
151
|
+
uses: actions/upload-artifact@v4
|
|
152
|
+
with:
|
|
153
|
+
name: python-package-distributions
|
|
154
|
+
path: dist/
|
|
155
|
+
retention-days: 7
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
dry_run:
|
|
9
|
+
description: "Build only, do not publish"
|
|
10
|
+
required: false
|
|
11
|
+
default: "false"
|
|
12
|
+
type: choice
|
|
13
|
+
options: ["true", "false"]
|
|
14
|
+
|
|
15
|
+
# Only one publish run at a time
|
|
16
|
+
concurrency:
|
|
17
|
+
group: publish-${{ github.workflow }}
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
# Re-run the full test matrix before publishing. Never release broken code.
|
|
25
|
+
test:
|
|
26
|
+
name: Unit tests (Python ${{ matrix.python-version }})
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: true
|
|
30
|
+
matrix:
|
|
31
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
32
|
+
env:
|
|
33
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- name: Install uv
|
|
38
|
+
uses: astral-sh/setup-uv@v4
|
|
39
|
+
with:
|
|
40
|
+
version: latest
|
|
41
|
+
enable-cache: true
|
|
42
|
+
cache-dependency-glob: "uv.lock"
|
|
43
|
+
|
|
44
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
45
|
+
run: uv python install ${{ matrix.python-version }}
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: uv sync --all-extras --dev --python ${{ matrix.python-version }}
|
|
49
|
+
|
|
50
|
+
- name: Run tests
|
|
51
|
+
run: uv run pytest tests/ -v --tb=short
|
|
52
|
+
|
|
53
|
+
lint:
|
|
54
|
+
name: Lint + format (ruff)
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- name: Install uv
|
|
59
|
+
uses: astral-sh/setup-uv@v4
|
|
60
|
+
with:
|
|
61
|
+
version: latest
|
|
62
|
+
enable-cache: true
|
|
63
|
+
cache-dependency-glob: "uv.lock"
|
|
64
|
+
- name: Set up Python
|
|
65
|
+
run: uv python install 3.12
|
|
66
|
+
- name: Install dependencies
|
|
67
|
+
run: uv sync --all-extras --dev
|
|
68
|
+
- name: ruff check
|
|
69
|
+
run: uv run ruff check src/ tests/
|
|
70
|
+
- name: ruff format --check
|
|
71
|
+
run: uv run ruff format --check src/ tests/
|
|
72
|
+
|
|
73
|
+
type-check:
|
|
74
|
+
name: Type check (mypy)
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
- name: Install uv
|
|
79
|
+
uses: astral-sh/setup-uv@v4
|
|
80
|
+
with:
|
|
81
|
+
version: latest
|
|
82
|
+
enable-cache: true
|
|
83
|
+
cache-dependency-glob: "uv.lock"
|
|
84
|
+
- name: Set up Python
|
|
85
|
+
run: uv python install 3.12
|
|
86
|
+
- name: Install dependencies
|
|
87
|
+
run: uv sync --all-extras --dev
|
|
88
|
+
- name: mypy
|
|
89
|
+
run: uv run mypy src/threatzone
|
|
90
|
+
|
|
91
|
+
version-check:
|
|
92
|
+
name: Verify tag matches pyproject.toml version
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
# Skip version check on manual workflow_dispatch dry runs
|
|
95
|
+
if: github.event_name == 'release'
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/checkout@v4
|
|
98
|
+
|
|
99
|
+
- name: Extract tag version
|
|
100
|
+
id: tag
|
|
101
|
+
run: |
|
|
102
|
+
TAG="${GITHUB_REF#refs/tags/}"
|
|
103
|
+
# Strip leading 'v' if present (e.g. v1.0.0 -> 1.0.0)
|
|
104
|
+
VERSION="${TAG#v}"
|
|
105
|
+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
106
|
+
echo "Tag version: ${VERSION}"
|
|
107
|
+
|
|
108
|
+
- name: Extract pyproject version
|
|
109
|
+
id: pyproject
|
|
110
|
+
run: |
|
|
111
|
+
VERSION=$(grep -E '^version = "' pyproject.toml | head -1 | sed -E 's/version = "([^"]+)"/\1/')
|
|
112
|
+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
113
|
+
echo "pyproject version: ${VERSION}"
|
|
114
|
+
|
|
115
|
+
- name: Extract SDK module version
|
|
116
|
+
id: module
|
|
117
|
+
run: |
|
|
118
|
+
VERSION=$(grep -E '^__version__ = "' src/threatzone/__init__.py | sed -E 's/__version__ = "([^"]+)"/\1/')
|
|
119
|
+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
120
|
+
echo "module version: ${VERSION}"
|
|
121
|
+
|
|
122
|
+
- name: Assert all three versions match
|
|
123
|
+
run: |
|
|
124
|
+
TAG="${{ steps.tag.outputs.version }}"
|
|
125
|
+
PYPROJECT="${{ steps.pyproject.outputs.version }}"
|
|
126
|
+
MODULE="${{ steps.module.outputs.version }}"
|
|
127
|
+
if [[ "$TAG" != "$PYPROJECT" || "$TAG" != "$MODULE" ]]; then
|
|
128
|
+
echo "::error::Version mismatch: tag=$TAG pyproject=$PYPROJECT module=$MODULE"
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
echo "All three versions match: $TAG"
|
|
132
|
+
|
|
133
|
+
build:
|
|
134
|
+
name: Build distribution
|
|
135
|
+
runs-on: ubuntu-latest
|
|
136
|
+
needs: [test, lint, type-check, version-check]
|
|
137
|
+
# If version-check was skipped (workflow_dispatch), don't require it
|
|
138
|
+
if: always() && needs.test.result == 'success' && needs.lint.result == 'success' && needs.type-check.result == 'success' && (needs.version-check.result == 'success' || needs.version-check.result == 'skipped')
|
|
139
|
+
steps:
|
|
140
|
+
- uses: actions/checkout@v4
|
|
141
|
+
|
|
142
|
+
- name: Install uv
|
|
143
|
+
uses: astral-sh/setup-uv@v4
|
|
144
|
+
with:
|
|
145
|
+
version: latest
|
|
146
|
+
enable-cache: true
|
|
147
|
+
cache-dependency-glob: "uv.lock"
|
|
148
|
+
|
|
149
|
+
- name: Set up Python
|
|
150
|
+
run: uv python install 3.12
|
|
151
|
+
|
|
152
|
+
- name: Install dependencies
|
|
153
|
+
run: uv sync --all-extras --dev
|
|
154
|
+
|
|
155
|
+
- name: Build wheel and sdist
|
|
156
|
+
run: uv build
|
|
157
|
+
|
|
158
|
+
- name: Verify wheel imports cleanly
|
|
159
|
+
run: |
|
|
160
|
+
uv run --with ./dist/*.whl python -c "
|
|
161
|
+
import threatzone
|
|
162
|
+
print(f'Built threatzone version: {threatzone.__version__}')
|
|
163
|
+
assert threatzone.__version__, 'version missing'
|
|
164
|
+
"
|
|
165
|
+
|
|
166
|
+
- name: Store distribution packages
|
|
167
|
+
# Skip under nektos/act — upload-artifact@v4 requires ACTIONS_RUNTIME_TOKEN
|
|
168
|
+
# which act cannot provide. Runs normally on real GitHub.
|
|
169
|
+
if: ${{ !env.ACT }}
|
|
170
|
+
uses: actions/upload-artifact@v4
|
|
171
|
+
with:
|
|
172
|
+
name: python-package-distributions
|
|
173
|
+
path: dist/
|
|
174
|
+
retention-days: 30
|
|
175
|
+
|
|
176
|
+
publish-pypi:
|
|
177
|
+
name: Publish to PyPI
|
|
178
|
+
needs: [build]
|
|
179
|
+
# Publish only on real releases, not dry runs
|
|
180
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
environment:
|
|
183
|
+
name: pypi
|
|
184
|
+
url: https://pypi.org/project/threatzone/
|
|
185
|
+
permissions:
|
|
186
|
+
id-token: write # Trusted publishing (OIDC)
|
|
187
|
+
|
|
188
|
+
steps:
|
|
189
|
+
- name: Download distributions
|
|
190
|
+
uses: actions/download-artifact@v4
|
|
191
|
+
with:
|
|
192
|
+
name: python-package-distributions
|
|
193
|
+
path: dist/
|
|
194
|
+
|
|
195
|
+
- name: Publish to PyPI
|
|
196
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
197
|
+
with:
|
|
198
|
+
skip-existing: false
|
|
199
|
+
verbose: true
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
|
|
31
|
+
# Installer logs
|
|
32
|
+
pip-log.txt
|
|
33
|
+
pip-delete-this-directory.txt
|
|
34
|
+
|
|
35
|
+
# Unit test / coverage reports
|
|
36
|
+
htmlcov/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
.coverage
|
|
40
|
+
.coverage.*
|
|
41
|
+
.cache
|
|
42
|
+
nosetests.xml
|
|
43
|
+
coverage.xml
|
|
44
|
+
*.cover
|
|
45
|
+
*.py,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
|
|
49
|
+
# Translations
|
|
50
|
+
*.mo
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Environments
|
|
54
|
+
.env
|
|
55
|
+
.venv
|
|
56
|
+
env/
|
|
57
|
+
venv/
|
|
58
|
+
ENV/
|
|
59
|
+
env.bak/
|
|
60
|
+
venv.bak/
|
|
61
|
+
|
|
62
|
+
# IDEs
|
|
63
|
+
.idea/
|
|
64
|
+
.vscode/
|
|
65
|
+
*.swp
|
|
66
|
+
*.swo
|
|
67
|
+
*~
|
|
68
|
+
|
|
69
|
+
# mypy
|
|
70
|
+
.mypy_cache/
|
|
71
|
+
.dmypy.json
|
|
72
|
+
dmypy.json
|
|
73
|
+
|
|
74
|
+
# ruff
|
|
75
|
+
.ruff_cache/
|
|
76
|
+
|
|
77
|
+
# OS
|
|
78
|
+
.DS_Store
|
|
79
|
+
Thumbs.db
|
|
80
|
+
|
|
81
|
+
# Project specific
|
|
82
|
+
PLAN.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
threatzone-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Malwation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|