Skip to content

fix(agent-memory): encode memory and message identifiers in URL paths - #454

Draft
jplbrun wants to merge 1 commit into
mainfrom
fix/hasi-agent-memory-url-injection
Draft

jplbrun wants to merge 1 commit into
mainfrom
fix/hasi-agent-memory-url-injection

Conversation

@jplbrun

@jplbrun jplbrun commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Disclaimer: Do not include SAP-internal or customer-specific information in this PR (e.g. internal system URLs, customer names, tenant IDs, or confidential configurations). This is a public repository.

Description

Caller-supplied memory and message IDs were inserted directly into URL paths, allowing reserved characters to change the request URL. Encode memory_id and message_id with quote(..., safe="") in memory retrieval, update and deletion, and message retrieval and deletion. Preserve the surrounding memories(<id>) and messages(<id>) syntax, identifier spelling and request bodies. Encoding failures raise the existing AgentMemoryHttpError.

Related Issue

N/A

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Dependency update

How to Test

Run from the repository root after checking out this branch.

  1. Run the Agent Memory unit suite:

    uv sync --frozen --all-extras --group dev
    uv run --frozen pytest tests/agent_memory/unit -q

    Expected: 252 passed, including 13 regression cases covering all five URL call sites, pre-encoded inputs, UUID spelling preservation, dot values and encoding failures.

  2. Compare the old URL construction with the updated client. This example prepares requests with Session.send mocked; it sends no network requests:

    uv run --frozen python - <<'PY'
    from unittest.mock import patch
    from requests import Request, Response
    from sap_cloud_sdk.core.protocol.http import HttpClient
    from sap_cloud_sdk.agent_memory.client import AgentMemoryClient
    
    base_url = "https://memory.example.invalid/agent-memory"
    memory_id = "x)/../OtherEntity?injected=yes#fragment"
    raw_url = f"{base_url}/v1/memories({memory_id})"
    print("Before:", Request("DELETE", raw_url).prepare().url)
    
    http = HttpClient(base_url)
    client = AgentMemoryClient(http, tenant="subscriber")
    response = Response()
    response.status_code = 204
    try:
        with patch("requests.Session.send", return_value=response) as send:
            client.delete_memory(memory_id)
            print("After: ", send.call_args.args[0].url)
    finally:
        http.close()
    PY

    Expected:

    Before: https://memory.example.invalid/agent-memory/v1/OtherEntity?injected=yes#fragment)
    After:  https://memory.example.invalid/agent-memory/v1/memories(x%29%2F..%2FOtherEntity%3Finjected%3Dyes%23fragment)
    

    Before, .. changes the path, injected=yes becomes a query, and fragment) becomes a URL fragment. Fragments are not sent in the HTTP request target. After, those characters remain encoded inside the memory identifier.

  3. Run the repository checks:

    uv run --frozen pre-commit run --files src/sap_cloud_sdk/agent_memory/client.py tests/agent_memory/unit/test_url_identifiers.py

    Expected: formatting, lint, whitespace and type checks pass. Unchanged YAML and lock-file hooks are skipped.

Checklist

  • I have read the Contributing Guidelines
  • I have verified that my changes solve the issue
  • I have added/updated automated tests to cover my changes
  • All tests pass locally
  • I have verified that my code follows the Code Guidelines
  • I have updated documentation (if applicable)
  • I have added type hints for all public APIs
  • My code does not contain sensitive information (credentials, tokens, etc.)
  • I have followed Conventional Commits for commit messages
  • (New modules only) I have attached e2e evidence showing the module working against a real BTP service
  • (Security-sensitive changes only) I have run /security-review and addressed all ❌ findings

Breaking Changes

No breaking changes for documented Agent Memory usage. Public client signatures, request bodies, and URLs for valid memory and message UUIDs are unchanged. These callers need no migration. UUID validation and normalization are not added.

This security fix changes how other identifier inputs behave:

  • URL delimiters such as ), /, ?, #, and % are encoded as identifier data instead of being interpreted as URL structure.
  • Pre-encoded identifiers are treated literally and encoded again. Callers passing pre-encoded values should pass the original raw identifier instead.
  • Unicode values that cannot be encoded raise AgentMemoryHttpError before an HTTP request.

Literal . and .. values remain allowed because they stay inside memories(...) or messages(...), rather than forming standalone path segments.

These behavior changes affect inputs outside the service's documented UUID format; identical behavior for every previously accepted input is not claimed.

Additional Notes

None

Signed-off-by: João Pedro Brun <joao.pedro.brun@sap.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant