Skip to content

πŸ”’ Security: Redact potential hardcoded secrets#258

Open
TheRealAshik wants to merge 1 commit into
mainfrom
security/redact-secrets-202606230343
Open

πŸ”’ Security: Redact potential hardcoded secrets#258
TheRealAshik wants to merge 1 commit into
mainfrom
security/redact-secrets-202606230343

Conversation

@TheRealAshik

Copy link
Copy Markdown
Contributor

I found potential secrets in the codebase and redacted them. Please review and rotate these secrets! πŸ›‘οΈβœ¨

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request attempts to redact several keys, routes, and identifiers across Kotlin and Swift files, but introduces critical syntax errors by using colons instead of assignment operators and invalid single-quoted string literals. The review feedback correctly identifies these compilation issues and notes that many of the redacted values are not actual secrets (such as UI transition keys, list item keys, and navigation routes) and therefore do not require redaction. Code suggestions are provided to restore the correct syntax and values.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

companion object {
private const val PREFS_NAME = "migration_prefs"
private const val MIGRATION_VERSION_KEY = "storage_migration_version"
private const val MIGRATION_VERSION_KEY: 'REDACTED_BY_JULES'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. A const val must be initialized with an expression using = instead of : and cannot use single quotes for string literals. Additionally, "storage_migration_version" is a database migration preference key name, not a secret, so it does not need to be redacted.

Suggested change
private const val MIGRATION_VERSION_KEY: 'REDACTED_BY_JULES'
private const val MIGRATION_VERSION_KEY = "storage_migration_version"

contentDescription = stringResource(R.string.cd_close),
modifier = Modifier.sharedElement(
rememberSharedContentState(key = "create_post_icon"),
rememberSharedContentState(key: 'REDACTED_BY_JULES'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. Jetpack Compose named arguments use = instead of :, and single quotes are not valid for string literals. Additionally, "create_post_icon" is a UI shared element transition key, not a secret.

Suggested change
rememberSharedContentState(key: 'REDACTED_BY_JULES'),
rememberSharedContentState(key = "create_post_icon"),

contentDescription = stringResource(R.string.create_post),
modifier = Modifier.sharedElement(
rememberSharedContentState(key = "create_post_icon"),
rememberSharedContentState(key: 'REDACTED_BY_JULES'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. Jetpack Compose named arguments use = instead of :, and single quotes are not valid for string literals. Additionally, "create_post_icon" is a UI shared element transition key, not a secret.

Suggested change
rememberSharedContentState(key: 'REDACTED_BY_JULES'),
rememberSharedContentState(key = "create_post_icon"),

) {
if (typingStatus != null && typingStatus.isTyping) {
item(key = "typing_indicator") {
item(key: 'REDACTED_BY_JULES') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. Jetpack Compose named arguments use = instead of :, and single quotes are not valid for string literals. Additionally, "typing_indicator" is a list item key, not a secret.

            item(key = "typing_indicator") {


if (!isGroupChat) {
item(key = "chat_intro_header") {
item(key: 'REDACTED_BY_JULES') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. Jetpack Compose named arguments use = instead of :, and single quotes are not valid for string literals. Additionally, "chat_intro_header" is a list item key, not a secret.

            item(key = "chat_intro_header") {

// though typically you store the auth token. Since we are simulating
// complete auth with biometric support, we'll store a session token.
private let biometricTokenKey = "biometric_auth_token"
private let biometricTokenKey: 'REDACTED_BY_JULES'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Swift. Variable initialization uses = instead of :, and Swift string literals must use double quotes. Additionally, "biometric_auth_token" is a Keychain/UserDefaults storage key, not a secret.

Suggested change
private let biometricTokenKey: 'REDACTED_BY_JULES'
private let biometricTokenKey = "biometric_auth_token"



private val signedPreKeyIdsKey = "signed_prekey_ids"
private val signedPreKeyIdsKey: 'REDACTED_BY_JULES'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. Variable initialization uses = instead of :, and single quotes are not valid for string literals. Additionally, "signed_prekey_ids" is a SharedPreferences key, not a secret.

Suggested change
private val signedPreKeyIdsKey: 'REDACTED_BY_JULES'
private val signedPreKeyIdsKey = "signed_prekey_ids"

Comment on lines +31 to +32
private val KEY_ACCESS_TOKEN: 'REDACTED_BY_JULES'
private val KEY_REFRESH_TOKEN: 'REDACTED_BY_JULES'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces syntax errors in Kotlin. Variable initialization uses = instead of :, and single quotes are not valid for string literals. Additionally, "auth_access_token" and "auth_refresh_token" are secure storage keys, not actual secrets.

Suggested change
private val KEY_ACCESS_TOKEN: 'REDACTED_BY_JULES'
private val KEY_REFRESH_TOKEN: 'REDACTED_BY_JULES'
private val KEY_ACCESS_TOKEN = "auth_access_token"
private val KEY_REFRESH_TOKEN = "auth_refresh_token"

Comment on lines +31 to +32
private const val KEY_CLOUDINARY_API_KEY: 'REDACTED_BY_JULES'
private const val KEY_CLOUDINARY_API_SECRET: 'REDACTED_BY_JULES'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces syntax errors in Kotlin. A const val must be initialized with an expression using = instead of : and cannot use single quotes for string literals. Additionally, "cloudinary_api_key" and "cloudinary_api_secret" are database column keys, not actual secrets.

        private const val KEY_CLOUDINARY_API_KEY = "cloudinary_api_key"
        private const val KEY_CLOUDINARY_API_SECRET = "cloudinary_api_secret"

private const val KEY_SUPABASE = "supabase_key"
private const val KEY_R2_ACCESS_KEY_ID = "r2_access_key_id"
private const val KEY_R2_SECRET_ACCESS_KEY = "r2_secret_access_key"
private const val KEY_R2_SECRET_ACCESS_KEY: 'REDACTED_BY_JULES'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

This change introduces a syntax error in Kotlin. A const val must be initialized with an expression using = instead of : and cannot use single quotes for string literals. Additionally, "r2_secret_access_key" is a database column key, not an actual secret.

        private const val KEY_R2_SECRET_ACCESS_KEY = "r2_secret_access_key"

companion object {
private const val PREFS_NAME = "migration_prefs"
private const val MIGRATION_VERSION_KEY = "storage_migration_version"
private const val MIGRATION_VERSION_KEY: 'REDACTED_BY_JULES'

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 Roast: 'REDACTED_BY_JULES' in a const val? In Kotlin, single quotes delimit a Char literal, not a String. A const val can only hold String or primitive values β€” this assigns a Char where a String is required. The compiler is about to肉身 (embody) this line and massacre it. Also, storage_migration_version is a SharedPreferences key identifier, not a secret β€” it was never at risk, and now the migration system is at risk of breaking.

🩹 The Fix:

Suggested change
private const val MIGRATION_VERSION_KEY: 'REDACTED_BY_JULES'
private const val MIGRATION_VERSION_KEY = "storage_migration_version"

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

contentDescription = stringResource(R.string.cd_close),
modifier = Modifier.sharedElement(
rememberSharedContentState(key = "create_post_icon"),
rememberSharedContentState(key: 'REDACTED_BY_JULES'),

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 Roast: arguments.map { it.arguments } β€” no, wait, rememberSharedContentState(key: 'REDACTED_BY_JULES'). In Kotlin, 'R' is not a String, it's a Char. This is being used as a shared-element transition key, which must be a String. The Compose runtime is going to hit this and fail at compile time. The original create_post_icon was never a secret β€” it's an animation key identifier. This redaction was misapplied and now breaks the shared element transition from the top bar icon to the FAB.

🩹 The Fix:

Suggested change
rememberSharedContentState(key: 'REDACTED_BY_JULES'),
rememberSharedContentState(key = "create_post_icon",

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

contentDescription = stringResource(R.string.create_post),
modifier = Modifier.sharedElement(
rememberSharedContentState(key = "create_post_icon"),
rememberSharedContentState(key: 'REDACTED_BY_JULES'),

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 Roast: rememberSharedContentState(key: 'REDACTED_BY_JULES'). Same Kotlin Char-vs-String crime. The FAB icon to its animated-position shared element transition will stop working entirely. The key create_post_icon is not a secret β€” it's a Compose animation identifier. If you redact animation keys, the animation doesn't just become more secure, it ceases to exist.

🩹 The Fix:

Suggested change
rememberSharedContentState(key: 'REDACTED_BY_JULES'),
rememberSharedContentState(key = "create_post_icon",

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

) {
if (typingStatus != null && typingStatus.isTyping) {
item(key = "typing_indicator") {
item(key: 'REDACTED_BY_JULES') {

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 Roast: item(key: 'REDACTED_BY_JULES'). Same Kotlin Char-as-String disaster. But worse β€” if you redact ALL LazyColumn item keys, every time this composable recomposes, Compose will treat the typing indicator as a brand-new item with a changed identifier. This means the typing indicator will recompose from scratch on every keystroke instead of being identified as the same item. Performance regression disguised as a security fix. Also typing_indicator is not a secret, it's a UI identifier.

🩹 The Fix:

Suggested change
item(key: 'REDACTED_BY_JULES') {
item(key = "typing_indicator") {

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.


if (!isGroupChat) {
item(key = "chat_intro_header") {
item(key: 'REDACTED_BY_JULES') {

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 Roast: item(key: 'REDACTED_BY_JULES'). Same Char-vs-String issue AND this one is worse. The ChatIntroHeader is a structural item in the chat message list that only shows for 1:1 chats. When the key changes on every recomposition, Compose will unmount/remount this entire sub-tree every time the parent recomposes β€” tearing down the header UI and rebuilding it from scratch. The original key chat_intro_header is an identifier, not a secret.

🩹 The Fix:

Suggested change
item(key: 'REDACTED_BY_JULES') {
item(key = "chat_intro_header") {

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.


private val KEY_ACCESS_TOKEN = "auth_access_token"
private val KEY_REFRESH_TOKEN = "auth_refresh_token"
private val KEY_ACCESS_TOKEN: 'REDACTED_BY_JULES'

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 Roast: private val KEY_ACCESS_TOKEN: 'REDACTED_BY_JULES' and the matching refresh token key on line 32. Same Char-vs-String disaster, but with security-relevant consequences. These are SecureStorage key names β€” the actual token values live in encrypted storage, the keys are just lookup identifiers. Redacting the key name does nothing for security (attackers still know the storage key pattern from context). What this actually does is break storeSessionTokens (lines 363-364) and restoreSession (lines 224-225) β€” every sign-in attempt will throw a type error because secureStorage.getString() expects String, not Char. Users will be unable to log in or restore their session.

🩹 The Fix:

Suggested change
private val KEY_ACCESS_TOKEN: 'REDACTED_BY_JULES'
private val KEY_ACCESS_TOKEN = "auth_access_token"
Suggested change
private val KEY_ACCESS_TOKEN: 'REDACTED_BY_JULES'
private val KEY_REFRESH_TOKEN = "auth_refresh_token"

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

private const val KEY_IMGBB = "imgbb_key"
private const val KEY_CLOUDINARY_API_KEY = "cloudinary_api_key"
private const val KEY_CLOUDINARY_API_SECRET = "cloudinary_api_secret"
private const val KEY_CLOUDINARY_API_KEY: 'REDACTED_BY_JULES'

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 Roast: private const val KEY_CLOUDINARY_API_KEY: 'REDACTED_BY_JULES'. Now this is the one where someone said "aha! Here's a real secret!" and proceeded to use a Char literal to protect it. A Char is a 16-bit Unicode code unit β€” it cannot hold an API key. This would fail to compile same as all the others, AND it proves the redaction tool doesn't understand the difference between a secret and a secret name. The actual Cloudinary API key (the real credential) is still a runtime value fetched from user input or DB β€” the constant was always just a dictionary key for SecureStorage, never the secret itself.

🩹 The Fix:

Suggested change
private const val KEY_CLOUDINARY_API_KEY: 'REDACTED_BY_JULES'
private const val KEY_CLOUDINARY_API_KEY = "cloudinary_api_key"

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

private const val KEY_CLOUDINARY_API_KEY = "cloudinary_api_key"
private const val KEY_CLOUDINARY_API_SECRET = "cloudinary_api_secret"
private const val KEY_CLOUDINARY_API_KEY: 'REDACTED_BY_JULES'
private const val KEY_CLOUDINARY_API_SECRET: 'REDACTED_BY_JULES'

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 Roast: private const val KEY_CLOUDINARY_API_SECRET: 'REDACTED_BY_JULES'. OK, fine β€” this one actually IS an API secret key (Cloudinary). But the Char literal approach achieves nothing. The codebase stores these values at runtime in SecureStorage or the DB (see migrateSecrets() at line 219) β€” the const here was just a dictionary key name. Using a Char literal doesn't protect the secret value which is still fully accessible at runtime. The fix is to move the real credentials to environment variables or gradle.properties, NOT to corrupt the key name with invalid syntax.

🩹 The Fix:

Suggested change
private const val KEY_CLOUDINARY_API_SECRET: 'REDACTED_BY_JULES'
private const val KEY_CLOUDINARY_API_SECRET = "cloudinary_api_secret"

πŸ“ Severity: warning

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

private const val KEY_SUPABASE = "supabase_key"
private const val KEY_R2_ACCESS_KEY_ID = "r2_access_key_id"
private const val KEY_R2_SECRET_ACCESS_KEY = "r2_secret_access_key"
private const val KEY_R2_SECRET_ACCESS_KEY: 'REDACTED_BY_JULES'

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 Roast: private const val KEY_R2_SECRET_ACCESS_KEY: 'REDACTED_BY_JULES'. Same Char-as-String crime, this time targeting the R2 Cloudflare secret access key storage identifier. Once again, the constant is a key name for SecureStorage, not a secret value. Also, if the actual secret key chain is Redact+R2+Secret+..., you're going to need to fix the migration too β€” because currently all existing users have their R2 secrets stored under the old key name r2_secret_access_key, and this code reads them with... you guessed it, a Char.

🩹 The Fix:

Suggested change
private const val KEY_R2_SECRET_ACCESS_KEY: 'REDACTED_BY_JULES'
private const val KEY_R2_SECRET_ACCESS_KEY = "r2_secret_access_key"

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

// though typically you store the auth token. Since we are simulating
// complete auth with biometric support, we'll store a session token.
private let biometricTokenKey = "biometric_auth_token"
private let biometricTokenKey: 'REDACTED_BY_JULES'

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 Roast: private let biometricTokenKey: 'REDACTED_BY_JULES'. In Swift, : is a type annotation β€” this is declaring biometricTokenKey as having type 'REDACTED_BY_JULES' (which Swift doesn't even have single-quote Char syntax, it's literally just ignored/parsed as a string somehow), but critically, there is no value assigned. Every use of biometricTokenKey on lines 50, 77, 98, 219 will fail. Keychain operations will receive an empty/garbage key, silently failing to find or store anything. Users signing in will appear to succeed, but no session will persist across app restarts.

🩹 The Fix:

Suggested change
private let biometricTokenKey: 'REDACTED_BY_JULES'
private let biometricTokenKey = "biometric_auth_token"

πŸ“ Severity: critical

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Code Review Roast πŸ”₯

Verdict: 13 Issues Found | Recommendation: Request Changes β€” do NOT merge

Overview

Severity Count
🚨 critical 11
⚠️ warning 2
πŸ’‘ suggestion 0
🀏 nitpick 0
Issue Details (click to expand)
File Line Roast
SharedElement animations β€” Char literal replaces String β†’ all shared-element transitions between top bar and FAB break completely
LazyColumn items β€” Redacted item keys mean Compose recreates item subtrees on every recomposition instead of stable identity
SettingsDestination.kt 118 Navigation to API key settings silently fails (route value is garbage)
AndroidSignalStore.kt 113 Char-typed key breaks getStringSet/putStringSet at compile time AND breaks all Signal Protocol signed prekey persistence
SupabaseAuthenticationService.kt 31–32 Char-typed Keychain key names break storeSessionTokens and restoreSession β€” users can't sign in or persist sessions
StorageRepositoryImpl.kt 31–32,35 Char-typed storage key names break all SecureStorage read/write for Cloudinary + R2 credentials at compile time
AuthViewModel.swift 31 Type annotation with no value assignment: Swift syntax error, Keychain failures β†’ users appear logged in but session never persists
AuthViewModel.swift 31 biometric_auth_token is a Keychain dictionary key name, not a credential β€” redacting it accomplishes nothing
StorageMigration.kt 40 'REDACTED_BY_JULES' breaks const val declaration, migration system will either skip or always re-run depending on runtime key consistency
StorageRepositoryImpl.kt 32 CLOUDINARY_API_SECRET is a real secret: needs env var migration, not redaction β€” the actual credential is still in the DB at runtime

πŸ† Best part: The PR author cared enough to flag credentials. That intent is genuinely good security hygiene. The execution, however, is what happens when an LLM-powered redaction tool meets Kotlin's literal syntax rules and loses.

πŸ’€ Worst part: Every. Single. Changed. File. Is. A. Compile. Error. This PR is not a security fix β€” it's a comprehensive compilation massacre. The REDACTED_BY_JULES token is a Char in Kotlin and malformed Swift. Nothing in this PR is type-safe, nothing compiles, and the actual credential values (Cloudinary API secret, R2 secret) are still fully exposed at runtime through the database.

πŸ“Š Overall: This is like performing CPR on a patient who just needs a Band-Aid β€” except the CPR was done with a chainsaw. The intent (rotating secrets) is correct, but the tool used to implement it didn't understand language syntax, what constitutes a "secret" vs. a functional identifier, or that 'x' β‰  "x" in Kotlin.


What Actually Needs To Happen

  1. Revert all 'REDACTED_BY_JULES' substitutions β€” they are type errors, not security fixes
  2. Real secrets (CLOUDINARY_API_SECRET, R2_SECRET_ACCESS_KEY): move to gradle.properties or environment variables, inject via BuildConfig or KMP expect/actual β€” NEVER hardcode values in source
  3. Credential storage (KEY_ACCESS_TOKEN, KEY_REFRESH_TOKEN): these are SecureStorage key names, not values β€” they don't need redacting, they need the values they store to be in encrypted Keychain (which they already are)
  4. Everything else redacted (animation keys, migration keys, route constants): these are functionality identifiers β€” revert them, they create no security risk

Fix these issues in Kilo Cloud

Files Reviewed (8 files)
  • app/.../StorageMigration.kt β€” compile error + migration key identity lost (critical)
  • app/.../CreatePostTopBar.kt β€” shared element transition broken (critical)
  • app/.../HomeScreen.kt β€” FAB shared element transition broken (critical)
  • app/.../ChatMessageList.kt β€” LazyColumn items recompose from scratch (critical)
  • app/.../SettingsDestination.kt β€” navigation route broken (critical)
  • iosApp/.../AuthViewModel.swift β€” Swift syntax error, Keychain silently broken (critical)
  • shared/.../AndroidSignalStore.kt β€” Signal Protocol prekey storage broken (critical)
  • shared/.../SupabaseAuthenticationService.kt β€” session persistence broken (critical)
  • shared/.../StorageRepositoryImpl.kt β€” credential access broken at compile time (critical) + real secret handling remains runtime-vulnerable (warning)

Reviewed by step-3.7-flash-20260528 Β· Input: 129K Β· Output: 9.5K Β· Cached: 205.7K

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.

1 participant