Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
d2707a7
docs(python): findings and ledger from the first tracer attempt
yujiyokoo Oct 15, 2026
8112223
docs(python): ADR-0001, the declaration is checked when the class is …
yujiyokoo Oct 19, 2026
1a52ddb
docs(python): ADR-0001 fixes
yujiyokoo Oct 19, 2026
aea2888
docs(python): revise ADR-0001 for the review and the next branch
yujiyokoo Oct 20, 2026
931dd37
docs(python): count the producible EQL types as next has them today
yujiyokoo Oct 20, 2026
f7979e7
docs(python): close the review threads on ADR-0001
yujiyokoo Oct 21, 2026
21b7853
docs(python): amend ADR-0001 from the spec review
yujiyokoo Oct 23, 2026
f22603c
feat(python): walking skeleton for the Python SDK (#1382)
yujiyokoo Oct 27, 2026
4f0ee8a
fix(python): mark the engine dirty for any stack-* edit, and trim the…
yujiyokoo Oct 27, 2026
b563176
fix(python): address the first review of the walking skeleton
yujiyokoo Oct 27, 2026
2ecc794
fix(python): resolve the workspace lockfile against vitaminc 0.8
yujiyokoo Oct 28, 2026
f5b1a3d
feat(ci): tests-python.yml for the Python SDK workspace (#1383)
yujiyokoo Oct 27, 2026
4bb2953
fix(ci): address the review of tests-python.yml (#1383)
yujiyokoo Oct 27, 2026
f071f04
fix(ci): hand the engine commit to the container wheel builds (#1383)
yujiyokoo Oct 28, 2026
94f236b
fix(ci): address the second review of tests-python.yml (#1383)
yujiyokoo Nov 2, 2026
d4cbdc3
test(python): verify the two engine facts the spec depends on (#1384)
yujiyokoo Oct 28, 2026
26e9bec
docs(python): use lower_snake_case error codes in frame-reader.md (#1…
yujiyokoo Oct 29, 2026
3dbba4a
feat(python): generate the EQL type markers from the catalog (#1385)
yujiyokoo Oct 28, 2026
f236a42
fix(python): address the review of the generated EQL markers (#1385)
yujiyokoo Oct 28, 2026
f78c05c
feat(python): read a model's declaration and check rule A (#1386)
yujiyokoo Oct 28, 2026
dc0ebf2
refactor(python): use lower_snake_case error codes and reasons (#1386)
yujiyokoo Oct 29, 2026
f4c4f83
feat(python): check what a declaration means (#1387)
yujiyokoo Oct 29, 2026
8684edb
fix(python): address the review of the declaration checks (#1387)
yujiyokoo Oct 29, 2026
1ed99c3
feat(python): redact declared classes and build a Cipher (#1388)
yujiyokoo Oct 29, 2026
a580e2c
fix(python): address the reviews of redaction and Cipher (#1388)
yujiyokoo Oct 29, 2026
306ab5f
feat(python): redact declared classes and build a Cipher (#1388)
yujiyokoo Oct 30, 2026
db749a1
feat(python): encrypt models into EQL v4 rows (#1389)
yujiyokoo Oct 30, 2026
d448064
fix(python): address the review of encrypt (#1389)
yujiyokoo Nov 2, 2026
815c0fa
fix(python): write a passthrough value as a plain scalar (#1389)
yujiyokoo Nov 2, 2026
125a655
feat(python): decrypt rows back into models (#1390)
yujiyokoo Nov 2, 2026
4705ec6
fix(python): address the review of decrypt (#1390)
yujiyokoo Nov 2, 2026
272c6e9
fix(python): encode a str column value once, and share untouched (#1390)
yujiyokoo Nov 2, 2026
d5a5c4b
test(python): name the target of the ignored SQLAlchemy relationship …
yujiyokoo Nov 4, 2026
ca8e429
docs(python): record that Ignore does not work on a SQLAlchemy collec…
yujiyokoo Nov 4, 2026
e877db5
feat(python): query an equality term (#1391)
yujiyokoo Nov 2, 2026
5430df0
fix(python): refuse a query on a field with no eq index (#1391)
yujiyokoo Nov 2, 2026
9e47677
feat(python): value rules for every producible type (#1392)
yujiyokoo Nov 2, 2026
dc5977a
fix(python): address the review of the value rules (#1392)
yujiyokoo Nov 2, 2026
b57646f
fix(python): derive fuzz.yml's path filter from the fuzz locks (#1392)
yujiyokoo Nov 2, 2026
94ece28
test(python): read the fuzz crates' compiled files with the shared wa…
yujiyokoo Nov 2, 2026
a6ba223
fix(python): leave the copied value corpus out of Biome (#1392)
yujiyokoo Nov 4, 2026
6175365
feat(python): parity with the Go SDK's rows, and heap hygiene (#1393)
yujiyokoo Nov 2, 2026
49f4747
fix(python): address the review of parity and heap hygiene (#1393)
yujiyokoo Nov 2, 2026
4d6400a
fix(python): wipe opened numbers, and close the switch race (#1393)
yujiyokoo Nov 3, 2026
079626b
fix(python): one client switch, and no plaintext moved out of a row (…
yujiyokoo Nov 3, 2026
ea4c96b
fix(python): take the datetime refusal's cause from to_python (#1393)
yujiyokoo Nov 3, 2026
32e71f1
feat(python): test the shared client from several threads (#1394)
yujiyokoo Nov 3, 2026
dbe9a71
test(python): share the sync contract helpers, and time the interrupt…
yujiyokoo Nov 3, 2026
6fc9550
test(python): count ZeroKMS requests by endpoint (#1394)
yujiyokoo Nov 3, 2026
fda6b53
docs(python): complete the README, test it, and score the SDK (#1395)
yujiyokoo Nov 3, 2026
6a8008a
chore(python): lock the test dependencies with uv.lock
yujiyokoo Nov 9, 2026
fa09562
docs(python): say that Dependabot groups the test updates
yujiyokoo Nov 9, 2026
733ccf9
feat(python): add a customer directory example
yujiyokoo Nov 9, 2026
306f770
feat(python): add a SQLAlchemy version of the customer directory
yujiyokoo Nov 10, 2026
546c2ba
docs(python): record one method for each query operator
yujiyokoo Nov 10, 2026
f37e072
feat(python): one query method for each order operator
yujiyokoo Nov 10, 2026
0f75eb2
docs(python): between waits for the engine
yujiyokoo Nov 10, 2026
4b0dee1
feat(python): match search with matches, verify and target_limit
yujiyokoo Nov 11, 2026
6fbefb9
test(python): heap hygiene covers verify
yujiyokoo Nov 11, 2026
e2bd3a0
feat(python): encrypt_rows for rows that contain only some fields
yujiyokoo Nov 11, 2026
338e333
feat(python): decrypt_rows, the reverse of encrypt_rows
yujiyokoo Nov 11, 2026
0b011f4
feat(python): configure_encryption for the SQLAlchemy ORM
yujiyokoo Nov 11, 2026
de617f5
feat(python): search encrypted columns through the SQLAlchemy ORM
yujiyokoo Nov 11, 2026
84c004e
feat(python): Alembic migrations for encrypted columns and their indexes
yujiyokoo Nov 11, 2026
1190fbd
refactor(python): the SQLAlchemy example uses configure_encryption
yujiyokoo Nov 11, 2026
ce71efd
feat(python): !=, in_ and passthrough checks in the SQLAlchemy layer
yujiyokoo Nov 11, 2026
1f14ce1
feat(python): a declared index replaces the SDK's; refuse unique EQL …
yujiyokoo Nov 11, 2026
e563a6d
docs(python): record the SQLAlchemy layer's design in ADR-0004
yujiyokoo Nov 11, 2026
f88719f
feat(python): decrypt the encrypted columns of a session query
yujiyokoo Nov 11, 2026
1d82ecb
feat(python): match search through the SQLAlchemy ORM
yujiyokoo Nov 11, 2026
5694c4d
docs(python): scorecard G6 covers the SQLAlchemy layer
yujiyokoo Nov 11, 2026
93c3afa
feat(python): a field declared Optional stores None as SQL NULL
yujiyokoo Nov 11, 2026
768f5d3
feat(python): an or_ of match searches through the SQLAlchemy ORM
yujiyokoo Nov 11, 2026
5e8def4
fix(python): an or_ of matches across an outer join no longer crashes
yujiyokoo Nov 11, 2026
d87bc86
fix(python): sort encrypted columns by their order term
yujiyokoo Nov 11, 2026
b5993cf
feat(python): a SQLAlchemy relationship needs no marker
yujiyokoo Nov 11, 2026
19a9d6a
fix(python): rewrite ORDER BY in an ORM select run on a connection
yujiyokoo Nov 11, 2026
5016066
fix(python): sort by the order term in aliases and subqueries
yujiyokoo Nov 11, 2026
d846c2e
fix(python): sort a labelled encrypted column by its order term
yujiyokoo Nov 11, 2026
30b15f8
fix(python): sort a column named by a string by its order term
yujiyokoo Nov 11, 2026
d4c1e60
fix(python): a plaintext label hides an encrypted column in ORDER BY
yujiyokoo Nov 11, 2026
687fe15
feat(python)!: configure_encryption covers every engine in a process
yujiyokoo Nov 11, 2026
c2f2bb5
fix(python): refuse an encrypted column inside a UniqueConstraint
yujiyokoo Nov 12, 2026
59e8659
test(python): run the SQLAlchemy tests with no profile and a keyword DSN
yujiyokoo Nov 12, 2026
7fcaff6
fix(python): cover Value::Null in the value_frame fuzz target
yujiyokoo Nov 12, 2026
980cb29
fix(python): keep the test key source out of wheels, and tighten CI
yujiyokoo Nov 12, 2026
4a7cc24
fix(python): refuse hand-written methods and read declarations correctly
yujiyokoo Nov 12, 2026
7043009
fix(python): give encrypt and decrypt the right error for each bad input
yujiyokoo Nov 12, 2026
46f3365
test(python): cover the paths the review found without a test
yujiyokoo Nov 12, 2026
98a41f9
fix(python): small fixes from the review, with tighter tests
yujiyokoo Nov 12, 2026
c917a7c
fix(python): say how to fix a bare ClassVar on Python 3.9
yujiyokoo Nov 12, 2026
1ae877b
fix(python): build a new engine client in a forked child
yujiyokoo Nov 12, 2026
c7593a3
fix(python): refuse a forked child the client its parent built
yujiyokoo Nov 12, 2026
12aad08
fix(python): resolve the root workspace's vitaminc, and test that it …
yujiyokoo Nov 12, 2026
14576a2
ci(python): give the CS_* credentials to the live test step alone
yujiyokoo Nov 12, 2026
fa6f259
fix(python): let the fuzz seeds reach the value frame read check
yujiyokoo Nov 12, 2026
d20d8dc
docs(python): add the customer directory example's findings
yujiyokoo Nov 12, 2026
2733f59
docs(python): say which decrypt errors do not name the row
yujiyokoo Nov 12, 2026
7943fd0
refactor(python): read the endpoint and client key with the engine's …
yujiyokoo Nov 12, 2026
3b4e6ab
build(python): lock maturin, and build with the locked copy
yujiyokoo Nov 12, 2026
0c51f59
chore(ci): say that Dependabot updates the locked maturin
yujiyokoo Nov 12, 2026
df241a8
fix(python): clearer refusal messages, and seal Pydantic's other prin…
yujiyokoo Nov 12, 2026
f75009f
test(python): pin the value numbers, the commit rules, and the genera…
yujiyokoo Nov 12, 2026
2d15f20
test(python): close the gaps the review marked low
yujiyokoo Nov 12, 2026
fed21a2
ci(python): cache both workspaces in one step, and check two more steps
yujiyokoo Nov 12, 2026
6691f7d
docs(python): close the example's first four findings
yujiyokoo Nov 12, 2026
f4318ee
fix(python): close the review findings on #1635
yujiyokoo Nov 13, 2026
732d755
fix(python): close the review findings on #1622
yujiyokoo Nov 13, 2026
24cd35f
fix(python): build and pass the tests on next
yujiyokoo Nov 13, 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
5 changes: 5 additions & 0 deletions .changeset/supply-chain-skill-uv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'stash': patch
---

The `stash-supply-chain-security` skill now lists `uv` among the ecosystems Dependabot updates. The repository's Python SDK has a `uv.lock` for its test dependencies, and a Dependabot entry for it.
52 changes: 46 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,10 @@ updates:

# ── Cargo (the root workspace — the stack-* crates) ─────────────
# The root workspace (the six stack-* crates and the three node binding
# crates) and the six detached workspaces beside it: the three cargo-fuzz
# crates, the two Go WASI guests and the EQL ORE timing harness's ciphertext
# generator. Each has its own Cargo.lock, and each is its own workspace
# root, so each is listed.
# crates) and the detached workspaces beside it: the three cargo-fuzz
# crates, the two Go WASI guests, the EQL ORE timing harness's ciphertext
# generator, and the Python SDK. Each has its own Cargo.lock, and each is
# its own workspace root, so each is listed.
- package-ecosystem: cargo
directories:
- /
Expand All @@ -262,9 +262,10 @@ updates:
- /languages/golang/encrypt/guest
- /languages/golang/auth/guest
- /packages/eql/tests/timing/ore_block_256/ctgen
- /languages/python
# Lockfile only, like the other two cargo entries. Every one of these
# workspaces shares crates by path with another: the fuzz crates and both
# guests depend on root-workspace crates, the encrypt guest also on
# workspaces shares crates by path with another: the fuzz crates, both
# guests and the Python SDK depend on root-workspace crates, the encrypt guest also on
# eql-bindings, and the EQL workspace on stack-encrypt and stack-kms. Those
# root crates take `{ workspace = true }` requirements from the root
# Cargo.toml, so a manifest edit made for any one of these lockfiles can
Expand Down Expand Up @@ -369,6 +370,45 @@ updates:
update-types:
- version-update:semver-major

# ── uv (languages/python — the Python SDK's test and build tools) ──
# The package has no runtime dependency. So uv.lock contains two sets of
# tools: the `test` extra (pytest, attrs, Pydantic, SQLAlchemy, psycopg and
# Alembic), and the `build` dependency group (maturin, the build backend),
# with what they need. `mise run build` installs both with
# `uv sync --locked`. Dependabot's uv parser reads `[dependency-groups]`
# as well as the extras, so maturin is updated with the rest. Both have
# lower bounds and no major upper bound, so a new minor or patch release
# changes only the lock.
- package-ecosystem: uv
directory: /languages/python
# Monthly, matching the other non-npm toolchains. No `day:`, for the
# reason recorded on the cargo entries.
schedule:
interval: monthly
cooldown:
default-days: 7
open-pull-requests-limit: 3
labels:
- dependencies
- supply-chain
commit-message:
prefix: "chore"
include: scope
groups:
# One pull request for the set, so the tests run once against all the
# new versions together.
python-test-minor-patch:
patterns:
- "*"
update-types:
- minor
- patch
ignore:
# Major bumps are reviewed and applied manually, not by Dependabot.
- dependency-name: "*"
update-types:
- version-update:semver-major

# ── GitHub Actions ─────────────────────────────────────────────
- package-ecosystem: github-actions
directory: /
Expand Down
32 changes: 26 additions & 6 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
name: Fuzz (crates)

# libFuzzer fuzzing for the stack crates' untrusted-input parsers (the
# detached `packages/*/fuzz/` crates and the `fuzz:*` mise tasks). Ported from
# cipherstash-suite's `fuzz.yml`, for the six targets in the imported set; the
# suite keeps its three cts-common targets. Two jobs with different roles:
# detached `packages/*/fuzz/` crates and the `fuzz:*` mise tasks), and the
# Python SDK's value conversion (`languages/python/crates/core/fuzz/`). Ported from
# cipherstash-suite's `fuzz.yml`, which keeps its three cts-common targets.
# The two matrices below list every target here. Two jobs with different
# roles:
#
# * fuzz-regression (pull_request and manual, blocking on PRs): builds every harness, which
# catches harness and API drift, and replays the committed seed corpus with
Expand All @@ -24,6 +26,18 @@ on:
- packages/stack-auth/**
- packages/stack-kms/**
- packages/stack-encrypt/**
# Every other crate a target compiles, from each fuzz crate's
# Cargo.lock: scripts/__tests__/fuzz-workflow-paths.test.mjs derives
# this list and fails when a path dependency is missing from it.
- packages/stack-profile/**
- packages/stack-encrypt-derive/**
- packages/eql/crates/eql-bindings/**
# The Python SDK's core crate, the manifest its dependencies come
# from, and the Go guest's EQL resolver it compiles by #[path]: the
# fuzz:python-values target.
- languages/python/crates/core/**
- languages/python/Cargo.toml
- languages/golang/encrypt/guest/src/targets.rs
- Cargo.toml
- Cargo.lock
- mise.toml
Expand Down Expand Up @@ -116,6 +130,7 @@ jobs:
- { task: "fuzz:plan-build", slug: plan-build }
- { task: "fuzz:json-wire", slug: json-wire }
- { task: "fuzz:json-text", slug: json-text }
- { task: "fuzz:python-values", slug: python-values }
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
Expand All @@ -140,6 +155,7 @@ jobs:
packages/stack-auth/fuzz
packages/stack-kms/fuzz
packages/stack-encrypt/fuzz
languages/python/crates/core/fuzz
key: ${{ matrix.slug }}

# `-runs=0` replays the committed seed corpus once and exits without
Expand All @@ -166,6 +182,7 @@ jobs:
- { task: "fuzz:plan-build", slug: plan-build, dir: packages/stack-encrypt, target: plan_build }
- { task: "fuzz:json-wire", slug: json-wire, dir: packages/stack-encrypt, target: json_wire_decode }
- { task: "fuzz:json-text", slug: json-text, dir: packages/stack-encrypt, target: json_text }
- { task: "fuzz:python-values", slug: python-values, dir: languages/python/crates/core, target: value_frame }
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
Expand Down Expand Up @@ -205,13 +222,16 @@ jobs:
run: mise run ${{ matrix.task }} -- "-max_total_time=$MAX_TOTAL_TIME"

# Drops inputs that add no coverage, so the saved corpus stays small.
# Skipped when the fuzz step found a crash.
# Skipped when the fuzz step found a crash. Run from the root, naming
# the fuzz crate: from languages/python, mise would load that folder's
# mise.toml and install its Python and uv, which this job does not
# cache.
- name: Minimise the corpus (${{ matrix.slug }})
working-directory: ${{ matrix.dir }}
env:
TARGET: ${{ matrix.target }}
FUZZ_DIR: ${{ matrix.dir }}/fuzz
run: |
cargo +nightly fuzz cmin "$TARGET" --sanitizer none --target "$(rustc -vV | sed -n 's/^host: //p')"
cargo +nightly fuzz cmin "$TARGET" --fuzz-dir "$FUZZ_DIR" --sanitizer none --target "$(rustc -vV | sed -n 's/^host: //p')"

# Saved even on a crash: the grown corpus is still worth keeping, and the
# crash input lives in artifacts/, not corpus/.
Expand Down
Loading
Loading