Skip to content

security: hard-gate chat-invoked URL ingestion on real user intent (VULN-13906 2/4)#2145

Draft
edwinjosechittilappilly wants to merge 3 commits into
security/vuln-13906-pr1-untrusted-fencingfrom
security/vuln-13906-pr2-url-intent-gate
Draft

security: hard-gate chat-invoked URL ingestion on real user intent (VULN-13906 2/4)#2145
edwinjosechittilappilly wants to merge 3 commits into
security/vuln-13906-pr1-untrusted-fencingfrom
security/vuln-13906-pr2-url-intent-gate

Conversation

@edwinjosechittilappilly

Copy link
Copy Markdown
Collaborator

Summary

Part 2 of 4 for VULN-13906. Stacked on #2144 (PR1 fencing) — this PR's diff should be reviewed against that branch, not main.

  • The URL Ingestion Tool's ensure_url() (Langflow URLComponent, flows/components/url.py) now refuses to fetch a URL unless it's actually present in the real current chat user message — not just whatever the model passed as a tool argument.
  • Reuses the existing backend→Langflow trusted-global-variable mechanism (same pattern as OPENRAG_INGEST_URL/OPENRAG_INGEST_TOKEN): chat_service.py injects X-Langflow-Global-Var-OPENRAG_CURRENT_USER_MESSAGE set from the real request, never from retrieved/uploaded content or model output.
  • flows/components/url.py is newly extracted from flows/openrag_url_mcp.json (no checked-in source existed for this component before) via scripts/extract_flow_component.py, and kept in sync via scripts/update_flow_components.py.
  • upload_context_chat sets the trusted value to only the synthetic "I'm uploading a document called '...'" framing — deliberately not the document body — so a URL embedded in a poisoned upload can never satisfy its own intent gate. This is the exact scenario from the pentest report.
  • The non-chat Knowledge API URL-submit path is untouched here: it has no chat message to compare against, and the explicit submission is itself the confirmed intent. Destination allowlisting/SSRF hardening for all paths (chat + Knowledge API) lands in PR3.

Test plan

  • tests/unit/test_chat_service_url_intent_header.pylangflow_chat sets the header from the real prompt; upload_context_chat sets it to upload-framing text only, excluding the document body (poisoned-doc URL never appears in the trusted value)
  • tests/unit/test_flow_url_intent_gate.py — intent check runs before the SSRF check in ensure_url(); the new input is present and synced across url.pyopenrag_url_mcp.json (template entry + field_order)
  • Full tests/unit suite — same 18 pre-existing failures as main/PR1, no new failures

…ULN-13906 PR2/4)

The URL Ingestion Tool (Langflow URLComponent in flows/openrag_url_mcp.json)
trusted whatever URL the model passed as a tool argument, so a poisoned
document's hidden instructions could drive a server-side fetch even though
the user never asked for one. Reuse the existing backend-injected
Langflow-global-variable mechanism (the same one used for OPENRAG_INGEST_*)
to pass the real current chat message into the flow as
OPENRAG_CURRENT_USER_MESSAGE, and refuse the fetch in ensure_url() unless
the requested URL is actually present in that trusted value.

flows/components/url.py is newly extracted (via
scripts/extract_flow_component.py) since the URL component previously had
no checked-in source; it's kept in sync with the flow JSON via
scripts/update_flow_components.py as usual. upload_context_chat sets the
trusted value to only the upload-confirmation framing text (never the
document body itself), so a URL embedded in the uploaded document can
never satisfy its own intent gate. The non-chat Knowledge API URL-submit
path is unaffected — it has no chat message to gate on, and treats the
explicit submission itself as confirmed intent (allowlist/SSRF checks
land in PR3).
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 83ec5ef3-f658-4b90-a0c0-19d1a39a2edb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security/vuln-13906-pr2-url-intent-gate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) tests labels Jul 23, 2026
autofix-ci Bot and others added 2 commits July 23, 2026 16:15
…ent methods (VULN-13906 PR2 follow-up)

The intent-gate global added in the previous commit resolves via Langflow's
load_from_db mechanism, which requires the variable name to be listed in
LANGFLOW_VARIABLES_TO_GET_FROM_ENVIRONMENT and given a baseline placeholder
default (same as the existing OPENRAG_INGEST_* vars) — otherwise the field
never resolves and the intent check silently no-ops. Wire it into
docker-compose.yml, the Helm chart (values.yaml + langflow-dotenv.yaml),
and the operator's DefaultLangflowEnvVars, so the gate actually functions
regardless of deployment method.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) docker tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant