TR 03187-Conformance Statement
CIVITAS/CORE aims to conform to Level 1 of BSI TR-03187, a German security recommendation for Urban Data Platforms. In the context of BSI TR-03187, CIVITAS/CORE implements requirements that pertain to "Entwickler" (Software Developers) and "Lösungsanbieter" (Integrators). Requirements that pertain to "Betreiber" (Operators) are out of scope.
The following sections detail how the pertinent requirements of BSI TR-03187 are fulfilled in CIVITAS/CORE.
Scope note on 3rd party components: The conformance assessment currently covers components developed by the CIVITAS/CORE project. Verification of 3rd party components (e.g. upstream container images, external libraries) is out of scope for the time being.
State covered: This statement reflects the current development state of CIVITAS/CORE — the staging branch of the deployment repository and the develop branch of the platform repository.
| Requirement | Status | Description |
|---|---|---|
| AR-1 Geringstmögliche Privilegien | Done | Least privilege enforced at multiple layers: OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. default allow := false with explicit permission grants only. Containers run as non-root (UID 1000/1001) with drop: [ALL] capabilities, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false. Dedicated Kubernetes ServiceAccounts per deployment. Spring Security anyRequest().authenticated() catch-all. Mandated through Security Architecture Principles. |
| AR-2 Legacy-Clienttechnologien vermeiden | Done | For components developed by CIVITAS/CORE, ESLint rules block legacy HTML elements (<object>, <embed>, <applet>) at error level in the CI pipeline (see ESLint config and implementation ticket). |
| AR-3 Nicht verwendete Abhängigkeiten entfernen | In Progress | Unused-dependency detection for JVM modules runs in CI via mvn dependency:analyze -DfailOnWarning=true in the portal backend, config-adapter and AuthZ pipelines, each with a curated exclusion list for dependencies the analyzer cannot resolve through indirection. SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. policy (Section 6.6) and the Deployment Checklist mandate reasonable effort toward minimizing shipped artifacts. Gaps: the portal-model pipeline runs the same check but tolerates its failure (allow_failure: exit_codes: [1] in .gitlab/ci/portal-model.yml), and model-forge, which the portal backend ships, has no such check (fix: platform!896; clean-up of model-forge: platform#2362). The config-adapter ships as a single container image bundling every adapter module, so unused adapter code cannot be pruned per-deployment (platform#1870), and the frontend has no equivalent unused-package-dependency check (platform#1875). |
| AR-4 Version Pinning | In Progress | Dependencies and container images of the product are pinned to specific versions, ensured through the Deployment Standards (section "Version Management"), the Frontend Style Guide (section "Imports / Exports"), and the Backend Style Guide (section "Imports"). External Helm chart versions are pinned in components/*/charts.yaml and container images in components/*/images.yaml; Java dependencies are pinned in pom.xml, frontend dependencies via pnpm-lock.yaml, and Dockerfiles are version-tagged (portal-backend and authz-repo additionally pin their base image by SHA256 digest). Implementation ticket: Pin Helm image tags to release versions. Gaps: the CI job that computes the version tag of every published image runs on the floating alpine:latest, and about 60 CI job images use floating tags without digest (platform#2300); the postgres role-setup Job uses postgres:16-alpine from a chart template, outside images.yaml (platform#2300). Some images are pinned by mutable tag rather than digest, and the authz deployments use pullPolicy: Always (deployment#278). Renovate proposes updates centrally; they are merged only after approval on the dependency dashboard, approvals are behind, and some pins have no Renovate manager yet, so pins can age (platform#1472). |
| AR-5 Sitzungs-IDs zufällig und eindeutig | Done | All JPA entities use @GeneratedValue(strategy = GenerationType.UUID) (Level 4 random UUIDs). Frontend session IDs (UML modeler, pipeline editor) use crypto.randomUUID(). Backend is stateless (SessionCreationPolicy.STATELESS), no server-side session IDs. Auth sessions delegated to KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs.. |
| AR-6 Sitzungsablauf nach Inaktivität | Done | KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. SSO Session Idle is set to 60 minutes. A frontend inactivity detector stops background token refresh and triggers logout after a period of no user interaction, so the KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. idle timeout takes effect. Implementation ticket: Add frontend inactivity detector to enforce session timeout. |
| AR-7 Rollenbasierte Benutzerverwaltung | Done | Role-based user management is implemented. Users are assigned role "Standard User" on registration. For details, see Authorization Data Model. |
| AR-8 Replay-Schutz | In Progress | User authentication is performed system-wide by OpenID Connect with signed JWT tokens including timestamps. Portal-backend has enable.idempotence: true (exactly-once per partition, portal-backend/src/main/resources/application.yaml), and the KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. consumers commit offsets manually after handling. mTLS via Linkerd provides transport-layer replay protection between components. The Flowable saga orchestrator rejects a re-delivered trigger by its KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. record coordinate (FlowableTriggerConsumer.java:192), and the adapters recover from duplicate creates rather than pre-checking for them: FrostSagaHandler.handleCreateProjectConflict() treats a FROST 409 as a duplicate and re-resolves the existing project, and the PostGIS adapter absorbs duplicate-key errors via SqlDdlSupport/PostgisDialect.isDuplicate(). Gaps: the saga trigger topic carries plain JSON without a message id, nonce or timestamp, so a re-produced identical trigger starts a second saga rather than being rejected — the KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows.-coordinate key is re-delivery idempotency, not replay protection; and deduplication is missing on the non-Flowable config-adapter adapter paths, which are the configured production set (ADAPTERS=apisix,keycloak,frost) and run alongside the orchestrator in the same process (platform#1584). |
| AR-9 Geheimnisse müssen änderbar sein | Done | As per SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. (section 9) and Deployment Standards (Section "Security"), secrets are stored through Kubernetes secrets or other secure means external to the application, in a changable manner. Exception: KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. stores secrets (passwords, signing keys) in database; all of them are changable through regular KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. mechanisms. |
| AR-10 Geheimnisse von Benutzern dürfen nicht zur Sicherstellung von Vertraulichkeit verwendet werden | Done | User credentials are managed exclusively by KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. and never used for data confidentiality. All confidentiality mechanisms (TLS, K8s secrets) use system-managed credentials. Architectural separation: KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. handles user secrets, everything else uses infrastructure-level secrets. The requirement is also part of the Security Architecture Principles. |
| AR-11 Kryptografische Verfahren sollen TR-02102-1 entsprechen | Done | Production code uses BSI-approved algorithms: AES-256-GCM for credential encryption (CryptoUtils.java), PBKDF2-HMAC-SHA256 with 600k iterations for key derivation (CryptoKeyLoader.java), HKDF-SHA256 for per-credential key isolation, RS256 and ES256 for JWT/WebAuthn signatures (KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. realm config). No weak ciphers (DES/3DES/RC4/Blowfish) in production code. Requirement to use BSI-approved cryptography is also part of Backend Code Style Guide and Deployment Standards. |
| AR-12 Standardframeworks für sicherheitskritische Funktionen | Done | Standard frameworks used for all security-critical functions: Spring Security (backend auth/authz), KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. (identity management), NextAuth (frontend session management), Jackson (deserialization), OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. (policy enforcement), Linkerd (mTLS). No custom cryptography or authentication implementations. Ensured through inclusion in Security Architecture Principles document. |
| AR-13 Ganze Zertifikatkette validieren | In Progress | Encryption between container instances is enforced through mandatory mTLS via the Linkerd service mesh, with defaultInboundPolicy: cluster-authenticated set cluster-wide (defaults/environment/global.yaml); the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. ingress edge requires an authenticated mesh identity by default. A Kyverno policy, shipped in Enforce mode, rejects meshed namespaces without an inbound policy; it does not yet check the value of that policy (fix: deployment!326). Accepted risk: the config-adapter's NiFiApache NiFiA stream processing and connector framework. In CIVITAS/CORE it is the pipeline engine for data integration and transformation (see ADR 047) and implements dataset-defined data flows. REST client does not validate the server certificate chain (trust-all X509TrustManager, always-true HostnameVerifier; the deployment sets NIFI_TLS_INSECURE_SKIP_VERIFY while the code reads NIFI_TLS_INSECURE). Both sides are in the mesh, so Linkerd mTLS authenticates the connection (platform#1644). Not applicable today: the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. configuration chart turns off certificate verification for HTTPS upstreams, but no shipped upstream uses HTTPS (deployment#370). Gap: the portal frontend turns off certificate verification for all outbound connections (NODE_TLS_REJECT_UNAUTHORIZED: "0"), including the token exchange with KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs., which goes through the public ingress and is not covered by the mesh (deployment#240). |
| AR-14 Alle vorgesehenen Sicherheitsmechanismen defaultmäßig anschalten | In Progress | Security mechanisms enabled by default: the frontend sets a nonce-based CSP, X-Content-Type-Options, X-Frame-Options, and Referrer-Policy via Next.js middleware (portal-frontend/src/middleware.ts); KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. has brute-force protection (10-attempt lockout, failureFactor: 10) in production; the Kyverno runtime policies ship enabled in Enforce mode (runtimePolicies.failureAction: Enforce in defaults/environment/global.yaml). APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. removes forged identity headers from every client request with the global rule strip-forged-identity-headers, so they cannot bypass the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs.→OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. filtering chain. Gaps: the default is a single namespace (singleNamespace: true), so trust-zone separation needs the operator to change it (deployment#182, see AR-16). The KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. client template enables the password grant (ROPC) for every client by default (deployment#236). The Superset route has no OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. check; 2.0 ships Superset without data sourcesData sourceA data-related element that represents the origin of data. It defines how data is connected, accessed, and ingested into the Platform, such as an external database or sensor network., and the OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. integration is planned (platform#1613). Security response headers are missing on API responses: APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. sets no HSTS/CSP (deployment#242), and the frontend middleware does not cover /api/* (platform#2341). |
| AR-15 Verschlüsselte Kommunikation | Done | Encryption between container instances is enforced through mandatory mTLS via the Linkerd service mesh, with defaultInboundPolicy: cluster-authenticated set cluster-wide (defaults/environment/global.yaml); the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. ingress edge requires an authenticated mesh identity by default. A Kyverno policy, shipped in Enforce mode, rejects meshed namespaces without an inbound policy. Caveat: some components have no transport encryption of their own and depend on the mesh: KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. overrides the chart default with tls: false and has no client authentication (deployment#333), etcd serves plain HTTP although tls.enabled: true is set (deployment#244), and Valkey has TLS off. Five NiFiApache NiFiA stream processing and connector framework. In CIVITAS/CORE it is the pipeline engine for data integration and transformation (see ADR 047) and implements dataset-defined data flows. ports accept connections without a mesh identity (deployment#371). |
| AR-16 Abschirmung unterschiedlicher Vertrauenszonen | In Progress | Namespace-based trust-zone isolation is available: each component can be deployed into its own namespace, every component ships a default-deny ingress NetworkPolicy with explicit per-component allow rules (defaults/environment/networkpolicies.yaml.gotmpl), and Linkerd mTLS is mandatory between pods. Gaps: the shipped default is still a single namespace (singleNamespace: true), so separate trust zones exist only when the operator sets singleNamespace: false (deployment#182). No component restricts outbound traffic (deployment#204). The portal-backend NetworkPolicy also admits the portal frontend directly, not only the gateway (platform#2358). In multi-namespace mode the component-namespace resolver fails open when a namespace cannot be resolved (deployment#255). |
| AR-17 Integritätsgeschützter Kanal für Deployment | Done | CIVITAS/CORE is delivered as source code and Helm charts through GitLab and container images through the GitLab Container Registry over TLS, so transport-layer integrity and authentication of the delivery channel are ensured. Container images carry SLSA Build Provenance attestations (in-toto predicate https://slsa.dev/provenance/v1) generated by docker buildx --provenance=true, binding the image digest to the CI build metadata. Release container images are signed manually by the Product Owner prior to release. Automating signing within the CI pipeline is tracked in platform#1697. |
| CT-1 Container-Hardening | Done | All container workloads apply the shared hardening baseline (runAsNonRoot: true, capabilities.drop: [ALL], readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, seccompProfile: RuntimeDefault) defined in defaults/environment/security.yaml.gotmpl and referenced by every chart's securityContext/podSecurityContext. Mandated through Deployment Standards (section "Security"). |
| CT-2 Werkzeuggestützte Prüfung auf Container-Sicherheitslücken | Done | Trivy scans are used to scan container images. (implementation ticket) |
| CT-3 Basisimages aus vertrauenswürdigen Quellen | In Progress | Container base images of the images CIVITAS/CORE builds are sourced from trusted, well-established providers: eclipse-temurin (Adoptium/Eclipse Foundation) for the Java components, node (Docker Official Image) for portal-frontend, alpine (Docker Official Image) for build stages, openpolicyagent/opa (CNCF project) for the policy engine, busybox (Docker Official Image) for init containers. AuthZ repository uses SHA256 digest pinning for reproducible builds. Mandated through Deployment Standards (section "Version Management"). Gaps: the NiFiApache NiFiA stream processing and connector framework. In CIVITAS/CORE it is the pipeline engine for data integration and transformation (see ADR 047) and implements dataset-defined data flows. bootstrap Job and init container use badouralix/curl-jq, an image from a personal Docker Hub namespace (platform#1643); GeoServer's message broker uses bitnamilegacy/rabbitmq with image verification turned off (platform#1654); NiFiApache NiFiA stream processing and connector framework. In CIVITAS/CORE it is the pipeline engine for data integration and transformation (see ADR 047) and implements dataset-defined data flows. downloads the PostgreSQL JDBC driver at every start without a checksum (deployment#372). |
| CT-4 Minimalistische Basisimages | Done | Base images across the deployment use minimized Alpine variants (e.g. eclipse-temurin:25-jre-alpine, node:22-alpine) for CIVITAS-developed services, and the OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. image uses a -static (distroless) variant — all minimal compared to full OS images. Mandated through the Backend Style Guide. |
| CT-5 Geheimnisse zur Laufzeit dynamisch bereitstellen | Done | As per SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. (section 9) and Deployment Standards (section "Security"), secrets are stored through Kubernetes secrets or other secure means external to the application. Helm templates use secretKeyRef to inject secrets from Kubernetes Secrets at runtime. No secrets are baked into container images. CI pipeline includes secret scanning to prevent hardcoded secrets from being merged. Exception: KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. stores secrets (passwords, signing keys) in its database; all of them are changeable through regular KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. mechanisms. Accepted risk: the config-adapter values contain literal default credentials for FROST and GeoServer instead of secretKeyRef, and the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. FROST route injects a fixed FROST credential; both services are reachable only inside the cluster, and generated credentials are planned (deployment#246, deployment#362). |
| CT-6 Gehärtete Clusterknoten | Not Applicable | Since CIVITAS/CORE assumes it runs on an existies kubernetes cluster, this requirement is out of scope for CIVITAS/CORE. |
| CT-8 Geringstmögliche Privilegien für Container-Images | Done | All CIVITAS/CORE-developed containers run as non-root: Helm deployments set runAsUser: 1000, runAsNonRoot: true. Dockerfiles create non-root users (UID 1001). Additionally: drop: [ALL] capabilities, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false. Ensured through Deployment Standards (section "Security"). |
| CT-9 Transportverschlüsselung & Integrität zwischen Pods | Done | Encryption between container instances is enforced through mandatory mTLS via the Linkerd service mesh, with defaultInboundPolicy: cluster-authenticated set cluster-wide (defaults/environment/global.yaml); the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. ingress edge is explicitly scoped to require an authenticated mesh identity by default. A Kyverno policy additionally flags any meshed namespace that omits an inbound policy; running that policy in Enforce rather than Audit mode is the operator's responsibility. |
| MS-3 Vermeidung fester Credentials für Managed-Service-Zugriffe | Not Applicable | Not applicable, as any possible managed service access is outside the scope of the application's design. |
| AUT-1 Einheitliche Vertrauensniveaus in der Authentifizierung | Done | Authentication is centralized through KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs.. For KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs., no fallback methods (e.g. password reset through security questions) are defined. Single trust level for all users. |
| AUT-2 Multi-Faktor-Authentisierung | In Progress | CIVITAS/CORE uses KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. as the identity provider for all components. MFA is available through KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. (WebAuthn and TOTP), configured via a custom browser flow with a 2FA subflow gated by a conditional-user-configured authenticator. OTP uses HmacSHA256; WebAuthn uses ES256/RS256. Gaps: the KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. admin console and the master realm are reachable at idm.<domain> with password-only login; the master realm has no MFA flow (fix: the gateway blocks the admin paths, deployment!324). Clients with the password grant (ROPC) cannot use WebAuthn (deployment#236). |
| AUT-3 Dedizierte Serviceaccounts | In Progress | Dedicated ServiceAccounts are created for config-adapter and etcd (serviceAccount.create: true); external Helm operators (cloudnative-pg, strimzi, keycloakx, apisixApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs.) ship their own. Mandated through Deployment Standards (section "Security"). Gaps: FROST, AuthZ/OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization., the portal backend and frontend, the APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. configuration Job and the NiFiApache NiFiA stream processing and connector framework. In CIVITAS/CORE it is the pipeline engine for data integration and transformation (see ADR 047) and implements dataset-defined data flows. bootstrap Job declare no serviceAccountName and fall back to the namespace default ServiceAccount; no workload sets automountServiceAccountToken: false (fix for the components we own: deployment!327). Tracked via Minimize ServiceAccount token exposure across CIVITAS workloads. |
| AUT-4 Kryptografisch abgesicherter Zugriff auf Infrastruktur | Done | Infrastructure access is the operator's responsibility. CIVITAS/CORE provides KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. with WebAuthn support (ES256/RS256) as an available cryptographic authentication mechanism. Operators can enable cryptographically secured access for administration. Direct KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. admin access is not a supported use case in the CIVITAS/CORE operational model. |
| AUT-5 Zugriffskontrollmodell definieren | Done | The authorization model is documented here |
| AUT-6 Sitzungs-IDs invalidieren | Done | CIVITAS/CORE uses KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. for authentication and KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. supports this out of the box. |
| AUT-7 Passwortrichtlinien | In Progress | KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. enforces password policies. The master/admin realm requires at least 20 characters with uppercase, lowercase, digit and special character. Authentication is centralized through KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. via NextAuth (OIDC), so the realm policies apply to all user-facing authentication. Gap: the instance realm requires at least 12 characters with uppercase, lowercase and digit (length(12) and upperCase(1) and lowerCase(1) and digits(1)); this meets neither alternative of AUT-7 (25 characters, or 8 characters with high complexity) (deployment#369). |
| W-1 Deny All als Default | Done | Four-layer deny-all-by-default: (1) OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. default allow := false with explicit permission-based allow rules only (main.regoRegoOPA's declarative policy language, used to express authorization rules such as checking role assignments at the most specific scope first.). (2) APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. bearer_only: true rejects requests without valid JWT. (3) Spring Security anyRequest().authenticated() catch-all (SecurityConfig.java). (4) NextAuth middleware denies unauthenticated access to all non-public paths. This is mandated as part of the SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. (Section "3. Requirements you need to fulfill"). |
| W-2 Zugriffskontrollmodell definieren | Done | The authorization model is documented in the Authorization Data Model and implemented via OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. RegoRegoOPA's declarative policy language, used to express authorization rules such as checking role assignments at the most specific scope first. policies (permission evaluation with provider-based endpoint mapping) and KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. RBAC. |
| W-3 Einheitlicher Zugriffskontrollmechanismus | Done | Unified access control for backend API traffic via APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. (API gateway) + OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. (policy engine). The /v1/* portal-backend route passes through OIDC authentication (APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. openid-connect plugin, bearer_only: true) and OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. authorization (components/portal/apisix-plugins.yaml).Deliberate exceptions to this chain: - Portal BFFBackend-For-FrontendAn architecture pattern where a dedicated backend serves a specific frontend, ensuring that sensitive tokens never reach the client side. (Next.js frontend): handles authentication via NextAuth; has no own authorization layer (access control to backend APIs still goes through APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. + OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization.). - KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. ( idm.*): self-authenticated — it is the identity provider. |
| W-4 Restriktive CORS-Policy | Done | No CORS configuration in backend (no @CrossOrigin annotations, no CorsConfiguration beans, no wildcard origins). Backend is not directly exposed to browsers — APISIXApache APISIXAn open-source API gateway for traffic management, security and observability. In CIVITAS/CORE it is used as the centralized entrypoint to route and protect externally exposed APIs. handles API gateway concerns. Ingress-level CORS is operator responsibility (see also W-17). CORS policy mandated through Backend Style Guide (section "CORS-Policy"). |
| W-5 Web-Server-Verzeichnisauflistung deaktivieren | Done | Spring Boot embedded Tomcat disables directory listing by default; no override found. Next.js serves only explicitly referenced assets from public/. No ResourceHandlerRegistry configured. |
| W-6 Nicht benötigte Datei-Metadaten und Sicherungskopien vermeiden | Done | Dockerfiles COPY only production artifacts (JARs, .next/, public/). No backup or metadata files in deployed images. AuthZ has explicit .dockerignore. Spring Boot and Jetty do not serve static files without specific provisions. |
| W-7 Sitzungs-IDs nach Logout invalidieren | Done | Federated logout implemented: NextAuth signOut() clears the JWT session and calls KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs.'s OIDC logout endpoint with id_token_hint (auth.config.ts signOut event). Both the local NextAuth JWT and the KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. SSO session are terminated. Implementation details documented in Authentication Flow. |
| W-8 Kurzlebige JWTs | Done | KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. token lifetimes (base-values.yaml.gotmpl): access token 5 minutes (accessTokenLifespan: 300), SSO Session Idle 60 minutes, SSO Session Max 10 hours. Admin realm stricter: SSO idle 30 minutes. Frontend session maxAge 10 hours.(Implementation ticket) |
| W-9 Sichere DB-Zugriffe (ORM/Prepared) | Done | Spring Data JPA with parameterized @Query annotations used throughout. All queries use :paramName binding — no raw SQL or string concatenation detected. Enforced through Backend Style Guide (section "Security"). |
| W-10 Vom Benutzer übergebene Objekte defensiv deserialisieren | Done | Jackson ObjectMapper is used without dangerous polymorphic typing (enableDefaultTyping()/activateDefaultTyping() are not used). Custom deserializers (e.g. UpstreamNodes.NodesDeserializer) validate JSON token types explicitly before deserializing. No XXE-vulnerable XML parsing. Mandated through the Backend Style Guide (section "Security"). |
| W-11 Clientseitige Eingabevalidierung | Done | Client-side validation implemented through React Hook Form with Zod schemas. Forms enforce email validation, length constraints (min/max), regex patterns, and required fields. Examples: UserInputDTO uses z.email(), z.string().min(2), phone validation with regex; DatasourceInputDTO uses z.string().trim().max(150). Mandated through Frontend Style Guide (section "Security Principles"). |
| W-12 Serverseitige Eingabevalidierung | Done | Server-side validation is enforced via Jakarta Bean Validation: @Validated on BaseController, @Valid @RequestBody on create/update endpoints, with constraint annotations (@NotBlank, @Email, @Size, slug patterns) on input DTOs such as DataSetInputDTO, DataSourceInputDTO, and NamedApiInputDTO. Mandated through the Backend Style Guide (section "Security"). |
| W-13 Limitierung Anzahl Ergebnisse Datenbankabfragen | Done | Pagination is enforced via @PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC) on BaseReadOnlyController.getAll(). BaseController extends BaseReadOnlyController, so every controller in the hierarchy inherits the paginated list endpoint; the concrete controllers that override getAll() re-declare the same defaults. List endpoints therefore return no unbounded result sets. |
| W-14 Standardzugangsdaten ändern | In Progress | Authentication is centralized through KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs.. The KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. admin password is injected from a Kubernetes Secret (keycloak-admin-user) generated by the secrets component at installation time. Gaps: GeoServer runs on its factory-default administrator account (deployment#362), and FROST uses fixed default credentials (deployment#246). |
| W-15 Whitelisting für Eingabedaten | Done | Jackson ObjectMapper does not enable polymorphic default typing (enableDefaultTyping()/activateDefaultTyping() are not used), so JSON deserialization will not instantiate arbitrary classes from client-supplied type hints. Custom deserializers (e.g. UpstreamNodes.NodesDeserializer) validate JSON token types explicitly before deserializing. No XXE-vulnerable XML parsing. Mandated through the Backend Style Guide (section "Security"). See also W-10. |
| W-16 Whitelisting für URLs in Eingabedaten | In Progress | URL inputs are not restricted by an explicit scheme/host/port allowlist; MQTT connector and SQL connector DSN fields accept any syntactically valid URL. Tracked as a security finding via platform#1865. |
| W-17 Whitelisting für Allow-Origin | Not Applicable | Should be done at ingress and is thus responsibility of the operator. We recommend the following CORS settings: * Access-Control-Allow-Origin allows only specific domains, not . Access-Control-Allow-Methods contains only the HTTP verbs required by the use case (e.g. GET, POST). * Access-Control-Allow-Headers is defined explicitly, not through wildcards. * Access-Control-Allow-Credentials is only set when required by the use base. |
| W-18 Detaillierte Fehlermeldungen Produktivsystemen vermeiden | Done | This is mandated as part of the SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. (Section "3. Requirements you need to fulfill") |
| W-19 Minimale Auskunft über Komponenten | In Progress | Actuator endpoints are restricted to health, info, and prometheus (portal-backend/src/main/resources/application.yaml). No server header configured (Spring Boot default: no disclosure). Stack traces are not exposed (include-stacktrace: never in application.yaml; on_param appears only in the local development profile). AuthZ health endpoint: show-details: never. Mandated as part of SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. policy (section 3). Gap: the frontend does not disable the Next.js X-Powered-By response header — poweredByHeader: false is absent from portal-frontend/next.config.ts and the middleware does not remove it — so responses name the framework, though not its version; tracked via platform#1091. |
| W-20 Nicht benötigte HTTP-Verben deaktivieren | Done | HTTP verbs are restricted per endpoint at the OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. authorization layer: each backend path enumerates its allowed methods in the endpoint-mapping data (authz/rego/data/backends/portal_backend/data.json), and any method not listed for a path is denied as unknown_endpoint (fail-secure default-deny, authz/rego/policy/main.rego). Independently, the portal backend's Spring MVC controllers map only the verbs each endpoint uses, so any unmapped verb returns 405 by framework default. |
| W-21 Keine sensiblen Daten in URLs | Done | Tokens transmitted exclusively in Authorization headers (Bearer), never in URLs. Refresh tokens sent via POST body (application/x-www-form-urlencoded). Frontend proxy explicitly strips cookies from forwarded requests. API path parameters used only for UUIDs, never for tokens or passwords. This is mandated as part of the SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. (Section "3. Requirements you need to fulfill"). |
| W-22 Clientseitiges Caching und Autocomplete abschalten | In Progress | API requests from the portal-frontend carry Cache-Control: no-store, and credential inputs in the connector configuration (MQTT and SQL password fields) render with type="password" and autoComplete="new-password", suppressing browser autofill and credential storage. Mandated in the Frontend Style Guide (section "Security Principles"). Gap: authenticated HTML pages (platform#1668) and backend API responses (platform#1666) do not consistently set Cache-Control: no-store. |
| W-23 HSTS aktivieren | Not Applicable | This needs to be set at the egress and is thus responsibility of the Operator. |
| IoT-1 Zugang allen IoT-Ressourcen standardmäßig verweigern | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| IoT-2 Einheitlicher Zugriffskontrollmechanismus | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. h Security Architecture Principles |
| IoT-3 Authentifizierung und Autorisierung für IoT-Geräte | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| IoT-4 Standardzugangsdaten ändern | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| IoT-5 Device Onboarding | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| IoT-6 Generierung von Netzschlüsseln nach Zufallsprinzip | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. h Security Architecture Principles |
| IoT-7 Erneuerung Netzschlüssel | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| IoT-8 Replay-Schutz | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| IoT-9 Eindeutige Identifikation von IoT-Geräten | Not Applicable | Not applicable, as there is no direct traffic from IoT devices to CIVITAS/CORE. |
| VPN-1 VPN-Verschlüsselungsverfahren gemäß TR-02101-1 | Not Applicable | Not applicable, as VPN is assumed to be handled by the Operator at an infrastructure level. Document in a suitable "Product Scope" type document. |
| VPN-2 Sichere Konfiguration VPN-Gateway | Not Applicable | Not applicable, as VPN is assumed to be handled by the Operator at an infrastructure level. Document in a suitable "Product Scope" type document. |
| L-1 Logging von Sicherheitsereignissen | Done | Security event logging implemented across components: OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. decision logs enabled (JSON, console) with sensitive header masking via mask.regoRegoOPA's declarative policy language, used to express authorization rules such as checking role assignments at the most specific scope first.. KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. logs 68 event types (LOGIN, LOGIN_ERROR, admin events with details, account changes, token events, MFA events; 30-day retention). Backend SecurityExceptionHandler logs auth failures (401), access denied (403), and JWT validation failures at WARN level. Config-adapter logs connector lifecycle events. Frontend logs proxy auth events via Pino (JSON in production). See concept ticket. |
| L-2 Logging von fehlgeschlagenen Authentisierungsvorgängen und allen Authorisierungvorgängen | Done | Failed authentication logged by: KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. LOGIN_ERROR + all *_ERROR event types (68 event types configured). Backend SecurityExceptionHandler catches 401/403 at WARN level. OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. decision logs capture authorization denials with reason codes (default_deny, permission_denied, unknown_endpoint). OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. mask.regoRegoOPA's declarative policy language, used to express authorization rules such as checking role assignments at the most specific scope first. redacts tokens, authorization headers, and userinfo from decision logs. See concept ticket. |
| L-3 Schutz von Logs vor unbefugtem Zugriff | Done | All CIVITAS/CORE components write logs exclusively to stdout without storing them. OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. decision logs mask sensitive headers (tokens, authorization, userinfo) via mask.regoRegoOPA's declarative policy language, used to express authorization rules such as checking role assignments at the most specific scope first.. KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. stores events in its database with 30-day retention; that database is only accessible by the KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. service. Log storage, integrity, and access control are the operator's responsibility; the hardening guide documents minimum expectations. |
| L-4 Sensible Informationen nicht loggen | In Progress | Sensitive data masking is implemented across components: OWASP Encode (Encode.forJava()) wraps log arguments in portal-backend (including the security exception handler), config-adapter, and the authz repository (UserContextService with UUID masking); maskSensitiveFields() masks connector secrets; the frontend logs only an allow-list of request headers; OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. mask.rego masks tokens, authorization headers, and userinfo in decision logs. Mandated by the Backend Style Guide (section "Logging"). Gaps: the frontend logger (pino) has no redaction configured, and Zod validation errors log the rejected payload; the portal backend logs user e-mail addresses at startup (fixes: platform!897, platform!898). OPAOpen Policy AgentThe platform's authorization service: it evaluates 'who may do what' against the CIVITAS/CORE authorization model. OPA is the platform's central Policy Decision Point (PDP) for API authorization. decision logs do not mask the cookie header (platform!890). The AuthZ decision log emits the full subject UUID unmasked (platform#1672); the frontend logs a raw KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. error JSON (platform#1669) and has console.* calls that bypass the logger (platform#1670). |
| DH-1 Verschlüsselung At-Rest | Not Applicable | Not applicable, as this is considered the responsibility of the operator. CIVITAS/CORE assumes encryption to be handled transparently at a volume level. |
| DH-2 Schutz geheimer Schlüssel | Done | As per SSDLCSecure Software Development LifecycleCIVITAS/CORE's development process, focused on shipping a secure-by-default product, a transparent supply chain, and high-quality security documentation. (section 9) and Deployment Standards (Section "Security"), secrets are stored through Kubernetes secrets or other secure means external to the application. Exception: KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. stores secrets (passwords, signing keys) in database. |
| DH-3 IPC-Schnittstellen müssen geschützt sein | In Progress | Internal service-to-service (IPC) calls are protected at the transport layer by mandatory Linkerd mTLS (defaultInboundPolicy: cluster-authenticated) and per-component NetworkPolicies; endpoints such as the AuthZ Repository's user-context endpoint rely on this mesh-and-network-policy boundary as the intended control. Gap: the KafkaApache KafkaA distributed event streaming platform. In CIVITAS/CORE it is used as the message bus to transport events, models and data in data flows. message bus has no topic-level authorization — mTLS authenticates connections and NetworkPolicies gate reachability, but any pod able to reach the broker can produce to or consume from any topic. Tracked via platform#1294, platform#1637, and deployment#245. |
| DH-4 Data Governance-Vorgaben etablieren | Done | CIVITAS/CORE provides features through which organizations can implement data governance: * Fine-grained permission management * Separation of Data SourcesData sourceA data-related element that represents the origin of data. It defines how data is connected, accessed, and ingested into the Platform, such as an external database or sensor network. and Data Sets * Data Sets, Data SourcesData sourceA data-related element that represents the origin of data. It defines how data is connected, accessed, and ingested into the Platform, such as an external database or sensor network., and Data Models can carry custom metadata tags * Data provenance tracking via Data SourceData sourceA data-related element that represents the origin of data. It defines how data is connected, accessed, and ingested into the Platform, such as an external database or sensor network. as a first-class entity |
| ORG-6 Schwachstellenmanagement | Done | Vulnerability management follows the process agreed in platform#1473. Roles: the Security Architect triages findings, rates them by their impact on the product and hands them to the teams as prepared merge requests or tickets; the Security Engineer owns the automated guardrails; the Product Owner plans the work, accepts risks and decides in an emergency. Known vulnerabilities are found by nightly scans (GitLab SAST, Dependency Scanning, Secret Detection, Trivy container scanning) on security dashboards for the development and the release state; Renovate proposes dependency updates. The process sets internal targets by severity, an emergency path for vulnerabilities that are actively exploited, and a release gate on main. Accepted risks are recorded as dismissals with a reason in the GitLab Security Dashboard; accepting a High or Critical risk needs the Product Owner's approval. Third parties report vulnerabilities as described in Reporting Vulnerabilities. Not yet in place: the release-gate job and the enrichment with CISA KEV and EPSS data. |
| ORG-7 Tests nur auf dedizierter Umgebung | Not Applicable | Neither Civitas Connect nor any of the members of the project team provide a production environment. Therefore this requirement is not applicable. All tests are run on dedicated environments with synthetic data. The requirement for synthetic data is part of the Security Architecture Principles. |
| ORG-8 Penetrationstests | Done | A penetration test of CIVITAS/CORE v2 (version 2.0-beta) was carried out by an external provider. Its findings go through the vulnerability management process (see ORG-6). |
Generated on 01 October 2026, 06:49 UTC from https://gitlab.com/civitas-connect/civitas-core/requirements (labels: requirement-source::tr-03187, tr-level::1).