diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index fadaabf57bb..faf797e376f 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,64 +1,179 @@ -# Bitwarden Clients - Claude Code Configuration +# CLAUDE.md -## Project Context Files +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -**Read these files before reviewing to ensure that you fully understand the project and contributing guidelines** +## Overview -1. @README.md -2. @CONTRIBUTING.md -3. @.github/PULL_REQUEST_TEMPLATE.md +### What This Project Does + +Bitwarden Clients is a monorepo containing all Bitwarden client applications: + +- **Web Vault** - Angular SPA for browser-based password management +- **Browser Extension** - Extensions for Chrome, Firefox, Safari, Edge, Opera +- **Desktop App** - Electron-based desktop application (Windows, macOS, Linux) +- **CLI** - Command-line interface for automation and scripting + +All clients share core libraries for business logic, cryptography, and UI components. + +### Key Concepts + +| Term | Definition | +| ---------------- | --------------------------------------------------------------------- | +| **Cipher** | An encrypted vault item (login, card, identity, secure note, SSH key) | +| **CipherView** | Decrypted representation of a Cipher for display | +| **EncString** | Encrypted string wrapper with encryption type metadata | +| **Vault** | User's encrypted collection of Ciphers | +| **Organization** | Shared vault for team/enterprise password sharing | +| **Collection** | Grouping mechanism for organizing Ciphers within Organizations | + +## Build and Development Commands + +**Install dependencies** (from repo root): + +```bash +npm ci +``` + +**Run apps in development** (from each app directory): + +```bash +# Web vault (apps/web) -- also used for proxying the API calls if you're running a local server +npm run build:bit:dev:watch # Bitwarden licensed version at http://localhost:8080 +npm run build:oss:watch # OSS version + +# Browser extension (apps/browser) +npm run build:watch:chrome # Chrome/Chromium +npm run build:watch:firefox # Firefox +npm run build:watch:safari # Safari + +# Desktop (apps/desktop) +npm run build:main:watch # Build main process +npm run build:renderer:watch # Build renderer (Angular) +npm run electron # Start Electron after building + +# CLI (apps/cli) +npm run build:oss:watch +node ./build/bw.js # Run CLI after building +``` + +**Linting and formatting**: + +```bash +npm run lint # Run ESLint + Prettier check +npm run lint:fix # Auto-fix ESLint issues +npm run prettier # Auto-format with Prettier +``` + +**Testing**: + +```bash +npm test # Run all tests across projects +npm test -- --testPathPattern="path/to/test" # Run specific test file +npm test -- --selectProjects=@bitwarden/common # Run tests for specific project + +x # In app directories (apps/web, apps/browser, etc.): +x npm test # Run tests for that app +x npm run test:watch # Watch mode +``` + +**Run single test file** (from root): + +```bash +npm test -- libs/common/src/vault/models/cipher.spec.ts +``` + +**NX commands** (monorepo task runner): + +```bash +npx nx test @bitwarden/common # Test specific library +npx nx lint @bitwarden/vault # Lint specific library +npx nx run-many -t test --all # Run all tests +npx nx affected -t test # Test only affected projects +``` ## Critical Rules -- **NEVER** use code regions: If complexity suggests regions, refactor for better readability - -- **CRITICAL**: new encryption logic should not be added to this repo. - +- **NEVER** add new encryption logic to this repo—cryptographic operations belong in the SDK - **NEVER** send unencrypted vault data to API services - -- **NEVER** commit secrets, credentials, or sensitive information. - -- **CRITICAL**: Tailwind CSS classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-p-4`). - - Missing prefix breaks styling completely. - - **NEVER** log decrypted data, encryption keys, or PII - - No vault data in error messages or console logs +- **CRITICAL**: Tailwind CSS classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-p-4`) +- **NEVER** use TypeScript enums—use const objects with type aliases (see pattern below) +- **NEVER** use code regions—refactor for readability instead -- **ALWAYS** Respect configuration files at the root and within each app/library (e.g., `eslint.config.mjs`, `jest.config.js`, `tsconfig.json`). +## Monorepo Architecture -## Mono-Repo Architecture +``` +apps/ +├── browser/ # Browser extension (Chrome, Firefox, Safari, Edge, Opera) +├── cli/ # Command-line interface +├── desktop/ # Electron desktop app +└── web/ # Web vault (Angular SPA) -This repository is organized as a **monorepo** containing multiple applications and libraries. The -main directories are: +libs/ +├── common/ # Core business logic, models, services (shared by all clients) +├── angular/ # Angular-specific abstractions and services +├── components/ # Reusable Angular UI components (CL - Component Library) +├── platform/ # Platform abstractions (crypto, storage, messaging) +├── state/ # State management infrastructure +├── auth/ # Authentication logic +├── vault/ # Vault-specific UI and services +├── key-management/ # Key derivation, rotation, biometrics +├── billing/ # Subscription and payment logic +├── admin-console/ # Organization admin features +├── tools/ # Generator, export, send functionality +└── [team-libs]/ # Team-owned domain libraries -- `apps/` – Contains all application projects (e.g., browser, cli, desktop, web). Each app is - self-contained with its own configuration, source code, and tests. -- `libs/` – Contains shared libraries and modules used across multiple apps. Libraries are organized - by team name, domain, functionality (e.g., common, ui, platform, key-management). - -**Strict boundaries** must be maintained between apps and libraries. Do not introduce -cross-dependencies that violate the intended modular structure. Always consult and respect the -dependency rules defined in `eslint.config.mjs`, `nx.json`, and other configuration files. - -## Angular Architecture Patterns - -**Observable Data Services (ADR-0003):** - -- Services expose RxJS Observable streams for state management -- Components subscribe using `async` pipe (NOT explicit subscriptions in most cases) - Pattern: - -```typescript -// Service -private _folders = new BehaviorSubject([]); -readonly folders$ = this._folders.asObservable(); - -// Component -folders$ = this.folderService.folders$; -// Template:
+bitwarden_license/ # Licensed features (enterprise, premium) +├── bit-web/ +├── bit-browser/ +├── bit-cli/ +└── bit-common/ ``` -For explicit subscriptions, MUST use `takeUntilDestroyed()`: +**Import paths**: Use `@bitwarden/common/*`, `@bitwarden/components`, etc. (defined in `tsconfig.base.json`). + +**Dependency boundaries**: Libraries cannot import from apps. Apps import from libs. Licensed code extends OSS code. + +## Data Models + +The codebase uses a layered model pattern: + +``` +Response (API) → Data (Storage) → Domain (Encrypted) → View (Decrypted) +``` + +| Layer | Purpose | Example | +| ------------ | ----------------------------- | -------------------------------------- | +| **Response** | API response DTOs | `CipherResponse` | +| **Data** | JSON-serializable for storage | `CipherData` | +| **Domain** | Encrypted business objects | `Cipher` (contains `EncString` fields) | +| **View** | Decrypted for UI display | `CipherView` (contains plain strings) | + +**Core Types** (from `libs/common/src/types/guid.ts`): + +```typescript +// Branded types for type-safe IDs +type UserId = Opaque; +type CipherId = Opaque; +type OrganizationId = Opaque; +type CollectionId = Opaque; +``` + +## Angular Patterns + +**Observable Data Services (ADR-0003)**: + +```typescript +// Service exposes Observable streams +private _data$ = new BehaviorSubject([]); +readonly data$ = this._data$.asObservable(); + +// Component uses async pipe +data$ = this.dataService.data$; +// Template:
+``` + +**Subscription cleanup** (required for explicit subscriptions): ```typescript constructor() { @@ -66,46 +181,211 @@ constructor() { } ``` -**Angular Signals (ADR-0027):** +**Signals**: Use Angular Signals only in components and presentational services. Use RxJS for cross-client services and complex reactive workflows. -Encourage the use of Signals **only** in Angular components and presentational services. - -Use **RxJS** for: - -- Services used across Angular and non-Angular clients -- Complex reactive workflows -- Interop with existing Observable-based code - -**NO TypeScript Enums (ADR-0025):** - -- Use const objects with type aliases instead -- Legacy enums exist but don't add new ones - -Pattern: +**No TypeScript Enums (ADR-0025)**: ```typescript -// ✅ DO +// ✅ Correct export const CipherType = Object.freeze({ Login: 1, SecureNote: 2, } as const); export type CipherType = (typeof CipherType)[keyof typeof CipherType]; -// ❌ DON'T +// ❌ Wrong - don't add new enums enum CipherType { Login = 1, - SecureNote = 2, } ``` -Example: `/libs/common/src/vault/enums/cipher-type.ts` +**Component Change Detection**: Use `OnPush` change detection strategy for all components. + +## State Management + +State is managed through `StateProvider` with typed `KeyDefinition`s: + +```typescript +// Define state key +const MY_STATE = KeyDefinition.record(STATE_DEFINITION, "myKey", { + deserializer: (data) => data, +}); + +// Use in service +this.state$ = this.stateProvider.getGlobal(MY_STATE).state$; +``` + +User-scoped state uses `UserKeyDefinition` and requires a `UserId`. + +## Feature Flags + +Feature flags are defined in `libs/common/src/enums/feature-flag.enum.ts`. Use `ConfigService` to check flags: + +```typescript +const enabled = await this.configService.getFeatureFlag(FeatureFlag.MyFlag); +``` + +Flags MUST be short-lived and removed once fully enabled. + +## Testing + +### Test Structure + +Tests are colocated with source files using `.spec.ts` suffix: + +``` +libs/common/src/vault/models/domain/cipher.ts +libs/common/src/vault/models/domain/cipher.spec.ts +``` + +### Test Utilities + +| Utility | Location | Purpose | +| ----------------------- | ------------------------------------------ | -------------------------------- | +| `mockEnc()` | `libs/common/spec/utils.ts` | Create mock EncString | +| `makeStaticByteArray()` | `libs/common/spec/utils.ts` | Create deterministic byte arrays | +| `FakeStateProvider` | `libs/common/spec/fake-state-provider.ts` | Mock state management | +| `FakeAccountService` | `libs/common/spec/fake-account-service.ts` | Mock account service | +| `trackEmissions()` | `@bitwarden/core-test-utils` | Track Observable emissions | + +### Unit Test Template + +```typescript +import { mock, MockProxy } from "jest-mock-extended"; + +describe("MyService", () => { + let sut: MyService; + let dependency: MockProxy; + + beforeEach(() => { + dependency = mock(); + sut = new MyService(dependency); + }); + + it("should do something", () => { + dependency.method.mockReturnValue(expected); + const result = sut.doSomething(); + expect(result).toEqual(expected); + }); +}); +``` + +### Component Test Template + +```typescript +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +describe("MyComponent", () => { + let fixture: ComponentFixture; + let component: MyComponent; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyComponent], + providers: [{ provide: MyService, useValue: mock() }], + }).compileComponents(); + + fixture = TestBed.createComponent(MyComponent); + component = fixture.componentInstance; + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); +``` + +## Code Style & Standards + +### Formatting + +- **Prettier** for code formatting (run `npm run prettier`) +- **ESLint** for linting (run `npm run lint`) +- Pre-commit hooks enforce formatting + +### Naming Conventions + +| Type | Convention | Example | +| ------------------- | -------------------------- | ------------------- | +| Files | kebab-case | `cipher.service.ts` | +| Classes | PascalCase | `CipherService` | +| Interfaces | PascalCase (no `I` prefix) | `CipherService` | +| Variables/Functions | camelCase | `getCipher()` | +| Constants | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` | +| Observables | camelCase with `$` suffix | `ciphers$` | + +### Import Order + +1. External packages (`@angular/*`, `rxjs`, etc.) +2. `@bitwarden/*` packages +3. Relative imports + +## Anti-Patterns + +### DO + +- Use `takeUntilDestroyed()` for subscription cleanup +- Use `OnPush` change detection +- Use const objects instead of enums +- Use branded types (`UserId`, `CipherId`) for IDs +- Use `async` pipe in templates +- Keep components focused and small +- Write unit tests for services +- Use dependency injection + +### DON'T + +- Add new TypeScript enums +- Add encryption logic (use SDK) +- Log sensitive data (PII, keys, vault data) +- Use Tailwind classes without `tw-` prefix +- Create manual subscriptions without cleanup +- Import from apps in libraries +- Use `any` type without justification +- Skip the View layer for displaying data +- Use code regions + +## Troubleshooting + +### Common Issues + +| Issue | Solution | +| ------------------------------------ | ----------------------------------------------- | +| Build fails with module errors | Run `npm ci` to reinstall dependencies | +| Tests fail with "Cannot find module" | Check `tsconfig.json` paths, run `npx nx reset` | +| Tailwind styles not applying | Ensure `tw-` prefix on all Tailwind classes | +| State not persisting | Verify `KeyDefinition` is correctly configured | +| Encryption errors | Check SDK is properly initialized | + +### Debug Tips + +**Web**: Use browser DevTools, check Network tab for API calls + +**Browser Extension**: Load unpacked extension, use `chrome://extensions` → "Inspect views" + +**Desktop**: Use `--inspect` flag, access DevTools via View → Toggle Developer Tools + +**CLI**: Add `--debug` flag, use `console.log` for quick debugging + +## Component Library + +Located in `libs/components/`. Use Storybook for development: + +```bash +npm run storybook # Start at http://localhost:6006 +npm run build-storybook # Build static Storybook +``` ## References -- [Web Clients Architecture](https://contributing.bitwarden.com/architecture/clients) -- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/) +### Official Documentation + +- [Clients Architecture](https://contributing.bitwarden.com/architecture/clients) +- [ADRs](https://contributing.bitwarden.com/architecture/adr/) - [Contributing Guide](https://contributing.bitwarden.com/) -- [Web Clients Setup Guide](https://contributing.bitwarden.com/getting-started/clients/) - [Code Style](https://contributing.bitwarden.com/contributing/code-style/) -- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/) + +### Internal Documentation + - [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions) +- [Getting Started](https://contributing.bitwarden.com/getting-started/clients/)