Commit graph

7 commits

Author SHA1 Message Date
jima
7b68e6404b Audit 2026-04-12: C++ hardening, crash fix, dead code, quality
Fifth full codebase audit across all five modules (lib-ssh, lib-terminal-view,
lib-terminal-keyboard, lib-vault-crypto, app).

Security:
- Added cppFlags to lib-vault-crypto build — vault_crypto.cpp JNI bridge was
  missing all compiler hardening flags (-fstack-protector-strong, -D_FORTIFY_SOURCE=2)

Bugs fixed:
- SessionNotifier crash: first{} → firstOrNull to prevent NoSuchElementException
- Keyboard modifiers not consumed on SwitchPage/ToggleNumBlock — armed CTRL/ALT
  would persist and incorrectly modify the next key press
- KeyManagerViewModel silent exception swallow — now logs errors via FileLogger
- TelnetSession.sendTerminalType() variable shadowing fix

Dead code removed:
- Vt100Parser empty class (Vt220Parser now extends BaseTermParser directly)
- XtermParser.sendPrimaryDA() redundant override (identical to parent)
- TerminalKeyboard dead fields: menuPopupActive, menuPopupItems, miniContainer
- SpecialAction.SETTINGS_OPENED never emitted
- Deprecated 3-arg saveHostKeyFingerprint overload (no callers)

Code quality:
- Color(0xFF6E7979) → AppColors.Muted in ConnectionListScreen
- Hardcoded "v1.0.0" → BuildConfig.VERSION_NAME in SettingsScreen
- SubscriptionScreen back button contentDescription for accessibility
- TAG → companion const val in StartupCommandRunner, PortForwardManager, SftpSessionManager
- TerminalRenderer swapped KDoc comments fixed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:09:05 +02:00
jima
e243b8e1e5 VP2 rotation fix, NumBlok mini toggle, HW keyboard auto-hide, audit
ViewPager2 rotation phantom (Google Issue 175796502):
- KeyboardView.onSizeChanged rebuilds buildPages() on any width
  transition including initial 0→actual layout, not just on resize.
  The first buildPages() from setup() runs at width=0 (view still
  detached), VP2's internal RecyclerView can't reliably re-anchor to
  the real dimensions that arrive later, leaving a fractional scroll
  offset that leaks the neighbor page on the right edge. A second
  buildPages() once the real size is known produces a clean result.
- KeyboardView.lastModifierStates caches CTRL/ALT/SHIFT state so it
  survives the rebuild.
- Works around an unfixed VP2 1.1.0 bug for non-FragmentStateAdapter
  usage. Fresh launches and all rotation paths confirmed clean on S23.

NumBlok toggle on mini numpad:
- KeyDefinition gains numLabel/numAction optional fields
- KeyAction gains ToggleNumBlock data object
- LayoutParser understands "numLabel"/"numAction" JSON and the
  "toggle_numblock" action type
- KeyboardPageView.numBlockActive swaps label rendering; toggle key
  gets LOCKED-style amber glow while active
- TerminalKeyboard.numBlockActive state; handleKeyAction swaps
  action→numAction; attachMiniTo carries state across rotation rebuilds
- Mini last row: Num 0 \ (base) → Num Ins ~ (numblock)
- Rows 1-3 map to PC-keypad nav: Home/↑/PgUp, ←/Esc/→, End/↓/PgDn
- QuickBarCustomizer serializer/deserializer learned toggle_numblock

Hardware keyboard auto-hide:
- MainActivity derives hasHwKeyboard from LocalConfiguration.current.
  keyboard != KEYBOARD_NOKEYS && hardKeyboardHidden != HARDKEYBOARDHIDDEN_YES
- LaunchedEffect(hasHwKeyboard) { ckbHidden = hasHwKeyboard } seeds
  the toggle on every HW kb connect/disconnect
- In HW kb mode the kebab Show/Hide controls both CKB and QuickBar as
  a pair: val qbShownByUser = if (hasHwKeyboard) !ckbHidden else
  quickBarVisible
- Normal mode unchanged (QB follows its own quickBarVisible pref)

Number row dropdown:
- KeyboardSettingsDialog now shows all four options (top/left/right/
  hidden) in both orientations. Previously left/right were filtered
  out in portrait, preventing the user from setting the mini mode
  without first rotating into landscape.
- Portrait-override on the effective numberRowMode stays in place
  (mini is landscape-only by design — documented in CLAUDE.md and
  auto-memory).

Audit fixes:
- KeyboardPageView: removed key.hint!! by capturing into a local
  hintText val so smart-cast flows through the render branches
- SSHKeyLoader.loadEd25519FromPkcs8: require(seed.size == 32) so a
  malformed PKCS#8 blob fails fast with a clear message instead of
  crashing deep inside EdDSAPrivateKeySpec
- vault_crypto.cpp nativeEncrypt: secure_zero(ptPtr, ptLen) before
  ReleaseByteArrayElements on all three paths (success + two error
  paths), matching the existing key-handling pattern for plaintext
  defence-in-depth through the JNI boundary

Docs:
- TECHNICAL.md: NumBlok, VP2 rebuild workaround, HW keyboard auto-hide,
  portrait mini override notes
- CLAUDE.md: lib-terminal-keyboard module description updated with
  the same invariants
- TODO.md: recently-completed items through 2026-04-06
- Audit.md: investigation log including false positives for future
  reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:23:13 +02:00
jima
ca16651d69 Security audit: TOFU hardening, FLAG_SECURE, paste sanitization, compiler hardening
Production security audit (OWASP-aligned, prod-only scope):
- Default TOFU to REJECT when no UI handler (prevents silent accept during service-start window)
- Add FLAG_SECURE with preventScreenCapture preference (default ON, Settings → Security)
- Sanitize bracketed paste content (strip \e[200~/\e[201~ to prevent paste-escape injection)
- Add VaultCrypto ProGuard keep rule (prevents R8 stripping JNI methods in release)
- Create network_security_config.xml (system CAs only, cleartext disabled)
- Add compiler hardening flags to both native modules (-fstack-protector-strong, -D_FORTIFY_SOURCE=2)
- Set EXTRA_IS_SENSITIVE on all clipboard writes (terminal copy, key copy, SFTP path copy)
- Remove file:// from URL detection (prevents local file access via crafted terminal output)
- Verify signing certificate in pro APK migration (prevents fake APK granting free features)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:04:05 +02:00
jima
f41d9298f6 Full codebase audit: security fixes, bug fixes, dead code, Compose migration
Security: QR key bytes zeroed in vault export/import, Socks5 null-safety fix.
Bugs: SSH auth cascade no longer silently succeeds without callback, stderr
readLoop no longer causes premature disconnect, dimension overlay cols×rows,
cursorUp/Down respects scroll region boundaries.
Quality: runBlocking→StateFlow (27 calls), MaterialAlertDialogBuilder→Compose
dialog, SSHJ stderr capture race serialized, dead code cleanup across all
modules, string resources for hardcoded text, doc updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:32:03 +02:00
jima
bb7662ca63 Audit: security hardening, bug fixes, dead code cleanup across all modules
vault-crypto: secure_zero for all sensitive buffers (Argon2 core, BLAKE2b,
GCM tag/lengths), JNI NewByteArray null checks, Kotlin input validation.
lib-ssh: SSHSession thread safety (debugLogBuffer sync), EOF check fix,
session reuse guard, stderr restoration in finally block.
lib-terminal-view: italic rendering (faux skew), hidden text (fg=bg),
DECCKM reset fix, reflowResize mode preservation, render thread TOCTOU fix.
lib-terminal-keyboard: dead code removal, kotlin.math migration, touch
allocation elimination in KeyboardPageView.
app: Hilt-injected CredentialStore (was duplicate instance), hardcoded
strings to resources (BiometricAuthManager, VaultImportViewModel),
null safety (!! elimination in 5 files), deprecated API fixes, unused
imports/strings cleanup, test API drift fixes, migration 10→11 test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:44:45 +02:00
jima
34325f7119 Full codebase audit: crypto hardening, 6 critical bugs, i18n, dead code cleanup
Security:
- lib-vault-crypto: secure_zero (volatile memset ptr) replaces memset for all
  sensitive memory zeroing, null checks on JNI array access, key/password
  buffers zeroed before release, input validation on Kotlin API
- lib-ssh: keyboard-interactive auth no longer falls back to empty password

Critical bugs fixed:
- SFTP multi-select delete now handles all selected files (was only first)
- reconnectSession() handles Telnet and Local shell types (was always SSH)
- _activeSessions StateFlow updates now atomic via .update{}
- Telnet sessions now execute startup commands
- Clipboard queries cached on UI thread (render thread crash risk on some OEMs)
- TextAttr color mask fixed from 20-bit to 24-bit

Thread safety: @Volatile on SftpSession.sftpClient, SftpViewModel._allEntries;
synchronized debugLogBuffer; onViewRecycled prevents allPageViews leak

i18n: 37 new string resources (EN/ES/SV) for disconnect reasons, network
status, terminal banners, port forward errors, toolbar labels, toast messages

Dead code: removed boilerplate tests, tautological assertions, unused
StateFlows/functions, redundant night theme, unused string resources

Tests: fixed startupCommands mock (false→true), updated stale test names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:00:26 +02:00
jima
2a3d18cd9c Vault export/import: encrypted backup with password or QR code
New lib-vault-crypto module with Argon2id + AES-256-GCM in C via JNI.
Export screen (full Scaffold) with item selection, password mode (min 12
chars, upper/lower/digit/special), and QR mode (background generation,
save/share gating, confirmation dialog). Import via bottom sheet with
SAF file picker, password/QR decryption, and conflict resolution.
Kebab menu replaces settings icon in ConnectionListScreen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:22:31 +02:00