Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a300fd0
Add Rust workspace ported from toolbox-rewrite
sandorstormen Oct 2, 2026
b50c78f
Update tree-sitter-mal to 1.3.1, un-ignore detector tests
sandorstormen Oct 2, 2026
f8fbfd7
Fix transitive closure in glob_assoc_traversal (Rust port)
sandorstormen Oct 2, 2026
9192a6c
Fix model metadata key mismatch between to_dict/from_dict (Rust port)
sandorstormen Oct 2, 2026
c3cc564
Port mal_analyzer.py's two cosmetic compiler warnings (Rust port)
sandorstormen Oct 2, 2026
cc5337d
Implement real include-cycle detection (Rust port)
sandorstormen Oct 2, 2026
5243d88
Remove partial-regeneration asset-removal ordering contract (Rust port)
sandorstormen Oct 2, 2026
c5de240
Port AttackGraph deserialization (from_dict/load_from_file)
sandorstormen Oct 2, 2026
2b9e7ad
Add Phase 1 of PyO3 Python bindings: LanguageGraph compat layer
sandorstormen Oct 2, 2026
fad903c
Add Phase 2 of PyO3 Python bindings: Model/ModelAsset compat layer
sandorstormen Oct 2, 2026
2e17518
Add Phase 3 of PyO3 Python bindings: AttackGraph/AttackGraphNode/Dete…
sandorstormen Oct 3, 2026
5cd343b
Add Phase 4 of PyO3 Python bindings: native cutover (Model/LanguageGr…
sandorstormen Oct 3, 2026
7b2b41a
Add Phase 5 of PyO3 Python bindings: packaging (maturin, cross-compil…
sandorstormen Oct 4, 2026
ad1f9eb
Cache AttackGraphNode.children/.parents PySets per node
sandorstormen Oct 5, 2026
f9ebaa5
Complete Phase 6 of PyO3 Python bindings: mal-simulator acceptance gate
sandorstormen Oct 5, 2026
3c894d7
Fix DynaMAL same-batch add+remove bug in partially_regenerate_graph
sandorstormen Oct 5, 2026
a2589fd
Fix two more same-batch add+remove bugs blocking mal-simulator's Dyna…
sandorstormen Oct 5, 2026
373e6cb
Add PEP 561 type stubs for maltoolbox._native
sandorstormen Oct 6, 2026
7d66856
Add missing Model._from_dict to the _native stub
sandorstormen Oct 6, 2026
67f69ff
Clarify mal-simulator-side bug attribution and update Phase 6 status …
sandorstormen Oct 6, 2026
0466237
Fix CI ImportError for maltoolbox._native
sandorstormen Oct 6, 2026
a959aa7
Fix wheel install test picking up every cp3xx wheel in dist/
sandorstormen Oct 6, 2026
63ed06f
Drop --no-index from wheel install smoke-test
sandorstormen Oct 6, 2026
091f1f4
Merge pull request #256 from mal-lang/pyo3-bindings
sandorstormen Oct 6, 2026
5702fa5
Trim verbose porting commentary from Rust doc/inline comments
sandorstormen Oct 6, 2026
7a1d716
Apply cargo fmt across the top-level workspace and standalone PyO3 cr…
sandorstormen Oct 6, 2026
bd5cf2b
Add Rust CI workflow: fmt and build checks
sandorstormen Oct 6, 2026
3dbf4f5
Rename python/ to py-bindings/ and drop implementation log
sandorstormen Oct 6, 2026
8daf041
Fix stale python/ doc-comment references missed in previous rename
sandorstormen Oct 6, 2026
876a73c
Address PR #255 crate-architecture review; rename crates/ to core/ an…
sandorstormen Oct 6, 2026
493f738
Fix clippy warnings in maltoolbox-attackgraph-py
sandorstormen Oct 6, 2026
c854d1d
Expose PyAttackGraph.__inner_capsule__ for cross-extension interop
sandorstormen Oct 7, 2026
b96258b
Expose PyModel.__inner_capsule__ for cross-extension interop
sandorstormen Oct 8, 2026
9c68e11
Move _native stubs beside the extension module so type checkers find …
sandorstormen Oct 12, 2026
598bac3
Fix partial regeneration missing parents reached through a [Subtype] hop
sandorstormen Oct 22, 2026
8e63c83
Hash graph-rebuild collections with FxHash instead of SipHash
sandorstormen Oct 22, 2026
ae2a372
Make LanguageGraph immutable and share it with Model via Rc
sandorstormen Oct 26, 2026
b9e7cba
Ignore .langs/ directory
sandorstormen Oct 26, 2026
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
240 changes: 186 additions & 54 deletions .github/workflows/publish-to-pypi-and-test-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,204 @@ name: Publish Python distributions to PyPI and TestPyPI
on: push

jobs:
build:
name: Build Python distribution
runs-on: ubuntu-latest
linux:
name: wheels (linux ${{ matrix.platform.target }})
runs-on: ${{ matrix.platform.runner }}
strategy:
fail-fast: false
matrix:
platform:
- runner: ubuntu-22.04
target: x86_64
- runner: ubuntu-22.04
target: aarch64
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: auto
- name: Test wheel (native target only)
if: matrix.platform.target == 'x86_64'
run: |
pip install mal-toolbox --find-links dist --force-reinstall
python -c "import maltoolbox"
- name: Upload wheels
uses: actions/upload-artifact@v6
with:
name: wheels-linux-${{ matrix.platform.target }}
path: dist

musllinux:
name: wheels (musllinux ${{ matrix.platform.target }})
runs-on: ${{ matrix.platform.runner }}
strategy:
fail-fast: false
matrix:
platform:
- runner: ubuntu-22.04
target: x86_64
- runner: ubuntu-22.04
target: aarch64
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: musllinux_1_2
- name: Upload wheels
uses: actions/upload-artifact@v6
with:
name: wheels-musllinux-${{ matrix.platform.target }}
path: dist

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
windows:
name: wheels (windows ${{ matrix.platform.target }})
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
platform:
- target: x64
python_arch: x64
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.10"
architecture: ${{ matrix.platform.python_arch }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Test wheel
run: |
pip install mal-toolbox --find-links dist --force-reinstall
python -c "import maltoolbox"
- name: Upload wheels
uses: actions/upload-artifact@v6
with:
name: wheels-windows-${{ matrix.platform.target }}
path: dist

- name: Install pypa/build
run: >-
python3 -m
pip install
build
--user
macos:
name: wheels (macos ${{ matrix.platform.target }})
runs-on: ${{ matrix.platform.runner }}
strategy:
fail-fast: false
matrix:
platform:
- runner: macos-15-intel
target: x86_64
- runner: macos-latest
target: aarch64
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Test wheel
run: |
pip install mal-toolbox --find-links dist --force-reinstall
python -c "import maltoolbox"
- name: Upload wheels
uses: actions/upload-artifact@v6
with:
name: wheels-macos-${{ matrix.platform.target }}
path: dist

- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
sdist:
name: sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v6
with:
name: wheels-sdist
path: dist

publish-to-pypi:
name: Publish Python distribution to PyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/mal-toolbox
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
publish-to-testpypi:
name: Publish Python distribution to TestPyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- linux
- musllinux
- windows
- macos
- sdist
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/mal-toolbox
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing

steps:
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: python-package-distributions
pattern: wheels-*
path: dist/
- name: Publish distribution to PyPI
merge-multiple: true
- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

publish-to-testpypi:
name: Publish Python distribution to TestPyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/mal-toolbox
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
publish-to-pypi:
name: Publish Python distribution to PyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- linux
- musllinux
- windows
- macos
- sdist
- publish-to-testpypi # TestPyPI is a gate: must succeed before the real publish
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/mal-toolbox
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
attestations: write # for the GitHub-native build provenance attestation below

steps:
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: python-package-distributions
pattern: wheels-*
path: dist/
- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
merge-multiple: true
- name: Generate artifact attestation
uses: actions/attest@v4
with:
repository-url: https://test.pypi.org/legacy/
subject-path: "dist/*"
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
2 changes: 1 addition & 1 deletion .github/workflows/run-mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ jobs:
- name: Type checking with MyPy
run: |
pip install mypy types-PyYAML
mypy maltoolbox tests --ignore-missing-imports
mypy python/maltoolbox tests --ignore-missing-imports
51 changes: 51 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Rust

on:
push:
branches:
- main
pull_request: ~

permissions:
contents: read

jobs:
fmt:
name: fmt (${{ matrix.workspace }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
workspace:
- .
- py-bindings
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: cargo fmt --check
working-directory: ${{ matrix.workspace }}
run: cargo fmt --all --check

build:
name: build (${{ matrix.workspace }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
workspace:
- .
- py-bindings
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.x'
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: ${{ matrix.workspace }}
- name: cargo build
working-directory: ${{ matrix.workspace }}
run: cargo build --workspace --all-targets --locked
2 changes: 1 addition & 1 deletion .github/workflows/test-pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
sudo apt-get update
sudo apt-get install -y graphviz
python -m pip install --upgrade pip
pip install .
pip install -e .
- name: Test with pytest
run: |
pip install pytest
Expand Down
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ nosetests.xml
!.github/workflows/*.yml
!examples/resources/*

# Rust crate test fixtures/golden files (.json/.yml), not covered by the
# blanket Python-oriented *.json/*.yaml/*.yml ignores above
!core/**/*.json
!core/**/*.yml
!core/**/*.yaml

# Rust
/target
# py-bindings/ is its own standalone Cargo workspace, so /target above
# doesn't reach it
/py-bindings/target
**/*.rs.bk
*.pdb

# uv lock file
uv.lock

Expand All @@ -73,3 +87,4 @@ uv.lock

# Profiling Data
prof/
.langs/
Loading
Loading