Skip to content

fix(a2a): decode percent-encoded password in redis url - #8206

Open
devYRPauli wants to merge 2 commits into
crewAIInc:mainfrom
devYRPauli:fix/a2a-redis-url-password
Open

devYRPauli wants to merge 2 commits into
crewAIInc:mainfrom
devYRPauli:fix/a2a-redis-url-password

Conversation

@devYRPauli

@devYRPauli devYRPauli commented Oct 15, 2026 •

Copy link
Copy Markdown

Related issue

Fixes #8205

Summary

_parse_redis_url now decodes the password from REDIS_URL with urllib.parse.unquote before it puts the password in the aiocache config. redis-py from_url decodes the password the same way, and crewai_core/lock_store.py already reads the same REDIS_URL through from_url. A URL with no password still adds no password key.

Verification

  • Tests added or updated for the changed behavior

  • Relevant tests and quality checks pass locally

  • New test TestParseRedisUrl.test_decodes_percent_encoded_password in lib/crewai/tests/a2a/utils/test_task.py checks the full config for redis://:p%40ss%2Fw%3Ard+1@cache.internal:6380/2. It fails on main and passes with this change.

  • unquote_plus in place of unquote fails the test, because the test password contains +.

  • I pointed aiocache.RedisCache at a RESP stub server with the parsed config. On main the client sends HELLO 3 AUTH default p%40ss%2Fw%3Ard+1. With this change it sends p@ss/w:rd+1. Versions: redis 8.0.0, aiocache 0.12.3.

  • pytest lib/crewai/tests/a2a on Python 3.12 with the a2a extra: 101 passed on main, 102 passed with this change.

  • ruff check, ruff format --check and mypy on task.py pass.

Additional context

One behavior changes. A password written unencoded that contains a valid %XX sequence is now decoded. redis-py decodes it the same way, so a REDIS_URL that works for the lock store is not affected.

Open PR #5700 moves this parsing to parse_cache_url in a new cache_config.py. That function also passes parsed.password unchanged, so it needs the same unquote if #5700 merges after this PR.

I used Claude Code CLI with Opus 5.5. I cannot set the llm-generated label, so please add it.


Note

Low Risk
Small, localized change to Redis URL parsing for A2A caching; aligns with redis-py and only affects password handling when REDIS_URL is set.

Overview
Fixes A2A Redis cache setup when REDIS_URL includes a percent-encoded password. _parse_redis_url now runs the URL password through urllib.parse.unquote before putting it in the aiocache config, matching redis-py from_url behavior (and the lock store path that already uses from_url).

A new unit test asserts the full parsed config for a URL with encoded @, /, :, and + in the password. URLs with no password are unchanged.

Reviewed by Cursor Bugbot for commit fbfcb6f. Bugbot is set up for automated code reviews on this repo. Configure here.

_parse_redis_url passed parsed.password from REDIS_URL to the aiocache
config unchanged, so a percent-encoded password reached the server
encoded. Decode it with unquote, as redis-py from_url does.

Fixes crewAIInc#8205

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@strix-security

strix-security Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Strix Security Review

Warning

This pull request has 57 commits after the last Strix review (fbfcb6f). Strix has not reviewed these changes.
Automatic review on push is off for this repository. To review the latest changes, tag @strix-security in a comment, or turn on re-review on push.

No security issues found.

Review summary

Reviewed the two-file change to _parse_redis_url in lib/crewai/src/crewai/a2a/utils/task.py and its accompanying test. The PR decodes the percent-encoded password from REDIS_URL with urllib.parse.unquote before storing it in the aiocache Redis config, matching redis-py's from_url behavior. The decoded password is a plain string value in a configuration dict with no dangerous sink, no attacker-controlled data flow, and no removed security control. The change is a correctness fix with no security impact.

Updated for fbfcb6f.


Reviewed by Strix
Re-run review · Configure security review settings

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e760f2cf-a9aa-4d4d-af2b-5706b8da6de9
📥 Commits

Reviewing files that changed from the base of the PR and between 3da2d4e and fbfcb6f.

📒 Files selected for processing: 2
  • lib/crewai/src/crewai/a2a/utils/task.py
  • lib/crewai/tests/a2a/utils/test_task.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📝 Walkthrough

The A2A task cache Redis URL parser now percent-decodes passwords before adding them to its configuration. A regression test checks the decoded password and the parsed host, port, and database.

Changes: A2A task cache Redis password

Layer / File(s) Summary
Decode and verify Redis URL passwords
lib/crewai/src/crewai/a2a/utils/task.py, lib/crewai/tests/a2a/utils/test_task.py
_parse_redis_url now decodes percent-encoded passwords. The test checks the decoded password and parsed connection settings.

Priority: ⬇️ Low

Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to fbfcb

The A2A cache configuration now receives the decoded Redis password, and passwords are still omitted when absent. The regression test covers encoded characters and +; no actionable merge risk is established.

Pre-merge checks: Passed 5
Passed Passed checks: 5 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: decoding percent-encoded Redis URL passwords for A2A.
Description check Passed The description includes the related issue, implementation summary, verification details, test coverage, compatibility context, and follow-up information. It satisfies the required template.
Linked Issues check Passed Issue #8205 requires decoding only the Redis password with urllib.parse.unquote, preserving +, and omitting password when no password exists. The PR changes _parse_redis_url to assign `unquote…
Out of Scope Changes check Passed The diff changes only Redis password parsing in task.py and adds a focused regression test in test_task.py. The test directly supports issue #8205. No unrelated implementation or test changes are …
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@VANDRANKI VANDRANKI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran this on Windows with Python 3.13.3, against main (40bb2d3) and the PR head. I installed a2a-sdk 0.3.x, aiocache and redis-py 8.1.0 into a temp folder so crewai.a2a.utils.task imports.

I compared _parse_redis_url with redis.connection.parse_url from redis-py, which from_url uses. I looked at the password in each result.

URL password main PR redis-py
p%40ss%2Fw%3Ard+1 p%40ss%2Fw%3Ard+1 p@ss/w:rd+1 p@ss/w:rd+1
pa%2523ss pa%2523ss pa%23ss pa%23ss
a%20b (rediss://) a%20b a b a b
plain plain plain plain
% alone, and p%zzw unchanged unchanged unchanged
no password no password key no password key none

So the PR matches redis-py for every URL I tried. The + stays a +, which is right, because unquote is used and not unquote_plus. The double-encoded %2523 decodes once only, as redis-py does.

The exact dict from the PR's test (redis://:p%40ss%2Fw%3Ard+1@cache.internal:6380/2) compares equal on the PR and not equal on main. I checked that equality by hand in a one-liner. I could not run the test through pytest. In my environment all 17 tests in tests/a2a/utils/test_task.py error during setup with RuntimeError: Network is disabled, on the PR and on main, before any test body runs.

One thing I saw that is outside this PR. The username in the URL is still dropped, and rediss:// still gives no TLS setting. For rediss://user:p%40ss@h:6380/1 the config is only cache, endpoint, port, db and password. redis-py returns user as the username. If a Redis ACL user is needed, auth would still fail after this fix. That could be a follow-up. I did not connect to a Redis server.

The change is correct for the password. Approving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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.

[BUG] A2A task cache passes a percent-encoded Redis password from REDIS_URL

2 participants