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
43 changes: 40 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,15 @@ Environments: `src/environments/environment.ts` (`useMocks:false`), `environment
policy-editor-page}`; darunter `policy-editor-page/policy-builder/` (**Kern-Feature**, Wizard mit
`components/`, `metadata/`, `validators/`, `helpers/`).
- `ui/` — wiederverwendbares Design-System auf Angular Material (`con-x-*`-Komponenten).
- `services/` — u.a. `services/policies/policy.service.ts` (HTTP-CRUD) und
`services/policies/policy-mapper/policy-odrl.mapper.ts` (Domänenmodell → ODRL/EDC).
- `services/` — u.a. `services/policies/policy.service.ts` (HTTP-CRUD),
`services/policies/policy-mapper/policy-odrl.mapper.ts` (Domänenmodell → ODRL/EDC) und
`services/http/` (`http-error.interceptor.ts` loggt fehlgeschlagene Requests,
`http-error.helper.ts` wählt den i18n-Key nach Statuscode).
- `shared/` — `types/` (Modelle), `pipes/`, `adapters/`.
- `mocks/` — MirageJS-Server + Mock-Daten.
- `src/fonts/` — selbst gehostete Schriften (Montserrat, Material Icons), eingebunden über
`src/styles/_fonts.scss`. **Keine** Google-Fonts-Links in `index.html` (Herkunft & Lizenzen:
`src/fonts/README.md`).

Routing ist **lazy** (`app.routes.ts`). **Pfad-Aliase** statt Relativimporte:
`@pages/* @ui/* @services/* @shared/* @mocks/* @env @features/policies/builder/*`.
Expand Down Expand Up @@ -127,6 +132,37 @@ Zielniveau **WCAG 2.2 AA** (Grundausstattung). Details & Restrisiken: `docs/acce
- **Prüfung:** `templateAccessibility`-Regeln laufen in `npm run lint`; ergänzend manuell
(Tastatur, NVDA, Zoom, axe/Lighthouse).

### Sicherheits-Konventionen

Aus dem OWASP-Top-10-Audit abgeleitet; bitte einhalten, sonst kommen die Befunde zurück.

- **Ein HTTP-Response ist Eingabe.** `http.get<Policy>()` prüft zur Laufzeit nichts — der Typ
ist ein Compile-Zeit-Versprechen. Daten aus der API vor der Verwendung validieren:
unbekannte Constraint-Typen über `keepKnownConstraints()` / `isKnownConstraintType()`
aussortieren (ein Index-Zugriff mit unbekanntem Typ liefert `undefined` und wirft beim
Rendern).
- **Nie ungeprüfte Werte als Transloco-Parameter.** Transloco durchsucht das Ergebnis einer
Ersetzung erneut nach Platzhaltern; ein Wert mit `{{…}}` wird ein zweites Mal aufgelöst und
kann die Schleife zum Nicht-Terminieren bringen. Werte gegen die Metadaten-Registry prüfen
(`USE_CASE_OPTIONS`, `FRAMEWORK_AGREEMENT_VALUE`), sonst `'—'`.
- **i18n-Keys nie aus Daten zusammensetzen** — immer aus der Registry entnehmen.
- **`encodeURIComponent` für jeden Wert in einer URL,** auch für Route-Parameter: Angular
dekodiert `%2F`/`%2E`, der Browser normalisiert anschließend `..` im Pfad.
- **`policyId` ist ein Identifier** (wird zum JSON-LD-`@id`) — Zeichensatz-Validierung in
`constraint-validators.ts` beibehalten.
- **HTTP-Fehler nie verwerfen:** `error: (err: unknown) => …` mit
`httpErrorMessageKey(err, '<fachlicher Fallback>')`. Nie Request-/Response-Bodies loggen.
- **Dev-Werkzeug gehört nicht ins Prod-Bundle.** `@if` steuert nur das Rendern; das Bundling
entscheiden die `import`-Statements. Neue Mock-/Debug-Komponenten über `fileReplacements`
ausschließen — dabei eine **Barrel-Datei** ersetzen, nicht die Komponente selbst
(Angular behält die `templateUrl`-Zuordnung am Pfad, sonst bricht der AOT-Build mit
`TS2339` ab; Muster: `ui/mock-data-switcher/index.ts`).
- **Keine externen Laufzeit-Ressourcen** (Fonts, Skripte, Styles von fremden Domains) —
sie übertragen die Nutzer-IP und machen Builds vom Netz abhängig.
- **Produktion baut mit `security.autoCsp` und `subresourceIntegrity`.** Wer an Critical-CSS
oder `index.html` schraubt, prüft das Ergebnis **im Browser** gegen den Prod-Build
(Stylesheet aktiv? CSP-Violations?), nicht nur im HTML-Quelltext.

### Architektur-Entscheidungen
- **Signal-first State** — kein externes State-Management.
- **Trennung Domänenmodell ↔ externes ODRL/EDC-Format.** Die Übersetzung ist isoliert im
Expand All @@ -143,7 +179,8 @@ Zielniveau **WCAG 2.2 AA** (Grundausstattung). Details & Restrisiken: `docs/acce
startet den Mock-Server selbst (via `start-server-and-test`), wartet auf `:4200`, fährt Cypress
headless und stoppt danach. Deckt die **Hauptflüsse** gegen den Mock-Modus ab: Policy erstellen,
ansehen, bearbeiten, löschen, Liste durchsuchen/filtern; inkl. empty-/no-results-States. Specs in
`cypress/e2e/*.cy.ts`. (`npm run cy:run` fährt nur Cypress gegen einen bereits laufenden Server.)
`cypress/e2e/*.cy.ts` (aktuell **20 Tests** in 6 Specs). (`npm run cy:run` fährt nur Cypress
gegen einen bereits laufenden Server.)
- **Selektor-Konvention:** UI-Elemente werden über `data-cy="…"`-Attribute angesprochen
(entkoppelt von CSS-Klassen & i18n-Text). Custom-Commands in `cypress/support/commands.ts`:
`cy.getByCy(sel)` und `cy.visitWithMode(path, 'empty'|'few'|'many')` (setzt
Expand Down
10 changes: 9 additions & 1 deletion frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,17 @@
{
"replace": "src/app/mocks/mock.service.ts",
"with": "src/app/mocks/mock.service.production.ts"
},
{
"replace": "src/app/ui/mock-data-switcher/index.ts",
"with": "src/app/ui/mock-data-switcher/index.production.ts"
}
],
"outputHashing": "all"
"outputHashing": "all",
"subresourceIntegrity": true,
"security": {
"autoCsp": true
}
},
"mocks": {
"fileReplacements": [
Expand Down
39 changes: 39 additions & 0 deletions frontend/cypress/e2e/policy-legal-text.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/// <reference types="cypress" />

// Der auf der Detailseite angezeigte Rechtstext wird jedes Mal neu aus den Constraints
// abgeleitet; rechtlich maßgeblich ist aber der beim Speichern übermittelte und persistierte
// Text. Weichen beide voneinander ab, warnt die Seite (siehe `hasDivergingLegalText`).
//
// Hier wird der Gutfall abgesichert: eine über die UI gespeicherte Policy darf NIE eine
// Warnung zeigen. Das ist der Regressionsschutz für die Ableitungslogik — ändert sie sich,
// ohne dass `legalText` neu erzeugt wird, schlägt dieser Test an.
//
// Der Schlechtfall (manipulierter legalText bei unveränderten Constraints) lässt sich hier
// nicht ehrlich nachstellen: MirageJS ersetzt XMLHttpRequest im Browser vollständig, und
// cy.intercept sieht die Requests deshalb nie. Er ist stattdessen als Unit-Test auf
// `hasDivergingLegalText` in legal-description.helper.spec.ts abgedeckt.

// zugriff-konsortium-mitglieder: ACCESS mit einer MEMBERSHIP-Bedingung.
const POLICY_ID = '00000000-0000-0000-0000-000000000002';

describe('Policy – Rechtstext', () => {
it('zeigt nach dem Speichern über die UI keine Abweichungs-Warnung', () => {
cy.visitWithMode(`/policies/${POLICY_ID}/edit`, 'few');
cy.getByCy('submit-policy').click();

// Detailseite ist erreicht und der Rechtstext gerendert …
cy.getByCy('policy-title').should('exist');
cy.getByCy('legal-text').should('exist');
// … ohne dass gespeicherter und abgeleiteter Text auseinanderlaufen.
cy.getByCy('legal-text-diverged').should('not.exist');
});

it('zeigt für eine Policy ohne gespeicherten Rechtstext keine Warnung', () => {
// Die Seed-Datensätze tragen keinen legalText — ohne gespeicherten Text gibt es
// nichts zu vergleichen, und es darf kein Fehlalarm entstehen.
cy.visitWithMode(`/policies/${POLICY_ID}`, 'few');

cy.getByCy('legal-text').should('exist');
cy.getByCy('legal-text-diverged').should('not.exist');
});
});
90 changes: 45 additions & 45 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@
"private": true,
"packageManager": "npm@11.0.0",
"dependencies": {
"@angular/animations": "^21.2.0",
"@angular/animations": "^21.2.19",
"@angular/cdk": "^21.2.2",
"@angular/common": "^21.2.0",
"@angular/compiler": "^21.2.0",
"@angular/core": "^21.2.0",
"@angular/forms": "^21.2.0",
"@angular/common": "^21.2.19",
"@angular/compiler": "^21.2.19",
"@angular/core": "^21.2.19",
"@angular/forms": "^21.2.19",
"@angular/material": "^21.2.2",
"@angular/platform-browser": "^21.2.0",
"@angular/router": "^21.2.0",
"@angular/platform-browser": "^21.2.19",
"@angular/router": "^21.2.19",
"@jsverse/transloco": "^8.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0"
},
"devDependencies": {
"@angular/build": "^21.2.2",
"@angular/cli": "^21.2.2",
"@angular/compiler-cli": "^21.2.0",
"@angular/compiler-cli": "^21.2.19",
"@eslint/js": "^10.0.1",
"@vitest/coverage-v8": "^4.1.10",
"angular-eslint": "21.3.0",
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/app/app.component.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
:host {
display: flex;
flex-direction: column;
min-height: 100vh;
}

.app-content {
min-height: calc(100vh - 64px);
flex: 1;

// Programmatic focus target for skip link / route changes — the landmark
// itself is not an interactive control, so no visible ring is needed.
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
import { filter } from 'rxjs';
import { environment } from '@env';
import { HeaderComponent } from '@ui/header/header.component';
import { MockDataSwitcherComponent } from '@ui/mock-data-switcher/mock-data-switcher.component';
import { MockDataSwitcherComponent } from '@ui/mock-data-switcher';

@Component({
selector: 'app-root',
Expand Down
Loading
Loading