Finding
The Android CredentialsVault implementation currently writes credentials.enc.json as a Base64-encoded JSON envelope. Base64 is reversible encoding, not encryption; a filesystem reader can recover provider API keys and any future marketplace OAuth token without a device-bound key. The source itself marks this path as a stub pending Android Keystore JNI.
Impact
Credentials persisted by the Android app are not protected at rest despite the encrypted-vault contract. PR #816 must not add the GitHub OAuth token to this envelope.
Acceptance criteria
- Protect persisted Android credentials with Android Keystore-backed authenticated encryption using a non-exportable key, unique nonce per write, and atomic owner-private storage.
- Bind ciphertext format/version and account key as authenticated data; reject tampering and fail closed.
- Migrate legacy Base64 envelopes without exposing values to logs/events/frontend; delete the legacy plaintext-equivalent file only after verified migration.
- Define safe behavior for locked/unavailable/invalidated Keystore keys. Until this lands, newly added high-value tokens must remain memory-only on Android and require re-login after restart.
- Add Android/JVM or instrumented tests for round-trip, tamper, key invalidation, migration, file permissions, and secret non-exposure.
- Independently security-review the implementation before merging.
Discovered during independent review of #816.
Finding
The Android
CredentialsVaultimplementation currently writescredentials.enc.jsonas a Base64-encoded JSON envelope. Base64 is reversible encoding, not encryption; a filesystem reader can recover provider API keys and any future marketplace OAuth token without a device-bound key. The source itself marks this path as a stub pending Android Keystore JNI.Impact
Credentials persisted by the Android app are not protected at rest despite the encrypted-vault contract. PR #816 must not add the GitHub OAuth token to this envelope.
Acceptance criteria
Discovered during independent review of #816.