FlawOpen

flawopen.com/Reference/PR bot security

AI PR Review Bots: Prompt Injection and SSRF Controls

AI PR Review Bots: Prevent Prompt-Injection SSRF

How untrusted pull-request text can influence AI tools, where SSRF risk begins, and how to constrain bot permissions and network access.

Security risk CWE-1427 / CWE-918 AI & Developer Workflow Security

ELI5 — A review assistant with a company network pass

A pull request is like a package delivered to a review desk: its contents may be useful, but they do not become company policy. If an AI reviewer can also fetch arbitrary URLs from inside a private network, a misleading instruction may influence where its tool looks. The risk depends on the tool and network permissions; reading hostile text alone does not grant network access.

Core concepts and key terms

Untrusted input:
Pull-request titles, descriptions, comments, diffs, and linked content from contributors.
Indirect prompt injection:
Untrusted text influences an agent because it is included in the model context.
SSRF boundary:
A server-side fetch tool can reach destinations the contributor cannot reach directly.
Impact conditions:
A reachable internal service, available credentials, and inadequate authorization or egress controls must also be present.

Mechanism and root cause

A review bot may combine contributor-controlled text with trusted review instructions. The model can propose a tool call based on either. Prompt wording and delimiters do not authorize network access. SSRF becomes possible only when application code accepts a model-selected destination and a network-capable tool can reach it. Access to cloud metadata or credentials is a further conditional impact, not an automatic result of prompt injection.

Conditional attack path

Step 1

Contributor controls PR content

An external contributor can influence titles, descriptions, comments, changed text, and linked material.

Step 2

Agent reads that content

The service places some untrusted fields into model context; this can influence the model’s response or tool proposal.

Step 3

Tool capability determines reach

If the agent can fetch arbitrary URLs, the runner may contact destinations allowed by its network path. Without that tool, this SSRF path is absent.

Step 4

Impact depends on deployment

Internal data or credentials are exposed only if reachable services, metadata settings, identity permissions, and output controls permit it.

Source code: unchecked dispatch vs. authorized read capability

MOCKED VULNERABLE DISPATCH
# INERT DEMO: model-selected URL reaches a mock tool without policy checks
from dataclasses import dataclass

@dataclass
class ToolCall:
    name: str
    arguments: dict[str, str]

class MockFetch:
    def __init__(self):
        self.calls = []

    def __call__(self, url: str) -> str:
        self.calls.append(url)
        return "fixture response"

def dispatch_unchecked(call: ToolCall, tools: dict):
    # The mock records a string; it makes no network request.
    return tools[call.name](**call.arguments)

mock_fetch = MockFetch()
call = ToolCall("fetch_url", {"url": "https://docs.example.invalid/"})
assert dispatch_unchecked(call, {"fetch_url": mock_fetch}) == "fixture response"
assert mock_fetch.calls == ["https://docs.example.invalid/"]
POLICY-GATED READ-ONLY TOOL
# SAFE DEMO: the agent receives only a read-only PR capability
from dataclasses import dataclass

@dataclass(frozen=True)
class ReviewContext:
    repository: str
    pull_request: int

@dataclass
class ToolCall:
    name: str
    arguments: dict[str, object]

READ_ONLY_TOOLS = {"read_pr_diff"}

def dispatch_review_call(call: ToolCall, context: ReviewContext, policy, tools):
    if call.name not in READ_ONLY_TOOLS:
        raise PermissionError("Tool is not available to this review")
    expected = {"repository": context.repository,
                "pull_request": context.pull_request}
    if call.arguments != expected:
        raise ValueError("Arguments must match the authorized review")
    if not policy.can_read_pr(context.repository, context.pull_request):
        raise PermissionError("Review principal cannot read this pull request")
    # The injected read_pr_diff function is trusted app code, not model output.
    return tools["read_pr_diff"](**expected)

Engineering hardening checklist

Related documented vulnerability — CVE-2023-32786

The reviewed GitHub advisory for CVE-2023-32786 says prompt injection in LangChain before 0.0.329 could force retrieval of an arbitrary URL (SSRF) and potentially influence downstream tasks. The advisory lists 0.0.329 as fixed. This is a documented URL-fetch chain; it does not establish a PR-bot incident or prove cloud-metadata theft.

Primary guidance and records

Browse the security directory → Explore reference guides →
See also Server-Side Request Forgery (SSRF, CWE-918): How It Works and How to Prevent It in Every Language — flawopen.com Node.js fetch() SSRF: Safe URL Fetching SSRF and Cloud Metadata: Credential Risks and Defenses