@panguard-ai/panguard-mcp-proxy 1.8.2 → 1.8.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.
Files changed (2) hide show
  1. package/dist/evaluator.js +22 -8
  2. package/package.json +4 -4
package/dist/evaluator.js CHANGED
@@ -207,18 +207,32 @@ export class ProxyEvaluator {
207
207
  durationMs,
208
208
  };
209
209
  }
210
- // Hard-DENY only on a trusted match (see shouldHardDeny); every other
211
- // match is still surfaced as 'ask' (user-in-the-loop), never silently
212
- // allowed. This is the proxy's false-positive control point the engine
213
- // runs full 'hunt' detection so nothing is missed.
210
+ // FALSE-POSITIVE CONTROL POINT (this is a SYNCHRONOUS per-tool-call gate).
211
+ // 1. Hard-DENY on a trusted match (shouldHardDeny: critical, or high+stable).
212
+ // 2. Otherwise surface an 'ask' advisory ONLY for a high-severity match —
213
+ // something serious enough to warrant a per-call heads-up even if
214
+ // unproven.
215
+ // 3. Medium/low matches are demoted to a SILENT allow here. The engine
216
+ // runs full 'hunt' detection, and many broad rules (e.g. keyword-
217
+ // presence rules that fire on any `curl`/`rm`/`env`, severity=medium,
218
+ // condition:any) would otherwise spam an advisory on routine tool calls
219
+ // — the dominant false-positive source. Their matches are still
220
+ // returned in matchedRules and the async daemon path keeps them for
221
+ // offline review; we just do not raise a per-call advisory/block on an
222
+ // unactionable low-severity broad match. Real attacks are
223
+ // critical/high-stable and hard-deny at step 1, so are unaffected.
214
224
  const blockMatch = matches.find((m) => shouldHardDeny(m.rule));
215
- const outcome = blockMatch ? 'deny' : 'ask';
216
- const topMatch = blockMatch ?? matches[0];
225
+ const askMatch = blockMatch ??
226
+ matches.find((m) => m.rule.severity === 'high' || m.rule.severity === 'critical');
227
+ const outcome = blockMatch ? 'deny' : askMatch ? 'ask' : 'allow';
228
+ const topMatch = blockMatch ?? askMatch ?? matches[0];
217
229
  return {
218
230
  outcome,
219
- reason: `${topMatch.rule.title} (${topMatch.rule.severity})`,
231
+ reason: outcome === 'allow'
232
+ ? `No actionable threat (low-severity match suppressed: ${topMatch.rule.title}, ${topMatch.rule.severity})`
233
+ : `${topMatch.rule.title} (${topMatch.rule.severity})`,
220
234
  matchedRules: matches.map((m) => m.rule.id),
221
- confidence: topMatch.confidence,
235
+ confidence: outcome === 'allow' ? 0 : topMatch.confidence,
222
236
  durationMs,
223
237
  };
224
238
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panguard-ai/panguard-mcp-proxy",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "MCP Proxy — runtime interception for AI agent tool calls using ATR rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,12 +21,12 @@
21
21
  "dependencies": {
22
22
  "@modelcontextprotocol/sdk": "^1.12.0",
23
23
  "agent-threat-rules": "^3.5.6",
24
- "@panguard-ai/atr": "1.8.2",
25
24
  "@panguard-ai/containment": "0.1.0",
26
- "@panguard-ai/panguard-guard": "1.8.2"
25
+ "@panguard-ai/atr": "1.8.4",
26
+ "@panguard-ai/panguard-guard": "1.8.4"
27
27
  },
28
28
  "peerDependencies": {
29
- "@panguard-ai/atr": "1.8.2"
29
+ "@panguard-ai/atr": "1.8.4"
30
30
  },
31
31
  "files": [
32
32
  "dist",