Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Secret Management Frontend - Phase 3: File Attachments UI Integration (Display)** (#200, Part of #191) - **MERGED 22.11.2025**
- `AttachmentUpload.tsx` component with drag-and-drop and file validation
- `AttachmentPreview.tsx` modal for images and PDFs with zoom controls
- Integration of `AttachmentList` component in `SecretDetail.tsx`
- Download, delete, and preview handlers with master key management
- File size limits (10MB) and type validation (images, PDFs, documents)
- Security: Blocks executable files, validates MIME types
- 21 new unit tests (547 total tests passing)
- Coverage: 87.95% (exceeds 80% requirement)
- Part of Secret Management Epic #191 (Phase 3/5)

- **Secret Management Frontend - Phase 2: Secret Create/Edit Forms** (#198, Part of #191) - **MERGED 22.11.2025**
- `SecretForm.tsx` reusable form component with validation
- `SecretCreate.tsx` page for creating new secrets
- `SecretEdit.tsx` page for editing existing secrets
- API service extensions: `createSecret()`, `updateSecret()`
- Client-side validation and error handling
- Password show/hide toggle for security
- 28 new unit tests (all passing)
- Routing: `/secrets/new` and `/secrets/:id/edit`
- Part of Secret Management Epic #191 (Phase 2/5)

- **Secret Management Frontend - Phase 1: Secret List & Detail Views** (#197, Part of #191) - **MERGED 22.11.2025**
- `SecretList.tsx` component with search, filtering, and pagination (20 items/page)
- `SecretDetail.tsx` full detail view with encrypted fields
- `SecretCard.tsx` reusable card component with badges
- API service: `secretApi.ts` with `getSecrets()`, `getSecretById()`
- Grid/List view toggle (persisted in localStorage)
- Filter by tags and expiration status
- Password show/hide toggle
- Tags, expiration badges, attachment count, shared indicator
- 31 new unit tests (450 total tests passing)
- Coverage: 87.95% (exceeds 80% requirement)
- Routing: `/secrets` and `/secrets/:id`
- Part of Secret Management Epic #191 (Phase 1/5)

- **Client-Side File Encryption - Phase 5: Security Audit & Documentation** (#174, Part of #143)
- Comprehensive security documentation in `CRYPTO_ARCHITECTURE.md`
- Security audit completed for all encryption code
Expand Down
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,50 @@ For comprehensive security details, threat model, and cryptographic guarantees,
- βœ… Phase 2: ShareTarget Integration (PR #178, merged 19.11.2025)
- βœ… Phase 3: Upload Integration (PR #187, merged 21.11.2025)
- βœ… Phase 4: Download & Decryption (PR #188, merged 21.11.2025)
- πŸ”„ Phase 5: Security Audit & Documentation (PR #190, this PR)
- βœ… Phase 5: Security Audit & Documentation (PR #190, merged 22.11.2025)

## πŸ” Secret Management (Password Vault UI)

SecPal provides a comprehensive **password vault UI** with encrypted storage, file attachments, and sharing capabilities.

**Features (Phases 1-3 Complete):**

- πŸ“‹ **Secret List & Detail Views**: Browse, search, and filter secrets with grid/list toggle
- Search by title, filter by tags/expiration status
- Pagination (20 items/page)
- Password show/hide toggle
- Expiration badges, attachment count, shared indicator
- ✏️ **Create/Edit Forms**: Full CRUD operations with validation
- Title, username, password, URL, notes fields
- Client-side validation
- Loading states and error handling
- πŸ“Ž **File Attachments**: Upload, download, preview encrypted files
- Drag-and-drop upload with file validation (10MB max)
- Image preview with zoom controls (50%-200%)
- PDF preview in modal
- File type validation (images, PDFs, documents)
- Download with automatic decryption

**Usage Example:**

```tsx
import { SecretList } from "@/pages/Secrets/SecretList";
import { SecretDetail } from "@/pages/Secrets/SecretDetail";

// List all secrets
<SecretList />

// View secret details
<SecretDetail secretId="uuid" />
```

**Implementation Status:**

- βœ… Phase 1: Secret List & Detail Views (PR #197, merged 22.11.2025)
- βœ… Phase 2: Secret Create/Edit Forms (PR #198, merged 22.11.2025)
- βœ… Phase 3: File Attachments UI Integration (PR #200, merged 22.11.2025)
- πŸ”œ Phase 4: Secret Sharing UI (Issue #195)
- πŸ”œ Phase 5: Offline Support & PWA Integration (Issue #196)

## 🌍 Internationalization (i18n)

Expand Down
22 changes: 15 additions & 7 deletions src/components/SecretForm.enhanced.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ describe("SecretForm", () => {
it("should generate password when generate button clicked", async () => {
render(<SecretForm {...defaultProps} />);

const passwordInput = screen.getByLabelText("Password", { selector: "input" });
const passwordInput = screen.getByLabelText("Password", {
selector: "input",
});
const generateButton = screen.getByLabelText(/generate password/i);

expect(passwordInput).toHaveValue("");
Expand All @@ -39,7 +41,9 @@ describe("SecretForm", () => {
it("should show password strength indicator when password is entered", async () => {
render(<SecretForm {...defaultProps} />);

const passwordInput = screen.getByLabelText("Password", { selector: "input" });
const passwordInput = screen.getByLabelText("Password", {
selector: "input",
});

// Type weak password
await userEvent.type(passwordInput, "abc");
Expand All @@ -52,7 +56,9 @@ describe("SecretForm", () => {
it("should update strength indicator as password improves", async () => {
render(<SecretForm {...defaultProps} />);

const passwordInput = screen.getByLabelText("Password", { selector: "input" });
const passwordInput = screen.getByLabelText("Password", {
selector: "input",
});

// Type weak password
await userEvent.type(passwordInput, "abc");
Expand All @@ -71,9 +77,9 @@ describe("SecretForm", () => {
it("should toggle password visibility", async () => {
render(<SecretForm {...defaultProps} />);

const passwordInput = screen.getByLabelText(
"Password", { selector: "input" }
) as HTMLInputElement;
const passwordInput = screen.getByLabelText("Password", {
selector: "input",
}) as HTMLInputElement;
const toggleButton = screen.getByLabelText(/show password/i);

expect(passwordInput.type).toBe("password");
Expand Down Expand Up @@ -233,7 +239,9 @@ describe("SecretForm", () => {
render(<SecretForm {...defaultProps} />);

const titleInput = screen.getByLabelText(/title/i);
const passwordInput = screen.getByLabelText("Password", { selector: "input" });
const passwordInput = screen.getByLabelText("Password", {
selector: "input",
});
const tagInput = screen.getByPlaceholderText(/enter tag name/i);
const dateInput = screen.getByLabelText(/expiration date/i);
const submitButton = screen.getByRole("button", { name: /save/i });
Expand Down