fullcourtdefense-cli 1.21.3 → 1.21.4

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.
@@ -437,6 +437,10 @@ function startDesktopChatGuard(runtime) {
437
437
  (0, notify_1.notifyOs)({
438
438
  title: 'FullCourtDefense: secret in Claude Desktop',
439
439
  message: `${finding.reason}. Remove it before sending — this text has not been protected.`,
440
+ // Security warning: use a top-most window, not a toast. Toasts are
441
+ // silently swallowed by Focus Assist / Do-Not-Disturb and per-app
442
+ // banner settings — verified in the field — which would hide the alert.
443
+ forceWindow: true,
440
444
  });
441
445
  }
442
446
  runtime.log(`Claude Desktop chat finding: ${finding.itemId} (${finding.reason}).`);
package/dist/notify.d.ts CHANGED
@@ -14,6 +14,13 @@ export interface OsNotification {
14
14
  message: string;
15
15
  /** Optional URL. Platforms that support click-through open it; others append it to the body. */
16
16
  url?: string;
17
+ /**
18
+ * Windows only: render a top-most alert WINDOW instead of a toast. Toasts are
19
+ * silently suppressed by Focus Assist / Do-Not-Disturb, per-app banner
20
+ * settings, and rapid-fire dedup — unacceptable for a security warning the
21
+ * user MUST see. A plain window is not a notification, so none of that applies.
22
+ */
23
+ forceWindow?: boolean;
17
24
  }
18
25
  /** Show a native OS notification. Best-effort, non-blocking, never throws. */
19
26
  export declare function notifyOs(n: OsNotification): void;
package/dist/notify.js CHANGED
@@ -4,9 +4,17 @@ exports.notifyOs = notifyOs;
4
4
  exports.consoleUrl = consoleUrl;
5
5
  exports.approvalsConsoleUrl = approvalsConsoleUrl;
6
6
  const child_process_1 = require("child_process");
7
- function spawnDetached(command, args) {
7
+ function spawnDetached(command, args, opts) {
8
8
  try {
9
- const child = (0, child_process_1.spawn)(command, args, { detached: true, stdio: 'ignore', windowsHide: true });
9
+ const child = (0, child_process_1.spawn)(command, args, {
10
+ detached: true,
11
+ stdio: 'ignore',
12
+ // windowsHide sets the process show-state to SW_HIDE, which a GUI child's
13
+ // FIRST top-level window inherits — that silently hid the alert form.
14
+ // showWindow:true (used for the forced alert) must NOT hide it.
15
+ windowsHide: opts?.showWindow ? false : true,
16
+ env: opts?.env ? { ...process.env, ...opts.env } : process.env,
17
+ });
10
18
  child.unref();
11
19
  }
12
20
  catch { /* best-effort */ }
@@ -19,7 +27,59 @@ function psQuote(value) {
19
27
  function asQuote(value) {
20
28
  return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
21
29
  }
30
+ /**
31
+ * Top-most WinForms alert window. Unlike a toast, this is an ordinary window,
32
+ * so Focus Assist / Do-Not-Disturb and per-app notification settings cannot
33
+ * hide it. Auto-dismisses after ~12s so it never blocks the machine, and reads
34
+ * its text from env vars (no string interpolation into the script → no quoting
35
+ * or injection issues with arbitrary secret-adjacent message text).
36
+ */
37
+ function notifyWindowsWindow(n) {
38
+ const body = n.url ? `${n.message}\n${n.url}` : n.message;
39
+ // Node composes the full label text (with real newlines) and hands it to
40
+ // PowerShell via an env var. The script does ZERO string concatenation or
41
+ // escaping — arbitrary secret-adjacent message text can never break parsing
42
+ // or inject code.
43
+ const script = [
44
+ "$ErrorActionPreference='SilentlyContinue';",
45
+ 'Add-Type -AssemblyName System.Windows.Forms;',
46
+ 'Add-Type -AssemblyName System.Drawing;',
47
+ '$f=New-Object System.Windows.Forms.Form;',
48
+ '$f.Text=$env:FCD_NOTIFY_TITLE;',
49
+ '$f.StartPosition="Manual";',
50
+ '$wa=[System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea;',
51
+ '$f.Size=New-Object System.Drawing.Size(460,160);',
52
+ '$f.Location=New-Object System.Drawing.Point(($wa.Right-475),($wa.Bottom-180));',
53
+ '$f.TopMost=$true;',
54
+ '$f.FormBorderStyle="FixedDialog";',
55
+ '$f.MaximizeBox=$false; $f.MinimizeBox=$false; $f.ShowInTaskbar=$true;',
56
+ '$f.BackColor=[System.Drawing.Color]::FromArgb(180,30,40);',
57
+ '$l=New-Object System.Windows.Forms.Label;',
58
+ '$l.Text=$env:FCD_NOTIFY_TEXT;',
59
+ '$l.ForeColor=[System.Drawing.Color]::White;',
60
+ '$l.Font=New-Object System.Drawing.Font("Segoe UI",10,[System.Drawing.FontStyle]::Bold);',
61
+ '$l.Dock="Fill"; $l.Padding=New-Object System.Windows.Forms.Padding(14);',
62
+ '$f.Controls.Add($l);',
63
+ '$tm=New-Object System.Windows.Forms.Timer;',
64
+ '$tm.Interval=12000;',
65
+ '$tm.Add_Tick({$tm.Stop();$f.Close()});',
66
+ '$tm.Start();',
67
+ '$f.Add_Shown({$f.Activate();$f.BringToFront()});',
68
+ '[System.Windows.Forms.Application]::Run($f);',
69
+ ].join(' ');
70
+ spawnDetached('powershell', ['-NoProfile', '-NonInteractive', '-STA', '-Command', script], {
71
+ env: {
72
+ FCD_NOTIFY_TITLE: n.title,
73
+ FCD_NOTIFY_TEXT: `${n.title}\r\n\r\n${body}`,
74
+ },
75
+ showWindow: true,
76
+ });
77
+ }
22
78
  function notifyWindows(n) {
79
+ if (n.forceWindow) {
80
+ notifyWindowsWindow(n);
81
+ return;
82
+ }
23
83
  const body = n.url ? `${n.message}\n${n.url}` : n.message;
24
84
  // WinRT toast via PowerShell — works from a plain console process for the
25
85
  // current user, no admin/UAC and no extra dependencies. Uses the stock
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.21.3"
2
+ "version": "1.21.4"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.21.3",
3
+ "version": "1.21.4",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {