Skip to content

test(mesh): test PKI helpers + shared conftest (1/9 of #195 split)#220

Merged
cagataycali merged 2 commits into
strands-labs:mainfrom
cagataycali:pr1/test-mesh-pki-conftest
May 26, 2026
Merged

test(mesh): test PKI helpers + shared conftest (1/9 of #195 split)#220
cagataycali merged 2 commits into
strands-labs:mainfrom
cagataycali:pr1/test-mesh-pki-conftest

Conversation

@cagataycali
Copy link
Copy Markdown
Member

Part 1 / 9 of the split of #195 — tracked by #219.

Adds shared test infrastructure that every later mesh security test PR depends on:

  • tests/mesh/_pki.py (128 LOC) — minimal in-process CA + cert helpers. Deterministic CN strings, fd-clean tempdir lifetime, no host paths. Used by the wire-config and transport-security suites in PR-3 to assemble mTLS chains.
  • tests/mesh/conftest.py (27 LOC) — shared pytest fixtures: env scrubbing for STRANDS_MESH_*, audit dir cleanup between tests, Zenoh mock harness.

Why this lands first

Every later test PR (PR-3, PR-6, PR-7, PR-8) imports from tests/mesh/_pki.py or relies on the conftest fixtures. Landing this in isolation gives every later PR a green CI baseline of zero new tests passing.

Reviewer focus

  • Test fixture safety — no host paths committed.
  • fd cleanup on tempdir teardown.
  • Deterministic CN strings (no random.uniform etc.) so test failures are reproducible.

Scope

Out of scope

No source code under strands_robots/ is touched in this PR — the helpers will be exercised by tests in subsequent PRs.


Landing order: PR-1 → PR-2/3/4/5 (parallel) → PR-6 → PR-7 → PR-8 → PR-9. Full plan: PR_LIST.md. Tracking: #219.

Adds shared test infrastructure used by every later mesh security test PR:

- tests/mesh/_pki.py: minimal in-process CA + cert helpers
  (deterministic CN strings, fd-clean tempdir lifetime,
   no host paths) used by the wire-config and transport-security
   suites to assemble mTLS chains in tests.
- tests/mesh/conftest.py: shared pytest fixtures (env scrubbing,
  audit dir cleanup, Zenoh mock).

Lands first in the split of strands-labs#195 so every later test PR has
a green CI baseline.

Tracking: strands-labs#219
Source PR: strands-labs#195
Copy link
Copy Markdown
Contributor

@yinsong1986 yinsong1986 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Adds shared test infrastructure for the upcoming mesh-security test split: an in-process CA + leaf-cert helper (tests/mesh/_pki.py, 128 LOC) and a tests/mesh/conftest.py (27 LOC) that defaults STRANDS_MESH_AUTH_MODE=none for any test under tests/mesh/. Scope matches the description -- no strands_robots/ source is touched, two files only, tests-only cryptography dependency. Landing this first makes the dependent PRs (#3/#6/#7/#8) cheaper to review.

What's good

  • Scope discipline: 2 files / 155 LOC / 0 deletions. Matches the PR description exactly.
  • AGENTS.md compliance: no host paths, no emojis in user-facing strings, deterministic CN strings (no random.uniform etc.), 0o600 on private key files.
  • Each test gets its own ephemeral CA via tmp_path, so cross-test bleed is structurally prevented.
  • Module docstring is explicit that this is test-only and points readers at the production provisioning path.

Concerns

  • Conftest scope is broader than stated. autouse=True on the env-scrubbing fixture means every future test added under tests/mesh/ silently inherits STRANDS_MESH_AUTH_MODE=none AND STRANDS_MESH_I_KNOW_THIS_IS_INSECURE=1. A future test that wants to verify the second-factor gate itself will now have to actively unset the var, which is the opposite of the intent. Worth narrowing to an opt-in fixture or scoping per-module. Inline below.
  • TestCA will trigger a PytestCollectionWarning on every run. Pytest treats classes whose name starts with Test as collection candidates; a frozen dataclass with a generated __init__ confuses the collector. Inline below.
  • common_name is interpolated directly into filesystem paths and X.509 SANs without validation. Test-only or not, the PR description's reviewer-focus list explicitly calls out path safety, and AGENTS.md > 'Sanitize user inputs' applies the same way _safe_join does in strands_robots/utils.py. Inline below.
  • Documentation drift: the module docstring says mesh/iot/provision.py but the actual module path is strands_robots/mesh/iot/provision.py. AGENTS.md > Review Learnings (#86) > 'Reference module names, not filenames' applies.
  • Test surface: this PR adds zero tests of the helpers themselves. The PR description treats that as a feature ("green CI baseline of zero new tests passing"), but it does mean a regression in make_test_ca / TestCA.issue will only surface when PR-3 lands. A handful of self-tests on the helpers (CA basicConstraints critical, leaf chain validates against the CA, key file is 0o600, two issue() calls on the same out_dir don't clobber each other) would catch silent breakage at the right layer.

Verification suggestions

# Reproduce the pytest collection warning (no test deps yet, but conftest is loaded):
hatch run test tests/mesh/ -W error::pytest.PytestCollectionWarning

# Confirm the autouse fixture leaks across the whole mesh suite, not just the new files:
hatch run test tests/mesh/test_mesh.py -s -p no:cacheprovider \
  --setup-show 2>&1 | grep -i 'STRANDS_MESH_AUTH_MODE\|_default_mesh_auth_mode_none'

# Sanity-check the CA helper end-to-end:
python - <<'PY'
from pathlib import Path
import tempfile, ssl
from tests.mesh._pki import make_test_ca
with tempfile.TemporaryDirectory() as d:
    ca = make_test_ca(Path(d))
    cp, kp = ca.issue('node-a', Path(d) / 'leaf')
    ctx = ssl.create_default_context(cafile=str(ca.cert_path))
    ctx.load_cert_chain(certfile=str(cp), keyfile=str(kp))
    print('chain ok:', cp, kp, oct(kp.stat().st_mode & 0o777))
PY

Comment thread tests/mesh/_pki.py
Comment thread tests/mesh/_pki.py Outdated
Comment thread tests/mesh/conftest.py
Comment thread tests/mesh/_pki.py Outdated
Four fixes from PR-220 review comments:

1. _pki.py: Add anchored CN regex validation in issue() + make_test_ca().
   common_name was interpolated directly into filesystem path and
   x509.DNSName(...) without validation. AGENTS.md > 'Sanitize user
   inputs' applies -- every later mesh-security test PR uses this
   helper, so issue('node/a', out_dir) or issue('../foo', out_dir)
   must fail loudly.
   Ref: strands-labs#220 (comment)

2. _pki.py: Rename TestCA -> EphemeralCA. The original triggered
   PytestCollectionWarning on every run because pytest's default rule
   matches Test*. Rename is cleaner than __test__ = False.
   Ref: strands-labs#220 (comment)

3. conftest.py: Tighten autouse fixture: (a) move second-factor flag
   into same conditional branch as auth-mode override so AUTH_MODE=mtls
   does NOT inject the insecure flag; (b) add @pytest.mark.mesh_auth_mode_default
   opt-out marker so tests exercising the gate itself don't pass by
   accident under autouse.
   Ref: strands-labs#220 (comment)

4. _pki.py: Fix docstring -- module path 'strands_robots.mesh.iot.provision'
   not filename 'mesh/iot/provision.py' (AGENTS.md PR strands-labs#86 review
   learning); 'Returns the generated CA' not 'loaded'.
   Ref: strands-labs#220 (comment)

Tracking: strands-labs#219
Source PR: strands-labs#195
@cagataycali
Copy link
Copy Markdown
Member Author

Thanks for the review @yinsong1986. All four threads addressed in 05989bf:

  1. CN validation — added _CN_RE = ^[A-Za-z0-9][A-Za-z0-9._-]*$ + _validate_cn() called at the top of issue() and as a free assertion on make_test_ca's hard-coded CN. Same shape rule as the mesh's source-id / peer-id validators (later in PR-2 / PR-6), so the test helper can't issue a CN that would be rejected by production code.
  2. TestCAEphemeralCA — rename, no __test__ = False shim. Only internal consumer was _pki.py itself; downstream tests import make_test_ca only.
  3. Autouse fixture — second-factor flag now in the same conditional branch as the auth-mode override (so AUTH_MODE=mtls doesn't silently get the insecure flag). Added @pytest.mark.mesh_auth_mode_default opt-out marker registered via pytest_configure so tests exercising the second-factor gate itself don't pass by accident.
  4. Docstring path — fixed to strands_robots.mesh.iot.provision (module path, not filename); make_test_ca docstring corrected to "Returns the generated CA".

Resolving the threads now.

Copy link
Copy Markdown
Contributor

@sundargthb sundargthb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cagataycali cagataycali merged commit cf632f4 into strands-labs:main May 26, 2026
6 of 7 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Strands Labs - Robots May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants