Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosGoogle Cloud Tech: Gemini is coming to your city(24.09.2026 um 15:00 Uhr)
AI & KI NachrichtenGoogle’s latest moonshot to put machine learning in space(24.09.2026 um 15:12 Uhr)
Windows Tipps & SecurityPoll: What's your favorite Surface of 2026?(24.09.2026 um 14:58 Uhr)
Sichere ProgrammierungStreaming Materialized Views for Live Read Models (2026)(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA Day Is Not 86400 Seconds: The DST Bug in Your Date Math(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungSetting up Traefik: reverse proxy with automatic HTTPS(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA 200 OK response does not prove a secret leak(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungHow hot do you like it?(24.09.2026 um 15:05 Uhr)
YouTube Security VideosGoogle Cloud Tech: Gemini is coming to your city(24.09.2026 um 15:00 Uhr)
AI & KI NachrichtenGoogle’s latest moonshot to put machine learning in space(24.09.2026 um 15:12 Uhr)
Windows Tipps & SecurityPoll: What's your favorite Surface of 2026?(24.09.2026 um 14:58 Uhr)
Sichere ProgrammierungStreaming Materialized Views for Live Read Models (2026)(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA Day Is Not 86400 Seconds: The DST Bug in Your Date Math(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungSetting up Traefik: reverse proxy with automatic HTTPS(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA 200 OK response does not prove a secret leak(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungHow hot do you like it?(24.09.2026 um 15:05 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Injecting Initial State into ADK Web UI with ASGI Middleware

If you're using Google's ADK Web UI for agent development, you've probably run into this: every time you create a new session, you have to manually set up the state your agent needs. Here's a clean way to automate that with a preset file…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

If you're using Google's ADK Web UI for agent development, you've probably run into this: every time you create a new session, you have to manually set up the state your agent needs. Here's a clean way to automate that with a preset file and ASGI middleware.






The Problem



ADK's Web UI is great for testing agents, but injecting initial state into a session is a bit of a hassle.



The current options are:





  • Add state via the 3-dot menu in the UI → You have to manually enter values every time you create a session


  • Use before_agent_callback with a first-run check → Extra code to write and maintain



Instead, I built a setup where you define initial state in a preset.yaml file, and a custom ASGI middleware automatically merges it into the session when you click "New Session" in the Web UI.






How It Works



When you click "New Session" in the Web UI, it internally calls POST /apps/{app_name}/users/{user_id}/sessions.



The middleware intercepts this request, reads preset.yaml, and merges its initial_state values into the request body's state before passing it to the application.



sequenceDiagram



The key point: you don't need to touch your agent code at all — just add the middleware at server startup.






Setup






Directory Structure






.
├── .venv
├── hello_agent
│ ├── __init__.py
│ ├── agent.py # Agent definition
│ └── preset.yaml # Preset config
└── preset_server.py # Startup script









Target Agent






from google.adk.agents.llm_agent import Agent

root_agent = Agent(
model='gemini-2.5-flash',
name='root_agent',
instruction="""Greet the user in the following format:
Hello, it
's currently {current_time} and the weather is {weather}.
""",
)






Without initial state, chatting with this agent in the Web UI will fail because current_time and weather don't exist in the session state.






Dependencies



You'll need pyyaml in addition to ADK's dependencies:




pip install pyyaml









Preset File (preset.yaml)



Define your initial state in YAML format:




initial_state:
current_time: "10:30 AM"
weather: "sunny"






Nested values are supported too.



Place the file in the same directory as agent.py, or inside an .adk/ directory. If both exist, .adk/preset.yaml takes priority.






Startup Script Implementation



The startup script has three main parts.






1. Loading preset.yaml






PRESET_FILENAME = "preset.yaml"

def find_preset_file(agent_dir: Path) -> Optional[Path]:
"""Search for preset.yaml in the agent directory."""
candidates = [
agent_dir / ".adk" / PRESET_FILENAME,
agent_dir / PRESET_FILENAME,
]
for p in candidates:
if p.is_file():
return p
return None

def load_preset(path: Path) -> dict:
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f) or {}
return {
"state": data.get("initial_state", {}),
}






It checks .adk/preset.yaml first, then falls back to preset.yaml, and returns the initial_state as a dictionary.






2. The Middleware (PresetMiddleware)



This ASGI middleware detects session creation requests and rewrites the body:




SESSION_PATH = re.compile(r"^/apps/([^/]+)/users/[^/]+/sessions$")

class PresetMiddleware:
def __init__(self, app, agents_dir: Path):
self.app = app
self.agents_dir = agents_dir

async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return

path = scope.get("path", "")
method = scope.get("method", "")
m = SESSION_PATH.match(path)

if method == "POST" and m:
app_name = m.group(1)
preset = self._load_preset(app_name)
if preset and preset.get("state"):
# Read request body
body_parts = []
while True:
message = await receive()
body_parts.append(message.get("body", b""))
if not message.get("more_body", False):
break
body = b"".join(body_parts)

# Merge state (preset as base, request values override)
data = json.loads(body) if body else {}
merged = {**preset["state"], **(data.get("state") or {})}
data["state"] = merged
new_body = json.dumps(data).encode()

# Update Content-Length and forward
# ... (header rewriting and receive replacement)
await self.app(scope, new_receive, send)
return

await self.app(scope, receive, send)






Here's what it does:




  1. Only targets POST /apps/{app_name}/users/{user_id}/sessions requests

  2. Reads the request body and merges initial_state from preset.yaml into state

  3. Updates Content-Length with the new body size and passes it to the application



Since request-side state takes priority, any values you manually set in the UI won't be overwritten by the preset.






3. CLI (adk web compatible)



The script supports all the same options as adk web, plus a --preset / --no-preset flag to toggle the middleware:




app = get_fast_api_app(
agents_dir=agents_dir,
# ... same options as adk web
)

if preset:
app.add_middleware(PresetMiddleware, agents_dir=agents_dir_path)









4. Full Startup Script




Full startup script




from __future__ import annotations
import sys
import json
import logging
import os
import re
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Optional

import click
import uvicorn
from fastapi import FastAPI

from google.adk.cli.fast_api import get_fast_api_app
from google.adk.cli.utils import logs

try:
import yaml
except ImportError:
print("PyYAML is required. Install with: pip install pyyaml")
sys.exit(1)

logger = logging.getLogger(__name__)

LOG_LEVELS = click.Choice(
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
case_sensitive=False,
)

SESSION_PATH = re.compile(r"^/apps/([^/]+)/users/[^/]+/sessions$")


# ---------------------------------------------------------------------------
# Preset file handling
# ---------------------------------------------------------------------------
PRESET_FILENAME = "preset.yaml"

def find_preset_file(agent_dir: Path) -> Optional[Path]:
"""Search for preset.yaml in .adk/ directory of the agent."""
candidates = [
agent_dir / ".adk" / PRESET_FILENAME,
agent_dir / PRESET_FILENAME,
]
for p in candidates:
if p.is_file():
return p
return None

def load_preset(path: Path) -> dict:
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f) or {}
return {
"state": data.get("initial_state", {}),
}


# ---------------------------------------------------------------------------
# PresetMiddleware (ASGI)
# ---------------------------------------------------------------------------

class PresetMiddleware:
"""ASGI middleware that injects preset state into POST /apps/{app_name}/users/{user_id}/sessions."""

def __init__(self, app, agents_dir: Path):
self.app = app
self.agents_dir = agents_dir

async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return

path = scope.get("path", "")
method = scope.get("method", "")
m = SESSION_PATH.match(path)

if method == "POST" and m:
app_name = m.group(1)
preset = self._load_preset(app_name)
if preset and preset.get("state"):
body_parts = []
while True:
message = await receive()
body_parts.append(message.get("body", b""))
if not message.get("more_body", False):
break
body = b"".join(body_parts)

data = json.loads(body) if body else {}
merged = {**preset["state"], **(data.get("state") or {})}
data["state"] = merged
new_body = json.dumps(data).encode()

new_headers = []
seen_ct = False
seen_cl = False
for key, value in scope.get("headers", []):
lower_key = key.lower()
if lower_key == b"content-length":
new_headers.append((b"content-length", str(len(new_body)).encode()))
seen_cl = True
elif lower_key == b"content-type":
new_headers.append((b"content-type", b"application/json"))
seen_ct = True
else:
new_headers.append((key, value))
if not seen_ct:
new_headers.append((b"content-type", b"application/json"))
if not seen_cl:
new_headers.append((b"content-length", str(len(new_body)).encode()))
scope["headers"] = new_headers

body_sent = False

async def new_receive():
nonlocal body_sent
if not body_sent:
body_sent = True
return {"type": "http.request", "body": new_body, "more_body": False}
return await receive()

logger.info(
"PresetMiddleware: injected state for app=%s keys=%s",
app_name,
list(preset["state"].keys()),
)
await self.app(scope, new_receive, send)
return

await self.app(scope, receive, send)

def _load_preset(self, app_name: str) -> Optional[dict]:
agent_dir = self.agents_dir / app_name
preset_path = find_preset_file(agent_dir)
if preset_path is None:
return None
try:
return load_preset(preset_path)
except Exception as e:
logger.warning("PresetMiddleware: failed to load preset for %s: %s", app_name, e)
return None


# ---------------------------------------------------------------------------
# CLI (adk web compatible)
# ---------------------------------------------------------------------------

@click.command()
@click.argument(
"agents_dir",
type=click.Path(exists=True, dir_okay=True, file_okay=False, resolve_path=True),
default=os.getcwd,
)
@click.option("--host", type=str, default="127.0.0.1", show_default=True, help="The binding host of the server.")
@click.option("--port", type=int, default=8000, help="The port of the server.")
@click.option("--allow_origins", multiple=True, help="Origins to allow for CORS.")
@click.option("-v", "--verbose", is_flag=True, default=False, help="Enable verbose (DEBUG) logging.")
@click.option("--log_level", type=LOG_LEVELS, default="INFO", help="Set the logging level.")
@click.option("--trace_to_cloud", is_flag=True, default=False, help="Enable cloud trace for telemetry.")
@click.option("--otel_to_cloud", is_flag=True, default=False, help="Write OTel data to Google Cloud.")
@click.option("--reload/--no-reload", default=True, help="Enable auto reload for server.")
@click.option("--a2a", is_flag=True, default=False, help="Enable A2A endpoint.")
@click.option("--reload_agents", is_flag=True, default=False, help="Enable live reload for agents changes.")
@click.option("--eval_storage_uri", type=str, default=None, help="Evals storage URI (e.g. gs://bucket).")
@click.option("--extra_plugins", multiple=True, help="Extra plugin classes or instances.")
@click.option("--url_prefix", type=str, default=None, help="URL path prefix for reverse proxy.")
@click.option("--session_service_uri", default=None, help="Session service URI.")
@click.option("--artifact_service_uri", type=str, default=None, help="Artifact service URI.")
@click.option("--memory_service_uri", type=str, default=None, help="Memory service URI.")
@click.option("--use_local_storage/--no_use_local_storage", default=True, show_default=True, help="Use local .adk storage.")
@click.option("--logo-text", type=str, default=None, help="Logo text in web UI.")
@click.option("--logo-image-url", type=str, default=None, help="Logo image URL in web UI.")
# --- preset-specific option ---
@click.option("--preset/--no-preset", default=True, show_default=True, help="Enable PresetMiddleware for auto state injection.")
def cli_preset_web(
agents_dir: str,
host: str,
port: int,
allow_origins: Optional[tuple[str, ...]],
verbose: bool,
log_level: str,
trace_to_cloud: bool,
otel_to_cloud: bool,
reload: bool,
a2a: bool,
reload_agents: bool,
eval_storage_uri: Optional[str],
extra_plugins: Optional[tuple[str, ...]],
url_prefix: Optional[str],
session_service_uri: Optional[str],
artifact_service_uri: Optional[str],
memory_service_uri: Optional[str],
use_local_storage: bool,
logo_text: Optional[str],
logo_image_url: Optional[str],
preset: bool,
):
"""Starts a FastAPI server with Web UI for agents (with preset support).

AGENTS_DIR: The directory of agents, where each subdirectory is a single
agent, containing at least __init__.py and agent.py files.

Example:

python preset_server.py path/to/agents_dir
python preset_server.py . --port 8080 --no-preset
"""
if verbose and log_level == "INFO":
log_level = "DEBUG"

logs.setup_adk_logger(getattr(logging, log_level.upper()))

agents_dir_path = Path(agents_dir).resolve()

@asynccontextmanager
async def _lifespan(app: FastAPI):
preset_msg = " + PresetMiddleware" if preset else ""
click.secho(
f"""
+-----------------------------------------------------------------------------+
| ADK Web Server started
{preset_msg:<53s}|
| |
| For local testing, access at http://
{host}:{port}.{" " * (29 - len(str(port)))}|
+-----------------------------------------------------------------------------+
""",
fg="green",
)
yield
click.secho(
"""
+-----------------------------------------------------------------------------+
| ADK Web Server shutting down... |
+-----------------------------------------------------------------------------+
""",
fg="green",
)

app = get_fast_api_app(
agents_dir=agents_dir,
session_service_uri=session_service_uri,
artifact_service_uri=artifact_service_uri,
memory_service_uri=memory_service_uri,
use_local_storage=use_local_storage,
eval_storage_uri=eval_storage_uri,
allow_origins=list(allow_origins) if allow_origins else None,
web=True,
trace_to_cloud=trace_to_cloud,
otel_to_cloud=otel_to_cloud,
lifespan=_lifespan,
a2a=a2a,
host=host,
port=port,
url_prefix=url_prefix,
reload_agents=reload_agents,
extra_plugins=list(extra_plugins) if extra_plugins else None,
logo_text=logo_text,
logo_image_url=logo_image_url,
)

if preset:
app.add_middleware(PresetMiddleware, agents_dir=agents_dir_path)
click.secho(
"PresetMiddleware: auto-injecting preset state into POST /apps/*/users/*/sessions",
fg="cyan",
)

config = uvicorn.Config(
app,
host=host,
port=port,
reload=reload,
)
server = uvicorn.Server(config)
server.run()


if __name__ == "__main__":
cli_preset_web()












Testing It Out



Run the startup script:




python preset_server.py .






If the middleware is active, you'll see this in the startup log:




PresetMiddleware: auto-injecting preset state into POST /apps/*/users/*/sessions






Click "New Session" in the Web UI, and the state gets injected automatically:



image1



Ask the agent for a greeting, and you'll see the values from preset.yaml reflected in the response:



image2






Wrap-Up




  • Define initial_state in preset.yaml and it gets auto-injected on session creation

  • Since it's implemented as ASGI middleware, no changes to your agent code are needed

  • Fully compatible with all adk web options



That's it — no more copy-pasting state values every time you start a new session.

CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
SOC Incident Playbook: Remote Code Execution (RCE) Defense
title: Detect Exploitation - Injecting Initial State into ADK Web UI with ASGI Middleware
id: c177e41f-49d2-4a68-93b7-b4bf3f6b2d2e
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Injecting Initial State into A" ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Injecting Initial State into ADK Web UI .... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Injecting Initial State into ADK Web UI with ASGI Middleware

Thematisch verwandte Begriffe: Injecting, Initial, State, into · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97152 | Nanomsg versions 0.5-beta through 1.x before 1.2.3 has a remotely exploi…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick