Pass 02: Zustandsanalyse (STATE)
| Eigenschaft | Wert |
|---|---|
| Pass-Nummer | 02 |
| Schlüssel | state |
| Pass-Name in der Ausgabe | state_analysis |
| Signal-Präfix | STATE |
| Typ | Deterministisch (Regel-Engine), kein LLM |
| Standard | Aktiv |
| Assessment-Typen | application; llm (innerhalb jedes Legs). Bei service übersprungen (ohne Quellcode keine Zustandsanalyse). Läuft nicht bei landing-zone-catalog. |
| Lizenzstufe | Nicht eingeschränkt (ab Community) |
Wofür der Pass gedacht ist
Zustandslose Komponenten lassen sich durch erneutes Deployment umziehen. Zustandsbehaftete Komponenten (Datenbanken, Queues, Session-Speicher) erfordern eine Datenmigration, einen Umstellungsplan und Aufmerksamkeit für Wiederherstellungsziele. Pass 02 beantwortet die Frage: „Welchen Zustand hält diese Anwendung, und wo liegt der Single Point of Failure?“
Er ermittelt die primäre Datenbank, die Nutzung von Redis als Cache und Job-Queue, das Session-Modell (zustandslose Tokens oder serverseitige Sessions) und ob die Bereitstellungstopologie ein Einzelknoten-Setup ist, das Anforderungen an die operative Resilienz wie DORA widerspricht. Für JVM-Dienste (Java, Kotlin, Spring Boot) findet er zusätzlich die relationale Datenbank (JDBC und JPA), serverseitige HTTP-Sessions, Spring-Session-Speicher, prozessinterne Caches und statischen veränderlichen Zustand (einschließlich Zustand in Kotlin-object-Deklarationen) sowie Session-Affinität (Sticky Sessions).
Was Sie bereitstellen müssen
- Den Quellcode der Anwendung (
source.path,source.vcs.urloder--source-path). - Der Pass liest:
package.jsonundprisma/schema.prismaim Wurzelverzeichnis,docker-compose.ymlim Wurzelverzeichnis oder indocker/sowie die Verzeichnissek8s/undhelm/im Wurzelverzeichnis. Für JVM-Dienste liest er zusätzlich über den zentralen Quellcode-Scope (Build-Ausgaben wietarget/ausgenommen): JVM-Quelldateien (.java,.kt,.kts,.groovy,.scala), Spring-Konfiguration (application*.yml|yaml|properties,bootstrap*, beliebige.properties),pom.xml/build.gradle(.kts)(mit ihren nach Scope aufgelösten Abhängigkeiten) sowie YAML-,.conf-,.cfg-,.xml- und.toml-Dateien für die Session-Affinität und Datenbank-URLs. Dateien einer Android-App (Mobilanwendung) werden übersprungen: alles unterhalb eines Ordnersandroid/(React Native, Flutter, Capacitor), Android-Module mit einerAndroidManifest.xmlund Build-Dateien, die das Android-Gradle-Plugin anwenden. Ein Repository, dessen einzige Java- oder Kotlin-Dateien zu einer solchen App gehören, behält daher die Prüfungen überpackage.json.
Es werden weder Zugangsdaten noch ein LLM benötigt.
Wie der Pass arbeitet
Primäre Datenbank. Existiert
prisma/schema.prisma, wird der Wert vonproviderals Datenbank-Engine übernommen und die Prisma-Version auspackage.jsongelesen. Ist nur der Prisma-Client als Abhängigkeit vorhanden, meldet der Pass, dass der Datenbanktyp nicht bestätigt werden kann. Ohne Prisma ermittelt ein JVM-Dienst seine Datenbank über die JDBC- und JPA-Muster aus Schritt 5 (#3485).Cache und Queue. Redis wird über
@keyv/redis,ioredisoderrediserkannt, eine Job-Queue überbull,@nestjs/bulloderbullmq. Läuft eine Queue auf Redis, wird Redis-Persistenz (AOF plus RDB) als erforderlich markiert.Session-Modell.
@nestjs/jwt,jsonwebtokenoderjoseohne Session-Bibliothek bedeutet zustandslose JWT-Authentifizierung.express-sessionoderkoa-sessionbedeutet serverseitige Sessions, die als zustandsbehaftete Komponente migriert werden müssen.Topologie.
k8s/oderhelm/im Wurzelverzeichnis bedeutet Kubernetes mit horizontaler Skalierung. Einedocker-compose.ymlim Wurzelverzeichnis oder indocker/ohne Kubernetes-Manifeste gilt als Einzelknoten-Compose-Bereitstellung und damit als Single Point of Failure.JVM-Session- und Zustandsmuster (#3422). Liegt eine JVM-Build-Datei oder JVM-Quelldatei im Scope, gleicht der Pass deterministische Muster zeilenweise ab (gemeinsamer Katalog-Matcher, Bezeichnergrenzen, lineare reguläre Ausdrücke) und liest Spring-Konfiguration über ihren gepunkteten Schlüssel (
spring.session.store-type, auch in verschachtelter YAML-Form):Befund Erkannt an Container-Sessions (im Speicher) HttpSession,.getSession(),@SessionAttributes,@SessionAttribute,@SessionScope,@Scope("session"),SCOPE_SESSION; Einstellungenserver.servlet.session.*/server.reactive.session.*Spring Session (externer Speicher) @EnableRedisHttpSession,@EnableJdbcHttpSession,@EnableMongoHttpSession,@EnableHazelcastHttpSessionund ähnliche; Schlüsselspring.session.*;spring-session-data-redis,spring-session-jdbcund ähnliche in der Build-DateiZustandslose Authentifizierung SessionCreationPolicy.STATELESS,.oauth2ResourceServer(,JwtDecoder,io.jsonwebtoken,com.auth0.jwtRedis RedisConnectionFactory,LettuceConnectionFactory,RedisTemplateund ähnliche;spring.redis.host,spring.data.redis.host(auchurl,cluster.nodes); der Redis-Starter in der Build-DateiCaches im Speicher ConcurrentMapCacheManager,CaffeineCacheManager,Caffeine.newBuilder, GuavaCacheBuilder.newBuilder, Ehcache;spring.cache.typesimple,caffeine,jcacheoderehcacheStatischer veränderlicher Zustand static-Feld eines veränderlichen JDK-Collection-Typs, das mitnewinitialisiert wird (static final Map<..> X = new ConcurrentHashMap<>()); unveränderlicheMap.of/List.ofzählen nichtZustand in Kotlin-Objekten (#3485) In .kt-Dateien ein direktes Mitglied einesobjectodercompanion objectoder eine Deklaration auf oberster Ebene, die einvarist oder einvalmit einer veränderlichen Collection oder einem Atomic (mutableMapOf,MutableList<..>,ConcurrentHashMap,AtomicIntegerund ähnliche); erfasst wird nur der Deklarationskopf (val handlers)Session-Affinität nginx.ingress.kubernetes.io/affinity,session-cookie-name,sessionAffinity: ClientIP,stickiness.enabled=true,stickysession,jvmRoute, Traefikloadbalancer.stickyJDBC (#3485) spring.datasource.url(auchspring.datasource.hikari.jdbc-url,spring.r2dbc.url,spring.flyway.url,spring.liquibase.url) undspring.datasource.driver-class-name; eine URLjdbc:<engine>:unter beliebigem Schlüssel in YAML,.propertiesoder XML (Helm-Values, Datasources von Anwendungsservern);JdbcTemplate,NamedParameterJdbcTemplate,JdbcClient,HikariDataSource,DataSourceBuilder,DriverManager.getConnection; JDBC-Starter und -Treiber im Build (PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, Db2, H2, HSQLDB, Derby, SQLite), ohne Test-ScopeJPA (#3485) @Entity,@EnableJpaRepositories,@PersistenceContext,EntityManager,JpaRepository<..>,JpaSpecificationExecutor<..>,R2dbcRepository<..>; Einstellungenspring.jpa.*;persistence.xml;hibernate.dialect;spring-boot-starter-data-jpa,spring-data-jpaundhibernate-coreim BuildDie Datenbank-Engine ergibt sich nur aus Treffern im Produktivcode: aus dem Subprotokoll der JDBC-URL, der Treiberklasse, dem Hibernate-Dialekt oder
spring.jpa.databaseoder aus einer Treiberabhängigkeit. Eine Fundstelle mit JDBC-URL erfasst nur das Präfixjdbc:<engine>, eine Konfigurationsfundstelle nur ihren Schlüssel; Hosts und Zugangsdaten gelangen daher nie in die Pass-Ausgabe.Das Session-Modell ergibt sich nur aus Treffern im Produktivcode: Spring Session ergibt
server_session_external, Container-Sessionsserver_session, zustandslose Authentifizierung ohne Session-Nutzungjwt_statelessund keines davonnone_detected(die Spring-Boot-Basislinie). Treffer im Testcode werden getrennt gezählt und im Signal genannt; unterassessment.test_code.mode: excludewerden Testdateien gar nicht gelesen.
Wie gemessen wird
| Signal | Wird erzeugt, wenn | Schweregrad | Bedeutung |
|---|---|---|---|
STATE-01 | Prisma-Schema vorhanden oder Prisma-Client in den Abhängigkeiten; ohne Prisma ein JVM-Dienst mit JDBC- oder JPA-Treffern im Produktivcode | informational | Primärer zustandsbehafteter Speicher und ORM (Konfidenz hoch mit Schema, mittel ohne). Bei JVM-Diensten: die Engine (eingebettete H2, HSQLDB, Derby oder SQLite als prozesslokaler Zustand gekennzeichnet), der Datenzugriff (JPA oder JDBC) und Evidenz als Datei:Zeile; Konfidenz hoch, wenn genau eine Server-Engine genannt ist, sonst mittel. Treffer nur im Testcode erzeugen kein Signal |
STATE-02 | Redis- oder Bull/BullMQ-Abhängigkeit gefunden; bei JVM-Diensten eine Spring-Data-Redis-Verbindung oder -Konfiguration | medium (Queue auf Redis oder Spring Session in Redis), low (nur Cache) | Queue-Jobs (bzw. aktive Sessions) gehen bei einem Redis-Neustart verloren, sofern keine AOF-plus-RDB-Persistenz aktiv ist; ein Risiko für das Recovery Point Objective (RPO) |
STATE-03 | Immer | informational | Session-Modell: jwt_stateless, server_session oder unknown; bei JVM-Diensten server_session_external, server_session, jwt_stateless oder none_detected, mit Evidenz als Datei:Zeile (Konfidenz hoch, mittel bei none_detected) |
STATE-04 | Immer | high (Einzelknoten-Compose), informational (Kubernetes oder unbekannt) | Bewertung des Single Point of Failure im Hinblick auf Resilienzanforderungen |
STATE-05 | JVM-Dienst mit Caches im Speicher, statischen veränderlichen Collections oder Zustand in Kotlin-Objekten | low (informational bei Vorkommen nur im Testcode) | Jede Replik hält ihre eigene Kopie: Caches laufen auseinander und statischer Zustand geht beim Neustart verloren; horizontale Skalierung braucht einen gemeinsamen Cache oder die Bestätigung, dass der Zustand eine reine Nachschlagetabelle ist |
STATE-06 | JVM-Dienst mit konfigurierter Session-Affinität | medium (informational bei Vorkommen nur im Testcode) | Instanzen sind nicht austauschbar; fällt ein Knoten aus, gehen die an ihn gebundenen Sessions verloren |
Die Outcomes folgen dem Schweregrad: informational-Signale sind neutral, Signale von low bis high sind negative.
Wie die Ergebnisse in spätere Passes einfließen. Pass 01 (INV) verweist über seine implies-Tags auf STATE-02 und STATE-04. STATE-04 mit hohem Schweregrad ist ein typischer Input für Resilienz-Controls, die Pass 11 (COMP) bewertet, wenn Frameworks wie DORA in assessment.regimes_active aktiv sind, sowie für die 7R-Bewertung in Pass 09 (SYNTH). Signale mit hohem Schweregrad werden außerdem zu Einträgen in den abgeleiteten Findings und im Risikoregister.
Der assessment-Block enthält primary_db, orm, cache_engine, queue_engine, redis_persistence_configured (immer false, da der Pass keine Laufzeitkonfiguration sieht), session_type, deployment_type (kubernetes, single_node_compose, unknown), spof_risk und eine Liste stateful_components (bei JVM-Diensten mit Spring Session zusätzlich ein Eintrag session_store und bei JDBC oder JPA ein Eintrag database). Für eine JVM-Datenbank ist primary_db eine Engine im Vokabular der Prisma-Provider (postgresql, mysql, sqlserver, sqlite, sonst mariadb, oracle, db2, h2, hsqldb, derby) oder unknown, wenn keine oder mehrere Server-Engines genannt sind; orm ist jpa oder jdbc, und database_engines listet alle gefundenen Engines sortiert auf. JVM-Dienste ergänzen jvm_state: spring_boot, stack_evidence, jvm_source_files, spring_config_files, android_files_excluded (nur wenn Dateien einer Android-App übersprungen wurden) und je Befund (http_session, spring_session, servlet_session_config, stateless_auth, in_memory_cache, static_mutable_state, redis, sticky_session, jdbc, jpa, kotlin_object_state) total, production, test, unclassified, stores (Spring-Session-Speicher bzw. die Datenbank-Engines von jdbc / jpa) und bis zu 20 locations (Datei, Zeile, Pfadklasse), Produktivcode zuerst.
Drittanbieter-Module und Werkzeuge
Keine. Der Pass nutzt eingebautes Dateilesen, JSON-Parsing, reguläre Ausdrücke und für die JVM-Muster den gemeinsamen Katalog-Matcher (catalogue-matcher.ts).
Ausgaben
wsp/runs/<ts>/passes/02-state.yamlmit Kopf (id: 2,name: state_analysis,signal_prefix: STATE), Signalen und demassessment-Block.
Konfiguration und Einschränkungen
Konfiguration
- Schlüssel für den Quellcode-Ort wie bei Pass 01;
--passes stateführt den Pass allein aus.
Einschränkungen und bekannte Lücken
- Datenbanken werden über Prisma und bei JVM-Diensten über JDBC und JPA erkannt. Python-, .NET- und Go-Dienste sind nicht abgedeckt, und Node.js-Datenbank-Clients außer Prisma (etwa
pg,mysql2, TypeORM) erzeugen keinSTATE-01. Nicht relationale Speicher von JVM-Diensten (MongoDB, Cassandra, Elasticsearch) werden nicht als primäre Datenbank gemeldet; generische Spring-Data-Repositories (CrudRepository) zählen nicht als JPA, weil Redis und MongoDB sie ebenfalls nutzen. - Eine Datenbank-URL, die nur über eine Umgebungsvariable oder einen Platzhalter (
${DB_URL}) gesetzt wird, nennt keine Engine; ohne Treiberklasse, Dialekt oder Treiberabhängigkeit ist die Engineunknown. Werden mehrere Server-Engines gefunden (etwa eine Reporting-Datenbank neben der Hauptdatenbank), istprimary_dbunknownunddatabase_engineslistet sie auf; der Pass entscheidet nicht, welche die primäre ist. - Die JVM-Muster lesen Quelltext, nicht die Laufzeitkonfiguration: Eine Session, die eine Bibliothek startet oder die über Umgebungsvariablen gesetzt wird, bleibt unsichtbar, und
none_detectedist ein Musterergebnis, kein Nachweis von Zustandslosigkeit. Zustand in Kotlin-Objekten wird über die Einrückung erkannt: Mitglieder eines einzeilig deklarierten Objekts (object X { var y = 0 }) und ungewöhnliche Formatierung bleiben unsichtbar, und einvar, das nur beim Start geschrieben wird, zählt trotzdem. - Der Android-Ausschluss beruht auf Pfaden und Build-Dateien: Java- oder Kotlin-Servercode, der in einem Ordner
android/liegt, wird ebenfalls übersprungen. STATE-05undSTATE-06werden nur für JVM-Dienste erzeugt; Session-Affinität in einem Node.js-Repository wird nicht gemeldet.STATE-04prüft nur zwei feste Compose-Pfade (docker-compose.ymlunddocker/docker-compose.yml). Pass 01 sucht drei Verzeichnisebenen tief und nach mehr Dateinamensmustern; die beiden Passes können daher zu unterschiedlichen Ergebnissen kommen, ob Compose vorhanden ist.- Redis-Persistenzeinstellungen werden nicht gelesen;
redis_persistence_configuredist immerfalse. - Objektspeicher, Dateifreigaben und andere zustandsbehaftete Infrastruktur deckt die Diensterkennung von Pass 01 ab, nicht dieser Pass.
