-
Notifications
You must be signed in to change notification settings - Fork 1
fix: validate retained visual locator regions #324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a4627d6
98c92ef
3a335ad
71f0940
fdd62a6
5d29001
8bcfea8
b1d1b10
7a49d3e
2673dac
3f2e60b
835a3bb
53c2b25
30701c0
e52168f
fc7188e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # 2.13.1 Buyer-safe image evidence | ||
|
|
||
| Buyer views and search keep legitimate captions while suppressing internal | ||
| VISION instructions. Region responses with invalid JSON value types now fail | ||
| closed with an explicit type error instead of being treated as valid evidence. |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -185,6 +185,7 @@ | |||||||||||||
| has_real_source_context, | ||||||||||||||
| ) | ||||||||||||||
| from lineageweave.http_client import HttpClientError | ||||||||||||||
| from lineageweave.observability import configure_telemetry | ||||||||||||||
|
|
||||||||||||||
| _POST_READ = "post_read" | ||||||||||||||
| _POST_ADMIN = "post_admin" | ||||||||||||||
|
|
@@ -194,6 +195,7 @@ | |||||||||||||
| async def lifespan(app: FastAPI): | ||||||||||||||
| """Open one asyncpg pool and one Valkey client for the process, and | ||||||||||||||
| close both on shutdown.""" | ||||||||||||||
| configure_telemetry("lineageweave") | ||||||||||||||
| settings = load_settings() | ||||||||||||||
| app.state.pool = await create_pool(settings.database_url) | ||||||||||||||
| app.state.valkey = create_valkey_client(settings.valkey_url) | ||||||||||||||
|
|
@@ -2655,6 +2657,11 @@ async def chat_about_post( | |||||||||||||
| status.HTTP_503_SERVICE_UNAVAILABLE, | ||||||||||||||
| "Post chat is unavailable: contextual-orchestrator returned no complete evidence object", | ||||||||||||||
| ) from exc | ||||||||||||||
| except Exception as exc: # noqa: BLE001 - provider boundary is fail-closed. | ||||||||||||||
| raise HTTPException( | ||||||||||||||
| status.HTTP_503_SERVICE_UNAVAILABLE, | ||||||||||||||
| "Post chat is unavailable: contextual-orchestrator returned no complete evidence object", | ||||||||||||||
| ) from exc | ||||||||||||||
|
Comment on lines
+2660
to
+2664
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Duplicate unreachable exception handler Two identical
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||
| cited_ids = list(answer.cited_post_ids) | ||||||||||||||
| async with pool.acquire() as conn: | ||||||||||||||
| await persist_post_chat(conn, post_id, question, answer.answer_text, cited_ids) | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # ADR 0121: Keep Internal Image Instructions Out of Buyer Evidence | ||
|
|
||
| ## Status | ||
|
|
||
| Accepted | ||
|
|
||
| ## Context | ||
|
|
||
| VISION analysis may receive or inherit an internal instruction such as | ||
| `This post is an image. Ask questions to read its text.` or its Korean | ||
| equivalent. That text is an agent instruction, not a caption describing the | ||
| source image. Persisted legacy content can still contain it even after the | ||
| prompt is corrected. | ||
|
|
||
| ## Decision | ||
|
|
||
| At the image and visual-region evidence boundary, normalize captions and suppress captions that | ||
| match the known internal instruction forms. Apply the same rule when creating | ||
| LLM/embedding placeholders and when rendering the buyer-facing post body. | ||
| Retain the original image, OCR text, tags, region coordinates, and provenance; | ||
| only the non-evidence caption is removed. If no useful caption remains, show | ||
| the image and available evidence without inventing a description. | ||
|
|
||
| The instruction matcher uses concrete imperative phrases for Korean text | ||
| extraction guidance rather than ordinary words such as `텍스트` or `질문`, so | ||
| legitimate captions that describe an image containing text remain evidence. | ||
|
|
||
| The configured Vision destination is also a trust boundary. The client rejects | ||
| credential-bearing URLs, invalid ports, loopback/private/link-local/reserved | ||
| IP literals, and known local or cloud-metadata hostnames even when local HTTP | ||
| is explicitly enabled. Compose uses the service name `orchestrator` for its | ||
| local HTTP route; a caller cannot opt into an internal IP destination by | ||
| setting `allow_insecure_http`. | ||
|
|
||
| Vision parse failures and unexpected provider failures are also trust-boundary | ||
| events. Their raw response or exception text is never returned in an API | ||
| payload or persisted as post-content detail; the product exposes a stable | ||
| unavailable message and schedules a retry where applicable. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Buyer screens cannot expose the analysis agent's instruction as post content. | ||
| - Existing persisted image rows are safe immediately; re-ingestion is not | ||
| required merely to hide the legacy caption. | ||
| - OCR, region evidence, and semantic search remain available. | ||
| - New provider-specific instruction variants require an explicit, reviewed | ||
| pattern and a regression test rather than a broad caption guess. | ||
| - A malformed or attacker-controlled Vision endpoint fails closed at client | ||
| construction, before an API key or image payload is sent. | ||
| - A malformed provider response cannot disclose gateway diagnostics through a | ||
| buyer-facing error or durable ingestion record. | ||
|
|
||
| ## References — APA 7th | ||
|
|
||
| MITRE. (2026). *CWE-209: Generation of error message containing sensitive | ||
| information*. https://cwe.mitre.org/data/definitions/209.html | ||
|
|
||
| National Institute of Standards and Technology. (2020). *Security and privacy | ||
| controls for information systems and organizations* (NIST Special Publication | ||
| 800-53 Rev. 5). https://doi.org/10.6028/NIST.SP.800-53r5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔍 Idle blocking reads emit a span every cycle
The worker loops forever calling
client.xread(..., block=1000), now wrapped intraced. With an OTLP endpoint configured this produces a new ~1s span roughly once per second per worker even while the stream is idle. No-op when telemetry is off; verify the emitted volume is acceptable when it is on.Was this helpful? React with 👍 or 👎 to provide feedback.