Skip to content

Release Python Oracle KMS Storage v1.1.0#965

Open
stas-schaller wants to merge 1 commit into
masterfrom
release/storage/python/oracle-kms/v1.1.0
Open

Release Python Oracle KMS Storage v1.1.0#965
stas-schaller wants to merge 1 commit into
masterfrom
release/storage/python/oracle-kms/v1.1.0

Conversation

@stas-schaller
Copy link
Copy Markdown
Contributor

@stas-schaller stas-schaller commented Mar 9, 2026

Summary

Release branch for v1.1.0: security hardening (CVE-2026-26007, AES-GCM nonce correction), eight correctness fixes from a GCP-parity audit, and a Python floor raise to 3.9.

Changes

Bug Fixes

  • CVE-2026-26007 (KSM-834): cryptography floor raised to >=46.0.5 — ECDH subgroup attack on SECT curves (CVSS 8.2)
  • AES-GCM nonce + hash (KSM-954): nonce corrected from 128-bit (pycryptodome default) to 96-bit per NIST SP 800-38D; config change detection switched from MD5 to SHA-256; existing encrypted blobs remain readable
  • Silent plaintext write at init (KSM-950): __init__ no longer writes {} to disk before encryption succeeds; KMS failures during initial get_key/encrypt_buffer now propagate
  • Silent KMS failure in encrypt/decrypt (KSM-951): encrypt_buffer() and decrypt_buffer() now raise on KMS error instead of returning empty bytes/string
  • delete_all() left file on disk (KSM-952): now calls os.remove() instead of re-encrypting an empty config
  • set() silently diverged on read-only file (KSM-953): PermissionError now propagates; in-memory and disk state stay consistent
  • Live reference from read_storage() (KSM-955): returns a defensive copy; decrypt_config() default changed from autosave=True to autosave=False
  • No thread safety (KSM-956): OracleKeyValueStorage now uses an internal threading.RLock for set(), delete(), change_key(), and decrypt_config()
  • self.config = None after empty bootstrap (KSM-957): load_config() no longer leaves config as None after parsing a plaintext {} init file; subsequent get/set/delete no longer crash with TypeError
  • Effective Python floor understated (KSM-958): requires-python tightened to >=3.9.2; cryptography>=46.0.5 excludes 3.9.0 and 3.9.1 via PyPI markers — pip now rejects those versions with a clear error instead of an opaque resolver failure

Maintenance

  • KSM-949: Python floor raised 3.6 → 3.9; Python 3.13 classifier added; 3.6/3.7/3.8 classifiers removed
  • keeper-secrets-manager-core floor raised from >=16.6.6 to >=17.2.1
  • oci floor updated with version markers: >=2.167.3,<2.168.2 on Python 3.9, >=2.174.0 on 3.10+; required to unlock cryptography>=46.0.5 (older oci capped cryptography<46.0.0)
  • pycryptodome floor raised to >=3.21.0; requests to >=2.32.0 (CVE-2026-47081); urllib3 to >=2.6.3
  • Publish workflow updated to GCP v1.1.0 parity: matrix test gate (3.9–3.13), PyPI OIDC trusted publishing, SHA-pinned actions, publish toggle, SBOM generation
  • New matrix CI workflow added for PR/push coverage on Python 3.9–3.13

Breaking Changes

  • Python 3.6, 3.7, and 3.8 are no longer supported. Pin to keeper-secrets-manager-storage-oracle-kms<1.1.0 to remain on a supported version.
  • decrypt_config() default changed: autosave now defaults to False (was True). Pass autosave=True explicitly to preserve the previous behavior (KSM-955).

Related Issues

KSM-834, KSM-949, KSM-950, KSM-951, KSM-952, KSM-953, KSM-954, KSM-955, KSM-956, KSM-957, KSM-958

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Mar 9, 2026

@stas-schaller stas-schaller changed the title Release keeper-secrets-manager-storage-oracle-kms v1.1.0 Release Python Oracle KMS Storage v1.1.0 Mar 9, 2026
@stas-schaller stas-schaller force-pushed the release/storage/python/oracle-kms/v1.1.0 branch from 81b1e69 to 4599784 Compare March 10, 2026 15:15
@stas-schaller stas-schaller force-pushed the release/storage/python/oracle-kms/v1.1.0 branch from 4599784 to 060c76a Compare March 27, 2026 18:31
@stas-schaller stas-schaller force-pushed the release/storage/python/oracle-kms/v1.1.0 branch from 060c76a to fe4556f Compare April 9, 2026 19:39
@stas-schaller stas-schaller force-pushed the release/storage/python/oracle-kms/v1.1.0 branch from fe4556f to 0e862c1 Compare May 12, 2026 16:14
Comment on lines 5 to 11
cryptography==46.0.5
idna==3.10
importlib_metadata==8.6.1
keeper-secrets-manager-core>=16.6.6
keeper-secrets-manager-core>=17.2.0
oci==2.146.0
pycparser==2.22
pycryptodome==3.21.0
pyOpenSSL==24.3.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 requirements.txt now pins cryptography==46.0.5 (for CVE-2026-26007) alongside pyOpenSSL==24.3.0, but pyOpenSSL 24.3.0's metadata declares Requires-Dist: cryptography<45,>=41.0.5. pip install -r requirements.txt will fail with ResolutionImpossible on modern pip, or silently downgrade cryptography on older pip — defeating the CVE fix that motivates this PR. Fix by bumping pyOpenSSL to a 46.x-compatible release (e.g. >=25.3.0) or dropping the pin altogether (it is a transitive of oci, not in pyproject.toml, and not imported in source).

Extended reasoning...

The conflict. The PR raises cryptography from 44.0.1 to 46.0.5 in requirements.txt to address CVE-2026-26007, but leaves pyOpenSSL==24.3.0 in place. pyOpenSSL 24.3.0's PyPI metadata declares Requires-Dist: cryptography<45,>=41.0.5, so the two pins are mutually unsatisfiable.

How it manifests. Running pip install -r requirements.txt against this file behaves one of two ways:

  1. Modern pip (resolver enabled, the default since 20.3): fails with ERROR: ResolutionImpossible and aborts the install.
  2. Older pip or --use-deprecated=legacy-resolver: silently downgrades cryptography to a <45 release, re-introducing CVE-2026-26007 — the exact vulnerability this release was created to patch.

Step-by-step proof.

  1. Inspect pyOpenSSL 24.3.0's METADATA on PyPI: Requires-Dist: cryptography<45,>=41.0.5.
  2. requirements.txt (lines 5–11) pins cryptography==46.0.5 and pyOpenSSL==24.3.0.
  3. pip install --dry-run pyOpenSSL==24.3.0 cryptography==46.0.5 (verified by a reviewer) yields: 'Cannot install cryptography==46.0.5 and pyopenssl==24.3.0 because these package versions have conflicting dependencies. ERROR: ResolutionImpossible'.
  4. pyOpenSSL 25.0.0+ relaxes the bound to support cryptography 46; 25.3.0 is a known-good pin.

Why existing guards do not catch it. The publish workflow in this PR no longer runs pip install -r requirements.txt — it builds the wheel and installs the wheel using pyproject.toml dependencies (which only lower-bound cryptography>=46.0.5 and do not list pyOpenSSL). So CI green does not exercise this file, and end users installing the published wheel from PyPI are not affected. But the file is shipped in the source tree and is the only frozen-environment recipe in the package — any developer setting up a dev environment by running pip install -r requirements.txt will hit the resolver failure (or the silent downgrade if they're on an older pip).

Related observation (not required to fix this bug, but worth flagging). oci==2.146.0 itself declares Requires-Dist: cryptography<46.0.0,>=3.2.1, so requirements.txt is doubly inconsistent with cryptography==46.0.5. A clean pip freeze regeneration after installing the package against pyproject would resolve both at once (e.g. by bumping oci to 2.174.0 and pyOpenSSL to 25.3.0+).

Suggested fix. Either (a) bump pyOpenSSL to 25.3.0 (or later) and oci to a release that allows cryptography 46.x; or (b) drop pyOpenSSL from requirements.txt entirely — grep confirms it is not imported in the package source, and it is not declared in pyproject.toml, so it is only there as a frozen transitive of oci. Regenerating requirements.txt via pip freeze after a fresh install would naturally pick a compatible set.

run: |
python3 -m build
python3 -m twine upload --verbose dist/* No newline at end of file
python3 -m build --no-isolation
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 The 'Build and Publish' step runs python3 -m build --no-isolation (line 131), but pyproject.toml declares setuptools_scm[toml]>=6.2 in build-system.requires and the 'Install build tools' step never installs it. pypa/build performs an unconditional dependency check under --no-isolation and exits with code 1 on missing requirements, so this workflow will fail every time it is dispatched. Fix by either dropping setuptools_scm[toml]>=6.2 from build-system.requires (it is unused — version is hard-coded to "1.1.0" and there is no [tool.setuptools_scm] section) or pre-installing it in the build-tools step.

Extended reasoning...

What breaks

sdk/python/storage/keeper_secrets_manager_storage_oracle_kms/pyproject.toml declares:

[build-system]
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

The new publish workflow at .github/workflows/publish.pypi.sdk.storage.oracle.kms.yml runs:

- name: Install build tools
  run: |
    python3 -m pip install --upgrade pip setuptools
    python3 -m pip install build twine 'wheel>=0.46.3'  # wheel pin: CVE-2026-24049

- name: Build and Publish
  working-directory: ./sdk/python/storage/keeper_secrets_manager_storage_oracle_kms
  ...
  run: |
    python3 -m build --no-isolation
    ...

Note: setuptools_scm is never installed. actions/setup-python@v5 on a fresh ubuntu-latest runner does not provide it either.

Why it fails (not just warns)

The refutation argues python -m build only emits a warning on missing build-system.requires deps. That is incorrect for --no-isolation. pypa/build runs check_dependency() against build-system.requires whenever isolation is disabled, and skip_dependency_check defaults to False. When any requirement is unsatisfied, build calls _cprint("ERROR ...") and raise SystemExit(1) before invoking the backend. Three independent verifiers reproduced this with current build releases — all observed exit code 1 with the error ERROR Missing dependencies: setuptools_scm[toml]>=6.2. Whether setuptools_scm is actually used by the backend is irrelevant because the check happens first.

Step-by-step proof

  1. Workflow is dispatched on workflow_dispatch.
  2. Install build tools step installs pip, setuptools, build, twine, wheel>=0.46.3. setuptools_scm is not installed.
  3. Build and Publish step runs python3 -m build --no-isolation from sdk/python/storage/keeper_secrets_manager_storage_oracle_kms.
  4. build parses pyproject.toml, reads build-system.requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"].
  5. build walks the requirements and resolves them against the host env. setuptools ✓, wheel ✓, setuptools_scm[toml]>=6.2 ✗.
  6. build prints ERROR Missing dependencies: setuptools_scm[toml]>=6.2 and exits 1.
  7. No dist/*.whl is produced. The subsequent pip install dist/*.whl, the import smoke test, and twine upload never run. The publish job fails. v1.1.0 cannot ship.

Impact

The entire purpose of this PR is to publish v1.1.0 (CVE-2026-26007 fix, Python floor bump, core dep bump). The workflow as written will fail on first dispatch — no wheel reaches PyPI.

Fix (pick one)

  • (Preferred) Drop setuptools_scm[toml]>=6.2 from build-system.requires in pyproject.toml. The package does not use it: version = "1.1.0" is statically declared, there is no [tool.setuptools_scm] section, and version is not in dynamic.
  • Or pre-install it in the build-tools step: python3 -m pip install build twine setuptools_scm[toml] 'wheel>=0.46.3'.
  • Or pass --skip-dependency-check to python -m build (worst option — masks future drift).

Why the refutation is wrong

The refutation claims the dep check only warns. That was true for very old build versions but is not true for any release in the past several years; the check is gated by skip_dependency_check (default False) and raises SystemExit(1) when failed. The original bug submitter hedged ("may still succeed in practice"), but the three verifiers who actually ran python -m build --no-isolation against a matching pyproject.toml all observed a hard failure.

python3 -m twine upload --verbose dist/* No newline at end of file
python3 -m build --no-isolation
python3 -m pip install dist/*.whl
python3 -c "import keeper_secrets_manager_storage_oracle_kms; print('import OK')"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 The post-build import sanity check resolves to the source tree, not the installed wheel. The step runs with working-directory: ./sdk/python/storage/keeper_secrets_manager_storage_oracle_kms, which contains a same-named source package subdirectory; python3 -c prepends CWD to sys.path[0], so the import always finds the local source even if the wheel is broken. Fix by running the check from a neutral directory (e.g. cd /tmp && python3 -c ...) or using python3 -P -c ... to disable CWD-on-sys.path.

Extended reasoning...

What the bug is

The new "Build and Publish" step (introduced in commit b033909, "ci: add post-build import check") does:

working-directory: ./sdk/python/storage/keeper_secrets_manager_storage_oracle_kms
run: |
  python3 -m build --no-isolation
  python3 -m pip install dist/*.whl
  python3 -c "import keeper_secrets_manager_storage_oracle_kms; print('import OK')"
  python3 -m twine upload --verbose dist/*

The working directory ./sdk/python/storage/keeper_secrets_manager_storage_oracle_kms itself contains a subdirectory of the same name (keeper_secrets_manager_storage_oracle_kms/ with __init__.py and the source modules). Per the Python docs for -c, sys.path[0] is set to '' (the current directory) and searched first. So the import resolves to the on-disk source package, not the wheel that was just installed via pip install dist/*.whl.

Step-by-step proof

  1. Workflow's working-directory for the step is ./sdk/python/storage/keeper_secrets_manager_storage_oracle_kms.
  2. Inside that directory there is a subdirectory keeper_secrets_manager_storage_oracle_kms/ (verified — it contains __init__.py, constants.py, oci_kms_client.py, etc.).
  3. python3 -c 'import keeper_secrets_manager_storage_oracle_kms; ...' runs with sys.path = ['', ...site-packages...].
  4. Python's import system finds ./keeper_secrets_manager_storage_oracle_kms/__init__.py (a regular package on sys.path[0]) before it reaches site-packages where the wheel was installed.
  5. print('import OK') succeeds even if the wheel is broken (missing files, wrong package name in metadata, bad [tool.setuptools] config, missing package-data, etc.).

Why this matters

The commit message "ci: add post-build import check" explicitly states the intent: catch wheel build problems before twine upload ships them to PyPI. Concrete failure modes the check will silently miss:

  • [tool.setuptools] packages = [...] typo dropping the package from the wheel
  • Missing package-data (so the wheel imports but is functionally broken)
  • Wrong package name in built metadata
  • A stale wheel left over in dist/ that dist/*.whl globs and pip installs alongside the new one

pip install dist/*.whl itself would still fail on a totally malformed archive, so the check isn't entirely useless — but for the class of bugs it was added to catch (structural problems in the wheel that import-time would reveal), it provides false confidence.

Fix

One-line. Either:

(cd /tmp && python3 -c "import keeper_secrets_manager_storage_oracle_kms; print('import OK')")

or use Python's -P flag to skip CWD-on-sys.path:

python3 -P -c "import keeper_secrets_manager_storage_oracle_kms; print('import OK')"

Either option makes the import resolve through site-packages, which is what the check was meant to test. Worth fixing before this workflow gets copied to the other storage SDKs.

persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v5
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Nit / supply-chain hardening consistency: at lines 52 and 108 is referenced by mutable tag, while every other action in this workflow (actions/checkout, Keeper-Security/ksm-action, actions/upload-artifact) is SHA-pinned with a version comment per the recent hardening in 310fb4f / 0e862c1. Since this PR explicitly touches both lines (bumping @v4@v5), it's the natural opportunity to complete the pattern by pinning to a commit SHA (e.g., actions/setup-python@<sha> # v5.x.x).

Extended reasoning...

What's inconsistent

Within .github/workflows/publish.pypi.sdk.storage.oracle.kms.yml, four distinct uses: references are present after this PR:

  • actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (lines 14, 47, 103) — SHA-pinned
  • actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 (line 88) — SHA-pinned
  • Keeper-Security/ksm-action@37abbada28637b6478ba845e73d3f8b9676572fe # v1.3.0 (line 114) — SHA-pinned
  • actions/setup-python@v5 (lines 52 and 108) — mutable tag, no SHA

The recent commits 310fb4f ci: harden publish workflow and 0e862c1 ci: add version comment to ksm-action SHA pin establish a clear @<sha> # <version> convention in this file. setup-python is the one outlier.

Why this matters

Git tags are mutable: the action publisher can move v5 to point at a different commit at any time, including after a credential compromise. SHA pins eliminate that risk because the SHA is content-addressable. This is the well-known OpenSSF Scorecard / GitHub Hardening third-party-actions guidance. Even though actions/setup-python is GitHub-owned (lower base risk than a random third-party action), the official guidance and this file's existing pattern both call for SHA pinning.

Why this PR is the right time

The PR diff at lines 52 and 108 changes actions/setup-python@v4actions/setup-python@v5 — so this PR is already updating these exact lines as part of the v1.1.0 release hardening. Pinning the SHA at the same time is a one-line further change with no additional review/testing cost; deferring it means a separate future PR to touch the same lines again.

Step-by-step proof of the inconsistency

  1. Open .github/workflows/publish.pypi.sdk.storage.oracle.kms.yml at line 52: uses: actions/setup-python@v5 — tag only.
  2. Same file, line 108: uses: actions/setup-python@v5 — tag only.
  3. Same file, line 14 (and 47, 103): uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 — SHA + version comment.
  4. Same file, line 88: uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 — SHA + version comment.
  5. Same file, line 114: uses: Keeper-Security/ksm-action@37abbada28637b6478ba845e73d3f8b9676572fe # v1.3.0 — SHA + version comment.
  6. git log -- .github/workflows/publish.pypi.sdk.storage.oracle.kms.yml shows commits 310fb4f / 0e862c1 introducing/standardizing the @<sha> # <version> pattern.

Caveats / acknowledgments

  • This is not a runtime bug — the workflow runs correctly today. Severity is nit.
  • Repo-wide, many other workflows still reference setup-python by tag (e.g. test.python.yml, publish.pypi.sdk.yml), so the repo-wide convention is mixed. The inconsistency being flagged is specifically within this file, which has otherwise been hardened to a uniform SHA-pin standard.
  • Fix is mechanical: pick the current actions/setup-python v5.x commit SHA, replace both @v5 references with @<sha> # v5.x.y, no other code changes needed.

Comment on lines +35 to +40
STATUS=$(curl -sS -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/keeper-secrets-manager-storage-oracle-kms/${VERSION}/json")
if [ "$STATUS" = "200" ]; then
echo "::error::keeper-secrets-manager-storage-oracle-kms ${VERSION} is already on PyPI. Aborting."
exit 1
fi
echo "keeper-secrets-manager-storage-oracle-kms $VERSION not yet on PyPI - ok to proceed"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The new job (lines 35-40) only fails when PyPI returns HTTP 200, so 5xx errors, 3xx redirects, or any non-404 response silently pass the check. Since twine itself rejects duplicate uploads this is defense-in-depth rather than a correctness bug, but the fix is one line — treat anything other than 404 as ambiguous: if [ "$STATUS" != "404" ]; then echo "::error::Unexpected status $STATUS"; exit 1; fi (and add --fail-with-body to the curl).

Extended reasoning...

What the bug is

The validate-versions job added in this PR (.github/workflows/publish.pypi.sdk.storage.oracle.kms.yml lines 35-40) probes PyPI to make sure the version about to be released does not already exist:

STATUS=$(curl -sS -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/keeper-secrets-manager-storage-oracle-kms/${VERSION}/json")
if [ "$STATUS" = "200" ]; then
  echo "::error::keeper-secrets-manager-storage-oracle-kms ${VERSION} is already on PyPI. Aborting."
  exit 1
fi
echo "keeper-secrets-manager-storage-oracle-kms $VERSION not yet on PyPI - ok to proceed"

The gate fires only when STATUS == "200". Any other HTTP code — including 5xx server errors, 3xx redirects, or unexpected values — is treated as "ok to proceed". curl is invoked with -sS (no --fail/--fail-with-body), so HTTP-level errors do not change its exit code.

Why existing safeguards don't fully cover this

GitHub Actions defaults to bash --noprofile --norc -eo pipefail, so set -e is active. That means a pure network-level curl failure (DNS error, connection refused) would abort the script because the STATUS=$(curl ...) substitution would inherit curl's non-zero exit code. So the bug isn't as broad as "any transient error fails open" — but HTTP-level failures (5xx, redirects) do slip through, because -sS without --fail returns exit 0 for those.

Step-by-step proof (PyPI 503 outage scenario)

  1. Maintainer dispatches the workflow to re-publish keeper-secrets-manager-storage-oracle-kms 1.0.0 (already on PyPI).
  2. PyPI is having an incident and https://pypi.org/pypi/.../1.0.0/json returns HTTP/1.1 503 Service Unavailable.
  3. curl -sS -o /dev/null -w "%{http_code}" writes 503 to stdout and exits with status 0 (no --fail).
  4. STATUS="503". The conditional [ "$STATUS" = "200" ] is false.
  5. The script prints ... not yet on PyPI - ok to proceed and the job succeeds.
  6. generate-sbom and publish jobs run, wasting CI time and the SBOM publish quota.
  7. twine upload ultimately calls PyPI's upload endpoint, which returns 400 File already exists, and the publish job fails with a confusing error several minutes later instead of the intended fast-fail.

Impact

Limited. Twine + PyPI remain the authoritative duplicate-version safeguard (PyPI rejects re-uploads with 400 File already exists), so this is not a correctness/security issue — there is no path that causes a wrong package to actually publish. The real cost is:

  • Wasted CI minutes and a misleading failure point in the SBOM/publish steps during a PyPI outage.
  • A weakened early-fail signal: the whole point of the validate-versions job is to fast-fail before doing 5+ minutes of build/SBOM work.

Suggested fix

Invert the check to treat only 404 as success, and let curl fail on transport errors:

STATUS=$(curl -sS --fail-with-body -o /dev/null -w "%{http_code}" \
  "https://pypi.org/pypi/keeper-secrets-manager-storage-oracle-kms/${VERSION}/json" || echo "$?")
if [ "$STATUS" = "200" ]; then
  echo "::error::Version ${VERSION} is already on PyPI. Aborting."
  exit 1
fi
if [ "$STATUS" != "404" ]; then
  echo "::error::Unexpected status \"$STATUS\" from PyPI — cannot verify version. Aborting."
  exit 1
fi
echo "Version $VERSION not yet on PyPI - ok to proceed"

This makes the check unambiguous: 200 means "already published" (fail), 404 means "new version" (proceed), anything else means "cannot determine" (fail, retry the workflow once PyPI is healthy).

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization has reached its monthly code review spending cap.

An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.

Once the cap resets or is raised, push a new commit or reopen this pull request to trigger a review.

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization has reached its monthly code review spending cap.

An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.

Once the cap resets or is raised, push a new commit or reopen this pull request to trigger a review.

…ge-oracle-kms v1.1.0

- Raise Python floor to 3.9.2; Core SDK dependency to >=17.2.1
- Fix AES-GCM nonce from 128-bit to 96-bit per NIST SP 800-38D (KSM-954)
- Replace MD5 with SHA-256 for config change detection (KSM-954)
- Fix plaintext {} written before encryption at init (KSM-950)
- Fix encrypt/decrypt silently returning empty on KMS error (KSM-951)
- Fix delete_all() leaving credential file on disk when KMS unavailable (KSM-952)
- Fix set() silently diverging on read-only config files (KSM-953)
- Fix read_storage() returning live reference; correct autosave default (KSM-955)
- Add thread-safety lock to OracleKeyValueStorage (KSM-956)
- Fix load_config() leaving self.config = None on empty bootstrap (KSM-957)
- Fix requires-python floor to >=3.9.2 (KSM-958)
- Upgrade cryptography to >=46.0.5; fixes CVE-2026-26007 (KSM-834)
- Upgrade urllib3 to 2.6.3, requests to 2.32.4
- Port 23-test suite from GCP patterns; all pass on Python 3.9-3.13
- Rewrite publish workflow with GCP v1.1.0 parity hardening
@stas-schaller stas-schaller force-pushed the release/storage/python/oracle-kms/v1.1.0 branch from 8ec2a4b to 2608edf Compare May 20, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants