Repository navigation
Conversation
|
Thanks for opening your first pull request in the Codna CLI! Please make sure the checks are green and that the change follows CONTRIBUTING.md. A maintainer will review it shortly. |
| if parts.username or parts.password: | ||
| raise ConfigError(f"{field} must not embed credentials") | ||
| if parts.query or parts.fragment: | ||
| raise ConfigError(f"{field} must not carry a query string or fragment") | ||
| path = parts.path.rstrip("/") | ||
| netloc = parts.netloc | ||
| return f"{parts.scheme}://{netloc}{path}" |
There was a problem hiding this comment.
🟡 MEDIUM · security — Empty username bypasses embedded-credential check in _validated_base_url
Python's urlsplit sets parts.username to the empty string "" (not None) when a URL contains a bare @ with no actual username, e.g. https://@host/path. The condition if parts.username or parts.password evaluates "" or None → None (falsy), so the check does not raise ConfigError. The function then proceeds to line 136 where netloc = parts.netloc is "@host", and the reconstructed URL is returned as https://@host — a URL that embeds userinfo syntax and can confuse downstream HTTP clients (some interpret an empty username as an authentication hint). The docstring explicitly promises to "fail-closed on every shape that could smuggle something past the consumer", but this shape slips through.
| if parts.username or parts.password: | |
| raise ConfigError(f"{field} must not embed credentials") | |
| if parts.query or parts.fragment: | |
| raise ConfigError(f"{field} must not carry a query string or fragment") | |
| path = parts.path.rstrip("/") | |
| netloc = parts.netloc | |
| return f"{parts.scheme}://{netloc}{path}" | |
| Replace the credential check with a check on `parts.userinfo` (which is non-empty for any `@`-containing netloc) or use `if parts.username is not None or parts.password is not None:` — but the safest fix is to test whether the netloc contains `@` at all: | |
| ```python | |
| # replace lines 131-132 with: | |
| if parts.username is not None or parts.password is not None: | |
| raise ConfigError(f"{field} must not embed credentials") |
or equivalently:
if '@' in parts.netloc:
raise ConfigError(f"{field} must not embed credentials")
_Reply `@codna fix` and Codna opens a verified, test-green, risk-gated fix PR._
<!-- codna:review:fp=52821c2046a62929 -->
<!-- codna:finding eyJmcCI6ICI1MjgyMWMyMDQ2YTYyOTI5IiwgInBhdGgiOiAiY29kbmEvY29uZmlnX2ZpbGUucHkiLCAibGluZSI6IDEzMSwgImVuZF9saW5lIjogMTM3LCAic2V2ZXJpdHkiOiAibWVkaXVtIiwgImNhdGVnb3J5IjogInNlY3VyaXR5IiwgInRpdGxlIjogIkVtcHR5IHVzZXJuYW1lIGJ5cGFzc2VzIGVtYmVkZGVkLWNyZWRlbnRpYWwgY2hlY2sgaW4gX3ZhbGlkYXRlZF9iYXNlX3VybCIsICJleHBsYW5hdGlvbiI6ICJQeXRob24ncyBgdXJsc3BsaXRgIHNldHMgYHBhcnRzLnVzZXJuYW1lYCB0byB0aGUgZW1wdHkgc3RyaW5nIGBcIlwiYCAobm90IGBOb25lYCkgd2hlbiBhIFVSTCBjb250YWlucyBhIGJhcmUgYEBgIHdpdGggbm8gYWN0dWFsIHVzZXJuYW1lLCBlLmcuIGBodHRwczovL0Bob3N0L3BhdGhgLiBUaGUgY29uZGl0aW9uIGBpZiBwYXJ0cy51c2VybmFtZSBvciBwYXJ0cy5wYXNzd29yZGAgZXZhbHVhdGVzIGBcIlwiIG9yIE5vbmVgIFx1MjE5MiBgTm9uZWAgKGZhbHN5KSwgc28gdGhlIGNoZWNrIGRvZXMgKipub3QqKiByYWlzZSBgQ29uZmlnRXJyb3JgLiBUaGUgZnVuY3Rpb24gdGhlbiBwcm9jZWVkcyB0byBsaW5lIDEzNiB3aGVyZSBgbmV0bG9jID0gcGFydHMubmV0bG9jYCBpcyBgXCJAaG9zdFwiYCwgYW5kIHRoZSByZWNvbnN0cnVjdGVkIFVSTCBpcyByZXR1cm5lZCBhcyBgaHR0cHM6Ly9AaG9zdGAgXHUyMDE0IGEgVVJMIHRoYXQgZW1iZWRzIHVzZXJpbmZvIHN5bnRheCBhbmQgY2FuIGNvbmZ1c2UgZG93bnN0cmVhbSBIVFRQIGNsaWVudHMgKHNvbWUgaW50ZXJwcmV0IGFuIGVtcHR5IHVzZXJuYW1lIGFzIGFuIGF1dGhlbnRpY2F0aW9uIGhpbnQpLiBUaGUgZG9jc3RyaW5nIGV4cGxpY2l0bHkgcHJvbWlzZXMgdG8gXCJmYWlsLWNsb3NlZCBvbiBldmVyeSBzaGFwZSB0aGF0IGNvdWxkIHNtdWdnbGUgc29tZXRoaW5nIHBhc3QgdGhlIGNvbnN1bWVyXCIsIGJ1dCB0aGlzIHNoYXBlIHNsaXBzIHRocm91Z2guIn0= -->
| DURABLE_RANGES: Final[dict[str, str]] = { | ||
| "temporalio": ">=1.34,<2", | ||
| "langgraph": ">=1,<2", | ||
| "langchain-core": ">=1,<2", | ||
| } |
There was a problem hiding this comment.
⚪ LOW · correctness — DURABLE_RANGES missing entry for langgraph-checkpoint
DURABLE_PINS has four entries including langgraph-checkpoint (minimum=(4,), maximum=(5,)), but DURABLE_RANGES only has three entries — "langgraph-checkpoint" is absent. In require_durable() the range is looked up with DURABLE_RANGES.get(requirement.distribution, ""), so when an unsupported version of langgraph-checkpoint is installed, the error details field "range" is "" instead of ">=4,<5". The comment states the dict mirrors the pyproject extra and is asserted equal by tests; the missing entry means that assertion would also be incomplete for this package, so a version-window change could go undetected.
| DURABLE_RANGES: Final[dict[str, str]] = { | |
| "temporalio": ">=1.34,<2", | |
| "langgraph": ">=1,<2", | |
| "langchain-core": ">=1,<2", | |
| } | |
| Add the missing entry to DURABLE_RANGES: | |
| ```python | |
| DURABLE_RANGES: Final[dict[str, str]] = { | |
| "temporalio": ">=1.34,<2", | |
| "langgraph": ">=1,<2", | |
| "langchain-core": ">=1,<2", | |
| "langgraph-checkpoint": ">=4,<5", # add this line | |
| } |
_Reply `@codna fix` and Codna opens a verified, test-green, risk-gated fix PR._
<!-- codna:review:fp=1b72456306330577 -->
<!-- codna:finding eyJmcCI6ICIxYjcyNDU2MzA2MzMwNTc3IiwgInBhdGgiOiAiY29kbmEvZHVyYWJsZS9fX2luaXRfXy5weSIsICJsaW5lIjogMTAyLCAiZW5kX2xpbmUiOiAxMDYsICJzZXZlcml0eSI6ICJsb3ciLCAiY2F0ZWdvcnkiOiAiY29ycmVjdG5lc3MiLCAidGl0bGUiOiAiRFVSQUJMRV9SQU5HRVMgbWlzc2luZyBlbnRyeSBmb3IgbGFuZ2dyYXBoLWNoZWNrcG9pbnQiLCAiZXhwbGFuYXRpb24iOiAiYERVUkFCTEVfUElOU2AgaGFzIGZvdXIgZW50cmllcyBpbmNsdWRpbmcgYGxhbmdncmFwaC1jaGVja3BvaW50YCAobWluaW11bT0oNCwpLCBtYXhpbXVtPSg1LCkpLCBidXQgYERVUkFCTEVfUkFOR0VTYCBvbmx5IGhhcyB0aHJlZSBlbnRyaWVzIFx1MjAxNCBgXCJsYW5nZ3JhcGgtY2hlY2twb2ludFwiYCBpcyBhYnNlbnQuIEluIGByZXF1aXJlX2R1cmFibGUoKWAgdGhlIHJhbmdlIGlzIGxvb2tlZCB1cCB3aXRoIGBEVVJBQkxFX1JBTkdFUy5nZXQocmVxdWlyZW1lbnQuZGlzdHJpYnV0aW9uLCBcIlwiKWAsIHNvIHdoZW4gYW4gdW5zdXBwb3J0ZWQgdmVyc2lvbiBvZiBgbGFuZ2dyYXBoLWNoZWNrcG9pbnRgIGlzIGluc3RhbGxlZCwgdGhlIGVycm9yIGRldGFpbHMgZmllbGQgYFwicmFuZ2VcImAgaXMgYFwiXCJgIGluc3RlYWQgb2YgYFwiPj00LDw1XCJgLiBUaGUgY29tbWVudCBzdGF0ZXMgdGhlIGRpY3QgbWlycm9ycyB0aGUgcHlwcm9qZWN0IGV4dHJhIGFuZCBpcyBhc3NlcnRlZCBlcXVhbCBieSB0ZXN0czsgdGhlIG1pc3NpbmcgZW50cnkgbWVhbnMgdGhhdCBhc3NlcnRpb24gd291bGQgYWxzbyBiZSBpbmNvbXBsZXRlIGZvciB0aGlzIHBhY2thZ2UsIHNvIGEgdmVyc2lvbi13aW5kb3cgY2hhbmdlIGNvdWxkIGdvIHVuZGV0ZWN0ZWQuIn0= -->
Automated mirror of cli/ (flattened), the root allowlist (server.json, glama.json, LICENSE, .gitleaks.toml) and the public-root/ community tree from thyn-ai/codna at 8c99e8e14e3db9cc33d8d3d65227dcb2c622d344. Merges itself via auto-merge once the mirror's required checks (codna review, Analyze (python)) pass and the codna-ai review approves; if a later sync lands first, this PR is closed as superseded -- every sync PR is a full snapshot of the exported surface.