Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
IT Security NachrichtenICYMI: August 2026 @AWS Security(24.09.2026 um 01:31 Uhr)
IT Security NachrichtenMaintenance Company in Dubai: What to Check Before You Sign(24.09.2026 um 01:33 Uhr)
IT Security DownloadsGitHub Release: ollama/ollama v0.34.4-rc1 (24.09.2026)(24.09.2026 um 01:36 Uhr)
IT Security DownloadsGitHub Release: google-gemini/gemini-cli v0.61.0 (24.09.2026)(24.09.2026 um 01:59 Uhr)
IT Security NachrichtenICYMI: August 2026 @AWS Security(24.09.2026 um 01:31 Uhr)
IT Security NachrichtenMaintenance Company in Dubai: What to Check Before You Sign(24.09.2026 um 01:33 Uhr)
IT Security DownloadsGitHub Release: ollama/ollama v0.34.4-rc1 (24.09.2026)(24.09.2026 um 01:36 Uhr)
IT Security DownloadsGitHub Release: google-gemini/gemini-cli v0.61.0 (24.09.2026)(24.09.2026 um 01:59 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

My Demo Script Found a Production Bug on Its First Run: A Tiny Post-Mortem

I built a demo script to show off a project. It failed on step 2 — and in doing so, it found a real bug that had been sitting in the codebase the whole time, invisible to a fully green test suite. This is a short technical report on what h…

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

I built a demo script to show off a project. It failed on step 2 — and in doing so, it found a real bug that had been sitting in the codebase the whole time, invisible to a fully green test suite.



This is a short technical report on what happened, why the tests missed it, and what it taught me about validating against remote APIs. Total incident cost: about ten minutes. Lessons: worth writing down.






Context



The project is an MCP (Model Context Protocol) server that wraps Google's gemini-3.1-flash-lite-image model — image generation and stateful editing exposed as four tools that any MCP client (Claude Code, a Google ADK agent, a Rust CLI) can call. I've written up the architecture separately; this post is only about the bug.



The relevant detail: the server validates tool arguments locally before calling the API. One of those arguments is thinking_level, the model's latency-vs-quality dial:




# server.py — as originally written
SUPPORTED_THINKING_LEVELS = {"minimal", "low", "medium", "high"}

@mcp.tool()
def generate_image(
prompt: str, aspect_ratio: str = "1:1", thinking_level: str = "medium"
) -> str:
...






Four allowed values, defaulting to medium. The test suite — 10 unit tests, all mocked — passed. Lint passed. The server had been demoed through agents and worked. Ship it, right?






The incident



I wrote a demo.sh that walks through the stack live: list the tools, generate an image, then do a stateful edit. To keep the demo cheap I picked the lowest thinking level:




cargo run --quiet -- generate "a tiny robot chef cooking ramen" 16:9 minimal






First run, step 2:




🔴 Image generation failed: Error code: 400 - {'error': {'message':
"'minimal' is not a supported thinking level for this model.
Allowed values are: low, high.", 'code': 'invalid_request'}}






The live API accepts exactly two thinking levels for this model: low and high. Not four.



Read that against the code above and the real bug jumps out — and it's much worse than a demo flag being wrong:




The server's default was medium. Every live call that didn't explicitly override thinking_level was a guaranteed HTTP 400.




The local validation layer was happily approving values the API would reject, and rejecting nothing that mattered. It wasn't validating the contract; it was validating a memory of the contract.






Why 10 passing tests missed it



The test suite mocks the API client:




@patch("server._get_client")
def test_generate_image_success(self, mock_get_client):
mock_client.interactions.create.return_value = mock_interaction
...
result = generate_image(prompt="test", thinking_level="medium")
self.assertIn("🟢 Image successfully saved!", result)






This is a good test — it verifies the server's own logic: argument handling, base64 decoding, file naming, error shaping. Mocked tests are supposed to isolate you from the network, and they do.



But that isolation cuts both ways: a mocked test can never detect that the remote contract changed (or was never what you thought). The mock returns whatever you tell it to, including for requests the real API would reject. My suite effectively asserted "the server correctly forwards medium to the API" — which it did. Correctly forwarding an invalid value is still a bug, just not one visible from inside the mock boundary.



Two things had to line up for this to reach production:





  1. The allowlist duplicated a remote contract. SUPPORTED_THINKING_LEVELS is a local copy of a fact the API owns. Local copies drift — whether because the docs were wrong, the model changed, or the set was written for a different model.


  2. Every prior live test happened to pass a valid value. Agents calling the tools tended to say high (quality) — masking the broken default and the two phantom values.






The fix



Mechanically boring, which is the point — the hard part was knowing:




-SUPPORTED_THINKING_LEVELS = {"minimal", "low", "medium", "high"}
+SUPPORTED_THINKING_LEVELS = {"low", "high"}

- prompt: str, aspect_ratio: str = "1:1", thinking_level: str = "medium"
+ prompt: str, aspect_ratio: str = "1:1", thinking_level: str = "low"






Plus the sweep that actually takes the time: three tool signatures and docstrings, the server's self-describing get_help text, the README, two articles, the API cheat-sheet doc — and a regression test that locks the new knowledge in:




# The live API only accepts low/high for this model; medium must be rejected
result = generate_image(prompt="test", thinking_level="medium")
self.assertIn("Unsupported thinking level 'medium'", result)






Then verification against the real thing: a live call with pure default parameters — the exact case that was broken — returning 🟢 Image successfully saved!. And because the server ships as a Docker image, a rebuild and push, since the published image contained the bug too.



One design decision earned its keep during all this: the server's tools never raise — they catch everything and return human-readable 🔴 ... strings. The 400 came back as legible text an agent (or a demo script, or me) could read and act on, instead of a stack trace tearing down the MCP session.






Lessons



1. Mocked tests verify your code. They cannot verify the contract. You need at least one test that touches the real API — even a single cheap smoke call. Mine now lives in the demo script and a /verify-stack routine: generate one tiny image with default parameters, because defaults are the values nobody passes explicitly and therefore nobody tests.



2. A local allowlist of remote-owned values is a drift time bomb. If you must pre-validate (it does give agents faster, clearer errors than a round-trip 400), treat the list as a cache of someone else's truth: comment where it came from, and pin a regression test to the values you've observed the API reject.



3. Test your defaults, specifically. The bug survived every live interaction before the demo because humans and agents kept overriding the broken default. f(x) being called a hundred times tells you nothing about f().



4. A demo script is the cheapest end-to-end test you'll ever write. It exercises the happy path a real user takes, with real credentials, against the real API — precisely the layer unit tests can't reach. Mine found a production bug on its first execution, before any audience did. Write the demo before you think you need one; run it in fast mode (DEMO_FAST=1) whenever the API-facing code changes.



5. Return errors agents can read. Tool calls that fail as readable text keep the conversation alive — the calling LLM can see Allowed values are: low, high and retry correctly on its own. That same property is what made this bug a ten-minute fix instead of a debugging session.






Timeline




































T+0
demo.sh first run: step 2 fails with HTTP 400
T+1 min Root cause identified from the error body: API allows low/high only
T+4 min Server validation + defaults fixed; regression test added
T+6 min Docs swept (README, articles, cheat-sheet, skill); 10/10 tests green
T+8 min Live verification with default params: 🟢
T+10 min Fixed image rebuilt and pushed to Docker Hub


The demo, incidentally, works great now — the stateful edit produces the same cyberpunk kitchen with a new neon RAMEN sign, pixel-for-pixel continuity intact. But the bug report turned out to be the better story.



Have your own "the demo found it" story? I'd love to hear it in the comments. 👇

CTI Threat Relationship Graph3 Knoten / 2 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
IR-PLAYBOOK-VULN-REMEDIATION
MEDIUM
SOC Incident Playbook: Vulnerability Remediation & Verification
1-Click Detection Engineering: Sigma & YARA Rules
SOC Ready
title: Detect Exploitation - My Demo Script Found a Production Bug on Its First Run: A Tiny Post-Mortem
id: f7bc7e5f-b91f-4d79-bed5-05cf8b6850ce
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 = "My Demo Script Found a Product" ascii wide
    condition:
        any of them
}
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten My Demo Script Found a Production Bug on Its First Run: A Tiny Post-Mortem

Thematisch verwandte Begriffe: Demo, Script, Found, Production · 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-96676 | A vulnerability was identified in Fast FAC1900R 20190827_2.0.2. The impa…
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