# Risk context for the distributed Spring PetClinic (spring-petclinic-microservices).
#
# This file lives at `.k9security/risk-context.yaml` in the repository. It is
# the input YOU write: it records how critical this system is, where its code
# actually runs, who is trusted, and which risks this deployment carries
# deliberately. k9 reads it when scoring dependency alerts and reports which
# revision of it produced the verdicts.
#
# THE THREE EXECUTION CONTEXTS FOLLOW THE DEPLOYMENT, NOT THE MODULE LIST.
# Eight Maven modules deploy into three network positions: the internet-facing
# api-gateway, the four business services behind it (customers, vets, visits,
# genai), and the three platform services (config, discovery, admin). Network
# position and input decide what a verdict means, so those are the contexts.
#
# TEST-SCOPE DEPENDENCIES BIND TO THE RUNTIME CONTEXTS, because a Maven
# module's test dependencies share its pom.xml with the runtime ones, so
# `paths` cannot split them into a separate build context. Each context's
# `applies_to` prose tells the agent to score them as not loaded in
# production instead, and the report's metadata appendix records how they
# were scored.
schema_version: "1.0"
project:
  name: spring-petclinic-microservices
  summary: >-
    Veterinary-clinic SaaS built from the Spring PetClinic microservices sample:
    an internet-facing API gateway (Spring Cloud Gateway + AngularJS UI) in front
    of customers, vets, visits and GenAI (LLM chatbot) services, plus Spring Cloud
    config, Eureka discovery and Spring Boot Admin servers. Java 17, Maven
    multi-module, one Spring Boot fat-jar container per service.
  open_source: true
reviewed:
  date: 2026-09-02
  by: skuenzli@k9security.io
  commit: 3858f9c630cf989bb6809a86edf47c2be78dc9f1
execution_contexts:
  - id: runtime-gateway
    default: true
    applies_to: >-
      Dependencies that execute in the deployed api-gateway: the server-side
      Spring Cloud Gateway / WebFlux / Reactor Netty request path, and the
      webjars (AngularJS, angular-ui-router, Bootstrap, marked, font-awesome)
      shipped to customers' browsers by this module. The module pom also
      declares test-scoped dependencies (JUnit, OkHttp, MockWebServer,
      spring-boot-starter-test); they share this pom and therefore bind here,
      but they never load in the deployed service — score them as not loaded
      in production (Maven scope `test`; see the operator's deps-gateway.txt). Alerts against the root `/pom.xml`
      (which only manages versions shared by every module) fall here by
      default; re-bind them to the module that actually consumes the
      dependency before scoring.
    frameworks:
      - name: Spring Cloud Gateway (WebFlux, Reactor Netty)
        roles: [web, api]
      - name: AngularJS 1.x
        roles: [browser-ui]
    paths:
      include:
        - "/spring-petclinic-api-gateway/pom.xml"
        - "/spring-petclinic-api-gateway/src/main/**"
      exclude:
        - "/vendor/**"
        - "/target/**"
    dependency_sources: ["/vendor"]
    asset_context:
      asset_criticality: tier_2
      network_exposure: public_unauth
      lifecycle: production
    deployment: |
      Operator-stated: the only internet-facing service, deployed exactly as
      shipped with no Spring Security; anonymous customers reach every route
      it exposes; deployed on Reactor Netty (the only server in its tree).
      Operator-stated (2026-09-03): the gateway NORMALISES HTTP/1.1 framing on
      the hop to the backends — it decodes each request into HTTP objects
      (chunk extensions dropped) and its Reactor Netty client re-encodes the
      forwarded request with framing it generates; only the BODY bytes pass
      through verbatim. Backend framing-level parsers (Tomcat chunk/TE/CL
      handling) therefore never see the client's raw framing; body-level
      parsers (multipart, JSON) do. Inferred from the repository: it serves the AngularJS UI
      from /, an aggregation endpoint at /api/gateway/owners/{id}, and proxies
      /api/customer/**, /api/vet/**, /api/visit/** and /api/genai/** to the
      internal services (StripPrefix=2), so request paths, query strings and
      JSON bodies from anonymous users are forwarded unchanged. Runs as a
      Spring Boot fat jar in an eclipse-temurin:17 container (docker/Dockerfile,
      profile `docker`), port 8080. The browser UI renders chatbot replies
      with marked.parse() into innerHTML (static/scripts/genai/chat.js).
      PINNED 2026-09-02 from the shipped configuration (the upstream
      spring-petclinic-microservices-config repository at 323993c, which
      config-server serves as deployed): server.http2.enabled is not set
      anywhere, so HTTP/2 is OFF (Spring Boot default) and this service speaks
      HTTP/1.1 only; server.compression.enabled is TRUE on this service only
      (api-gateway.yml: mime-types application/json,text/css,
      application/javascript, min-response-size 2048) — that is RESPONSE
      compression; inbound request decompression is not configured;
      management.endpoints.web.exposure.include is '*' (application.yml), so
      EVERY actuator endpoint (env, heapdump, threaddump, loggers, ...) is
      served by this internet-facing, unauthenticated service. Jolokia is on
      the classpath. HTTP CLIENTS USE DEFAULT CONFIGURATION (operator, pinned
      2026-09-02): the Spring Cloud Gateway proxy client has
      spring.cloud.gateway.httpclient.compression unset → false, so the
      Reactor Netty client that forwards anonymous traffic to the backends
      never installs a response decompressor; the separate Spring WebClient
      used by the owner-aggregation endpoint is built through Spring's
      Reactor connector, whose default is compress(true), so THAT client does
      install HttpContentDecompressor — its input is backend responses from
      cluster-internal services, which do not compress. Data handled: pet-owner records (names,
      addresses, phone numbers) pass through this service; the operator has
      not classified this data, so data_classification is left unset.
  - id: runtime-services
    applies_to: >-
      Dependencies that execute in the deployed customers-service,
      vets-service, visits-service and genai-service: Spring MVC / WebFlux
      request handling, JPA/Hibernate, the MySQL driver, Spring AI and the
      OpenAI client. Each module pom also declares test-scoped dependencies
      (JUnit, AssertJ, spring-boot-starter-test, webmvc-test); they share the
      pom and therefore bind here, but they never load in the deployed
      service — score them as not loaded in production (Maven scope `test`).
      chaos-monkey-spring-boot is on the classpath but only activates under
      the `chaos-monkey` Spring profile (scripts/run_all.sh, local use);
      HSQLDB is on the classpath but the operator runs the `mysql` profile.
    frameworks:
      - name: Spring Boot 4 / Spring MVC
        roles: [api]
      - name: Spring Data JPA / Hibernate (MySQL)
        roles: [api]
      - name: Spring AI (OpenAI chat client with tool calling)
        roles: [api]
    paths:
      include:
        - "/spring-petclinic-customers-service/pom.xml"
        - "/spring-petclinic-customers-service/src/main/**"
        - "/spring-petclinic-vets-service/pom.xml"
        - "/spring-petclinic-vets-service/src/main/**"
        - "/spring-petclinic-visits-service/pom.xml"
        - "/spring-petclinic-visits-service/src/main/**"
        - "/spring-petclinic-genai-service/pom.xml"
        - "/spring-petclinic-genai-service/src/main/**"
      exclude:
        - "/vendor/**"
        - "/target/**"
    dependency_sources: ["/vendor"]
    asset_context:
      asset_criticality: tier_2
      network_exposure: internal
      lifecycle: production
    deployment: |
      Operator-stated: listen only on the cluster network behind the gateway;
      MySQL profile in production; genai-service enabled with a vendor LLM API
      key (supplied by environment variable). Operator-stated (2026-09-03):
      every service is deployed on Reactor Netty WHERE IT IS AVAILABLE — so
      genai-service, whose tree carries both Reactor Netty and Tomcat, runs
      Reactor Netty (its application.yml sets web-application-type: reactive;
      Tomcat and Spring MVC are on its classpath but never constructed);
      customers, vets and visits carry only Tomcat and run Tomcat.
      PINNED 2026-09-02 from the
      shipped configuration (upstream config repo at 323993c): HTTP/2 OFF on
      every service (server.http2.enabled never set), server.compression
      NOT enabled on any of these four services (only api-gateway sets it),
      and management.endpoints.web.exposure.include is '*' — every actuator
      endpoint is served on the cluster network without authentication.
      HTTP CLIENTS USE DEFAULT CONFIGURATION (operator, pinned 2026-09-02):
      genai-service's load-balanced WebClient (Spring's Reactor connector,
      default compress(true) → HttpContentDecompressor installed) talks to
      customers/vets services, which do not compress; its plain
      RestClient.builder().build() resolves to Apache HttpClient 5 by
      classpath detection (httpclient5 is present), with that library's
      default Content-Encoding handling; the Spring AI OpenAI client talks to
      the LLM vendor over Spring's default client stack. No client anywhere
      is customised. Inferred: although not internet-routable, every
      request body, path and query an anonymous customer sends to the gateway
      is forwarded here without authentication, so treat these services'
      HTTP input as attacker-controlled. customers-service creates/updates
      owners and pets; visits-service creates visits; vets-service is
      read-only with a Caffeine cache. genai-service accepts a free-text chat
      prompt at POST /chatclient, sends it to the LLM with 10-message memory,
      and lets the LLM call tools that list owners, add owners and add pets
      via customers-service and vets-service (PetclinicTools); it loads a
      pre-embedded vectorstore.json at startup. Each service runs as a Spring
      Boot fat jar in an eclipse-temurin:17 container. Schema and data
      initialisation (db/mysql/schema.sql, data.sql) run at startup per the
      README. Data stored: owner names, addresses, cities, telephone numbers,
      pets and visit descriptions; the operator has not classified this data,
      so data_classification is left unset.
  - id: runtime-platform
    applies_to: >-
      Dependencies that execute in the deployed config-server (Spring Cloud
      Config), discovery-server (Eureka) and admin-server (Spring Boot Admin
      with Jolokia). The config-server and discovery-server module poms also
      declare test-scoped dependencies; they bind here because they share the
      pom, but they never load in the deployed service — score them as not
      loaded in production (Maven scope `test`).
    frameworks:
      - name: Spring Cloud Config Server
        roles: [api]
      - name: Spring Cloud Netflix Eureka Server
        roles: [api]
      - name: Spring Boot Admin Server
        roles: [web]
    paths:
      include:
        - "/spring-petclinic-config-server/pom.xml"
        - "/spring-petclinic-config-server/src/main/**"
        - "/spring-petclinic-discovery-server/pom.xml"
        - "/spring-petclinic-discovery-server/src/main/**"
        - "/spring-petclinic-admin-server/pom.xml"
        - "/spring-petclinic-admin-server/src/main/**"
      exclude:
        - "/vendor/**"
        - "/target/**"
    dependency_sources: ["/vendor"]
    asset_context:
      asset_criticality: tier_2
      network_exposure: internal
      lifecycle: production
    deployment: |
      Operator-stated: listen only on the cluster network behind the gateway;
      deployed on Reactor Netty where available — admin-server (Spring Boot
      Admin, WebFlux) runs Reactor Netty; config-server and discovery-server
      carry only Tomcat and run Tomcat. Inferred: the gateway routes none of
      its paths to these services, so
      customer input does not reach them directly; their clients are the
      other services in the cluster. config-server pulls application
      configuration at runtime from the public Git repository
      https://github.com/spring-petclinic/spring-petclinic-microservices-config
      (branch main) unless the `native` profile points it at a local
      directory. PINNED 2026-09-02: production uses the GIT backend exactly as
      shipped (the Dockerfile activates only the `docker` profile; `native` is
      never enabled), against the upstream repository at commit 323993c
      (2026-05-02), so that external repository controls every service's
      runtime configuration. Its values that decide verdicts here: HTTP/2 off
      everywhere, response compression on api-gateway only, actuator
      exposure '*' everywhere. HTTP/2 OFF and compression OFF on these three
      services too. discovery-server serves the Eureka registry and dashboard on
      8761. admin-server (Spring Boot Admin) discovers every registered
      service and offers management of their actuator endpoints, with Jolokia
      (JMX over HTTP) on the classpath. No login is configured on any of
      these in this repository. Each runs as a Spring Boot fat jar in an
      eclipse-temurin:17 container.
trust_model:
  untrusted_actors:
    - id: anonymous-internet-user
      description: >-
        Anyone on the internet. Reaches every gateway route without logging
        in and controls the full HTTP request: paths, query strings and JSON
        bodies for owner/pet/visit create, update and read; the free-text
        chatbot prompt, which the LLM may turn into tool calls that create
        owners and pets; and, through the chatbot reply rendered as Markdown
        in the browser, content shown to other users of the same browser
        session. Operator-stated exposure; controlled inputs inferred from
        the gateway routes and controllers.
  trusted_actors:
    - id: operator
      description: >-
        The small operator running the service. Builds and deploys the
        containers, holds the LLM API key and database credentials, and has
        access to the cluster network (actuator, Eureka, Spring Boot Admin,
        config-server).
    - id: upstream-config-repository
      description: >-
        Maintainers of the public spring-petclinic-microservices-config Git
        repository, which config-server pulls at runtime as shipped. Listed
        as trusted because the deployment relies on it. PINNED 2026-09-02:
        production uses that repository (git backend, branch main, commit
        323993c at review time) — not a fork and not the `native` backend.
  out_of_scope_actors: []
accepted_risks: []
