Add custom header and URL query string options to the ntfy publisher#1695
Add custom header and URL query string options to the ntfy publisher#1695justadityaraj wants to merge 1 commit into
Conversation
Lets users authenticate ntfy notifications through a reverse proxy or tunnel (Pangolin, Tailscale, ...) in front of the ntfy instance. Adds three optional, backward-compatible settings that default to empty and are no-ops when unset: - NTFY_URL_QUERY_STRING: appended to the request URL (e.g. p_token=...). A leading '?' is tolerated, and the value is redacted from error logs / the plugin result file since the request URL can carry a secret token. - NTFY_CUSTOMHEADER_NAME / NTFY_CUSTOMHEADER_VALUE: a custom request header, skipped with a warning if it would clobber a built-in header (e.g. Authorization) so ntfy's own auth stays intact. Secret-bearing fields are password-masked in the UI. Addresses netalertx#1663.
📝 WalkthroughWalkthroughAdds three new NTFY publisher configuration settings (URL_QUERY_STRING, CUSTOMHEADER_NAME, CUSTOMHEADER_VALUE) and updates the send() function to apply an optional query string and custom header with collision avoidance, plus redacts query-string secrets from logged/returned error messages. ChangesNTFY publisher enhancement
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Send as send()
participant Config as Plugin Settings
participant Requests as requests.post
Send->>Config: Read NTFY_URL_QUERY_STRING, NTFY_CUSTOMHEADER_NAME/VALUE
Send->>Send: Strip leading "?" from query string
Send->>Send: Check header name collision with built-in headers
alt no collision
Send->>Send: Add custom header
else collision detected
Send->>Send: Skip custom header, log warning
end
Send->>Requests: POST with params and headers
alt request fails
Requests-->>Send: RequestException
Send->>Send: Redact query string from error message
Send->>Send: Log and return redacted response_text
else success
Requests-->>Send: Response
end
Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@front/plugins/_publisher_ntfy/ntfy.py`:
- Line 128: The custom header value can leak through InvalidHeader exceptions
when building the request headers in ntfy.py. Update the header-setting and
error-handling path around the headers[custom_header_name] assignment (and the
code that logs via mylog(...) or persists response_text) so any exception text
is sanitized before being logged or returned, specifically redacting the
offending NTFY_CUSTOMHEADER_VALUE while preserving the rest of the error
context.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 814246b1-d8bc-4a64-b086-9387411ea169
📒 Files selected for processing (2)
front/plugins/_publisher_ntfy/config.jsonfront/plugins/_publisher_ntfy/ntfy.py
| if custom_header_name.lower() in {k.lower() for k in headers}: | ||
| mylog('none', [f'[{pluginName}] ⚠ Custom header "{custom_header_name}" collides with a built-in header; skipping it.']) | ||
| else: | ||
| headers[custom_header_name] = custom_header_value |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how the repository's installed requests version renders invalid header values.
python - <<'PY'
import inspect
import requests
from requests.exceptions import InvalidHeader, RequestException
print("InvalidHeader is RequestException:", issubclass(InvalidHeader, RequestException))
print(inspect.getsource(requests.utils._validate_header_part))
PYRepository: netalertx/NetAlertX
Length of output: 1154
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant function and nearby error handling in front/plugins/_publisher_ntfy/ntfy.py.
python3 - <<'PY'
from pathlib import Path
path = Path("front/plugins/_publisher_ntfy/ntfy.py")
lines = path.read_text().splitlines()
for start, end in [(1, 240)]:
for i in range(start, min(end, len(lines)) + 1):
print(f"{i:4d}: {lines[i-1]}")
PYRepository: netalertx/NetAlertX
Length of output: 7586
Redact the custom header value from request errors. InvalidHeader includes the offending header value in the exception text, so a bad NTFY_CUSTOMHEADER_VALUE can leak into mylog(...) and the persisted response_text. Redact it before logging or returning the exception.
🤖 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 `@front/plugins/_publisher_ntfy/ntfy.py` at line 128, The custom header value
can leak through InvalidHeader exceptions when building the request headers in
ntfy.py. Update the header-setting and error-handling path around the
headers[custom_header_name] assignment (and the code that logs via mylog(...) or
persists response_text) so any exception text is sanitized before being logged
or returned, specifically redacting the offending NTFY_CUSTOMHEADER_VALUE while
preserving the rest of the error context.
|
Hi @justadityaraj, Thanks a lot for the PR. The code looks good. Just double checking if this code was tested on an actual NTFY setup. Happy to merge if that is confirmed, as I don't have a NTFY instance + pangolin setup running currently. Thanks, |
|
OH, one more thing, can you update the README.md with some sample values? I think it would be clearer to the user to see examples e.g. what a valid custom header value looks like. |
| ] | ||
| }, | ||
| { | ||
| "function": "URL_QUERY_STRING", |
There was a problem hiding this comment.
let's add sample values into the README.md in case people are confused how a valid value should look like
|
Sure thing, will try to get back to you with both those things done by today. |
📌 Description
Adds optional custom header and URL query string support to the ntfy publisher, so notifications can authenticate through a reverse proxy or tunnel (Pangolin, Tailscale, ...) sitting in front of the ntfy instance.
Three new optional settings, all default to empty and are no-ops when unset (existing setups are unaffected):
NTFY_URL_QUERY_STRING— appended to the request URL (e.g.p_token=tokenId.tokenValue). This is the reporter's Pangolin use case, and the option you leaned toward. A leading?is tolerated.NTFY_CUSTOMHEADER_NAME/NTFY_CUSTOMHEADER_VALUE— a custom request header (the issue's original ask / @legionGer's sketch), for proxies that authenticate via a header.Both approaches are included since the issue asked for "custom headers or query parameters" — happy to drop either if you'd prefer to keep just one.
🔍 Related Issues
Addresses #1663
📋 Type of Change
🧪 Testing Steps
The plugin module runs app-coupled code at import (
conf.tz = timezone(get_setting_value('TIMEZONE'))), so I verified the changed logic in isolation rather than end-to-end:requeststreats a stringparamsas the query string (p_token=x→...?p_token=x) and thatparams=None(empty setting) leaves the URL unchanged.?is stripped so bothp_token=...and?p_token=...produce a correct single-?URL....?p_token=SECRETinto...?<redacted>before it is logged / written to the result file.Authorization,Title, ...), case-insensitively.config.jsonvalidates as JSON;ntfy.pycompiles.I don't run ntfy behind Pangolin, so I have not exercised a live send through a running instance — the reporter offered to test on the
netalertx-devimage.✅ Checklist
get_setting_valuereturns""for missing keys)🙋 Additional Notes
A few security touches, since the query string / header can carry a secret:
requestsexceptions).NTFY_URL_QUERY_STRINGandNTFY_CUSTOMHEADER_VALUEare password-masked in the UI, and the query-string description warns that URL values can still land in proxy/server access logs (so headers are preferable for secrets).No unit test is included: no publisher plugin currently has one, and
ntfy.pyisn't importable standalone. Happy to addtest/plugins/test_ntfy_publisher.pyif you'd like — it'd want a small refactor to lift the query-string/redaction/collision logic into an importable helper first. Just say the word.Summary by CodeRabbit
New Features
Bug Fixes
?characters are automatically ignored.