Skip to content

Add custom header and URL query string options to the ntfy publisher#1695

Open
justadityaraj wants to merge 1 commit into
netalertx:mainfrom
justadityaraj:feat/ntfy-custom-query-string
Open

Add custom header and URL query string options to the ntfy publisher#1695
justadityaraj wants to merge 1 commit into
netalertx:mainfrom
justadityaraj:feat/ntfy-custom-query-string

Conversation

@justadityaraj

@justadityaraj justadityaraj commented Jul 1, 2026

Copy link
Copy Markdown

📌 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

  • ✨ New feature

🧪 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:

  • Confirmed requests treats a string params as the query string (p_token=x...?p_token=x) and that params=None (empty setting) leaves the URL unchanged.
  • Confirmed a leading ? is stripped so both p_token=... and ?p_token=... produce a correct single-? URL.
  • Confirmed the redaction turns a failed-request exception URL ...?p_token=SECRET into ...?<redacted> before it is logged / written to the result file.
  • Confirmed the collision guard skips (and warns) when the custom header name matches a built-in header (Authorization, Title, ...), case-insensitively.
  • config.json validates as JSON; ntfy.py compiles.

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-dev image.


✅ Checklist

  • I have read the Contribution Guidelines
  • I have tested my changes locally (behaviour verified in isolation — see Testing Steps)
  • I have updated relevant documentation (settings carry in-app descriptions)
  • I have verified my changes do not break existing behavior (all new settings default empty / no-op; get_setting_value returns "" for missing keys)
  • I am willing to respond to requested changes and feedback

🙋 Additional Notes

A few security touches, since the query string / header can carry a secret:

  • The query string is redacted from error logs and the plugin result file (the request URL otherwise appears verbatim in requests exceptions).
  • NTFY_URL_QUERY_STRING and NTFY_CUSTOMHEADER_VALUE are 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.py isn't importable standalone. Happy to add test/plugins/test_ntfy_publisher.py if 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

    • Added optional support for custom HTTP headers when sending notifications through the NTFY plugin.
    • Added an optional URL query-string field for proxy or tunnel setups.
    • Expanded plugin settings with clearer input types and descriptions for the new options.
  • Bug Fixes

    • Improved handling of query-string formatting so leading ? characters are automatically ignored.
    • Reduced the chance of request failures by avoiding custom-header conflicts with existing required headers.
    • Improved error messages by masking sensitive query-string details before they are shown or stored.

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.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds 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.

Changes

NTFY publisher enhancement

Layer / File(s) Summary
New config settings for query string and custom header
front/plugins/_publisher_ntfy/config.json
Adds URL_QUERY_STRING (password type, appended to request URL), CUSTOMHEADER_NAME, and CUSTOMHEADER_VALUE (password type) settings entries with descriptive UI text.
Query string and custom header handling in send()
front/plugins/_publisher_ntfy/ntfy.py
Strips leading ? from NTFY_URL_QUERY_STRING, passes it as request params, and applies NTFY_CUSTOMHEADER_NAME/VALUE only when it doesn't collide (case-insensitively) with existing built-in headers, logging a warning on collision.
Redact query string secrets in error handling
front/plugins/_publisher_ntfy/ntfy.py
Updates the RequestException handler to regex-redact the query portion of the error message before logging and returning it, instead of logging the raw exception.

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
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding custom header and URL query string options to the ntfy publisher.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 82049d1 and 9395566.

📒 Files selected for processing (2)
  • front/plugins/_publisher_ntfy/config.json
  • front/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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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))
PY

Repository: 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]}")
PY

Repository: 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.

@jokob-sk

jokob-sk commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

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,
j

@jokob-sk

jokob-sk commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

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",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add sample values into the README.md in case people are confused how a valid value should look like

@justadityaraj

Copy link
Copy Markdown
Author

Sure thing, will try to get back to you with both those things done by today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants