@panguard-ai/panguard-guard 1.6.0 → 1.7.0

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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/index.d.ts +9 -0
  3. package/dist/cli/index.d.ts.map +1 -1
  4. package/dist/cli/index.js +128 -29
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +46 -14
  8. package/dist/config.js.map +1 -1
  9. package/dist/daemon/index.js +20 -18
  10. package/dist/daemon/index.js.map +1 -1
  11. package/dist/dashboard/dashboard.html +2428 -205
  12. package/dist/dashboard/index.d.ts +104 -0
  13. package/dist/dashboard/index.d.ts.map +1 -1
  14. package/dist/dashboard/index.js +710 -58
  15. package/dist/dashboard/index.js.map +1 -1
  16. package/dist/engines/atr-engine.d.ts +24 -2
  17. package/dist/engines/atr-engine.d.ts.map +1 -1
  18. package/dist/engines/atr-engine.js +80 -20
  19. package/dist/engines/atr-engine.js.map +1 -1
  20. package/dist/event-processor.js +6 -4
  21. package/dist/event-processor.js.map +1 -1
  22. package/dist/guard-engine.d.ts +41 -1
  23. package/dist/guard-engine.d.ts.map +1 -1
  24. package/dist/guard-engine.js +124 -19
  25. package/dist/guard-engine.js.map +1 -1
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +5 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/investigation/index.js +3 -3
  31. package/dist/investigation/index.js.map +1 -1
  32. package/dist/llm-detect.d.ts +19 -0
  33. package/dist/llm-detect.d.ts.map +1 -1
  34. package/dist/llm-detect.js +72 -5
  35. package/dist/llm-detect.js.map +1 -1
  36. package/dist/memory/baseline.d.ts.map +1 -1
  37. package/dist/memory/baseline.js +4 -7
  38. package/dist/memory/baseline.js.map +1 -1
  39. package/dist/net/validate-outbound-url.d.ts +33 -0
  40. package/dist/net/validate-outbound-url.d.ts.map +1 -0
  41. package/dist/net/validate-outbound-url.js +95 -0
  42. package/dist/net/validate-outbound-url.js.map +1 -0
  43. package/dist/notify/webhook.d.ts.map +1 -1
  44. package/dist/notify/webhook.js +10 -0
  45. package/dist/notify/webhook.js.map +1 -1
  46. package/dist/redact.d.ts +35 -0
  47. package/dist/redact.d.ts.map +1 -0
  48. package/dist/redact.js +39 -0
  49. package/dist/redact.js.map +1 -0
  50. package/dist/response/file-quarantine.d.ts +2 -0
  51. package/dist/response/file-quarantine.d.ts.map +1 -1
  52. package/dist/response/file-quarantine.js +35 -9
  53. package/dist/response/file-quarantine.js.map +1 -1
  54. package/dist/rule-loader.d.ts +14 -1
  55. package/dist/rule-loader.d.ts.map +1 -1
  56. package/dist/rule-loader.js +62 -4
  57. package/dist/rule-loader.js.map +1 -1
  58. package/dist/rule-sync.d.ts +22 -0
  59. package/dist/rule-sync.d.ts.map +1 -1
  60. package/dist/rule-sync.js +152 -66
  61. package/dist/rule-sync.js.map +1 -1
  62. package/dist/threat-cloud/index.d.ts +11 -14
  63. package/dist/threat-cloud/index.d.ts.map +1 -1
  64. package/dist/threat-cloud/index.js +24 -24
  65. package/dist/threat-cloud/index.js.map +1 -1
  66. package/dist/threat-cloud/tc-key-provisioner.d.ts.map +1 -1
  67. package/dist/threat-cloud/tc-key-provisioner.js +19 -1
  68. package/dist/threat-cloud/tc-key-provisioner.js.map +1 -1
  69. package/dist/types.d.ts +9 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/package.json +12 -12
@@ -15,6 +15,13 @@
15
15
  */
16
16
  import type { DashboardStatus, DashboardEvent, ThreatMapEntry, GuardConfig, ThreatVerdict } from '../types.js';
17
17
  import { DashboardRelayClient } from './relay-client.js';
18
+ /**
19
+ * Remove the persisted dashboard launch token. Exported so `pga guard stop` /
20
+ * `pga guard uninstall` can guarantee the secret never outlives the daemon even
21
+ * when the process is killed without a graceful `stop()` (e.g. SIGTERM mid-run).
22
+ * Best-effort: a missing file or permission error is not fatal.
23
+ */
24
+ export declare function removeDashboardToken(): void;
18
25
  /** Relay configuration for connecting to a remote Manager */
19
26
  export interface DashboardRelayOptions {
20
27
  readonly managerUrl: string;
@@ -33,6 +40,7 @@ export declare class DashboardServer {
33
40
  private readonly maxRecentEvents;
34
41
  private readonly port;
35
42
  private getConfig;
43
+ private configApplier;
36
44
  private getRulesProvider;
37
45
  private readonly authToken;
38
46
  private rateLimits;
@@ -40,6 +48,16 @@ export declare class DashboardServer {
40
48
  private readonly relayConfig;
41
49
  constructor(port: number, relayConfig?: DashboardRelayOptions);
42
50
  setConfigGetter(getter: () => GuardConfig): void;
51
+ /**
52
+ * Wire the engine's live-config applier. When a config-writing handler (e.g.
53
+ * the Threat Cloud / mode POST) persists a new config to disk, it ALSO calls
54
+ * this so the engine mutates its in-memory `this.config` + `this.mode` and
55
+ * re-arms enforcement for the new mode. Without it, the daemon kept serving
56
+ * the stale config it was constructed with: GET /api/config and /api/status
57
+ * returned the old value, enforcement kept the old mode, and the UI control
58
+ * snapped back — the change looked rejected even though disk was updated.
59
+ */
60
+ setConfigApplier(applier: (cfg: GuardConfig) => void): void;
43
61
  /**
44
62
  * Wire a callback that returns the live set of ATR rules currently loaded by
45
63
  * the engine. Used by SARIF / Evidence Pack export so the auditor sees the
@@ -59,9 +77,20 @@ export declare class DashboardServer {
59
77
  addVerdict(verdict: ThreatVerdict): void;
60
78
  addThreatMapEntry(entry: ThreatMapEntry): void;
61
79
  getAuthToken(): string;
80
+ /**
81
+ * Persist the launch token to ~/.panguard-guard/dashboard-token with 0o600 so
82
+ * a separate `pga up` invocation (rerun, already-running daemon, headless,
83
+ * copied URL) can construct the authenticated launch URL. The token is the
84
+ * launch secret that mints the auth cookie, so it is written owner-only and
85
+ * never logged — same handling as the cookie. Best-effort: a write failure
86
+ * just means the CLI falls back to printing guidance instead of a live URL.
87
+ */
88
+ private persistAuthToken;
62
89
  private handleRequest;
63
90
  private checkRateLimit;
64
91
  private serveIndex;
92
+ /** Parse the one-time launch token from a `GET /?token=...` request URL. */
93
+ private extractLaunchToken;
65
94
  /**
66
95
  * Compose the status envelope returned by /api/status.
67
96
  * Adds a `license` field so the SPA can gate Pilot-only UI without making
@@ -69,6 +98,26 @@ export declare class DashboardServer {
69
98
  * (cliTier overrides licenseKey) or defaults to community.
70
99
  */
71
100
  private buildStatusResponse;
101
+ /**
102
+ * Real per-layer health, derived from live engine state — NOT hard-coded.
103
+ * The dashboard's "Layer A/B Active" used to be static green in the HTML, so it
104
+ * stayed green even with zero rules loaded or the engine stopped. This computes
105
+ * the truth: Layer A (deterministic pattern) is active only when rules are
106
+ * actually loaded AND the engine is running; Layer B (deterministic heuristic +
107
+ * baseline) tracks the running state; Layer C (optional semantic) reflects
108
+ * whether the user has configured a BYO LLM (config.ai) — it is advisory and
109
+ * off-by-default, so "off" is a healthy state, not a fault.
110
+ */
111
+ private computeLayers;
112
+ /**
113
+ * Honest enforcement posture. The dashboard used to print PROTECTED whenever
114
+ * mode === 'protection', even with an all-off enforcement policy that takes no
115
+ * action — the exact "says PROTECTED, prevents nothing" overclaim the audit
116
+ * flagged. This reports what the daemon will ACTUALLY do: 'protected' only when
117
+ * protection mode is on AND at least one response action is armed; otherwise
118
+ * 'monitoring' (detect + notify, no automatic OS response).
119
+ */
120
+ private computeEnforcement;
72
121
  private normalizeLicenseTier;
73
122
  /**
74
123
  * SARIF 2.1.0 stub export. Production wiring to the migrator's evidence
@@ -107,13 +156,68 @@ export declare class DashboardServer {
107
156
  private jsonResponse;
108
157
  private handleSkillsApi;
109
158
  private handleAiConfigGet;
159
+ /**
160
+ * True when `host` is a private/internal/loopback/link-local IPv4 address (or
161
+ * `localhost`). Self-contained so the SSRF check stays in this file.
162
+ */
163
+ private static isPrivateHost;
110
164
  private static isValidEndpointUrl;
165
+ /**
166
+ * If `host` is an IPv6-mapped IPv4 address, return the embedded dotted-quad
167
+ * IPv4; otherwise null. Accepts the URL bracket form ([...]), the
168
+ * dotted-quad tail (::ffff:1.2.3.4) and the hex tail (::ffff:0102:0304).
169
+ */
170
+ private static extractMappedIpv4;
111
171
  private handleRulesApi;
112
172
  private handleThreatCloudGet;
113
173
  private handleThreatCloudPost;
114
174
  private handleLoadedRulesApi;
115
175
  private handleProxyVerdictsApi;
116
176
  private handleInstalledSkillsApi;
177
+ /**
178
+ * Read a JSON request body (≤10KB) for the action POST endpoints. Resolves to
179
+ * the parsed object, or null after having already written the error response
180
+ * (payload too large / invalid JSON / stream error) — callers just return on null.
181
+ */
182
+ private readJsonBody;
183
+ /**
184
+ * POST /api/skills/quarantine — move a flagged file-based skill into quarantine.
185
+ * Resolves the skill name under ~/.claude/skills/, contains it against path
186
+ * traversal, confirms it exists, then uses the existing FileQuarantine (atomic
187
+ * move + 0o700 perms + restorable manifest). MCP-server config entries are not
188
+ * file-based and cannot be quarantined this way.
189
+ */
190
+ private handleQuarantinePost;
191
+ /**
192
+ * POST /api/skills/whitelist — mark a skill safe (false positive) by adding it
193
+ * to the local skill-whitelist.json the installed-skills view reads. Atomic
194
+ * write (tmp + rename), owner-only perms.
195
+ */
196
+ private handleWhitelistPost;
197
+ /**
198
+ * POST /api/skills/unwhitelist — remove a skill from the local whitelist, so a
199
+ * mistaken "Mark safe" is reversible from the dashboard (the whitelist used to
200
+ * be append-only with no in-product undo). Atomic write, owner-only perms.
201
+ */
202
+ private handleUnwhitelistPost;
203
+ /**
204
+ * GET /api/skills/quarantined — the list of currently quarantined files, so the
205
+ * user can SEE what's quarantined and restore it. The quarantine confirm dialog
206
+ * promises restorability; this + /api/skills/restore make that promise real.
207
+ */
208
+ private handleQuarantinedListApi;
209
+ /**
210
+ * POST /api/skills/restore — restore a quarantined file to its original path,
211
+ * honoring the reversibility the UI promises. Body: { id }.
212
+ */
213
+ private handleRestorePost;
214
+ /**
215
+ * GET /api/rule-update — surface the daily notify-only rule-update check that
216
+ * rule-sync writes to <dataDir>/rule-update.json. The backend computed this
217
+ * signal ("newer detection rules published") but nothing read it, so the most
218
+ * actionable security update was invisible. Notify-only: never auto-applies.
219
+ */
220
+ private handleRuleUpdateApi;
117
221
  private handleAgentsApi;
118
222
  private sendToClient;
119
223
  private broadcast;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,EACX,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,oBAAoB,EAA0B,MAAM,mBAAmB,CAAC;AAsCjF,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAgD;IAC9D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAO;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,gBAAgB,CAQR;IAChB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;gBAEpD,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,qBAAqB;IAkB7D,eAAe,CAAC,MAAM,EAAE,MAAM,WAAW,GAAG,IAAI;IAIhD;;;;OAIG;IACH,gBAAgB,CACd,MAAM,EAAE,MAAM,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,GACD,IAAI;IAID,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4HtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B3B,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IASpD,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAQtC,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAYxC,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAgB9C,YAAY,IAAI,MAAM;IAItB,OAAO,CAAC,aAAa;IAmLrB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,UAAU;IAWlB;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,oBAAoB;IAc5B;;;;;;OAMG;IACH;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAyE7B;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAqGzB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IA2D5B,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAmCjC,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,oBAAoB;IAoD5B,OAAO,CAAC,qBAAqB;IAuD7B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,sBAAsB;YA4BhB,wBAAwB;YAuFxB,eAAe;IA8C7B,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,gBAAgB;IA4BxB,cAAc,IAAI,oBAAoB,GAAG,IAAI;CAG9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"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"}