Skip to content

chore(deps): bump eu.anifantakis:ksafe from 2.1.0 to 2.1.3#174

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/gradle/eu.anifantakis-ksafe-2.1.3
Open

chore(deps): bump eu.anifantakis:ksafe from 2.1.0 to 2.1.3#174
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/gradle/eu.anifantakis-ksafe-2.1.3

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 1, 2026

Copy link
Copy Markdown
Contributor

Bumps eu.anifantakis:ksafe from 2.1.0 to 2.1.3.

Release notes

Sourced from eu.anifantakis:ksafe's releases.

2.1.3

KSafe 2.1.3 - Security Patch

This is a critical security patch resolving a vulnerability where aggressive memory caching could bypass the requireUnlockedDevice hardware policy.

🔒 Security Fixes

  • Fixed an in-memory cache bypass for requireUnlockedDevice Previously, a key-value pair written with requireUnlockedDevice = true could be read while the device was locked if the value or its decryption key had been cached in RAM during a prior unlocked state. This bypassed the native OS hardware security modules entirely.
  • Coordinated Cache Bypass Architecture We have implemented a strict bypass mechanism across all architectural layers for keys requiring unlocked devices:
    • Core Level: KSafeCore now evaluates metadata before hitting the plaintextCache or memoryCache, forcing a native read if an unlocked device is required.
    • Android Engine: AndroidKeystoreEncryption now bypasses its software DEK cache and queries the Keystore hardware directly, restoring the native UserNotAuthenticatedException on locked devices.
    • iOS/macOS Engine: AppleKeychainEncryption now bypasses its keyBytesCache and queries the Keychain directly, strictly enforcing the kSecAttrAccessibleWhenUnlockedThisDeviceOnly policy. Highly sensitive keys are now guaranteed to be cryptographically enforced by the underlying OS hardware on every read, fixing the lock bypass vulnerability on both Android and iOS.

Full Changelog: ioannisa/KSafe@2.1.2...2.1.3

2.1.2

Highlights

  • Android ENCRYPTED-memory reads are now ~570× faster on real hardware. Before 2.1.2, every ENCRYPTED-memory read ran its AES-GCM inside the Android Keystore (TEE) — a hardware round-trip measured at ~8 ms/op on a Galaxy S24 Ultra (emulators hid this at ~0.2 ms). 2.1.2 keeps the per-datastore master key (KEK) non-exportable in the TEE but uses it to wrap a data-encryption key (DEK) that is unwrapped once into memory; per-value AES-GCM then runs in userspace. On an S24 Ultra, ENCRYPTED Direct read dropped 8.25 ms → 0.0144 ms — flipping KSafe from ~143×/161× slower to ~3.4×/2.6× faster than EncryptedSharedPreferences / KVault on the decrypt-every-read path. Brings Android in line with the Apple (CryptoKit) and JVM (JCE) engines.

  • HARDWARE_ISOLATED and strict requireUnlockedDevice are unchanged. Both keep their key in the TEE and decrypt there on every op — the DEK fast path applies only to the relaxed DEFAULT master (usable while the device is locked anyway), so no security property is lost.

  • No migration, no data loss. DEK ciphertext is self-describing (MAGIC || VERSION header), so pre-2.1.2 TEE ciphertext and new DEK ciphertext coexist. Existing values upgrade lazily on their next write.

  • Cross-type numeric reads no longer silently lose data. A persisted primitive can now be read back as any other numeric type — the full Int/Long/Float/Double matrix coerces when the value is faithfully representable. Previously Float↔Double returned the default (silent data loss on Android / iOS / macOS / JVM; web was unaffected).

  • JVM: a transient OS key-vault outage at startup no longer destroys data. A momentarily-unreachable Keychain / Secret Service / DPAPI (locked keychain, login keyring not yet up, SSH / headless launch) used to make KSafe fall back to the software vault and treat it as healthy — which could permanently delete recoverable ciphertext and overwrite the real key. KSafe now distinguishes "vault present but unreachable" from "no vault" and fails safe. Upgrade strongly recommended for JVM / Compose Desktop consumers using OS-backed key custody.

  • A failed write no longer corrupts the rest of the batch — or lies about it. One failing encrypt used to drop every write in the same coalescing window (including unrelated and plain writes), and the failed value kept being served from cache for the rest of the process. Failures are now isolated per entry and the optimistic cache is rolled back.

  • getOrCreateSecret no longer silently rotates an unreadable secret under PLAIN_TEXT. A secret that couldn't be decrypted at cold start (locked device / unavailable vault / corrupt blob) was treated as absent and replaced — orphaning anything encrypted under it (e.g. a SQLCipher DB). It now refuses to rotate and surfaces the condition.


Changed

  • Android: software-DEK fast path for relaxed DEFAULT encryption. A random AES key (the DEK, sized per KSafeConfig.keySize) is generated once per safe, wrapped (AES-GCM) by the non-exportable Keystore master key (KEK), and persisted as a single reserved entry (__ksafe____DEK____) in the safe's own DataStore — KSafe uses no SharedPreferences anywhere. It is unwrapped once into an in-process cache, after which every relaxed-DEFAULT encrypt/decrypt is pure-CPU AES-GCM with no TEE round-trip. DEK ciphertext carries a 5-byte MAGIC("KSD1") + VERSION header; decrypt routes by it (with a GCM-auth fallback to the legacy TEE path), so no envelope-version bump and no common-code change. A corrupt/mismatched DEK self-heals on the next write; clearAll() wipes it, but deleting an individual key never does.

    • Security trade-off (Android only): the relaxed-DEFAULT DEK lives in process memory after first use — the same posture as EncryptedSharedPreferences / Tink and KSafe's own Apple and JVM engines. The KEK never leaves the TEE. Apps that require key material to never reside in memory should use HARDWARE_ISOLATED or requireUnlockedDevice = true.
    • Lazy DEK creation: the DEK is generated on the first real encrypt, not at construction. Prewarm now warms only the wrapping KEK, so an unencrypted-only safe never writes a DEK.
  • Android DataStore lifecycle is now robust to close-then-recreate on the same file. The factory tracks each datastore's owning scope and awaits its completion (bounded) before reopening — removing the intermittent IllegalStateException: There are multiple DataStores active for the same file under rapid re-init.

  • KSafe.protectionInfo (Android) now discloses the in-memory DEK. The custody string notes that relaxed DEFAULT values use a TEE-wrapped AES key held in memory, plus a relaxed_default_uses_software_dek note. intendedLevel / effectiveLevel stay HARDWARE_BACKED (custody is still hardware-rooted via the KEK).


... (truncated)

Changelog

Sourced from eu.anifantakis:ksafe's changelog.

[2.1.3] - 2026-06-17

Security patch release for aggressive memory caching bypassing the requireUnlockedDevice hardware lock policy.

Fixed

  • requireUnlockedDevice = true security policy could be bypassed due to in-memory caching (All Platforms). A key-value pair written with the requireUnlockedDevice = true flag is designed to be inaccessible while the user's device is locked. However, because KSafeCore maintained a fast, optimistic plaintextCache and memoryCache, a read on a locked device could return the secret value directly from process memory if it had been cached while the device was previously unlocked. The in-memory read bypassed the hardware OS security modules entirely. Furthermore, the platform-specific encryption engines (AndroidKeystoreEncryption and AppleKeychainEncryption) also aggressively cached raw AES bytes or Data Encryption Keys (DEKs) in memory, which could short-circuit hardware lock checks even if the core cache was bypassed.
    • The Fix: A coordinated cache bypass architecture has been implemented across all layers. KSafeCore now consults the metadata for a key before checking the plaintextCache. If the key requires an unlocked device, it completely ignores the in-memory cache and forces a native decryption request. The native engine interface now receives the requireUnlockedDevice intent. On Android, this forces the AndroidKeystoreEncryption engine to bypass its software DEK cache and hit the Keystore hardware directly, which natively throws a UserNotAuthenticatedException on locked devices. On Apple platforms, AppleKeychainEncryption bypasses its keyBytesCache and queries the Keychain, which enforces kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Highly sensitive keys are now guaranteed to be cryptographically enforced by the underlying OS hardware on every read, fixing the lock bypass vulnerability on both Android and iOS.

[2.1.2] - 2026-06-10

Android performance, numeric type-coercion, and data-loss hardening release. Drop-in upgrade from 2.1.1 — on-disk format is unchanged and existing data keeps working without migration.

Highlights

  • Android: ENCRYPTED-memory "decrypt on every read" is now userspace AES — ~570× faster on real hardware. Before 2.1.2 each ENCRYPTED-memory read ran its AES-GCM inside the Android Keystore (TEE), so every read was a hardware round-trip: ~8 ms/op on a Galaxy S24 Ultra (an emulator's software keystore hid this — it looked like ~0.2 ms). 2.1.2 keeps the per-datastore master key (the KEK) non-exportable in the TEE but uses it to wrap a data-encryption key (DEK) that is unwrapped once into process memory; per-value AES-GCM then runs in userspace. Measured on an S24 Ultra, ENCRYPTED Direct read dropped 8.25 ms → 0.0144 ms, which flips KSafe from ~143×/161× slower than EncryptedSharedPreferences / KVault to ~3.4×/2.6× faster on the apples-to-apples decrypt-every-read path. This brings Android in line with the Apple (CryptoKit) and JVM (JCE) engines, which already held raw key bytes in memory and did userspace AES. Most relevant if you use KSafeMemoryPolicy.ENCRYPTED for frequently-read secrets.
  • HARDWARE_ISOLATED and the strict requireUnlockedDevice master are unchanged. Both keep their key inside the TEE and decrypt there on every op — the DEK fast path applies only to the relaxed (requireUnlockedDevice = false) DEFAULT master, which is usable while the device is locked anyway, so no security property is lost there.
  • No migration, no data loss. DEK ciphertext is self-describing (a MAGIC || VERSION header), so the engine reads pre-2.1.2 TEE ciphertext via the original path and new ciphertext via the DEK path — both coexist. Existing values are upgraded lazily on their next write, exactly like the v1→v2 envelope migration.
  • Cross-type numeric reads no longer silently lose data. A persisted primitive can now be read back as any other numeric type — the full Int/Long/Float/Double matrix coerces when the value is faithfully representable (e.g. a key shipped as ksafe(0f) later read as Double, or ksafe(0) read as Long). Previously the unencrypted path coerced Int↔Long but not Float↔Double — those returned the default (silent data loss on Android / iOS / macOS / JVM; web was unaffected). See Fixed.
  • JVM: a transient OS key-vault outage at startup no longer destroys data. A momentarily-unreachable Keychain / Secret Service / DPAPI (locked keychain, login keyring not yet up, SSH / headless launch) previously made KSafe silently fall back to the software vault and treat that as healthy, which could permanently delete still-recoverable ciphertext and even overwrite the real OS-vault key on the next launch. KSafe now distinguishes "OS vault present but unreachable" from "no OS vault" and fails safe instead of destructively. Upgrade strongly recommended for JVM / Compose Desktop consumers using OS-backed key custody. See Fixed.
  • A failed write no longer corrupts the rest of the batch — or lies about it afterwards. One failing encrypt (e.g. a requireUnlockedDevice write while the device is locked) used to drop every write that happened to share the same coalescing window — including unrelated keys and plain writes — and the failed value kept being served from the in-memory cache for the rest of the process even though it never reached disk. Failures are now isolated per entry and the optimistic cache is rolled back on failure. See Fixed.
  • getOrCreateSecret no longer silently rotates an unreadable secret under PLAIN_TEXT. Under KSafeMemoryPolicy.PLAIN_TEXT, a secret that couldn't be decrypted at cold start (locked device / unavailable vault / corrupt blob) was treated as absent and replaced — permanently orphaning anything encrypted under it (e.g. a SQLCipher database). It now correctly refuses to rotate and surfaces the condition. See Fixed.

Changed

  • Android AndroidKeystoreEncryption adds a software-DEK fast path for relaxed DEFAULT encryption. A random AES key (the DEK, sized per KSafeConfig.keySize) is generated once per safe, wrapped (AES-GCM) by the non-exportable Keystore master key (the KEK), and persisted as a single reserved Base64 entry (__ksafe____DEK____) in the safe's own DataStore — KSafe uses no SharedPreferences anywhere, not even for the DEK. The __ksafe_ prefix keeps the entry in KSafe's internal namespace (the same convention as per-key metadata), so the core never surfaces it as a user value and clearAll() wipes it with everything else. It is unwrapped once into an in-process cache (mirroring the Apple engine's keyBytesCache), after which every encrypt/decrypt of a relaxed-DEFAULT value is pure-CPU AES-GCM with no Keystore/TEE round-trip. DEK ciphertext carries a 5-byte MAGIC("KSD1") + VERSION header; decrypt routes by that header (with a GCM-auth fallback to the legacy TEE path), so the change needs no envelope-version bump and no common-code change. deleteKey drops the cached DEK and the KEK; the persisted DEK is cleared + regenerated under a fresh KEK when its KEK is permanently invalidated or the stored wrapped DEK fails to unwrap (corrupt blob / KEK mismatch) — so a damaged DEK self-heals on the next write instead of failing forever — and is wiped by clearAll(), but never by deleting an individual key, so removing one entry can't brick the rest. The engine is constructed with a DataStore-backed WrappedDekStore (internal); an internal useSoftwareDek flag exists as a test / escape hatch and is not exposed publicly.

    • Security trade-off (Android only): the relaxed-DEFAULT DEK lives in process memory after first use — the same posture as EncryptedSharedPreferences / Tink and KSafe's own Apple and JVM engines (and the PLAIN_TEXT/LAZY_PLAIN_TEXT plaintext caches). The KEK never leaves the TEE. Apps that require key material to never reside in app memory should use KSafeWriteMode.Encrypted(KSafeEncryptedProtection.HARDWARE_ISOLATED) or requireUnlockedDevice = true, both of which keep the per-call TEE path.
    • Lazy DEK creation: the DEK is generated + persisted on the first real encrypt, not at construction. Prewarm now warms only the wrapping KEK (via the new KSafeEncryption.prewarmKey, which the Android engine overrides), so an unencrypted-only safe never writes a DEK and construction performs no DataStore I/O for the DEK. Other engines keep the prior prewarm behavior through the interface's default.
  • Android DataStore lifecycle is now robust to close-then-recreate on the same file. The factory tracks each datastore's owning CoroutineScope; when a safe is recreated on a path whose previous owner is still tearing down, it awaits that scope's completion (bounded) before opening a new DataStore — DataStore frees a file only once its scope's Job completes. This removes an intermittent IllegalStateException: There are multiple DataStores active for the same file under rapid re-init (instrumented tests, DI hot-reload).

  • KSafe.protectionInfo (Android) custody text now discloses the in-memory DEK. The custody string notes that relaxed DEFAULT values use a TEE-wrapped AES key held in memory, and a relaxed_default_uses_software_dek note is added, so protectionInfo stays truthful. intendedLevel / effectiveLevel remain HARDWARE_BACKED (the key custody is still hardware-rooted via the KEK).

Fixed

  • Unencrypted FloatDouble reads silently returned the default (data loss); the full numeric matrix now coerces. KSafeCore.convertStoredValue (commonMain) — the read path for plain, typed-DataStore primitives — had Int↔Long coercion arms but no Float↔Double ones, so a key written as Float and later read as Double (or vice-versa) hit else -> defaultValue and lost the stored value. This affected Android, iOS, macOS, and JVM (all store primitives typed via DataStore); the web target was unaffected because its values round-trip through localStorage as strings, which the existing String arms already coerce. The branch now coerces the whole Int/Long/Float/Double matrix, following the original Long→Int rule: widening is exact (or, for large magnitudes, loses only precision); narrowing or decimal→integer coerces only when the value is faithfully representable — an out-of-range integer, an overflowing decimal, or a decimal with a fractional part read as an integer falls back to the caller's default rather than silently truncating or wrapping. No migration needed — existing on-disk values read correctly the moment a key's declared type changes. (The encrypted path was already type-tolerant via type-tagless JSON.)

  • JVM (critical): a transient OS key-vault outage at construction could permanently destroy keys and data. JvmKeyVaultProvider.pick() runs each OS vault (Windows DPAPI / macOS Keychain / Linux Secret Service) through a write→read-back→delete self-test at construction. If that self-test failed for a transient reason — a locked macOS Keychain, a Linux login keyring not yet on D-Bus (SSH / headless / session-autostart before unlock) — selection silently fell back to the legacy software vault with the degrade flag left unset, i.e. treated as a healthy choice. Two independent permanent-data-loss paths followed: (1) KSafeCore's startup orphan sweep saw "No encryption key found" for entries whose key lives only in the now-unreachable OS vault and deleted the still-recoverable ciphertext and metadata from disk; (2) the construction-time master-key prewarm minted a fresh "junk" key into the legacy DataStore migration source, which the next, healthy launch's legacy-first migration then copied over the real OS-vault key (delete-then-add), making everything encrypted under it permanently undecryptable. A constructible-but-unreachable OS vault is now flagged distinctly from "no OS vault is present here": reads of an unresolvable key report "unavailable" (not "absent"), so the orphan sweep leaves the ciphertext intact until the vault is reachable again; and key creation refuses to mint into the legacy migration source while the OS vault is unreachable (failing closed, with an actionable one-time warning and the -Dksafe.jvm.keyVault=software opt-out), so no junk key can later overwrite the real one. The runtime-LinkageError degrade path (a jlink runtime missing jdk.unsupported) and the 2.0 → 2.1 legacy-key migration are unchanged. The self-test canary now also uses a unique alias per attempt: the OS stores are per-user and shared by every KSafe process and instance on the machine, so the old fixed alias let two concurrent self-tests interleave (one's cleanup deleted the other's canary between its write and read-back) — two apps launching at login, or one app constructing two instances in parallel, could fail a perfectly healthy vault's self-test and put that session into the fail-safe state for no reason. Affects JVM / Compose Desktop consumers using OS-backed key custody (shipped 2.1.0 / 2.1.1).

  • A single failing encrypt dropped every other write in the coalesced batch. The write coalescer (KSafeCore.processWrites, commonMain) encrypted a batch's entries inside one coroutineScope with awaitAll, whose fail-fast cancels the siblings — so one throwing encryptSuspend (e.g. a requireUnlockedDevice = true write while the device is locked, or any transient Keystore/Keychain error) propagated out before applyBatch ran, silently discarding every other write that merely landed in the same ~16 ms coalescing window, including plain writes and encrypted writes to unrelated keys. Encryption is now isolated per entry: a failing encrypt drops only its own key and completes only that caller exceptionally, while every other write in the batch still commits.

  • A failed write left its never-persisted value served from the cache for the rest of the process. Both the suspend and fire-and-forget put paths mutate the optimistic in-memory cache (and protection metadata, and the dirty-key set) before the disk commit, and nothing rolled them back when the batch failed — so subsequent reads returned the value that never reached disk until the process restarted (the dirty flag pinned it past every reconciliation snapshot), and after restart it silently reverted. Failures now roll back the affected keys' optimistic state (memory cache, the plaintext side-cache used by ENCRYPTED_WITH_TIMED_CACHE / LAZY_PLAIN_TEXT, protection metadata, and dirty flags) so reads fall back to the prior persisted value, or the default — on both a per-entry encrypt failure and a whole-batch applyBatch failure. Fire-and-forget putDirect failures, previously fully silent, now emit a SEVERE log. The rollback is ownership-gated: each write claims its key before its optimistic state, and a failed write only reverts a key it is still the latest writer for — so rolling back can never strip the in-flight state of a newer write to the same key issued while the failed batch was processing (the newer write's own commit, or failure, resolves the key).

  • getOrCreateSecret silently rotated an existing-but-unreadable secret under KSafeMemoryPolicy.PLAIN_TEXT. The "never silently rotate" guard distinguishes "secret absent" from "secret present but unreadable" via getKeyInfo, which decided existence solely from the in-memory cache. Under PLAIN_TEXT the cache is built by decrypting every encrypted entry at cold start and dropping any that fails to decrypt (locked device, temporarily-unavailable vault, corrupt/tampered blob) from the cache — so a still-present secret read as absent, the guard fell through, and a brand-new secret was minted whose write overwrote the old ciphertext, permanently orphaning everything encrypted under it (e.g. a SQLCipher database keyed by that secret). getKeyInfo now also consults the protection-metadata map, which tracks on-disk existence independently of whether the value can currently be decrypted and of the memory policy, so the guard correctly throws instead of rotating. The other three memory policies cache ciphertext (so the entry stayed present) and were already protected; PLAIN_TEXT was the gap.

  • Android: co-existing KSafe instances on the same file could open a second DataStore — or have their store cancelled when another instance closed. The factory dedupes the per-file DataStore so repeated construction (DI re-init, multiple holders) doesn't trip DataStore's "multiple DataStores active for the same file" guard, but the dedup had two races: (1) the cache insert was non-atomic (getOrPut), so two instances constructed concurrently on the same file could each open a DataStore and the second tripped the guard — swallowed by the cache-load path, leaving that instance to return defaults and drop its writes; and (2) only the instance that created the DataStore cancelled its scope on close(), so closing that one instance cancelled the DataStore out from under any other still-live instance on the same file (its subsequent reads/writes then hit a cancelled scope). The factory now shares a single ref-counted per-file backend behind a per-path lock: creation is atomic (never two DataStores for one file) and the scope is torn down only when the last instance on that path closes.

  • JVM/Desktop: two KSafe instances on the same file — or a quick close()-then-recreate — silently broke reads and writes. Unlike Android, the JVM factory created a fresh DataStore on every construction with no per-file dedup, so a second live instance on the same fileName tripped DataStore's "multiple DataStores active for the same file" guard inside its collector (swallowed → the instance returned defaults and dropped its writes), and an immediate recreate after close() raced DataStore's file release (it frees a file only once the owning scope completes). The JVM factory now uses the same ref-counted, per-file backend as Android: atomic creation, a bounded await on a prior owner's teardown before reopening, and teardown only on the last close — so concurrent same-file instances and close()→recreate both work. Both the normal DataStore backend and the no-sun.misc.Unsafe JSON-file fallback are covered.

  • Apple (iOS/macOS): concurrent first-time key creation could clobber the key and silently lose data. AppleKeychainEncryption had no lock serializing key creation, even though KSafeCore relies on each engine doing so (Android and JVM both do). On first launch — or after clearAll() — the construction-time master-key prewarm races the first batch of DEFAULT writes, which encrypt up to 8 entries in parallel against the same per-datastore master alias. Two threads could both find no key, both generate one, and the delete-then-add Keychain store let the second overwrite the first after the first had already produced ciphertext under its key — so that value became permanently undecryptable (and, being a GCM-authentication failure rather than a "key not found", it wasn't even reclaimed by the orphan sweep). Key creation is now serialized (a reentrant lock with a double-checked cache), so concurrent callers resolve to a single shared key; the lock-free read path and the in-memory key cache are unchanged. Also hardened: a Secure-Enclave store failure no longer silently falls back to a divergent plain key under the same identifier. Affects all Apple targets (shipped 2.1.0 / 2.1.1); upgrade recommended if you target iOS/macOS and use the default (DEFAULT) encryption.

... (truncated)

Commits
  • bc46ebc update changelog with 2.1.3 changes
  • 4fa64bc fix: requireUnlockedDevice parameter to decrypt methods for enhanced security
  • 7e01293 test(ios): harden legacy-migration durability probe against DataStore reacqui...
  • f83897c Update README.md
  • 9c6adcb chore: update library versions for kotlinxCoroutinesCore, jna, and gradleTest...
  • be9160d chore: update library versions for kotlinxCoroutinesCore, jna, and gradleTest...
  • 34bbff5 refactor: improve comments for clarity and consistency in test files
  • 810d266 refactor: improve comments for clarity and consistency in test files
  • 076092c refactor: update comment to clarify purpose of macOS nullable-default regress...
  • 79e3854 refactor: clean up comments for clarity and consistency across KSafe modules
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [eu.anifantakis:ksafe](https://github.com/ioannisa/ksafe) from 2.1.0 to 2.1.3.
- [Release notes](https://github.com/ioannisa/ksafe/releases)
- [Changelog](https://github.com/ioannisa/KSafe/blob/main/CHANGELOG.md)
- [Commits](ioannisa/KSafe@2.1.0...2.1.3)

---
updated-dependencies:
- dependency-name: eu.anifantakis:ksafe
  dependency-version: 2.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file java Pull requests that update java code labels Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file java Pull requests that update java code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants