Redact credentials from logs across Python and bash#528
Conversation
bashio echoes full supervisor API responses at debug level, leaking the MQTT broker password (GET /services/mqtt) and the add-on's own option values such as the Marstek account email/password (GET /addons/self/info) into the add-on log — visible to anyone the user shares a debug log with (discussion #520). Route all add-on output (and the inherited stdout of the Python app) through a line-buffered Python filter that masks passwords, tokens, secrets, usernames and mailbox values in JSON responses, plus credentials embedded in scheme://user:pass@host URIs. The existing config-dump redaction (print_redacted_config) is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV
The run.sh stream filter only covers the Home Assistant add-on path; standalone deployments (the root Dockerfile's `CMD ["astrameter"]` and docker-compose) launch the app directly and bypassed it entirely. Add an in-app redacting log formatter in config/logger.py so credentials are masked on every deployment. It operates on the fully-rendered line, so a secret can't slip through a message, a structured arg, or an exception traceback. Masks URI userinfo (scheme://user:pass@host) and password/token/secret/api-key/username/mailbox values in both JSON and inline key=value / key: value forms. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV
|
Warning Review limit reached
More reviews will be available in 47 minutes and 28 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. WalkthroughThis PR adds secret redaction to addon startup output and Python logging. It masks credential-like values in rendered log lines, traceback text, and streamed stdout/stderr, and adds tests for helper behavior and handler installation. ChangesSecret redaction flow
Sequence DiagramsequenceDiagram
participant setLogLevel as setLogLevel()
participant handlers as root logger handlers
participant fmt as _RedactingFormatter
participant redact as redact_secrets()
setLogLevel->>handlers: install _RedactingFormatter
handlers->>fmt: format LogRecord
fmt->>redact: redact rendered line
redact-->>fmt: masked output
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Steering evaluation (base vs head)Overall: 0 improved, 0 regressed, 15 unchanged across 15 metrics — mean 0% (unchanged). Priority: priority-weighted 0% (unchanged) — ✅ no do-no-harm guardrail regressions. Lower is better for every metric. See Metrics are the per-scenario mean of 5 seeds. Aggregate — mean across 31 scenarios
📊 Interactive grid-power charts (zoom / hover / toggle series) are in the self-contained What do these metrics mean?
Per-scenario tables (31 scenarios)mixed_cadence/eff — settle 50.8→50.8s, overshoot 164.5→164.5W, RMS 21.9→21.9W
mixed_cadence/fair — settle 47.7→47.7s, overshoot 70.4→70.4W, RMS 12.9→12.9W
mixed_cadence_solar/eff — settle 52.5→52.5s, overshoot 384.7→384.7W, RMS 28.6→28.6W
mixed_cadence_solar/fair — settle 56.0→56.0s, overshoot 75.4→75.4W, RMS 23.7→23.7W
mixed_venus_b2500/eff — settle 81.2→81.2s, overshoot 221.9→221.9W, RMS 18.6→18.6W
mixed_venus_b2500/fair — settle 75.4→75.4s, overshoot 231.1→231.1W, RMS 22.3→22.3W
phase_imbalance — settle 53.4→53.4s, overshoot 145.2→145.2W, RMS 30.3→30.3W
single_venus_d_solar — settle 24.2→24.2s, overshoot 94.4→94.4W, RMS 15.9→15.9W
single_venus_d_steps — settle 26.3→26.3s, overshoot 90.3→90.3W, RMS 15.5→15.5W
single_venus_d_washer — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 61.0→61.0W
single_venus_drain — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 907.3→907.3W
single_venus_fill — settle 360.0→360.0s, overshoot 0.0→0.0W, RMS 953.6→953.6W
single_venus_noisy — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 94.3→94.3W
single_venus_pv — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 60.8→60.8W
single_venus_solar — settle 26.8→26.8s, overshoot 80.3→80.3W, RMS 17.8→17.8W
single_venus_solar_slow — settle 33.9→33.9s, overshoot 68.3→68.3W, RMS 22.8→22.8W
single_venus_steps — settle 26.0→26.0s, overshoot 88.0→88.0W, RMS 14.7→14.7W
single_venus_steps_slow — settle 40.5→40.5s, overshoot 98.5→98.5W, RMS 14.8→14.8W
single_venus_trace — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 278.9→278.9W
single_venus_washer — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 61.0→61.0W
two_venus/eff — settle 18.1→18.1s, overshoot 126.1→126.1W, RMS 14.0→14.0W
two_venus/fair — settle 18.4→18.4s, overshoot 116.7→116.7W, RMS 13.8→13.8W
two_venus_noisy/eff — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 94.3→94.3W
two_venus_noisy/fair — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 94.2→94.2W
two_venus_slow/fair — settle 41.8→41.8s, overshoot 174.5→174.5W, RMS 14.0→14.0W
two_venus_solar/eff — settle 26.0→26.0s, overshoot 396.6→396.6W, RMS 20.4→20.4W
two_venus_solar/fair — settle 25.9→25.9s, overshoot 151.4→151.4W, RMS 20.4→20.4W
two_venus_trace/eff — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 283.1→283.1W
two_venus_trace/fair — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 282.1→282.1W
venus_d_plus_c/eff — settle 20.1→20.1s, overshoot 128.9→128.9W, RMS 14.7→14.7W
venus_d_plus_c/fair — settle 21.6→21.6s, overshoot 121.0→121.0W, RMS 14.6→14.6W
📊 Open the interactive report — |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
ha_addon/run.sh (1)
25-29: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winFilter can crash on a single bad byte and take down all logging (and possibly the addon).
sys.stdinis opened in text mode with the locale encoding. A non-UTF-8 byte anywhere in the merged stream raisesUnicodeDecodeError, the filter exits, and the upstream writers (this script and the inherited Python app at line 34) then hit a broken pipe /SIGPIPE— losing all subsequent output and potentially terminating the process. For a wrapper whose whole job is to stay in the log path, make it decode-tolerant and resilient.🛡️ Make the stream tolerant of bad bytes
import re, sys + +try: + sys.stdin.reconfigure(errors="replace") + sys.stdout.reconfigure(errors="replace") +except AttributeError: + pass🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ha_addon/run.sh` around lines 25 - 29, The stream filter in the stdin-to-stdout loop is too fragile because `sys.stdin` text decoding can raise on a bad byte and kill the logging path. Update the `run.sh` Python filter logic around the `for line in iter(sys.stdin.readline, '')` loop so it tolerates undecodable input and keeps forwarding output, using a resilient decode strategy and safe handling in the `PATTERNS` substitution/write path. Make sure the wrapper continues processing subsequent lines instead of exiting on a single malformed byte.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ha_addon/run.sh`:
- Around line 14-23: The config redaction patterns in print_redacted_config() do
not cover MQTT USERNAME, so it can still appear in the shared logs. Update the
PATTERNS list in run.sh to redact USERNAME alongside the existing MAILBOX,
PASSWORD, ACCESSTOKEN, TOKEN, and SECRET matches, ensuring the config dump masks
any USERNAME=... entries before logging.
---
Nitpick comments:
In `@ha_addon/run.sh`:
- Around line 25-29: The stream filter in the stdin-to-stdout loop is too
fragile because `sys.stdin` text decoding can raise on a bad byte and kill the
logging path. Update the `run.sh` Python filter logic around the `for line in
iter(sys.stdin.readline, '')` loop so it tolerates undecodable input and keeps
forwarding output, using a resilient decode strategy and safe handling in the
`PATTERNS` substitution/write path. Make sure the wrapper continues processing
subsequent lines instead of exiting on a single malformed byte.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c1ecb448-b272-4b9d-8828-835f9cc79c59
📒 Files selected for processing (3)
ha_addon/run.shsrc/astrameter/config/logger.pysrc/astrameter/config/logger_test.py
Address CodeRabbit review on #528: - The stdout/stderr redaction filter read stdin in the locale encoding, so a non-UTF-8 byte (likely under a C/POSIX locale) would raise UnicodeDecodeError, kill the filter and break the log path. Reconfigure stdin/stdout as UTF-8 with errors="replace" so one bad byte can't take logging down. - print_redacted_config() masked MAILBOX/PASSWORD/ACCESSTOKEN/TOKEN/SECRET but not USERNAME, so the MQTT USERNAME= line in the config dump still reached the log. Add USERNAME to the redaction set. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV
* Redact secrets from Home Assistant add-on logs bashio echoes full supervisor API responses at debug level, leaking the MQTT broker password (GET /services/mqtt) and the add-on's own option values such as the Marstek account email/password (GET /addons/self/info) into the add-on log — visible to anyone the user shares a debug log with (discussion #520). Route all add-on output (and the inherited stdout of the Python app) through a line-buffered Python filter that masks passwords, tokens, secrets, usernames and mailbox values in JSON responses, plus credentials embedded in scheme://user:pass@host URIs. The existing config-dump redaction (print_redacted_config) is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV * Redact secrets in the app's own logging, not just the add-on The run.sh stream filter only covers the Home Assistant add-on path; standalone deployments (the root Dockerfile's `CMD ["astrameter"]` and docker-compose) launch the app directly and bypassed it entirely. Add an in-app redacting log formatter in config/logger.py so credentials are masked on every deployment. It operates on the fully-rendered line, so a secret can't slip through a message, a structured arg, or an exception traceback. Masks URI userinfo (scheme://user:pass@host) and password/token/secret/api-key/username/mailbox values in both JSON and inline key=value / key: value forms. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV * Drop changelog entry for log redaction Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV * Harden add-on log redaction: decode-tolerant filter, mask USERNAME Address CodeRabbit review on #528: - The stdout/stderr redaction filter read stdin in the locale encoding, so a non-UTF-8 byte (likely under a C/POSIX locale) would raise UnicodeDecodeError, kill the filter and break the log path. Reconfigure stdin/stdout as UTF-8 with errors="replace" so one bad byte can't take logging down. - print_redacted_config() masked MAILBOX/PASSWORD/ACCESSTOKEN/TOKEN/SECRET but not USERNAME, so the MQTT USERNAME= line in the config dump still reached the log. Add USERNAME to the redaction set. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV --------- Co-authored-by: tomquist <528585+tomquist@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Add comprehensive credential redaction to prevent passwords, tokens, and other secrets from appearing in logs, regardless of how the application is launched (Home Assistant add-on, Docker, CLI, etc.). Redaction covers both Python logging output and bash/supervisor API responses.
Changes
Python logging: Introduce
redact_secrets()function and_RedactingFormatterclass that mask credentials in fully-rendered log lines (including tracebacks). Patterns cover:mqtt://user:pass@host→mqtt://***:***@host)Integration: Install the redacting formatter on all root handlers when
setLogLevel()is called, ensuring all logging passes through credential masking.Bash redaction: Add a Python-based line filter in
ha_addon/run.shthat applies the same redaction patterns to stdout/stderr before they reach the log. This covers supervisor API responses (MQTT broker password, add-on config values) that bashio echoes at debug level.Tests: Add comprehensive test coverage for
redact_secrets()with both sensitive and benign message examples, plus end-to-end tests verifying redaction works through the root logger and in traceback text.Implementation Details
ASTRAMETER_NO_LOG_REDACTenvironment variable allows disabling bash redaction if needed (set by the script itself to avoid recursive filtering).PASSWORD,password,marstek_password, etc.https://claude.ai/code/session_01BfVkp21s7kFFcCEmMJh1JV
Summary by CodeRabbit