-
Notifications
You must be signed in to change notification settings - Fork 0
๐ก๏ธ Sentinel: [MEDIUM] Fix DSN redaction credential leaks #995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
14f5eab
2bb6795
5d6bc50
87c918b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,15 +57,21 @@ def _password_candidates_from_dsn(dsn: str) -> set[str]: | |
| netloc, query = _split_dsn_best_effort(dsn) | ||
|
|
||
| if password: | ||
| decoded_password = unquote_plus(password) | ||
| candidates.add(password) | ||
| candidates.add(quote(password, safe="")) | ||
| candidates.add(decoded_password) | ||
| candidates.add(quote(decoded_password, safe="")) | ||
| candidates.add(quote_plus(decoded_password, safe="")) | ||
|
Comment on lines
+60
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ฏ Functional Correctness | ๐ก Minor | โก Quick win ๐ Supported by static analysis๐ Script executed: printf '%s\n' '--- repository conventions ---'
find /tmp/coderabbit-repo-knowledge/contextualwisdomlab-pg-erd-cloud-d2563f2a -path '*/backend*' -type f -name '*.md' -print
printf '%s\n' '--- target outline ---'
ast-grep outline backend/app/dsn_redaction.py
printf '%s\n' '--- target implementation ---'
cat -n backend/app/dsn_redaction.py | sed -n '1,140p'
printf '%s\n' '--- focused tests ---'
find . -type f \( -iname '*dsn*' -o -iname '*redact*' \) -printRepository: ContextualWisdomLab/pg-erd-cloud Length of output: 6264 ๐ Script executed: printf '%s\n' '--- applicable conventions ---'
cat /tmp/coderabbit-repo-knowledge/contextualwisdomlab-pg-erd-cloud-d2563f2a/conventions/backend.md
cat /tmp/coderabbit-repo-knowledge/contextualwisdomlab-pg-erd-cloud-d2563f2a/conventions/backend-app.md
printf '%s\n' '--- focused redaction tests ---'
cat -n backend/tests/test_dsn_redaction.py
printf '%s\n' '--- relevant learnings ---'
find /tmp/coderabbit-repo-knowledge/contextualwisdomlab-pg-erd-cloud-d2563f2a/learnings -type f -maxdepth 1 -print -exec cat {} \;Repository: ContextualWisdomLab/pg-erd-cloud Length of output: 5931 DSN userinfo ๋น๋ฐ๋ฒํธ์๋ userinfo์ ๐ค Prompt for AI Agents |
||
|
|
||
| if "@" in netloc: | ||
| userinfo = netloc.rsplit("@", 1)[0] | ||
| if ":" in userinfo: | ||
| raw_password = userinfo.split(":", 1)[1] | ||
| decoded_raw_password = unquote_plus(raw_password) | ||
| candidates.add(raw_password) | ||
| candidates.add(unquote(raw_password)) | ||
| candidates.add(decoded_raw_password) | ||
| candidates.add(quote(decoded_raw_password, safe="")) | ||
| candidates.add(quote_plus(decoded_raw_password, safe="")) | ||
|
|
||
| for part in query.split("&"): | ||
| key, sep, raw_value = part.partition("=") | ||
|
|
@@ -86,7 +92,9 @@ def _redact_secret_occurrences(message: str, secret: str) -> str: | |
| if len(secret) > 4: | ||
| return message.replace(secret, "***") | ||
|
|
||
| pattern = re.compile(rf"(?<![A-Za-z0-9]){re.escape(secret)}(?![A-Za-z0-9])") | ||
| prefix = r"(?<![A-Za-z0-9])" if secret and secret[0].isalnum() else "" | ||
| suffix = r"(?![A-Za-z0-9])" if secret and secret[-1].isalnum() else "" | ||
| pattern = re.compile(rf"{prefix}{re.escape(secret)}{suffix}") | ||
|
Comment on lines
+95
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ Info: Short symbol-bounded secrets match more broadly Dropping the alphanumeric boundary when a secret's edge is non-alnum (backend/app/dsn_redaction.py:95-97) makes short symbol-only candidates like Was this helpful? React with ๐ or ๐ to provide feedback.
Comment on lines
+95
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ฏ Functional Correctness | ๐ก Minor | โก Quick win ๋น์์ซ์ ์ ์ฉ ์งง์ secret์๋ ๋ฌด์ ํ ๋งค์นญ์ ์ ์ฉํ์ง ๋ง์ธ์.
secret์ ์์ซ์๊ฐ ์ ํ ์์ผ๋ฉด ๊ธฐ์กด ๊ฒฝ๊ณ๋ฅผ ์ ์งํ์ธ์. ๋๋ DSN ๋ฌธ๋งฅ์ ํ์ธํ ๋ค ํด๋น ํ๋ณด๋ง ์นํํ์ธ์. ๐งฐ Tools๐ช ast-grep (0.45.2)[warning] 96-96: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling. (redos-non-literal-regex-python) ๐ค Prompt for AI Agents |
||
| return pattern.sub("***", message) | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,3 +43,22 @@ def test_malformed_dsn_still_redacts_embedded_secrets() -> None: | |
| assert "s3cr3t" not in redacted | ||
| assert "q/secret" not in redacted | ||
| assert "password=***" in redacted | ||
|
|
||
|
|
||
| def test_short_password_surrounded_by_non_alphanumerics() -> None: | ||
| dsn = "postgresql://user:=ab=@db.example.com/app" | ||
| error = "driver failed for =ab= with password==ab= while using postgresql://user:=ab=@db.example.com/app" | ||
|
|
||
| redacted = redact_dsn_error_message(error, dsn) | ||
|
|
||
| assert "=ab=" not in redacted | ||
| assert "postgresql://user:***@db.example.com/app" in redacted | ||
|
Comment on lines
+48
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ฏ Functional Correctness | ๐ก Minor | โก Quick win ๋ณ๊ฒฝ๋ ๊ฒฝ๊ณ ์กฐ๊ฑด์ ์ง์ ์ฌํํ๋๋ก ํ ์คํธ๋ฅผ ์์ ํ์ธ์. ํ์ฌ ๋ชจ๋ ์ค๋ฅ ๋ฌธ์์ด์ As per coding guidelines: ๋์์ ๋ณ๊ฒฝํ Python ์ฝ๋์๋ ์ง์ค ํ ์คํธ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ๊ฐฑ์ ํด์ผ ํฉ๋๋ค. ๐ค Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
|
|
||
| def test_short_password_is_alphanumeric_but_bounded_by_non_alphanumeric() -> None: | ||
| dsn = "postgresql://user:pass@db.example.com/app" | ||
| error = "driver failed for =pass= with password==pass=" | ||
|
|
||
| redacted = redact_dsn_error_message(error, dsn) | ||
|
|
||
| assert "=pass=" not in redacted or "pass" not in redacted | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐ Info: unquote import left unused
After switching all call sites to
unquote_plus,unquoteat backend/app/dsn_redaction.py:4 is no longer referenced. No ruff/flake8 gate exists and mypy ignores unused imports, so CI stays green, but the import is now dead.(Refers to this code)
Was this helpful? React with ๐ or ๐ to provide feedback.