fix(security): redact sensitive URL parameters in logs#853
Conversation
Add pkg/redact package to centralize redaction of sensitive query parameters (apikey, api_key, passkey, token, password) from URLs and error messages. Also handles userinfo passwords and proxy path segments containing API keys. Apply redaction to all identified leak points: - pkg/gojackett: HTTP errors and enclosure URLs - pkg/prowlarr: request errors - internal/services/jackett: fetchCaps, Download, service logs - internal/api/handlers: jackett discovery, torrent URLs - internal/proxy: middleware, handler, retry transport - internal/metrics: basic auth credentials Fixes #839
|
Warning Rate limit exceeded@s0up4200 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 12 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughAdds a new pkg/redact package and integrates redaction across logging and error wrapping in handlers, proxy, metrics, services, and clients to prevent leaking sensitive URL/query/basic-auth data; includes unit tests and a regression test. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
pkg/redact/redact.go (2)
14-23: Redaction primitives are well-scoped; consider small proxy-path regex enhancementURL/userinfo/query redaction in
URLString, plus the layeredString/ProxyPathhelpers, covers the sensitive cases (apikey/api_key/passkey/token/password, userinfo passwords,/proxy/{key}/segments) without overreaching. The fallback fromURLStringtoStringon parse failure is a good safety net.One minor, optional improvement:
proxyPathRegex((/proxy/)([^/]+)(/|$)) will not match/proxy/<key>?...when applied to arbitrary strings (because?prevents the final(/|$)from matching). It’s already correct for real URLs viaURLString(which passes only the path), but if you expect to feed full raw request lines or log messages intoString, you might want to extend the terminator to[/?#]to also catch query/fragment cases.Also applies to: 28-78, 101-118, 120-127, 129-140
80-99: URLError redacts correctly but may drop outer error context when misused
URLErrorcorrectly finds a*url.Errorand replaces its URL withURLString(urlErr.URL), which fixes the core leak forhttp.Clientfailures. Note that it returns a newurl.Error, discarding any outer wrappers that may have been added around the original error. That’s fine for current call sites that pass the rawDoerror, but if you later callURLErroron already-wrapped errors you’ll lose that higher-level context.To avoid surprises, either (a) document that callers should pass the raw transport error, or (b) consider mutating
urlErr.URLin place and returning the originalerrso any outer wrapping is preserved.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
internal/api/handlers/jackett.gointernal/api/handlers/torrents.gointernal/metrics/server.gointernal/proxy/handler.gointernal/proxy/middleware.gointernal/proxy/retry_transport.gointernal/services/jackett/client.gointernal/services/jackett/client_test.gointernal/services/jackett/service.gopkg/gojackett/http.gopkg/gojackett/methods.gopkg/prowlarr/client.gopkg/redact/redact.gopkg/redact/redact_test.gopkg/redact/url_error_test.go
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-06T12:11:04.963Z
Learnt from: Audionut
Repo: autobrr/qui PR: 553
File: internal/services/crossseed/service.go:1045-1082
Timestamp: 2025-11-06T12:11:04.963Z
Learning: The autobrr/qui project uses a custom go-qbittorrent client library (github.com/autobrr/go-qbittorrent) that supports both "paused" and "stopped" parameters when adding torrents via the options map. Both parameters should be set together when controlling torrent start state, as seen in internal/services/crossseed/service.go and throughout the codebase.
Applied to files:
internal/services/jackett/client.gointernal/api/handlers/torrents.go
📚 Learning: 2025-11-25T22:46:03.762Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 632
File: internal/backups/service.go:1401-1404
Timestamp: 2025-11-25T22:46:03.762Z
Learning: In qui's backup service (internal/backups/service.go), background torrent downloads initiated during manifest import intentionally use a fire-and-forget pattern with the shared service context (s.ctx). Per-run cancellation is not needed, as orphaned downloads completing after run deletion are considered harmless and acceptable. This design prioritizes simplicity over per-run lifecycle management for background downloads.
Applied to files:
internal/services/jackett/client.gointernal/api/handlers/torrents.go
📚 Learning: 2025-11-27T15:33:39.007Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 641
File: internal/services/jackett/service.go:309-330
Timestamp: 2025-11-27T15:33:39.007Z
Learning: In internal/services/jackett/service.go, synchronous execution paths (test executors, interactive searches with explicit indexer selection, and scheduler-unavailable fallback) intentionally pass jobID=0 to result callbacks. This is acceptable because outcome tracking via ReportIndexerOutcome is only used for async cross-seed operations that always use the scheduler and receive unique jobIDs. Interactive searches and tests do not use outcome tracking.
Applied to files:
internal/api/handlers/jackett.gointernal/services/jackett/client_test.go
📚 Learning: 2025-12-11T08:40:01.329Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 746
File: internal/services/reannounce/service.go:480-481
Timestamp: 2025-12-11T08:40:01.329Z
Learning: In autobrr/qui's internal/services/reannounce/service.go, the hasHealthyTracker, getProblematicTrackers, and getHealthyTrackers functions intentionally match qbrr's lenient tracker health logic (skip unregistered trackers and check if any other tracker is healthy) rather than go-qbittorrent's strict isTrackerStatusOK logic (which treats unregistered as an immediate failure). For multi-tracker torrents, if one tracker is working, reannouncing won't help. The duplication of the health check logic across these three functions is acceptable as it's a simple one-liner, and extracting it would add unnecessary complexity.
Applied to files:
internal/proxy/handler.gointernal/api/handlers/torrents.go
🧬 Code graph analysis (8)
pkg/prowlarr/client.go (1)
pkg/redact/redact.go (1)
URLError(83-99)
pkg/gojackett/http.go (1)
pkg/redact/redact.go (3)
URLError(83-99)URLString(28-78)String(107-118)
internal/services/jackett/client.go (1)
pkg/redact/redact.go (2)
URLError(83-99)URLString(28-78)
internal/services/jackett/service.go (1)
pkg/redact/redact.go (1)
URLString(28-78)
internal/proxy/handler.go (1)
pkg/redact/redact.go (1)
String(107-118)
internal/services/jackett/client_test.go (1)
internal/services/jackett/client.go (1)
DiscoverJackettIndexers(481-565)
internal/metrics/server.go (1)
pkg/redact/redact.go (1)
BasicAuthUser(131-140)
internal/api/handlers/torrents.go (1)
pkg/redact/redact.go (1)
URLString(28-78)
🔇 Additional comments (23)
internal/services/jackett/client_test.go (1)
7-16: Regression test for API-key redaction looks solidThe new imports and
TestDiscoverJackettIndexers_RedactsAPIKeycleanly exercise the failure path without depending on a specific error shape, while asserting both absence of the raw key and presence ofapikey=REDACTEDwhen applicable. No issues found.Also applies to: 143-175
pkg/prowlarr/client.go (1)
17-19: SearchIndexer error wrapping now safely redacts request URLsWrapping
c.httpClient.Doerrors withredact.URLError(err)insidefmt.Errorf("prowlarr request failed: %w", …)achieves URL redaction for*url.Errorwithout changing the surrounding message or control flow. This is aligned with the PR’s objectives and looks correct.Also applies to: 149-152
internal/services/jackett/service.go (1)
26-31: Redactingbase_urlin search logs is appropriate and non-invasiveUsing
redact.URLString(idx.BaseURL)for thebase_urllog field keeps the existing observability while ensuring any misconfigured base URLs with query secrets or userinfo are masked. No functional behavior changes; this is a safe hardening step.Also applies to: 1785-1810
internal/api/handlers/jackett.go (1)
19-22: Discovery error logging now properly redacts base URL and error messageThe
DiscoverIndexershandler’s error path now logsbase_urlviaredact.URLStringand the error text viaredact.String(err.Error()), which should block the Prowlarr API-key leak that motivated issue #839 without altering response codes or bodies. This looks correct and consistent with the new redact utilities.Also applies to: 840-865
internal/api/handlers/torrents.go (1)
25-29: URL logging in AddTorrent is now redaction-awareSwitching the three error logs in
AddTorrentto useredact.URLString(url)ensures that any sensitive query parameters or userinfo embedded in magnet/HTTP URLs are masked before hitting logs, while keeping the rest of the context intact. No control-flow changes; this is a straightforward hardening.Also applies to: 553-599
internal/metrics/server.go (1)
12-18: Invalid metrics basic-auth credentials are now logged without passwordsUsing
redact.BasicAuthUser(cred)in the warning for malformedbasicAuthUsersConfigavoids exposing rawuser:passwordpairs in logs while still indicating which entry was bad. This is a good, low-risk improvement.Also applies to: 32-41
pkg/gojackett/methods.go (1)
13-16: GetEnclosureCtx now wraps errors with a redacted enclosure stringChanging the
errors.Wrapcontext from the rawenclosuretoredact.URLString(enclosure)preserves the usefulness of the error message while masking any sensitive data in the enclosure URL. Control flow and return types are unchanged.Also applies to: 86-90
internal/proxy/handler.go (1)
264-269: LGTM! Appropriate redaction of proxy errors.The change correctly applies redaction to error messages that may contain sensitive URL parameters while preserving all relevant context (client, instanceId, method) for debugging.
pkg/redact/url_error_test.go (2)
14-117: LGTM! Comprehensive test coverage for URLError redaction.The test cases cover all critical scenarios including nil errors, various sensitive parameters (apikey, token, password, api_key, passkey), multiple params, wrapped errors, and non-url.Error cases. The assertions properly verify both the presence of REDACTED markers and the absence of actual secrets.
119-140: LGTM! Important validation of error type preservation.This test ensures that
URLError()maintains theurl.Errortype forerrors.As()compatibility, which is crucial for error handling code that checks error types. The verification that the Op field is preserved and the URL is redacted is thorough.internal/proxy/retry_transport.go (3)
52-59: LGTM! Proper redaction on successful retry.The URL redaction here ensures that even successful retry logs don't expose sensitive query parameters or credentials.
66-71: LGTM! Comprehensive redaction for non-retryable errors.Both the error message and URL are redacted, providing defense-in-depth since errors often contain the request URL with sensitive parameters.
80-108: LGTM! Consistent redaction across all retry paths.The redaction is applied uniformly across all logging scenarios: non-idempotent methods, max retries exceeded, and retry attempts. This consistency ensures no log path accidentally leaks sensitive data.
pkg/gojackett/http.go (2)
30-36: LGTM! Proper error redaction in HTTP client.The dual redaction approach (both the error via
URLErrorand the URL parameter viaURLString) ensures comprehensive protection against secret leakage in HTTP errors.
111-132: LGTM! Retry logic properly redacts sensitive data.The redaction is correctly applied to both the returned error and the retry logging callback, ensuring that the full retry sequence doesn't leak secrets while maintaining debuggability.
internal/services/jackett/client.go (2)
254-262: LGTM! Consistent redaction across all caps fetch paths.The redaction is uniformly applied to all three backend types (Jackett, Prowlarr, Native), ensuring that caps fetch errors don't leak API keys regardless of the backend configuration.
Also applies to: 289-297, 324-332
366-374: LGTM! Download errors properly redacted.Both network errors and HTTP status errors are appropriately redacted. The
DownloadErrorstruct stores the redacted URL, ensuring that any subsequent error handling or logging won't accidentally leak the original URL with sensitive parameters.internal/proxy/middleware.go (2)
112-114: LGTM! Appropriate reduction of logged API key details.Logging only whether the key exists (rather than the key itself or portions of it) is the right approach for security. This still provides debugging capability to identify missing keys without exposing sensitive data.
116-122: Good catch! Return statement prevents unauthorized access.The addition of the
returnstatement after the "Missing API key" error response is critical. Without it, execution would continue tonext.ServeHTTP(w, r.WithContext(ctx))at line 162, potentially allowing unauthenticated requests to proceed. This is an important security fix.pkg/redact/redact_test.go (4)
11-145: LGTM! Excellent test coverage for URLString.The tests comprehensively cover:
- All sensitive parameter types (apikey, api_key, passkey, token, password)
- Case-insensitive matching (critical for security)
- Edge cases (empty strings, empty param values, multi-valued params)
- Combined scenarios (userinfo + query params, proxy paths)
- Non-sensitive parameters (ensuring they're preserved)
This thorough coverage helps ensure no regression in the redaction logic.
147-245: LGTM! Comprehensive testing of string-based redaction.The test cases cover important real-world scenarios including:
- Error messages containing sensitive data
- Unparseable URL fragments (ensuring fallback to regex works)
- Multiple parameters in a single string
- Combination scenarios (userinfo, proxy paths)
The coverage ensures that even malformed or partial URLs in error messages don't leak secrets.
247-287: LGTM! Focused proxy path redaction tests.The test cases appropriately cover the proxy path redaction scenarios including edge cases like trailing slashes and keys at the end of paths. The exact-match assertions are appropriate for this specific use case.
289-329: LGTM! Complete basic auth redaction tests.The test cases cover all relevant scenarios for basic authentication credential redaction, including edge cases like empty passwords and complex password characters. The tests ensure usernames are preserved while passwords are consistently redacted.
- Split isRetryableError into smaller helper functions to reduce cognitive complexity - Rename max parameter to maxBackoff to avoid shadowing builtin - Use range over int (Go 1.22+) for backoff calculation loop - Remove deprecated netErr.Temporary() call - Add nolint:wrapcheck directive for RoundTrip (transport errors should not be wrapped) - Use testify assertions in redact package tests
Apply redact.ProxyPath() to URL paths in logger middleware and proxy handler to prevent API key leakage in regular debug logs. The previous commit only covered error logs.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
internal/api/middleware/logger.go (1)
46-46: Proxy path redaction correctly applied.The change properly redacts proxy path patterns (e.g.,
/proxy/{key}/→/proxy/REDACTED/) in the access log.Note that
r.URL.Pathcontains only the path component, so query parameters (e.g.,?apikey=...) are not logged here. This is a safe approach that prevents any query parameter leaks in this middleware. If more comprehensive request logging is desired for debugging while maintaining security, consider usingredact.URLString(r.URL.String())to log the complete URL with all sensitive components redacted.Optional: Log complete redacted URL for more comprehensive debugging
If logging the full request URL (including query parameters but redacted) would be valuable for debugging, consider:
- "url": redact.ProxyPath(r.URL.Path), + "url": redact.URLString(r.URL.String()),This would log the complete URL with query parameters like
apikey,passkey,token, etc. automatically redacted, providing more debugging context while maintaining security.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
internal/api/middleware/logger.gointernal/proxy/handler.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/proxy/handler.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-28T18:44:10.496Z
Learnt from: s0up4200
Repo: autobrr/qui PR: 876
File: internal/logstream/hub_test.go:188-192
Timestamp: 2025-12-28T18:44:10.496Z
Learning: In Go 1.25 (Aug 2025), use wg.Go(func()) to spawn a goroutine and automate the Add/Done lifecycle. Replace manual patterns like wg.Add(1); go func(){ defer wg.Done(); ... }() with wg.Go(func(){ ... }). Ensure the codebase builds with Go 1.25+ and apply this in relevant Go files (e.g., internal/logstream/hub_test.go). If targeting older Go versions, maintain the existing pattern.
Applied to files:
internal/api/middleware/logger.go
🧬 Code graph analysis (1)
internal/api/middleware/logger.go (1)
pkg/redact/redact.go (1)
ProxyPath(122-127)
🔇 Additional comments (1)
internal/api/middleware/logger.go (1)
13-14: LGTM!The import is correctly added to support the redaction functionality used below.
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/autobrr/qui](https://github.com/autobrr/qui) | minor | `v1.11.0` → `v1.12.0` | --- ### Release Notes <details> <summary>autobrr/qui (ghcr.io/autobrr/qui)</summary> ### [`v1.12.0`](https://github.com/autobrr/qui/releases/tag/v1.12.0) [Compare Source](autobrr/qui@v1.11.0...v1.12.0) #### Changelog ##### New Features - [`202e950`](autobrr/qui@202e950): feat(automations): Add `free_space` condition ([#​1061](autobrr/qui#1061)) ([@​Barcode-eng](https://github.com/Barcode-eng)) - [`3b106d6`](autobrr/qui@3b106d6): feat(automations): make conditions optional for non-delete rules and add drag reorder ([#​939](autobrr/qui#939)) ([@​s0up4200](https://github.com/s0up4200)) - [`0684d75`](autobrr/qui@0684d75): feat(config): Add QUI\_\_OIDC\_CLIENT\_SECRET\_FILE env var ([#​841](autobrr/qui#841)) ([@​PedDavid](https://github.com/PedDavid)) - [`dac0173`](autobrr/qui@dac0173): feat(config): allow disabling tracker icon fetching ([#​823](autobrr/qui#823)) ([@​s0up4200](https://github.com/s0up4200)) - [`dc10bad`](autobrr/qui@dc10bad): feat(crossseed): add cancel run and opt-in errored torrent recovery ([#​918](autobrr/qui#918)) ([@​s0up4200](https://github.com/s0up4200)) - [`cd1fcc9`](autobrr/qui@cd1fcc9): feat(crossseed): add custom category option for cross-seeds ([#​907](autobrr/qui#907)) ([@​s0up4200](https://github.com/s0up4200)) - [`d189fe9`](autobrr/qui@d189fe9): feat(crossseed): add indexerName to webhook apply + fix category mode defaults ([#​916](autobrr/qui#916)) ([@​s0up4200](https://github.com/s0up4200)) - [`03a147e`](autobrr/qui@03a147e): feat(crossseed): add option to skip recheck-required matches ([#​825](autobrr/qui#825)) ([@​s0up4200](https://github.com/s0up4200)) - [`edae00a`](autobrr/qui@edae00a): feat(crossseed): add optional hardlink mode for cross-seeding ([#​849](autobrr/qui#849)) ([@​s0up4200](https://github.com/s0up4200)) - [`0938436`](autobrr/qui@0938436): feat(crossseed): add source aliasing for WEB/WEB-DL/WEBRip precheck matching ([#​874](autobrr/qui#874)) ([@​s0up4200](https://github.com/s0up4200)) - [`65f6129`](autobrr/qui@65f6129): feat(crossseed): show failure reasons, prune runs, and add cache cleanup ([#​923](autobrr/qui#923)) ([@​s0up4200](https://github.com/s0up4200)) - [`e10fba8`](autobrr/qui@e10fba8): feat(details): torrent details panel improvements ([#​884](autobrr/qui#884)) ([@​s0up4200](https://github.com/s0up4200)) - [`6921140`](autobrr/qui@6921140): feat(docs): add Docusaurus documentation site ([@​s0up4200](https://github.com/s0up4200)) - [`6a5a66c`](autobrr/qui@6a5a66c): feat(docs): add Icon and webUI variables to the Unraid install guide ([#​942](autobrr/qui#942)) ([@​BaukeZwart](https://github.com/BaukeZwart)) - [`281fce7`](autobrr/qui@281fce7): feat(docs): add local search plugin ([#​1076](autobrr/qui#1076)) ([@​s0up4200](https://github.com/s0up4200)) - [`566de08`](autobrr/qui@566de08): feat(docs): add qui logo, update readme, remove v4 flag ([@​s0up4200](https://github.com/s0up4200)) - [`b83ac5a`](autobrr/qui@b83ac5a): feat(docs): apply minimal.css theme to Docusaurus ([@​s0up4200](https://github.com/s0up4200)) - [`fe6a6df`](autobrr/qui@fe6a6df): feat(docs): improve documentation pages and add support page ([@​s0up4200](https://github.com/s0up4200)) - [`62a7ad5`](autobrr/qui@62a7ad5): feat(docs): use qui favicon ([@​s0up4200](https://github.com/s0up4200)) - [`5d124c0`](autobrr/qui@5d124c0): feat(orphan): add auto cleanup mode ([#​897](autobrr/qui#897)) ([@​s0up4200](https://github.com/s0up4200)) - [`3172ad9`](autobrr/qui@3172ad9): feat(settings): add log settings + live log stream ([#​876](autobrr/qui#876)) ([@​s0up4200](https://github.com/s0up4200)) - [`3c1b34b`](autobrr/qui@3c1b34b): feat(torrents): add "torrent introuvable" to unregistered status ([#​836](autobrr/qui#836)) ([@​kephasdev](https://github.com/kephasdev)) - [`afe4d39`](autobrr/qui@afe4d39): feat(torrents): add tracker URL editing for single torrents ([#​848](autobrr/qui#848)) ([@​s0up4200](https://github.com/s0up4200)) - [`76dedd7`](autobrr/qui@76dedd7): feat(torrents): update GeneralTabHorizontal to display limits and improve layout ([#​1078](autobrr/qui#1078)) ([@​martylukyy](https://github.com/martylukyy)) - [`6831c24`](autobrr/qui@6831c24): feat(ui): unify payment options into single dialog ([@​s0up4200](https://github.com/s0up4200)) - [`4dcdf7f`](autobrr/qui@4dcdf7f): feat(web): add local file access indicator to instance cards ([#​911](autobrr/qui#911)) ([@​s0up4200](https://github.com/s0up4200)) - [`a560e5e`](autobrr/qui@a560e5e): feat(web): compact torrent details panel ([#​833](autobrr/qui#833)) ([@​martylukyy](https://github.com/martylukyy)) - [`557e7bd`](autobrr/qui@557e7bd): feat: add issue/discussion template ([#​945](autobrr/qui#945)) ([@​s0up4200](https://github.com/s0up4200)) - [`8b93719`](autobrr/qui@8b93719): feat: add workflow automation system with category actions, orphan scanner, and hardlink detection ([#​818](autobrr/qui#818)) ([@​s0up4200](https://github.com/s0up4200)) ##### Bug Fixes - [`b85ad6b`](autobrr/qui@b85ad6b): fix(automations): allow delete rules to match incomplete torrents ([#​926](autobrr/qui#926)) ([@​s0up4200](https://github.com/s0up4200)) - [`ae06200`](autobrr/qui@ae06200): fix(automations): make tags field condition operators tag-aware ([#​908](autobrr/qui#908)) ([@​s0up4200](https://github.com/s0up4200)) - [`ace0101`](autobrr/qui@ace0101): fix(crossseed): detect folder mismatch for bare file to folder cross-seeds ([#​846](autobrr/qui#846)) ([@​s0up4200](https://github.com/s0up4200)) - [`1cc1243`](autobrr/qui@1cc1243): fix(crossseed): enforce resolution and language matching with sensible defaults ([#​855](autobrr/qui#855)) ([@​s0up4200](https://github.com/s0up4200)) - [`cefb9cd`](autobrr/qui@cefb9cd): fix(crossseed): execute external program reliably after injection ([#​1083](autobrr/qui#1083)) ([@​s0up4200](https://github.com/s0up4200)) - [`867e2da`](autobrr/qui@867e2da): fix(crossseed): improve matching with size validation and relaxed audio checks ([#​845](autobrr/qui#845)) ([@​s0up4200](https://github.com/s0up4200)) - [`4b5079b`](autobrr/qui@4b5079b): fix(crossseed): persist custom category settings in PATCH handler ([#​913](autobrr/qui#913)) ([@​s0up4200](https://github.com/s0up4200)) - [`cfbbc1f`](autobrr/qui@cfbbc1f): fix(crossseed): prevent season packs matching episodes ([#​854](autobrr/qui#854)) ([@​s0up4200](https://github.com/s0up4200)) - [`c7c1706`](autobrr/qui@c7c1706): fix(crossseed): reconcile interrupted runs on startup ([#​1084](autobrr/qui#1084)) ([@​s0up4200](https://github.com/s0up4200)) - [`7d633bd`](autobrr/qui@7d633bd): fix(crossseed): use ContentPath for manually-managed single-file torrents ([#​832](autobrr/qui#832)) ([@​s0up4200](https://github.com/s0up4200)) - [`d5db761`](autobrr/qui@d5db761): fix(database): include arr\_instances in string pool cleanup + remove auto-recovery ([#​898](autobrr/qui#898)) ([@​s0up4200](https://github.com/s0up4200)) - [`c73ec6f`](autobrr/qui@c73ec6f): fix(database): prevent race between stmt cache access and db close ([#​840](autobrr/qui#840)) ([@​s0up4200](https://github.com/s0up4200)) - [`a40b872`](autobrr/qui@a40b872): fix(db): drop legacy hardlink columns from cross\_seed\_settings ([#​912](autobrr/qui#912)) ([@​s0up4200](https://github.com/s0up4200)) - [`e400af3`](autobrr/qui@e400af3): fix(db): recover wedged SQLite writer + stop cross-seed tight loop ([#​890](autobrr/qui#890)) ([@​s0up4200](https://github.com/s0up4200)) - [`90e15b4`](autobrr/qui@90e15b4): fix(deps): update rls to recognize IP as iPlayer ([#​922](autobrr/qui#922)) ([@​s0up4200](https://github.com/s0up4200)) - [`8e81b9f`](autobrr/qui@8e81b9f): fix(proxy): honor TLSSkipVerify for proxied requests ([#​1051](autobrr/qui#1051)) ([@​s0up4200](https://github.com/s0up4200)) - [`eb2bee0`](autobrr/qui@eb2bee0): fix(security): redact sensitive URL parameters in logs ([#​853](autobrr/qui#853)) ([@​s0up4200](https://github.com/s0up4200)) - [`40982bc`](autobrr/qui@40982bc): fix(themes): prevent reset on license errors, improve switch performance ([#​844](autobrr/qui#844)) ([@​s0up4200](https://github.com/s0up4200)) - [`a8a32f7`](autobrr/qui@a8a32f7): fix(ui): incomplete torrents aren't "Completed: 1969-12-31" ([#​851](autobrr/qui#851)) ([@​finevan](https://github.com/finevan)) - [`5908bba`](autobrr/qui@5908bba): fix(ui): preserve category collapse state when toggling incognito mode ([#​834](autobrr/qui#834)) ([@​jabloink](https://github.com/jabloink)) - [`25c847e`](autobrr/qui@25c847e): fix(ui): torrents with no creation metadata don't display 1969 ([#​873](autobrr/qui#873)) ([@​finevan](https://github.com/finevan)) - [`6403b6a`](autobrr/qui@6403b6a): fix(web): column filter status now matches all states in category ([#​880](autobrr/qui#880)) ([@​s0up4200](https://github.com/s0up4200)) - [`eafc4e7`](autobrr/qui@eafc4e7): fix(web): make delete cross-seed check rely on content\_path matches ([#​1080](autobrr/qui#1080)) ([@​s0up4200](https://github.com/s0up4200)) - [`d57c749`](autobrr/qui@d57c749): fix(web): only show selection checkbox on normal view ([#​830](autobrr/qui#830)) ([@​jabloink](https://github.com/jabloink)) - [`60338f6`](autobrr/qui@60338f6): fix(web): optimize TorrentDetailsPanel for mobile view and make tabs scrollable horizontally ([#​1066](autobrr/qui#1066)) ([@​martylukyy](https://github.com/martylukyy)) - [`aedab87`](autobrr/qui@aedab87): fix(web): speed limit input reformatting during typing ([#​881](autobrr/qui#881)) ([@​s0up4200](https://github.com/s0up4200)) - [`df7f3e0`](autobrr/qui@df7f3e0): fix(web): truncate file progress percentage instead of rounding ([#​919](autobrr/qui#919)) ([@​s0up4200](https://github.com/s0up4200)) - [`2fadd01`](autobrr/qui@2fadd01): fix(web): update eslint config for flat config compatibility ([#​879](autobrr/qui#879)) ([@​s0up4200](https://github.com/s0up4200)) - [`721cedd`](autobrr/qui@721cedd): fix(web): use fixed heights for mobile torrent cards ([#​812](autobrr/qui#812)) ([@​jabloink](https://github.com/jabloink)) - [`a7db605`](autobrr/qui@a7db605): fix: remove pnpm-workspace.yaml breaking CI ([@​s0up4200](https://github.com/s0up4200)) - [`c0ddc0a`](autobrr/qui@c0ddc0a): fix: use prefix matching for allowed bash commands ([@​s0up4200](https://github.com/s0up4200)) ##### Other Changes - [`fff52ce`](autobrr/qui@fff52ce): chore(ci): disable reviewer ([@​s0up4200](https://github.com/s0up4200)) - [`7ef2a38`](autobrr/qui@7ef2a38): chore(ci): fix automated triage and deduplication workflows ([#​1057](autobrr/qui#1057)) ([@​s0up4200](https://github.com/s0up4200)) - [`d84910b`](autobrr/qui@d84910b): chore(docs): move Tailwind to documentation workspace only ([@​s0up4200](https://github.com/s0up4200)) - [`37ebe05`](autobrr/qui@37ebe05): chore(docs): move netlify.toml to documentation directory ([@​s0up4200](https://github.com/s0up4200)) - [`e25de38`](autobrr/qui@e25de38): chore(docs): remove disclaimer ([@​s0up4200](https://github.com/s0up4200)) - [`c59b809`](autobrr/qui@c59b809): chore(docs): update support sections ([#​1063](autobrr/qui#1063)) ([@​s0up4200](https://github.com/s0up4200)) - [`b723523`](autobrr/qui@b723523): chore(tests): remove dead tests and optimize slow test cases ([#​842](autobrr/qui#842)) ([@​s0up4200](https://github.com/s0up4200)) - [`662a1c6`](autobrr/qui@662a1c6): chore(workflows): update runners from 4vcpu to 2vcpu for all jobs ([#​859](autobrr/qui#859)) ([@​s0up4200](https://github.com/s0up4200)) - [`46f2a1c`](autobrr/qui@46f2a1c): chore: clean up repo root by moving Docker, scripts, and community docs ([#​1054](autobrr/qui#1054)) ([@​s0up4200](https://github.com/s0up4200)) - [`2f27c0d`](autobrr/qui@2f27c0d): chore: remove old issue templates ([@​s0up4200](https://github.com/s0up4200)) - [`04f361a`](autobrr/qui@04f361a): ci(triage): add labeling for feature-requests-ideas discussions ([@​s0up4200](https://github.com/s0up4200)) - [`f249c69`](autobrr/qui@f249c69): ci(triage): remove needs-triage label after applying labels ([@​s0up4200](https://github.com/s0up4200)) - [`bdda1de`](autobrr/qui@bdda1de): ci(workflows): add self-dispatch workaround for discussion events ([@​s0up4200](https://github.com/s0up4200)) - [`a9732a2`](autobrr/qui@a9732a2): ci(workflows): increase max-turns to 25 for Claude workflows ([@​s0up4200](https://github.com/s0up4200)) - [`d7d830d`](autobrr/qui@d7d830d): docs(README): add Buy Me a Coffee link ([#​863](autobrr/qui#863)) ([@​s0up4200](https://github.com/s0up4200)) - [`266d92e`](autobrr/qui@266d92e): docs(readme): Clarify ignore pattern ([#​878](autobrr/qui#878)) ([@​quorn23](https://github.com/quorn23)) - [`9586084`](autobrr/qui@9586084): docs(readme): add banner linking to stable docs ([#​925](autobrr/qui#925)) ([@​s0up4200](https://github.com/s0up4200)) - [`e36a621`](autobrr/qui@e36a621): docs(readme): use markdown link for Polar URL ([@​s0up4200](https://github.com/s0up4200)) - [`9394676`](autobrr/qui@9394676): docs: add frontmatter titles and descriptions, remove marketing language ([@​s0up4200](https://github.com/s0up4200)) - [`ba9d45e`](autobrr/qui@ba9d45e): docs: add local filesystem access snippet and swizzle Details component ([@​s0up4200](https://github.com/s0up4200)) - [`4329edd`](autobrr/qui@4329edd): docs: disclaimer about unreleased features ([#​943](autobrr/qui#943)) ([@​s0up4200](https://github.com/s0up4200)) - [`735d065`](autobrr/qui@735d065): docs: improve external programs, orphan scan, reverse proxy, tracker icons documentation ([@​s0up4200](https://github.com/s0up4200)) - [`78faef2`](autobrr/qui@78faef2): docs: remove premature tip and fix stat command ([@​s0up4200](https://github.com/s0up4200)) - [`eaad3bf`](autobrr/qui@eaad3bf): docs: update social card image in Docusaurus configuration ([@​s0up4200](https://github.com/s0up4200)) - [`02a68e5`](autobrr/qui@02a68e5): refactor(crossseed): hardcode ignore patterns for file matching ([#​915](autobrr/qui#915)) ([@​s0up4200](https://github.com/s0up4200)) **Full Changelog**: <autobrr/qui@v1.11.0...v1.12.0> #### Docker images - `docker pull ghcr.io/autobrr/qui:v1.12.0` - `docker pull ghcr.io/autobrr/qui:latest` #### What to do next? - Join our [Discord server](https://discord.autobrr.com/qui) Thank you for using qui! </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi42OS4yIiwidXBkYXRlZEluVmVyIjoiNDIuNjkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/3060 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Summary
pkg/redactpackage to centralize redaction of sensitive query parameters (apikey,api_key,passkey,token,password) from URLs and error messagesuser:pass@host→user:REDACTED@host) and proxy path segments (/proxy/{key}/→/proxy/REDACTED/)Fixes #839
Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.