omni-notify-mcp 1.3.21 → 1.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/ui/server.js +31 -39
  2. package/package.json +1 -1
package/dist/ui/server.js CHANGED
@@ -152,32 +152,37 @@ const LEGACY_SECRETS_PATH = join(homedir(), ".notify-mcp-secrets");
152
152
  function e164(s) {
153
153
  return String(s ?? "").replace(/[^\d+]/g, "");
154
154
  }
155
+ // The ONE list of credential-shaped fields, read by both maskSecrets() (out)
156
+ // and mergePreservingSecrets() (back in). Before story #442 these were two
157
+ // hand-written lists of `if`/`guard` calls kept in sync by hand — that's
158
+ // exactly how ntfy.token got added to a channel (story #427's "brainless
159
+ // config" import) without ever being added to either list, and leaked in the
160
+ // clear over `GET /api/config` for as long as the channel existed. A field
161
+ // belongs here the moment its channel gets one that authenticates outbound
162
+ // (a token, password, or bearer webhook URL) — see
163
+ // tests/smoke.test.mjs's naming-convention test, which independently catches
164
+ // a field like this one being added to defaultConfig() but not to this list.
165
+ const SENSITIVE_FIELDS = [
166
+ ["email", "pass"],
167
+ ["email", "clientSecret"],
168
+ ["email", "refreshToken"],
169
+ ["email", "accessToken"],
170
+ ["sms", "secretAccessKey"],
171
+ ["telegram", "token"],
172
+ ["ntfy", "token"],
173
+ ["discord", "webhookUrl"],
174
+ ["discord", "clientSecret"],
175
+ ["slack", "webhookUrl"],
176
+ ["slack", "botToken"],
177
+ ["slack", "clientSecret"],
178
+ ["teams", "webhookUrl"],
179
+ ];
155
180
  function maskSecrets(config) {
156
181
  const c = JSON.parse(JSON.stringify(config));
157
- if (c.email?.pass)
158
- c.email.pass = MASKED;
159
- if (c.email?.clientSecret)
160
- c.email.clientSecret = MASKED;
161
- if (c.email?.refreshToken)
162
- c.email.refreshToken = MASKED;
163
- if (c.email?.accessToken)
164
- c.email.accessToken = MASKED;
165
- if (c.sms?.secretAccessKey)
166
- c.sms.secretAccessKey = MASKED;
167
- if (c.telegram?.token)
168
- c.telegram.token = MASKED;
169
- if (c.discord?.webhookUrl)
170
- c.discord.webhookUrl = MASKED;
171
- if (c.discord?.clientSecret)
172
- c.discord.clientSecret = MASKED;
173
- if (c.slack?.webhookUrl)
174
- c.slack.webhookUrl = MASKED;
175
- if (c.slack?.botToken)
176
- c.slack.botToken = MASKED;
177
- if (c.slack?.clientSecret)
178
- c.slack.clientSecret = MASKED;
179
- if (c.teams?.webhookUrl)
180
- c.teams.webhookUrl = MASKED;
182
+ for (const [sec, field] of SENSITIVE_FIELDS) {
183
+ if (c[sec]?.[field])
184
+ c[sec][field] = MASKED;
185
+ }
181
186
  delete c.whatsapp;
182
187
  return c;
183
188
  }
@@ -192,24 +197,11 @@ function mergePreservingSecrets(existing, update) {
192
197
  }
193
198
  if (typeof update.muteAll === "boolean")
194
199
  merged.muteAll = update.muteAll;
195
- const guard = (path) => {
196
- const [sec, field] = path;
200
+ for (const [sec, field] of SENSITIVE_FIELDS) {
197
201
  if (update[sec]?.[field] === MASKED) {
198
202
  merged[sec][field] = existing[sec]?.[field] ?? "";
199
203
  }
200
- };
201
- guard(["email", "pass"]);
202
- guard(["email", "clientSecret"]);
203
- guard(["email", "refreshToken"]);
204
- guard(["email", "accessToken"]);
205
- guard(["sms", "secretAccessKey"]);
206
- guard(["telegram", "token"]);
207
- guard(["discord", "webhookUrl"]);
208
- guard(["discord", "clientSecret"]);
209
- guard(["slack", "webhookUrl"]);
210
- guard(["slack", "botToken"]);
211
- guard(["slack", "clientSecret"]);
212
- guard(["teams", "webhookUrl"]);
204
+ }
213
205
  return merged;
214
206
  }
215
207
  const app = express();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-notify-mcp",
3
- "version": "1.3.21",
3
+ "version": "1.3.22",
4
4
  "description": "Reach your AI agents from anywhere — and let them reach you. Every notification channel is set up in one config UI with one-click server launch: desktop, Telegram, Slack, SMS, email, ntfy. Agents then notify you when long work finishes or a decision is needed, ask you a question and wait for your answer, and receive and reply to the messages you send them — with Do Not Disturb and idle detection deciding what actually gets through.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",