en un clic
code-review-and-security-audit
// Review code for quality, correctness, and security vulnerabilities. Use when the user asks to review code, audit for security issues, or check for bugs and anti-patterns.
// Review code for quality, correctness, and security vulnerabilities. Use when the user asks to review code, audit for security issues, or check for bugs and anti-patterns.
| name | code-review-and-security-audit |
| description | Review code for quality, correctness, and security vulnerabilities. Use when the user asks to review code, audit for security issues, or check for bugs and anti-patterns. |
| argument-hint | file path, component name, or scope of review |
You are performing code review and security auditing for MakeMeDown Text Reader — a Kotlin / Jetpack Compose Android reader and editor for EPUB 3, Markdown, and plain text, with a biometric-locked private documents space.
This skill covers two complementary concerns:
Text / AnnotatedString. Don't waste time auditing JNI buffer handling or addJavascriptInterface — they don't exist here.USE_BIOMETRIC. No storage, no network, no contacts.filesDir/private_documents/ directly via java.io.File.kotlinx.serialization (recents.json, settings.json) — not SharedPreferences, not Room.Screen state with a custom Saver — no androidx.navigation.MainActivity is a FragmentActivity because BiometricPrompt requires it. Don't suggest converting it to ComponentActivity.private_documents/ in both backup_rules.xml and data_extraction_rules.xml. Any review of those files must preserve that exclusion.!! unless justified by an invariant the caller can see.DocumentLoader, DocumentSaver, EpubParser, PrivateStore) must run on Dispatchers.IO; UI updates must come back to the main dispatcher. Flag any blocking I/O on the composition thread.rememberCoroutineScope, LaunchedEffect(key), and produceState over manually managed Jobs. Composables must not leak coroutines on recomposition.remember keys: any remember { … } whose computation depends on inputs must use remember(key1, …) — flag stale captures.InputStream, OutputStream, ZipFile, ZipInputStream, and ParcelFileDescriptor must be closed via use { }. No bare close() in finally.DocumentLoader / DocumentSaver return sealed result types or throw — don't silently swallow IOException. The reader screen surfaces failures via Snackbar; preserve that contract.kotlinx.serialization + sealed Screen navigation. Don't introduce androidx.navigation, Hilt, Room, RxJava, or Moshi.vars, missing key() in LazyColumn, expensive work outside remember.MarkdownReader, EpubReader, and PlainTextReader — when AnnotatedString span construction is repeated, extract.if/when/try nesting; prefer early returns or extraction.Settings.Strings where a sealed DocumentSource (SAF Uri vs private://name) would be clearer.StateFlow/mutableStateOf; flag unnecessary var or top-level mutable collections.when (block) chains over EpubBlock / Markdown AST nodes that grow large can sometimes be cleaned up via a renderer interface — but only if the win is real, sealed-class when is idiomatic and exhaustive.let/apply/also, destructuring, extension functions, buildList/buildString, runCatching for boundary-only error capture.derivedStateOf for derived state, snapshotFlow for bridging to coroutines, CompositionLocalProvider for ambient values (the project already does this for reader font / scale).Context lookups with logic; suggest passing dependencies in. EpubParser, MarkdownToBlocks, and PrivateStore should be unit-testable without an Android device.This is the most security-sensitive surface in the app. Audit carefully.
PrivateSpaceSession flag is process-scoped (not persisted) and is cleared on the right lifecycle events.DeviceAuth (auth/DeviceAuth.kt): BiometricPrompt is configured with setDeviceCredentialAllowed(true) so any enrolled biometric or the system PIN/pattern/password satisfies it. Don't suggest tightening this to biometric-only — it's deliberate so users without enrolled fingerprints can still use the space.DeviceAuth call. There must be no time-based "I authenticated 30 seconds ago" cache. Flag any change that introduces one.FileProvider exposure: the manifest <provider> must remain exported=false, grantUriPermissions=true, with authority ${applicationId}.fileprovider. res/xml/file_paths.xml must expose only <files-path name="private_documents" path="private_documents/" /> — anything broader (<files-path path="."/>, <files-path path=""/>, <external-files-path>, etc.) leaks recents.json, settings.json, or other internal state and must be flagged Critical.Intent.ACTION_SEND must use FLAG_GRANT_READ_URI_PERMISSION only — never FLAG_GRANT_WRITE_URI_PERMISSION, never FLAG_GRANT_PERSISTABLE_URI_PERMISSION.res/xml/backup_rules.xml or res/xml/data_extraction_rules.xml must keep private_documents/ excluded from both cloud backup and device-to-device transfer. Flag any removal as Critical.PrivateStore: file names entering PrivateStore must be validated — no path separators (/, \), no .., no absolute paths, no NUL bytes. The store must resolve every path under filesDir/private_documents/ and reject anything that escapes (canonical path check). Treat path traversal here as Critical.Log.d / println of file contents, file names, or unlock state should not exist in release builds. Flag any logging of private document content or names.reader/epub/EpubParser.kt):
ZipEntry name must be canonicalized and verified to live under the intended extraction root. Flag any File(root, entry.name) without that check as Critical.XmlPullParser, DocumentBuilder, or SAXParser must disable external entities and DTDs (FEATURE_SECURE_PROCESSING, disallow-doctype-decl, no XMLConstants.ACCESS_EXTERNAL_*). Flag missing hardening as High.hrefs and image srcs must be resolved relative to the OPF base and confined to the zip — never opened as filesystem paths.reader/markdown/): clickable links must go through a safe URI launcher. Reject or neutralize javascript:, file://, content://, intent:, and other unexpected schemes; allow http, https, mailto (and consider whether tel: is desired). Flag raw Intent(ACTION_VIEW, Uri.parse(href)) without scheme filtering.BitmapFactory is reasonable, but cap dimensions and use inSampleSize for large images to avoid OOM. Cache scoped per chapter, not globally.RecentsRepository, SettingsRepository): malformed recents.json / settings.json (corrupted file, partial write, schema drift) must not crash the app. Decoding should be wrapped and fall back to defaults. Writes should be atomic (write to temp + rename) to survive crashes mid-write.Uri handling: persistable URI permissions (takePersistableUriPermission) must be released when entries are dropped from recents. openInputStream / openOutputStream results may be null — handle it.MainActivity should validate any incoming ACTION_VIEW URIs (MIME / scheme) before passing them to the loader. Don't trust intent.data blindly.MainActivity is the only exported component; <provider> is exported=false. Verify no new <activity>, <service>, <receiver>, or <provider> is added with android:exported="true" without an explicit reason.allowBackup: project intentionally allows backup at the app level but excludes private_documents/ via the backup rules. Don't flip android:allowBackup to false without understanding the trade-off.networkSecurityConfig / cleartext: app does no network. If anything ever introduces an HTTP call, flag it.isMinifyEnabled = false. If that changes, ensure kotlinx.serialization keep rules and BiometricPrompt callbacks survive shrinking.Runtime.exec() / ProcessBuilder — there should be none in this app; flag any introduction.System.loadLibrary / native code — flag any introduction.java.util.Random is fine for non-security uses (e.g. picking a placeholder). Anything touching auth must use SecureRandom.gradle/libs.versions.toml): flag known-vulnerable versions; the dependency surface is small (Compose BOM, org.jetbrains:markdown, kotlinx.serialization, androidx.biometric) so this should be tractable.Report findings using this structure:
Issues that must be fixed — security vulnerabilities (zip slip, FileProvider over-exposure, share-gate bypass, path traversal in PrivateStore), crashes, data loss risks.
Issues that should be fixed — logic bugs, recomposition hazards, missing error handling, code smell with real impact.
Suggestions for improvement — style, readability, minor optimizations, refactor opportunities.
For each finding, include:
When invoked without arguments, review recently changed files:
git diff --name-only HEAD~5
When invoked with a specific scope (file, directory, or component name), focus the review on that area.
For a full audit, systematically review in this order (highest-risk first):
PrivateSpaceSession lifecycle).app/src/main/AndroidManifest.xml, app/src/main/res/xml/file_paths.xml, app/src/main/res/xml/backup_rules.xml, app/src/main/res/xml/data_extraction_rules.xml.app/build.gradle.kts, gradle/libs.versions.toml.Commit code changes and push via Git. Use when the user asks to commit, push, or save their work to the repository.
Writing/updating project documentation (README, PRIVACY-POLICY, NOTICES, changelogs) and maintaining F-Droid metadata. Use when the user asks to update docs, write changelogs, or modify F-Droid store listings.
Release engineering tasks including version bumping, building release APKs, creating git tags, writing changelogs, and preparing F-Droid releases. Use when the user asks to prepare a release, bump version, tag a release, or build for distribution.