Release Python SDK v17.4.0 + Helper 1.1.3 - #1112
Draft
stas-schaller wants to merge 31 commits into
Draft
Conversation
…ret and delete_folder
save_cache() used plain open(..., 'wb') which creates files at 0644, exposing the cleartext transmission key to any user on the machine. Switch to os.open with O_CREAT|O_TRUNC|0o600 and add os.chmod after write to also correct permissions on files that already exist at a wider mode. Add two regression tests: one for a freshly created file, one for an existing world-readable file being overwritten. Both are skipped on Windows where permission bits carry different semantics. Fixes KSM-1122.
…ure (KSM-1123) The bare except in caching_post_function caught both the HTTP call and save_cache. A FileNotFoundError from save_cache after a successful 200 caused the live response to be discarded and the fallback cache path to be attempted instead, surfacing a FileNotFoundError to the caller. Restructure so only the HTTP call is in the try block. If the network call succeeds, save_cache failures are silently swallowed and the live response is returned. Also tighten the except clause from bare except to except Exception. Add a regression test that mocks post_function to return 200 and save_cache to raise FileNotFoundError, asserting the live response and original transmission key are preserved. Fixes KSM-1123.
…file-permissions fix(python): cache file permissions and live-response masking (KSM-1122, KSM-1123)
…SM-1152) KSM-819 fixed the empty-list case (custom=[]) by switching from a truthiness check to `is not None`. That left the never-set case (custom=None) still omitting the key entirely. The vault API requires custom to be present on every create payload. Move custom into the base dict with a None→[] default so it is always serialized. Replace the test_none_custom_omitted regression test from KSM-819 with test_unset_custom_serializes_as_empty_list that asserts the corrected behavior. Fixes KSM-1152.
…122, KSM-1123, KSM-1152)
…-create-custom fix(python): always serialize custom as [] in RecordCreate.to_dict (KSM-1152)
get_file_data() passed None directly to requests.get() when the vault had not yet propagated the download URL after an upload, producing an untyped MissingSchema exception. Guard on a falsy url and raise KeeperError with a retry message. Jira: KSM-1131
…rl-guard fix(python): KSM-1131 raise KeeperError when file download URL is null
…nit__ For complex fields (address, name, host, paymentCard, etc.), __init__ accessed self.value[0] unconditionally after the is-not-None check. When the server returns an unpopulated field as "value": [], the empty list passes the None check but raises IndexError on index access. Change `if self.value is not None` to `if self.value` so an empty list is treated the same as None -- attribute variables default to None. Jira: KSM-1119
…-empty-field fix(python): KSM-1119 guard against IndexError in FieldType.__init__ for empty value list
Two throttle backoff defects, both ported from KSM-1030 (Java) and KSM-1035 (JS): 1. Jitter was two-sided: random.uniform(-0.25, 0.25) could reduce the delay below its computed floor, causing an immediate re-throttle. Changed to random.uniform(0, 0.25) so the floor is always respected. 2. _parse_throttle had no upper bound on retry_after: a misbehaving backend could supply an arbitrarily large value. Added MAX_THROTTLE_DELAY_SEC = 176 (= BASE * 2**4, the last-retry ceiling) and capped with min(..., MAX_THROTTLE_DELAY_SEC). Jira: KSM-1033
…le-jitter-cap fix(python): KSM-1033 one-sided throttle jitter and retry_after cap
… custom keys (KSM-1069) The error='key' retry path in _post_query had no upper bound, looping indefinitely if the server kept suggesting a key the SDK could not resolve. For IL5 deployments with a pinned custom server public key, this was effectively unbounded by design: the server would keep suggesting a standard key that the deployment cannot use. - Add MAX_KEY_ROTATION_RETRIES = 3 to keeper_globals - Add _parse_key_rotation() static method mirroring _parse_throttle() - In _post_query, intercept error='key' inline before handler_http_error: raise immediately when a custom key is pinned (IL5); cap standard key-rotation retries at MAX_KEY_ROTATION_RETRIES - Update il5_test to reflect the new correct behavior (raise immediately on key-rotation when custom key is pinned, not silently accept rotation) - Add key_rotation_test.py with unit tests for _parse_key_rotation and integration tests for all three exit paths
…-key-rotation-cap fix(python): bound key-rotation retries; raise immediately for IL5 custom keys (KSM-1069)
…elper core floor to 17.4.0 get_cache_file_path was declared @classmethod but never used cls -- all other KSMCache methods are @staticmethod. Converts the decorator to match. (KSM-1121) Helper setup.py core floor raised from >=17.3.0 to >=17.4.0, required before the 17.4.0 PyPI publish since KSM-1119 and KSM-1127 both depend on behavior introduced in this release's core.
…static-consistency fix(python): KSMCache staticmethod consistency + helper core floor bump
…s connection schema Both fields were present in the Java SDK (PamSettingsConnection) but absent from the Python helper's v3 field_type.py schema for pamDatabase records. Adds them alongside the existing connection fields so they appear in template generation output and are available for record construction. (KSM-1127)
…ttings-db-fields fix(python/helper): add database and dbConnectionMethod to PamSettings connection schema
…-747) Records created by non-SDK clients (Commander, Vault UI) inside shared folders appear in response.records[] with innerFolderUid set and their record key encrypted with the folder key, not the app key. The SDK was always using the app key, silently failing AES-GCM authentication and moving every such record to bad_records. Fix: before the records loop, build a folder_key_map from folders_resp (the same data already used by the folder path). For any flat record with innerFolderUid present in the map, decrypt its record key with the folder key; fall back to the app key for records without innerFolderUid. Regression test in shared_folder_test.py constructs a raw response with a folder key encrypted under the app key and a flat record whose record key is encrypted under the folder key, confirming the fix. Jira: KSM-747
…folder-key-decryption fix(python): use folder key for flat records with innerFolderUid (KSM-747)
When a record has individual share access and also belongs to a shared folder, the vault returns it in both response.records[] and response.folders[].records[]. After the folder-key decryption fix, both copies decrypt correctly, causing get_secrets() to return two entries for the same UID. Deduplication by recordUid is applied after both response paths are processed. Jira: KSM-1145
…-dedup-records fix(python): deduplicate records returned via both flat and folder paths (KSM-1145)
…ries Fix semicolons, passive voice, overlong sentences, and participial endings in the new changelog bullets. No content changes.
…h (KSM-1019) * fix(sdk/python/core): honor kms_cache_file_name override in KSMCache cache path PR #1034 (KSM-1004) routed cache operations through get_cache_file_path(), which re-derived the path from KSM_CACHE_DIR and ignored the kms_cache_file_name class attribute. Assigning KSMCache.kms_cache_file_name was silently dropped. Honor an explicit kms_cache_file_name override again (detected via an import-time default snapshot) while keeping lazy KSM_CACHE_DIR resolution. Add a regression test and bump to 17.3.1 (including the keeper_globals hardcoded fallback and smoke-test assertions). Fixes #1044. * fix(sdk/python/core): detect kms_cache_file_name override by identity (KSM-1019) Replace the value-equality override check in KSMCache.get_cache_file_path with an identity comparison against a _DefaultCachePath(str) sentinel, so an explicit kms_cache_file_name assignment is honored even when its text equals the import-time default (e.g. "ksm_cache.bin" when KSM_CACHE_DIR was unset at import). The previous "!=" check silently dropped such an override once KSM_CACHE_DIR was set, a narrower case of the KSM-1004 regression this line fixes. Add a collision-case regression test and the 17.3.1 README changelog entry. KSM-1004 lazy KSM_CACHE_DIR resolution and all default behavior are preserved. * chore(sdk/python/core): drop 17.3.1 version bump and changelog (folded into 17.4.0) This fix is being consolidated into the 17.4.0 release. The single version bump and the KSM-1019 changelog entry now live in the 17.4.0 release PR (#1057), so revert _version.py, keeper_globals.py, smoke_test.py and the README changelog back to the release-branch baseline. This PR now carries only the KSMCache code fix and its regression tests. * refactor(sdk/python/core): single-source default cache path and clarify restore note (KSM-1019) Address second review round: - extract _default_cache_path() so the import-time sentinel and the lazy branch of get_cache_file_path build the default path from one expression, removing the silent-drift risk between the two copies - reword the restore-default note: restore by assigning KSMCache.kms_cache_file_name = KSMCache._default_cache_file_name, not by re-assigning the sentinel itself - add test_kms_cache_file_name_override_can_be_restored_to_default locking in the documented restore path - drop a stray blank line left in the README by the 17.3.1 changelog removal * test(sdk/python/core): cover plain default cache path and sentinel text consistency (KSM-1019) Close two gaps in the path-resolution coverage matrix: - the untouched default (no override, no KSM_CACHE_DIR) resolving to ksm_cache.bin in the current working directory, exercised end to end through save_cache - the import-time sentinel text staying equal to _default_cache_path() output, so editing or re-inlining either copy of the expression fails a test instead of silently changing what the public attribute advertises
…orage config (KSM-299) * fix(sdk/python/core): raise clear KeeperError for malformed InMemoryKeyValueStorage config (KSM-299) InMemoryKeyValueStorage given a config string that is not valid JSON (or base64-encoded JSON) raised a cryptic "TypeError: object of type 'NoneType' has no len()". The error path called len() on the json_to_dict() result (None) instead of the source string, and %-formatting evaluated that len() argument before the KeeperError could even be constructed. Preserve the original config string in config_str and report its length, so a malformed config now raises a clear KeeperError explaining the problem. Add regression tests mirroring the existing "must raise KeeperError, not TypeError" pattern in this suite. * chore(sdk/python/core): bump to 17.4.0 and consolidate changelog Bump python-core from 17.3.0 to 17.4.0 across the three coupled locations (_version.py, the keeper_globals.py hardcoded fallback, and the smoke_test version assertions) and add a 17.4.0 README changelog entry covering KSM-299, KSM-1019, KSM-1080 and KSM-1085.
…chema (KSM-1140) (#1096) * feat(python): add full PAM connection settings fields to CLI helper schema Expands PamSettings.connection with the complete PamSettingsConnection field set (RDP, SSH/Terminal, VNC, database, Telnet, Kubernetes, nested SFTP sub-settings), plus allowSupplyHost at the PamSetting level. PamSettingsPortForward gains useSpecifiedLocalPort, localPort, and allowKeeperDBProxy. PamRemoteBrowserSettings gains audio control fields and browser session fields sourced from vault TypeScript definitions. Jira: KSM-1140 * fix(python): address KSM-1140 review feedback - Move changelog entry from core README to helper README Version 1.1.3 section - Update changelog text to accurately reflect live additions only - Strip inert sub-schemas from list-typed portForward and connection fields (value_type: list fields are terminal leaves; nested schemas are never read) - Bump helper setup.py from 1.1.2 to 1.1.3 * fix(python): fix allowSupplyHost depth and restore KSM-1127 fields - Move allowSupplyHost to pamSettings top-level schema (value[0].allowSupplyHost) so it is live; previously nested inside connection made it value[0].connection.allowSupplyHost which no vault type has - Restore database and dbConnectionMethod to connection.schema (accidentally removed when stripping inert keys in round-1 review pass, reverting KSM-1127) * docs(python): fix KSM-1140 changelog - allowSupplyHost is at PamSettings level The previous entry said "PamSettings.connection schema" which is the location where values are silently discarded. allowSupplyHost is a sibling of connection, not inside it.
* fix(python): bump cryptography floor to >=46.0.7 (CVE-2026-39892) Passing a non-contiguous buffer to APIs accepting Python buffers, such as Hash.update(), can overflow. Affects 45.0.0 through 46.0.6, fixed in 46.0.7. The open floor of >=46.0.5 resolves happily to 46.0.5 or 46.0.6, both vulnerable, and this is the published keeper-secrets-manager-core package - a fresh install could land on an affected version. Cherry-picked from ba6b0678 (Sergey Aldoukhov), scoped to sdk/python/core only; the same commit's Oracle KMS storage bump is handled separately under KSM-1215 since storage packages release independently. KSM-1213 * docs(python): add KSM-1213 security changelog entry for CVE-2026-39892 cryptography floor bump --------- Co-authored-by: Sergey Aldoukhov <saldoukhov@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Release branch for Python SDK v17.4.0 and Helper 1.1.3: NSF folder-decryption parity, cache and permission hardening, twelve bug fixes, and throttle improvements. PR #1096 (KSM-1140 PAM connection fields) is still in review against this branch.
Changes
New Features
databaseanddbConnectionMethod(KSM-1127): added toPamSettingsconnection schema in HelperBug Fixes
get_secrets()deduplication (KSM-1145): records accessible via both a shared folder and an individual share no longer appear twiceinnerFolderUidin the flatrecords[]array now use the folder key instead of the app keyget_folders()crash safety (KSM-1080): undecryptable folders are now skipped instead of raising; the remaining folders are returned normallydelete_secret()/delete_folder()partial failure (KSM-1085): both methods now raiseKeeperErrorlisting rejected UIDsRecordCreate.to_dict()custom field (KSM-1152):"custom"is now always included as[]in the payloadget_file_data()null URL (KSM-1131): raisesKeeperErrorwith a retry message instead of a barerequestsexceptionretry_afteris capped atMAX_THROTTLE_DELAY_SEC(176s)KSMCachefile name override (KSM-1019): an explicitkms_cache_file_nameassignment now always takes precedence overKSM_CACHE_DIRInMemoryKeyValueStoragemalformed config (KSM-299): raisesKeeperErrorwith a clear message instead of a crypticTypeErrorFieldType.__init__empty value list (KSM-1119): guards against an empty value list in HelperMaintenance
KSMCache.get_cache_file_patha@staticmethod; bumped Helper core floor to 17.4.0 (KSM-1121)Breaking Changes
None.
Related Issues