Commit Graph

804 Commits

Author SHA1 Message Date
schalli 6326064ad3 feat(09-06): GREEN — implement mergeCerts + PFX-create + convertCert pfx output
- CertManagerService.mergeCerts: parse all files via detectFormat/parsePemChain/toForgeBuffer,
  concatenate PEM chain or build PKCS12 via toPkcs12Asn1
- Open Question 1 resolved: toPkcs12Asn1(null, certs, password) works in node-forge 1.4.0
  (null private key accepted — cert-only PFX without fallback needed)
- PFX output requires non-empty password → BadRequestException if missing (T-09-02)
- All forge calls in try/catch → BadRequestException; password never logged (T-09-02)
- bytesToHex→Buffer.from(hex,'hex')→base64 for binary safety (Pitfall 1 avoidance)
- convertCert gains pfx output target (reuses same null-key toPkcs12Asn1 pattern)
- FORMAT_MIME extended with pfx: 'application/x-pkcs12'
- NotImplementedException import removed (no longer used)
- 27/27 API cert-manager tests green; tsc --noEmit exits 0
2026-07-02 07:49:42 +02:00
schalli f43e92c49f test(09-06): RED — failing mergeCerts spec (PEM chain + password-PFX round-trip)
- 4 new mergeCerts tests: PEM chain 2 blocks, PFX round-trip with password,
  missing PFX password → BadRequestException, garbage input → BadRequestException
- Controller enforces 2-file minimum; service tests use 1-2 files directly
- All 4 fail against NotImplementedException stub (RED confirmed)
- Prior 23 tests remain green
2026-07-02 07:47:28 +02:00
schalli db7a85cc4c docs(09-05): complete convert-vertical-slice plan 2026-07-02 07:41:34 +02:00
schalli f89d6566b2 feat(09-05): implement ConvertTab + convertCertAction + render tests
Tessera CI/CD / Tests (push) Has been cancelled
Tessera CI/CD / Build & Publish Images (push) Has been cancelled
Tessera CI/CD / Lint & Type Check (push) Has been cancelled
- Add FileResponse interface to actions.ts
- Add convertCertAction(input, targetFormat): builds FormData with
  file/pemText/password + targetFormat, calls postForm convert endpoint
- Implement ConvertTab: native select for pem/der/p7b targetFormat,
  Konvertieren button with loading swap, error classification, empty state
- On success: calls downloadBase64(filename, content, mimeType)
- 3 new ConvertTab tests: format selector options, downloadBase64 invoked
  on success, text-destructive error on format rejection
- All 14 web cert-manager tests green
2026-07-02 07:39:41 +02:00
schalli 59694642dd feat(09-05): implement convertCert + wire POST /convert (GREEN)
- Add FileResponse interface and FORMAT_MIME map to service
- Implement convertCert: parses any input format (PEM/DER/PFX/P7B) via
  same logic as parseCert; serializes to pem/der/p7b targetFormat
- DER output uses bytesToHex→Buffer.from(hex,'hex') to avoid utf-8
  corruption (Pitfall 1 / T-09-06)
- P7B output: pkcs7.createSignedData + pem.encode (PEM-wrapped PKCS7)
- Wrap all forge ops in try/catch → BadRequestException (T-09-01)
- Controller: add @Body('pemText') + reject when neither file nor pemText
- Fix: re-add NotImplementedException import for mergeCerts stub
- All 23 API cert-manager tests green (including 4 new convertCert)
2026-07-02 07:37:41 +02:00
schalli 37db58b816 test(09-05): add failing convertCert spec (RED)
- PEM→DER round-trip identity test (re-parses DER base64 → verify CN)
- DER→PEM round-trip identity test (re-parses PEM base64 → verify CN)
- PEM→P7B: asserts mimeType + P7B contains ≥1 cert
- malformed input: expects BadRequestException
- All 4 fail against NotImplementedException stub (RED confirmed)
2026-07-02 07:35:51 +02:00
schalli a1da5d9083 docs(09-04): complete split-slice plan 2026-07-02 07:17:46 +02:00
schalli 33b1bc3172 feat(09-04): SplitTab UI + splitCertsAction + render tests
- actions.ts: export SplitEntry + SplitResponse interfaces; add splitCertsAction(file) → POST /split
- SplitTab.tsx: Aufteilen button (disabled without file); per-cert download list (bg-secondary rows)
  each row: subject.cn, validity.notAfter, Herunterladen button → downloadBase64
  empty state / error state (text-destructive) matching InspectTab pattern
- cert-manager.test.tsx: 2 new SplitTab tests (success: 2 download buttons; error: text-destructive)
- All 11 cert-manager web tests green; production files type-clean
2026-07-02 07:16:18 +02:00
schalli 2c4ada347c feat(09-04): GREEN — implement splitCerts + SplitResponse interface
Tessera CI/CD / Tests (push) Has been cancelled
Tessera CI/CD / Build & Publish Images (push) Has been cancelled
Tessera CI/CD / Lint & Type Check (push) Has been cancelled
- Export SplitEntry + SplitResponse interfaces
- splitCerts: PEM chain path via parsePemChain; P7B path via messageFromPem (PEM) or messageFromAsn1 (DER)
- Each cert entry: index, filename cert-N.pem, content base64 PEM, subject.cn, validity.notAfter
- BadRequestException on malformed input / unsupported format (T-09-01)
- POST /split already wired in controller with 5MB file limit (T-09-03, T-09-04)
- All 19 API tests green; type-check clean
2026-07-02 07:14:22 +02:00
schalli eec66311a7 test(09-04): RED — failing splitCerts spec (fullchain PEM, P7B bundle, malformed)
- splitCerts fullchain PEM: expects count 2, two single-PEM-block certs with correct CN
- splitCerts P7B PEM bundle: expects at least one cert in result
- splitCerts malformed input: expects BadRequestException
- All three tests FAIL against NotImplementedException stub (RED confirmed)
- All 16 prior tests still pass
2026-07-02 07:12:37 +02:00
schalli da676705d9 docs(09-03): complete inspect-slice plan 2026-07-01 23:57:54 +02:00
schalli 64a8e725e7 feat(09-03): InspectTab UI + inspectCertAction + render tests
- Added inspectCertAction to actions.ts (JSON path for pemText, multipart path for file)
- Added CertDetails interface to actions.ts (mirrors API response shape)
- Implemented InspectTab: Analysieren button, loading state, grid-cols-2 result grid
- InspectTab handles wrong-password error (t('error.wrongPassword')) and generic error
- Added 2 new InspectTab tests: success grid (subject CN + SHA-256) and error (text-destructive)
- Fixed setup.ts: explicit expect.extend(matchers) for vitest@4.x compatibility
  (Rule 1: @testing-library/jest-dom/vitest not extending global expect in vitest 4)
- Fixed existing test: getByText -> getAllByText for 'Analysieren' (now appears in tab nav + button)
- 9/9 cert-manager tests pass
2026-07-01 23:55:41 +02:00
schalli ba994635e8 feat(09-03): GREEN — implement parseCert + export CertDetails interface
- Implemented CertManagerService.parseCert for PEM/DER/PFX/P7B inputs
- Exported CertDetails interface (subject, issuer, validity, san, keyType, keyBits, serialNumber, signatureAlgorithm, fingerprint, pemPreview)
- PFX with wrong password → BadRequestException (T-09-02: never logged, never echoed)
- All forge operations wrapped in try/catch → BadRequestException (T-09-01)
- buildReverseOids() converts OID → human-readable algorithm name
- P7B handles both PEM-wrapped and binary DER (RESEARCH Pitfall 4)
- Controller already wired correctly from Plan 01 (fileSize 5MB, pemText, file, password)
- All 16 cert-manager tests pass (16/16)
2026-07-01 23:41:03 +02:00
schalli 7c2e506a2e test(09-03): RED — failing parseCert spec (PEM/DER/PFX/wrong-password/malformed)
Tessera CI/CD / Tests (push) Has been cancelled
Tessera CI/CD / Build & Publish Images (push) Has been cancelled
Tessera CI/CD / Lint & Type Check (push) Has been cancelled
- Added parseCert describe block with 5 failing tests
- Covers PEM input, DER input, PFX+correct-password, PFX+wrong-password (BadRequestException), malformed input (BadRequestException)
- Existing 11 helper tests still pass
- Fixtures built via node-forge (RSA-1024, DER from asn1.toDer, PFX via toPkcs12Asn1)
2026-07-01 23:39:39 +02:00
schalli b1aa1d0d2d wip: phase 09 paused after wave 1 (2/6 plans done) 2026-07-01 23:27:53 +02:00
schalli 76d1a31583 docs(phase-09): update tracking after wave 1 2026-07-01 23:26:56 +02:00
schalli a9cce06ca3 fix(09-02): repair i18n JSON after wave-1 merge conflict resolution 2026-07-01 23:26:55 +02:00
schalli 4fc448b1d4 merge(09-02): cert-manager web shell + i18n (resolve de/en.json conflict) 2026-07-01 23:26:12 +02:00
schalli 637f4674ca merge(09-01): resolve app.module.ts conflict (CertManagerModule + FavoritesModule)
Tessera CI/CD / Lint & Type Check (push) Failing after 37s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Publish Images (push) Has been skipped
2026-07-01 23:25:48 +02:00
schalli 7ad4f22a75 docs(09-02): complete cert-manager frontend shell plan 2026-07-01 23:24:25 +02:00
schalli 2cb01f743d test(09-02): add shell render tests for CertManagerPage (GREEN)
- 7 tests passing: title, all 4 tab labels, hidden password field, per-tab empty states
- Tests use vi.mock('next-intl') pattern per project convention (matches sidebar, VehicleTable tests)
- Validates T-09-02 threat mitigation: password field absent on initial render
2026-07-01 23:23:17 +02:00
schalli 3506d60dbe docs(09-01): complete cert-manager API foundation plan summary
- SUMMARY.md with task results, deviations, stub tracking, threat scan
- Self-check: all 9 files found, 3 commits verified, 11 tests green
2026-07-01 23:22:51 +02:00
schalli 8bb5cf208d feat(09-01): scaffold cert-manager module + shared node-forge helpers (GREEN)
- cert-manager.module.ts: OnModuleInit + seedCertManagerModule (CERT-06)
- cert-manager.seed.ts: slug='cert-manager', category='security-tools', isSystem=true
- cert-manager.service.ts: detectFormat, toForgeBuffer, getFingerprint, parsePemChain;
  operation stubs parseCert/splitCerts/mergeCerts/convertCert throw NotImplementedException
- cert-manager.controller.ts: 4 POST routes with FileInterceptor/FilesInterceptor
  (5 MB limit each), @UseModule('cert-manager') guard, BadRequestException on missing input
- dto/: ParseCertDto, MergeCertsDto, ConvertCertDto
- app.module.ts: CertManagerModule added to imports array
- All 11 Vitest tests pass; type-check clean
2026-07-01 23:21:36 +02:00
schalli 42a41f77d0 feat(09-02): build cert-manager page shell, components, and client helpers
- CertManagerPage: 'use client', useTranslations('certManager'), max-w-4xl layout
- Shared input card with DropZone, OR divider, PEM textarea, conditional PasswordField
- PasswordField renders null when show=false (T-09-02 threat mitigation)
- Tab nav: Analysieren / Aufteilen / Zusammenfuehren / Konvertieren
- Tab stubs: InspectTab, SplitTab, MergeTab, ConvertTab (empty state only)
- actions.ts: API_URL const, downloadBase64(atob->Blob->URL), postForm(credentials:'include')
- File/paste mutual exclusion: selecting one clears the other
- No shadcn/Radix; Tailwind utilities only; inline SVG eye icon
2026-07-01 23:20:10 +02:00
schalli a06694f915 test(09-01): add failing spec for cert-manager seed + helpers (RED)
- Test: seedCertManagerModule calls seedModule with slug='cert-manager',
  category='security-tools', isSystem=true
- Test: detectFormat returns pem/der/pfx/p7b based on extension + content sniff
- Test: getFingerprint returns uppercase colon-separated hex (sha1 + sha256)
- Test: parsePemChain returns array of length 2 for two concatenated PEMs
2026-07-01 23:18:08 +02:00
schalli 82a80e7634 feat(09-02): add certManager i18n namespace (de + en)
- Added certManager namespace to de.json with full key set (tabs, dropZone, paste, password, or, actions, emptyState, error)
- Added certManager namespace to en.json with matching key structure
- German copy matches UI-SPEC Copywriting Contract exactly
- Both files share identical key paths under certManager
2026-07-01 23:17:58 +02:00
schalli a13a8a763f chore(09-01): install node-forge + Vitest runner for @tessera/api
- Add node-forge@^1.4.0 runtime dependency (certificate crypto)
- Add @types/node-forge@^1.3.14 and vitest@^3 dev dependencies
- Create apps/api/vitest.config.ts (environment: node, passWithNoTests)
- Add test + test:watch scripts to apps/api/package.json
2026-07-01 23:17:23 +02:00
schalli 812eb06d9b docs(09): create phase plan + resolve open questions 2026-07-01 16:28:31 +02:00
schalli 063666af3b docs(09): create cert-manager phase plan (6 plans)
Tessera CI/CD / Lint & Type Check (push) Failing after 41s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Publish Images (push) Has been skipped
2026-07-01 16:24:31 +02:00
schalli ad7de8de2d docs(09): research phase 9 cert-manager module
Tessera CI/CD / Lint & Type Check (push) Failing after 43s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Publish Images (push) Has been skipped
2026-07-01 16:05:42 +02:00
schalli 6ee31a22fb docs(09): UI design contract 2026-07-01 15:49:15 +02:00
schalli 09d5231148 docs(09): UI design contract 2026-07-01 15:47:00 +02:00
schalli 5b75b3284a wip: phase 9 cert-manager paused at planning (UI-SPEC needed) 2026-07-01 15:43:47 +02:00
schalli a32f5f948b docs(09): add Phase 9 Cert Manager to roadmap + capture context
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 15:39:24 +02:00
schalli e35276243a fix(calendar): EWS uses NTLM auth + edit form stays open after save
- Replace ews-javascript-api (Basic Auth only) with httpntlm for EWS connections
- testEwsConnection uses GetFolder SOAP via NTLM
- fetchViaEws uses FindItem CalendarView SOAP via NTLM
- Edit form no longer auto-closes on save — shows "Erfolgreich gespeichert" instead
- Test button in edit mode uses saved credentials via /sources/:id/test endpoint
- Add saveSuccess i18n key (de/en)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 14:05:07 +02:00
schalli 51d8c2f14e fix(calendar): SSRF exception for Exchange + error messages + domain in edit
- SSRF check skipped for Exchange type (internal EWS servers are common)
- testConnectionFromConfig catches SSRF/validation errors, returns {success:false,error} instead of throwing 403
- updateSource reads existing.type to determine effective type for SSRF check
- Panel shows saveError/editSaveError on failed add/update
- Edit form initialValues now includes domain field
- i18n: calendar.saveError key added (de+en)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:41:32 +02:00
schalli b719291bdc fix(accent-color): restore color on reload + apply sidebar vars
header.tsx: accentColor was missing from setUser call on mount —
applyAccentColor(undefined) fired on every reload, removing --primary.

auth-store: also set --sidebar-accent (15% opacity) and
--sidebar-accent-foreground so active sidebar items match accent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:30:33 +02:00
schalli 42daa87e5e feat(calendar): add domain field and test-connection button to Exchange sources
- Prisma: domain String? added to CalendarSource model (db push applied)
- DTOs: domain in CreateCalendarSourceDto, UpdateCalendarSourceDto, new TestCalendarSourceConfigDto
- Service: domain in SOURCE_SAFE_SELECT, addSource, updateSource; new testConnectionFromConfig method
- Controller: POST /calendar/sources/test-config (before :id routes to avoid collision)
- ExchangeProvider: domain in all source interfaces; passed as 3rd arg to EWS WebCredentials
- Frontend: domain in CalendarSource/CreateSourcePayload/UpdateSourcePayload; testSourceConfig API fn
- Form: domain field (Exchange-only), "Test connection" button with idle/loading/success/error states
- i18n: de+en keys for formFieldDomain, formFieldDomainHint, formTestConnection, formTesting, formTestSuccess, formTestFailed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:15:13 +02:00
schalli 2e0e7290ba fix(calendar): remove setState call from render in isFormValid
validateUrl() calls setUrlError() — calling it during render triggers
React error #301 (cannot update component while rendering). Remove it
from the isFormValid computation; onChange/onBlur already keep urlError
in sync so !urlError is sufficient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:01:03 +02:00
schalli f6173bbe35 docs(quick-260701-abc): fix i18n missing keys — summary and state update
Tessera CI/CD / Lint & Type Check (push) Successful in 45s
Tessera CI/CD / Tests (push) Has been cancelled
Tessera CI/CD / Build & Publish Images (push) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:06:32 +02:00
schalli e5b76b735a fix(i18n): add missing marketplace.accessDenied and translate calendar form
- Add marketplace.accessDenied to de.json and en.json
- Add 12 calendar form field/action keys to widgets.calendar in both locales
- Replace all hardcoded English strings in calendar-source-form.tsx with t() calls
- Remove locale-detection hack on Cancel button (was comparing t() result to English string)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:05:52 +02:00
schalli eebceb298d fix(08): apply code review findings (CR-01, CR-02, WR-01–05, IN-01)
- CR-01: fix SSRF bypass — isPrivateIpv6 now delegates ::ffff:<ipv4> to
  isPrivateIpv4, covering 172.16-31.x and 169.254.x ranges
- CR-02: add ParseUUIDPipe to GET /favorites widgetId param + service guard
  so missing widgetId returns 400 instead of leaking all user favorites
- WR-01: link-widget — replace raw 'link.error' key with t('link.error') (4 sites)
- WR-02: favorites-widget — fix load-path error to use t('favorites.error')
- WR-03: widget-catalog-modal — move aria-hidden from outer wrapper to backdrop
- WR-04: calculator — remove duplicate M button (MR clone); MC/MR/M+/M−/MS remain
- WR-05: schema — add FavoriteLink→WidgetInstance FK with onDelete:Cascade
- IN-01: create-widget.dto.ts — update comment from four to eight supported types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 11:03:16 +02:00
schalli 54e4731b36 docs(08): add code review report 2026-07-01 10:59:02 +02:00
schalli 34df808d83 docs(phase-08): update tracking after wave 4 2026-07-01 10:51:53 +02:00
schalli 71689fc5df merge(08-04): Link widget sharing Favorites backend 2026-07-01 10:51:34 +02:00
schalli dcff93e2ae docs(08-04): complete Link widget plan summary
- 3/3 tasks complete (RED test → GREEN implementation → full suite gate)
- 88/88 web tests passing (18 test files, +7 link-widget tests)
- web + api tsc --noEmit clean
- LinkWidget: single-link D-06 via FavoriteLink backend reuse
2026-07-01 10:51:08 +02:00
schalli e9914f61cb feat(08-04): LinkWidget implementation + page.tsx wiring (GREEN)
- LinkWidget: single-link widget reusing FavoriteLink backend (D-06)
- Single-link enforcement: add form hidden when link exists
- List (row) and tile (grid) view modes, switchable in edit mode
- Inline add/edit/delete forms in edit mode
- Letter fallback span + icon with onError hide (T-08-11)
- Links with target="_blank" rel="noreferrer" (T-08-12)
- i18n keys added to de.json + en.json (link.*)
- wireLinkWidget(LinkWidget) added to portal page.tsx
- All 7 link-widget tests pass (GREEN); tsc --noEmit clean
2026-07-01 10:48:58 +02:00
schalli 4657e50494 test(08-04): add failing tests for LinkWidget single-link contract (RED)
- fetchFavorites called with instanceId on mount
- Link renders as anchor with target="_blank" rel="noreferrer" (T-08-12)
- Empty + edit mode shows add form; createFavorite called on submit
- Single-link enforcement: add form hidden when link exists (D-06)
- Edit mode: updateFavorite called with id and new title
- View toggle: list default, tile container on gridView click
- Letter fallback: iconUrl null shows first uppercase letter
2026-07-01 10:46:50 +02:00
schalli 7b42a3aef9 docs(phase-08): update tracking after wave 3 2026-07-01 10:39:05 +02:00
schalli 423ce90608 merge(08-03): Favorites widget with backend persistence
Tessera CI/CD / Lint & Type Check (push) Successful in 45s
Tessera CI/CD / Tests (push) Has been cancelled
Tessera CI/CD / Build & Publish Images (push) Has been cancelled
2026-07-01 10:38:47 +02:00