stage-348: apply Opus SHOULD-FIX-pre-merge — add '://' to _SENSITIVE_LOWER_MARKERS
Opus advisor flagged that PR #2171's credential prefilter only listed specific DB scheme prefixes and form keys, letting OAuth callback URLs, URL userinfo, signed-URL query params bypass the hard agent redactor. Adding the generic '://' marker restores the WebUI-as-hard-safety-boundary contract. Plain URLs without sensitive substrings still pass through unchanged because the redactor itself only mutates sensitive substrings. Regression-pinned with 5 new parametric cases in test_security_redaction.py plus 1 negative-case companion. Verified test FAILS without the fix and PASSES with it.
This commit is contained in:
1 parent
3bfa0fc885
commit
7c2b2785e7
3 files changed
+51
No files matched your search
@@ -24,6 +24,10 @@
|
||||
|
||||
- **PR #2190** by @xz-dev — Thinking-card reasoning updates now update text in place during reasoning deltas instead of rebuilding the card DOM on every append. Preserves expand state and scroll position across reasoning streaming, so users reading a long reasoning block don't get bounced to the top on every chunk. When a thinking card exists, the update path now sets `pre.textContent` directly; full-rebuild path only fires when no existing card is present.
|
||||
|
||||
### Stage-348 maintainer fixes
|
||||
|
||||
- **`api/helpers.py:_SENSITIVE_LOWER_MARKERS` — add `"://"` URL marker** — Opus SHOULD-FIX-pre-merge on PR #2171's credential prefilter. The prefilter listed only specific DB scheme prefixes (`postgres://`, `mysql://`, `mongodb://`, `redis://`, `amqp://`) and a closed set of form keys (`token=`, `secret=`, `password=`, `authorization=`, `key=`), so OAuth callback URLs (`https://example.com/callback?code=AUTH_OPAQUE`), URL userinfo (`https://admin:supersecret@api.example.com/v1`), and signed-URL query params (`?signature=...`, `?session=...`) bypassed the hard agent redactor entirely — defeating the "WebUI API responses are a hard safety boundary" comment at `helpers.py:189`. Adding the generic `"://"` marker routes every http(s)/ws(s)/ftp URL to the hard redactor (which then selectively redacts only the sensitive substrings — plain `https://example.com/guide.html` URLs still pass through unchanged). Regression-pinned with 5 new parametric cases in `tests/test_security_redaction.py` (`test_redact_text_prefilter_covers_url_userinfo_and_sensitive_query_params`) covering OAuth code, URL userinfo, signed-URL signature, session query param, and WebSocket token — plus a negative-case `test_redact_text_prefilter_admits_plain_urls_without_sensitive_params` confirming the redactor doesn't over-redact plain URLs. Verified by reverting the fix locally: all 5 sensitive-URL cases fail; restoring the fix: all 5 pass. ~6 LOC code + ~50 LOC test.
|
||||
|
||||
## [v0.51.54] — 2026-05-13 — Release AD (stage-347 — singleton self-built — NVIDIA NIM prefix preservation fix #2179)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -256,6 +256,7 @@ _SENSITIVE_LOWER_MARKERS = (
|
||||
"mongodb://",
|
||||
"redis://",
|
||||
"amqp://",
|
||||
"://", # stage-348 Opus SHOULD-FIX: catch http(s)/ws(s)/ftp URL userinfo + sensitive query params (#2171 follow-up)
|
||||
"access_token",
|
||||
"refresh_token",
|
||||
"id_token",
|
||||
|
||||
@@ -188,6 +188,52 @@ def test_redact_text_prefilter_covers_known_prefixed_credentials(prefix, suffix)
|
||||
assert token not in result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text,must_be_redacted", [
|
||||
# OAuth callback URL with `code=` query param — agent's
|
||||
# _redact_url_query_params should fire
|
||||
("https://example.com/callback?code=AUTH_OPAQUE_VALUE", "AUTH_OPAQUE_VALUE"),
|
||||
# URL userinfo (user:password embedded in scheme://) — agent's
|
||||
# _redact_url_userinfo should fire
|
||||
("https://admin:supersecretpassword@api.example.com/v1", "supersecretpassword"),
|
||||
# Signed-URL sensitive query param
|
||||
("https://cdn.example.com/file.zip?signature=ABCDEFGHIJKL", "ABCDEFGHIJKL"),
|
||||
# Session-token query param
|
||||
("https://example.com/dashboard?session=xyzABC999DEF", "xyzABC999DEF"),
|
||||
# WebSocket URL with token query param
|
||||
("wss://example.com/ws?token=jwt_ABCDEFGHIJ", "jwt_ABCDEFGHIJ"),
|
||||
])
|
||||
def test_redact_text_prefilter_covers_url_userinfo_and_sensitive_query_params(text, must_be_redacted):
|
||||
"""Stage-348 Opus follow-up to PR #2171: the credential prefilter must
|
||||
catch URL userinfo and sensitive query params so they still reach the
|
||||
hard agent redactor instead of bypassing it.
|
||||
|
||||
Pre-fix, the prefilter only listed specific DB scheme prefixes
|
||||
(postgres://, mysql://, etc.) and a closed set of form keys, so OAuth
|
||||
callback URLs pasted into chat could pass through to the response
|
||||
verbatim. The fix adds the generic "://" marker so http(s)/ws(s)/ftp
|
||||
URLs always route to the hard redactor.
|
||||
"""
|
||||
import api.helpers as helpers
|
||||
|
||||
result = helpers._redact_text(text, _enabled=True)
|
||||
|
||||
assert must_be_redacted not in result, (
|
||||
f"sensitive substring {must_be_redacted!r} leaked through prefilter: "
|
||||
f"result was {result!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_redact_text_prefilter_admits_plain_urls_without_sensitive_params():
|
||||
"""Stage-348 follow-up companion: plain URLs with no sensitive params
|
||||
still pass through unchanged (the `://` marker only routes to the hard
|
||||
redactor; the redactor itself only mutates sensitive substrings)."""
|
||||
import api.helpers as helpers
|
||||
|
||||
plain = "Check the docs at https://example.com/guide.html for details."
|
||||
result = helpers._redact_text(plain, _enabled=True)
|
||||
assert result == plain
|
||||
|
||||
|
||||
def test_redact_value_works_with_legacy_agent_redact_signature(monkeypatch):
|
||||
"""_redact_text must tolerate older redact_sensitive_text(text) signatures."""
|
||||
fake_agent = types.ModuleType("agent")
|
||||
|
||||
Reference in new issue
Block a user