KeyPears

KeyPears

Federated Diffie-Hellman Key Exchange System

← Back to blog

What We Found in Our Codebase Audit

December 6, 2025 · KeyPears Team

We recently paused feature development to audit our entire codebase. KeyPears shipped fast—cross-device sync, server authentication, encrypted secret storage—and we wanted to make sure we hadn't accumulated technical debt or, worse, security issues along the way.

What we found was instructive. The good news: our architecture is sound, our cryptography is correct, and our zero-knowledge design holds up. The concerning news: we found debug logging that would have exposed cryptographic keys in production. This is exactly why we audit.

What We Looked For

Our audit covered six packages across the monorepo, examining each for:

  1. General Software Best Practices: Linting, type checking, test coverage, code quality
  2. Third-Party Dependencies: Outdated packages, security vulnerabilities
  3. Security Assessment: Crypto implementation, zero-knowledge verification, input validation
  4. Scalability: N+1 queries, memory leaks, re-render optimization
  5. UI/UX Consistency: Theme compliance, accessibility, loading states
  6. File-Specific Checks: Config correctness, route typing, component structure

We ran automated tools first (pnpm lint, pnpm typecheck, pnpm test, cargo clippy), then manually reviewed each package against our checklist.

The Critical Finding: Debug Logging

The most significant discovery was in our Tauri app's vault creation and import flows. During development, we'd added extensive console.log statements to debug the cryptographic key derivation process:

// What we found and removed:
console.log("Password Key:", passwordKey.buf.toHex());
console.log("Login Key:", loginKey.buf.toHex());
console.log("Encryption Key:", encryptionKey.buf.toHex());
console.log("Decrypted Vault Key:", vaultKey.buf.toHex());

There were approximately 80 of these statements across two files: import-vault.tsx and new-vault.3.tsx. Each one logged a sensitive cryptographic key in hexadecimal format.

In development, this is harmless—helpful, even, for understanding the key derivation flow. In production, it's a disaster waiting to happen. Anyone with access to browser developer tools could see every key involved in vault encryption. The zero-knowledge architecture we carefully designed would be meaningless if the client itself was leaking keys to the console.

We removed all 80 statements. The code now proceeds silently, as it should.

Lesson learned: Debug logging during development is fine, but it must be removed before shipping. Our audit checklist now includes searching for console.log statements containing key-related terms (Key, password, secret, token).

Type Safety Improvements

React Router v7 provides a href() function that type-checks route paths at compile time. If you rename a route file, any href("/old-path") calls will fail to compile—catching errors before they reach production.

We found several places where developers had used string literals instead:

// What we found:
<Link to="/">Home</Link>
<Link to="/new-vault/1">Create Vault</Link>

// What it should be:
<Link to={href("/")}>Home</Link>
<Link to={href("/new-vault/1")}>Create Vault</Link>

The difference seems minor, but it matters. With string literals, renaming /new-vault/1 to /vault/new/step-1 would silently break links. With href(), the compiler catches it immediately.

We updated all navigation in the Tauri app to use type-safe routes: navbar, footer, vault creation wizard, import flow. It's a small change that prevents a category of bugs entirely.

UI Consistency: Theme Colors

KeyPears uses the Catppuccin color palette with CSS variables for theming. Error text should use text-destructive, which maps to the appropriate red in both light and dark modes.

We found inconsistent usage:

// Inconsistent:
<p className="text-red-500">{error}</p>

// Consistent:
<p className="text-destructive">{error}</p>

The difference is subtle in light mode but significant in dark mode, where text-red-500 might not have sufficient contrast against dark backgrounds. Using theme variables ensures the design system works correctly across all themes.

We standardized error colors in several components: the password generator, password memorizer, and vault name input.

Config Typo: The Silent Build Breaker

In tauri.conf.json, we found:

{
  "frontendDist": "../ts-tauri/dist"
}

The correct path is ../tauri-ts/dist. The folders are named tauri-ts (for TypeScript) and tauri-rs (for Rust), not ts-tauri.

This typo hadn't caused problems yet because we typically run the dev server rather than building production bundles locally. But it would have failed the first time someone tried to build a release binary, causing confusion and wasted debugging time.

Lesson learned: Config files deserve the same scrutiny as code. Paths, URLs, and identifiers are easy to typo and hard to spot in review.

What Passed With Flying Colors

Not everything was problems. Much of the codebase held up well:

Cryptography: Our three-tier key derivation (password → passwordKey → encryptionKey + loginKey) is correctly implemented. The server never receives encryption keys, only login keys—and those are further hashed server-side. The zero-knowledge architecture is sound.

Memory management: All intervals, timers, and event listeners in the Tauri app have proper cleanup in useEffect return functions. No memory leaks.

Sync performance: The background sync service uses exponential backoff on errors (5s → 10s → 20s), preventing thundering herd problems. Pagination is implemented for activity logs.

Accessibility: Interactive elements have proper aria-label attributes. Keyboard navigation works throughout the app.

Rust code: Our Tauri backend is minimal (~43 lines) by design—all business logic is in TypeScript. cargo clippy passes with no warnings.

The Audit Process

For future reference, here's how we structured the audit:

  1. Automated checks first: Run lint, typecheck, and tests for each package. Fix any failures before proceeding.

  2. Dependency review: Run pnpm outdated for each package. Update dependencies to latest patch versions.

  3. Manual review by category: Work through the checklist systematically. Security issues get fixed immediately; style issues get noted for later.

  4. Document findings: Update the audit guide with lessons learned. Future audits benefit from past discoveries.

We've published our full audit checklist in the repository at docs/audit.md. It covers everything from TypeScript best practices to zero-knowledge architecture verification to UI accessibility checks.