_symlink entries in plugin tarballs are skipped (security posture, correct) but
now emit a logger.warning so operators can audit what was dropped:
"skipping symlink in plugin tarball (not supported for security): <name> -> <target>"
Added test_safe_extract_logs_warning_for_skipped_symlink asserting the warning
is present in caplog records at WARNING level. All 211 tests pass (+1 new).
known-issues.md updated.
Add fcntl.flock around token read/write in load_token() and save_token():
- load_token(): shared lock (LOCK_SH | LOCK_NB) before reading.
Returns None if lock is contended rather than blocking.
- save_token(): exclusive lock (LOCK_EX | LOCK_NB) before writing.
Gracefully degrades (logs warning, skips write) if another writer
holds the lock; in-memory _token is still updated so this instance
functions correctly. Releases lock in finally block.
Concurrent readers are safe. Concurrent writers are serialised. The
platform's one-token-per-workspace invariant is preserved.
known-issues.md updated.
KI-007 (High): Add isinstance(value, str) guard to _is_hex() so
non-string arguments return False cleanly instead of raising TypeError.
Updated test_is_hex_non_string to assert False instead of expecting
pytest.raises(TypeError).
KI-005 (High): Add _scan_for_secrets() to manifest.py that walks all
string values in plugin.yaml and reports common credential patterns
(sk-, ghp_, AKIA, bearer tokens, long hex strings, password/api_key
assignments). Call it from validate_manifest(). Skips the sha256
field since it's a content-addressed hash, not a secret.
Run: pytest → 210 passed, 1 skipped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(security): add plugin content integrity verification (SHA256)
SDK-side follow-up to molecule-core PR #1019 (pinned-ref supply-chain fix).
Changes:
- verify_plugin_sha256(plugin_dir, expected_sha) — content-addressed manifest
hash over sorted (relpath, SHA256(content)) pairs; plugin.yaml excluded
from its own hash to avoid circular dependency
- _walk_files(root) / _sha256_file(path) — internal helpers
- install_plugin() calls verify_sha256 after atomic rename; on mismatch
deletes plugin dir and raises ValueError before setup.sh runs
- PLUGIN_YAML_SCHEMA gains optional sha256 field (64-char lowercase hex)
- validate_manifest() validates sha256 format when present
Tests (12 new):
- sha256_file correctness, walk_files ordering, verify_* (match/mismatch/invalid)
- install_plugin sha256 verified: setup.sh runs
- install_plugin sha256 mismatch: raises ValueError, setup.sh NOT run
- install_plugin no sha256: backward-compat, skips verification
- validate_manifest sha256: valid/invalid/non-hex/absent
Pre-existing: 4 async tests in test_sdk.py fail without pytest-asyncio
(not related to this change).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): add pytest-asyncio markers to async adaptor tests
The 4 tests using async def were failing because pytest-asyncio was not
installed and pytest.ini set asyncio_mode=auto (which requires it). Add
@pytest.mark.asyncio to each async test and add pytest-asyncio as a
test optional dependency so CI gets the right extras when installing.
Fixes: 4 FAILED tests in test_sdk.py
---------
Co-authored-by: Molecule AI SDK-Dev <sdk-dev@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>