@wardnmesh/sdk-node 0.2.3 → 0.4.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.
- package/README.md +74 -2
- package/dist/detectors/base.d.ts +18 -1
- package/dist/detectors/base.js +43 -1
- package/dist/detectors/state.js +8 -2
- package/dist/integrations/vercel.js +2 -1
- package/dist/middleware/express.js +2 -1
- package/dist/middleware/nextjs.js +2 -1
- package/dist/types.d.ts +14 -2
- package/dist/utils/rule-validator.js +1 -1
- package/dist/wardn.d.ts +20 -0
- package/dist/wardn.js +103 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
# @wardnmesh/sdk-node
|
|
2
2
|
|
|
3
|
+
> **Latest Version: v0.4.0** (Released 2026-01-17)
|
|
4
|
+
|
|
3
5
|
**WardnMesh.AI** (formerly AgentGuard) is an active defense middleware for AI Agents. This SDK allows you to verify LLM inputs/outputs, block prompt injections, and prevent data exfiltration in real-time.
|
|
4
6
|
|
|
7
|
+
## ✨ What's New in v0.4.0
|
|
8
|
+
|
|
9
|
+
🚨 **Breaking Changes**: Action-based architecture replaces boolean `allowed` field.
|
|
10
|
+
|
|
11
|
+
- ✅ **Granular Actions**: `block`, `confirm`, `warn`, `log`, `allow` instead of `true`/`false`
|
|
12
|
+
- ✅ **Confirmation Support**: Native user confirmation for high-risk operations
|
|
13
|
+
- ✅ **Enhanced Context**: Richer violation metadata with `recommendedAction` and `scope`
|
|
14
|
+
- ✅ **Fail-Closed Security**: Defensive design defaults to `'block'` on invalid states
|
|
15
|
+
|
|
16
|
+
📖 [Migration Guide](MIGRATION_v0.4.0.md) | [CHANGELOG](CHANGELOG.md)
|
|
17
|
+
|
|
5
18
|
## Features
|
|
6
19
|
|
|
7
20
|
- 🛡️ **Active Defense**: Blocks prompt injections, jailbreaks, and PII leaks.
|
|
@@ -13,9 +26,68 @@
|
|
|
13
26
|
## Installation
|
|
14
27
|
|
|
15
28
|
```bash
|
|
16
|
-
npm install @wardnmesh/sdk-node
|
|
29
|
+
npm install @wardnmesh/sdk-node@0.4.0
|
|
17
30
|
# or
|
|
18
|
-
yarn add @wardnmesh/sdk-node
|
|
31
|
+
yarn add @wardnmesh/sdk-node@0.4.0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## v0.4.0 API Overview
|
|
35
|
+
|
|
36
|
+
The new action-based API provides fine-grained control over threat responses:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Wardn } from '@wardnmesh/sdk-node';
|
|
40
|
+
|
|
41
|
+
const guard = Wardn.getInstance();
|
|
42
|
+
const result = await guard.scan({ prompt: userInput });
|
|
43
|
+
|
|
44
|
+
// Handle different threat levels
|
|
45
|
+
switch (result.action) {
|
|
46
|
+
case 'block':
|
|
47
|
+
// Critical violation - deny immediately
|
|
48
|
+
throw new Error('Security violation');
|
|
49
|
+
|
|
50
|
+
case 'confirm':
|
|
51
|
+
// High-risk - request user approval
|
|
52
|
+
const approved = await getUserConfirmation(result.confirmationDetails);
|
|
53
|
+
if (!approved) throw new Error('Operation denied');
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case 'warn':
|
|
57
|
+
// Medium-risk - log warning and allow
|
|
58
|
+
console.warn('Security warning:', result.violations);
|
|
59
|
+
break;
|
|
60
|
+
|
|
61
|
+
case 'log':
|
|
62
|
+
// Low-risk - log for monitoring
|
|
63
|
+
console.log('Security event:', result.violations);
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
case 'allow':
|
|
67
|
+
// No violations - safe to proceed
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Continue with your logic...
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Confirmation Dialog Example
|
|
75
|
+
|
|
76
|
+
When `action === 'confirm'`, the SDK provides pre-formatted confirmation context:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
if (result.action === 'confirm') {
|
|
80
|
+
const { message, timeout, defaultAction } = result.confirmationDetails;
|
|
81
|
+
|
|
82
|
+
console.log(message);
|
|
83
|
+
// ⚠️ Security Alert
|
|
84
|
+
// Rule: recursive_delete
|
|
85
|
+
// Severity: HIGH
|
|
86
|
+
// Description: Detected dangerous recursive delete operation
|
|
87
|
+
|
|
88
|
+
const approved = await getUserInput(); // Your confirmation UI
|
|
89
|
+
if (!approved) throw new Error('Operation denied by user');
|
|
90
|
+
}
|
|
19
91
|
```
|
|
20
92
|
|
|
21
93
|
## Quick Start
|
package/dist/detectors/base.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ToolData, Violation, Rule, Detector, StateProvider, DetectorType } from '../types';
|
|
1
|
+
import { ToolData, Violation, Rule, Detector, StateProvider, DetectorType, ThreatAction } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Abstract base detector class
|
|
4
4
|
*
|
|
@@ -11,8 +11,25 @@ export declare abstract class BaseDetector implements Detector {
|
|
|
11
11
|
* Generate unique violation ID
|
|
12
12
|
*/
|
|
13
13
|
protected generateViolationId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* v0.4.0: Map severity to recommended action, with optional rule action override
|
|
16
|
+
*
|
|
17
|
+
* Default mapping:
|
|
18
|
+
* - critical → block (immediate threat)
|
|
19
|
+
* - high → confirm (needs user approval)
|
|
20
|
+
* - medium → warn (notify but allow)
|
|
21
|
+
* - low → log (record only)
|
|
22
|
+
*
|
|
23
|
+
* Rules can override default mapping via rule.action
|
|
24
|
+
*/
|
|
25
|
+
protected getRecommendedActionForSeverity(rule: Rule): ThreatAction;
|
|
26
|
+
/**
|
|
27
|
+
* v0.3.0: Determine scope description based on rule category
|
|
28
|
+
*/
|
|
29
|
+
protected getScopeForCategory(category: string): string;
|
|
14
30
|
/**
|
|
15
31
|
* Create violation object
|
|
32
|
+
* v0.4.0: Use rule.action override for recommendedAction
|
|
16
33
|
*/
|
|
17
34
|
protected createViolation(rule: Rule, toolData: ToolData, additionalInfo?: Record<string, unknown>): Violation;
|
|
18
35
|
/**
|
package/dist/detectors/base.js
CHANGED
|
@@ -14,8 +14,47 @@ class BaseDetector {
|
|
|
14
14
|
// Note: Using substring() instead of deprecated substr()
|
|
15
15
|
return `violation_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* v0.4.0: Map severity to recommended action, with optional rule action override
|
|
19
|
+
*
|
|
20
|
+
* Default mapping:
|
|
21
|
+
* - critical → block (immediate threat)
|
|
22
|
+
* - high → confirm (needs user approval)
|
|
23
|
+
* - medium → warn (notify but allow)
|
|
24
|
+
* - low → log (record only)
|
|
25
|
+
*
|
|
26
|
+
* Rules can override default mapping via rule.action
|
|
27
|
+
*/
|
|
28
|
+
getRecommendedActionForSeverity(rule) {
|
|
29
|
+
// Rule action override takes precedence
|
|
30
|
+
if (rule.action) {
|
|
31
|
+
return rule.action;
|
|
32
|
+
}
|
|
33
|
+
// Default severity-to-action mapping
|
|
34
|
+
switch (rule.severity) {
|
|
35
|
+
case 'critical': return 'block';
|
|
36
|
+
case 'high': return 'confirm'; // v0.4.0: Changed from 'warn' to 'confirm'
|
|
37
|
+
case 'medium': return 'warn';
|
|
38
|
+
case 'low': return 'log';
|
|
39
|
+
default: return 'block';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* v0.3.0: Determine scope description based on rule category
|
|
44
|
+
*/
|
|
45
|
+
getScopeForCategory(category) {
|
|
46
|
+
switch (category) {
|
|
47
|
+
case 'workflow': return 'Workflow safety';
|
|
48
|
+
case 'quality': return 'Code quality';
|
|
49
|
+
case 'safety': return 'Security';
|
|
50
|
+
case 'network_boundary': return 'Network access';
|
|
51
|
+
case 'supply_chain': return 'Supply chain';
|
|
52
|
+
default: return 'Security violation';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
17
55
|
/**
|
|
18
56
|
* Create violation object
|
|
57
|
+
* v0.4.0: Use rule.action override for recommendedAction
|
|
19
58
|
*/
|
|
20
59
|
createViolation(rule, toolData, additionalInfo) {
|
|
21
60
|
return {
|
|
@@ -30,7 +69,10 @@ class BaseDetector {
|
|
|
30
69
|
filePath: (toolData.parameters.file_path || toolData.parameters.TargetFile || toolData.parameters.AbsolutePath || toolData.parameters.path),
|
|
31
70
|
additionalInfo
|
|
32
71
|
},
|
|
33
|
-
timestamp: new Date().toISOString()
|
|
72
|
+
timestamp: new Date().toISOString(),
|
|
73
|
+
// v0.4.0: Support rule.action override
|
|
74
|
+
recommendedAction: this.getRecommendedActionForSeverity(rule),
|
|
75
|
+
scope: this.getScopeForCategory(rule.category)
|
|
34
76
|
};
|
|
35
77
|
}
|
|
36
78
|
/**
|
package/dist/detectors/state.js
CHANGED
|
@@ -25,7 +25,10 @@ class StateDetector extends base_1.BaseDetector {
|
|
|
25
25
|
additionalInfo: {
|
|
26
26
|
message: `Required state '${config.requiredState}' is '${currentState || 'undefined'}', expected '${config.targetStateValue}'.`
|
|
27
27
|
}
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
|
+
// v0.4.0: Support rule.action override
|
|
30
|
+
recommendedAction: this.getRecommendedActionForSeverity(rule),
|
|
31
|
+
scope: this.getScopeForCategory(rule.category)
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
if (config.stateDerivation?.validityDurationMs) {
|
|
@@ -46,7 +49,10 @@ class StateDetector extends base_1.BaseDetector {
|
|
|
46
49
|
additionalInfo: {
|
|
47
50
|
message: `Required state '${config.requiredState}' has expired (last verified ${Math.floor(timeDiff / 1000)}s ago).`
|
|
48
51
|
}
|
|
49
|
-
}
|
|
52
|
+
},
|
|
53
|
+
// v0.4.0: Support rule.action override
|
|
54
|
+
recommendedAction: this.getRecommendedActionForSeverity(rule),
|
|
55
|
+
scope: this.getScopeForCategory(rule.category)
|
|
50
56
|
};
|
|
51
57
|
}
|
|
52
58
|
}
|
|
@@ -27,7 +27,8 @@ function createWardnMiddleware(config) {
|
|
|
27
27
|
prompt: content,
|
|
28
28
|
context: { source: "vercel-ai-sdk" },
|
|
29
29
|
});
|
|
30
|
-
|
|
30
|
+
// v0.3.0: Check action instead of allowed
|
|
31
|
+
if (result.action === 'block') {
|
|
31
32
|
throw new Error(`Security Violation: ${result.violations[0]?.description || "Blocked by Wardn"}`);
|
|
32
33
|
}
|
|
33
34
|
};
|
|
@@ -29,7 +29,8 @@ function createExpressMiddleware(guard, config) {
|
|
|
29
29
|
return next();
|
|
30
30
|
}
|
|
31
31
|
const result = await guard.scan(wardnRequest);
|
|
32
|
-
|
|
32
|
+
// v0.3.0: Check action instead of allowed
|
|
33
|
+
if (result.action === 'block') {
|
|
33
34
|
if (config?.onBlock) {
|
|
34
35
|
return config.onBlock(req, res, result);
|
|
35
36
|
}
|
|
@@ -28,7 +28,8 @@ function createNextMiddleware(config) {
|
|
|
28
28
|
ip: req.ip || req.headers.get("x-forwarded-for") || "unknown",
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
|
-
|
|
31
|
+
// v0.3.0: Check action instead of allowed
|
|
32
|
+
if (result.action === 'block') {
|
|
32
33
|
return server_1.NextResponse.json({
|
|
33
34
|
error: "Request blocked by Wardn Security Policy",
|
|
34
35
|
code: "WARDN_BLOCK",
|
package/dist/types.d.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Rule Schema - Core type definitions for WardnMesh rules
|
|
3
3
|
*/
|
|
4
4
|
export type RuleCategory = "workflow" | "quality" | "safety" | "network_boundary" | "supply_chain";
|
|
5
|
-
export type Severity = "critical" | "
|
|
5
|
+
export type Severity = "critical" | "high" | "medium" | "low";
|
|
6
6
|
export type DetectorType = "sequence" | "state" | "pattern" | "content_analysis" | "semantic";
|
|
7
|
+
export type ThreatAction = "allow" | "block" | "warn" | "log" | "confirm";
|
|
7
8
|
export type EscalationLevel = "none" | "warning" | "critical" | "block";
|
|
8
9
|
export interface SequenceDetectorConfig {
|
|
9
10
|
lookback: number;
|
|
@@ -88,6 +89,7 @@ export interface Rule {
|
|
|
88
89
|
};
|
|
89
90
|
escalation: EscalationConfig;
|
|
90
91
|
autofix?: AutofixConfig;
|
|
92
|
+
action?: ThreatAction;
|
|
91
93
|
}
|
|
92
94
|
export interface ToolData {
|
|
93
95
|
toolName: string;
|
|
@@ -110,6 +112,8 @@ export interface Violation {
|
|
|
110
112
|
description: string;
|
|
111
113
|
context: ViolationContext;
|
|
112
114
|
timestamp: string;
|
|
115
|
+
recommendedAction: ThreatAction;
|
|
116
|
+
scope?: string;
|
|
113
117
|
}
|
|
114
118
|
export interface ViolationContext {
|
|
115
119
|
toolName: string;
|
|
@@ -198,9 +202,17 @@ export interface WardnRequest {
|
|
|
198
202
|
metadata?: Record<string, unknown>;
|
|
199
203
|
}
|
|
200
204
|
export interface ScanResult {
|
|
201
|
-
|
|
205
|
+
action: ThreatAction;
|
|
202
206
|
violations: Violation[];
|
|
203
207
|
latencyMs: number;
|
|
204
208
|
metadata: Record<string, unknown>;
|
|
209
|
+
confirmationDetails?: {
|
|
210
|
+
message: string;
|
|
211
|
+
timeout: number;
|
|
212
|
+
defaultAction: 'allow' | 'block';
|
|
213
|
+
ruleId: string;
|
|
214
|
+
ruleName: string;
|
|
215
|
+
severity: Severity;
|
|
216
|
+
};
|
|
205
217
|
}
|
|
206
218
|
export type PatternConfig = PatternDetectorConfig;
|
|
@@ -12,7 +12,7 @@ const VALID_DETECTOR_TYPES = [
|
|
|
12
12
|
"content_analysis",
|
|
13
13
|
"semantic",
|
|
14
14
|
];
|
|
15
|
-
const VALID_SEVERITIES = ["critical", "
|
|
15
|
+
const VALID_SEVERITIES = ["critical", "high", "medium", "low"];
|
|
16
16
|
const VALID_CATEGORIES = [
|
|
17
17
|
"workflow",
|
|
18
18
|
"quality",
|
package/dist/wardn.d.ts
CHANGED
|
@@ -33,12 +33,32 @@ export declare class Wardn {
|
|
|
33
33
|
* @returns ScanResult with allowed/blocked status and violations
|
|
34
34
|
*/
|
|
35
35
|
scan(request: WardnRequest): Promise<ScanResult>;
|
|
36
|
+
/**
|
|
37
|
+
* v0.3.0: Determine final action based on all violations
|
|
38
|
+
* Priority: block > confirm > warn > log > allow
|
|
39
|
+
*
|
|
40
|
+
* Defensive design: Uses fail-closed approach - defaults to 'block' if
|
|
41
|
+
* recommendedAction is missing or invalid, ensuring security by default.
|
|
42
|
+
*/
|
|
43
|
+
private determineAction;
|
|
36
44
|
/** Normalize request to ToolData format */
|
|
37
45
|
private normalizeRequest;
|
|
38
46
|
/** Run detector for a single rule */
|
|
39
47
|
private detectViolation;
|
|
40
48
|
/** Handle semantic detection */
|
|
41
49
|
private detectSemanticViolation;
|
|
50
|
+
/**
|
|
51
|
+
* v0.4.0: Map severity to recommended action, with optional rule action override
|
|
52
|
+
*
|
|
53
|
+
* Default mapping:
|
|
54
|
+
* - critical → block (immediate threat)
|
|
55
|
+
* - high → confirm (needs user approval)
|
|
56
|
+
* - medium → warn (notify but allow)
|
|
57
|
+
* - low → log (record only)
|
|
58
|
+
*
|
|
59
|
+
* Rules can override default mapping via rule.action
|
|
60
|
+
*/
|
|
61
|
+
private getRecommendedActionForSeverity;
|
|
42
62
|
/** Report violation to telemetry */
|
|
43
63
|
private reportViolation;
|
|
44
64
|
/** Report scan completion to telemetry */
|
package/dist/wardn.js
CHANGED
|
@@ -175,7 +175,7 @@ class Wardn {
|
|
|
175
175
|
async scan(request) {
|
|
176
176
|
if (this.isShutdown) {
|
|
177
177
|
return {
|
|
178
|
-
|
|
178
|
+
action: 'allow',
|
|
179
179
|
violations: [],
|
|
180
180
|
latencyMs: 0,
|
|
181
181
|
metadata: { error: true, errorDetails: "Wardn instance is shutdown" },
|
|
@@ -205,8 +205,10 @@ class Wardn {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
await this.stateProvider.setState(currentSessionId, stateAdapter.exportState());
|
|
208
|
+
// v0.3.0: Determine action based on violations
|
|
209
|
+
const action = this.determineAction(violations);
|
|
208
210
|
const result = {
|
|
209
|
-
|
|
211
|
+
action,
|
|
210
212
|
violations,
|
|
211
213
|
latencyMs: Date.now() - startTime,
|
|
212
214
|
metadata: {
|
|
@@ -214,6 +216,35 @@ class Wardn {
|
|
|
214
216
|
sessionId: currentSessionId,
|
|
215
217
|
},
|
|
216
218
|
};
|
|
219
|
+
// v0.4.0: Add confirmation details if action is 'confirm'
|
|
220
|
+
if (action === 'confirm') {
|
|
221
|
+
const confirmViolation = violations.find(v => v.recommendedAction === 'confirm');
|
|
222
|
+
if (confirmViolation) {
|
|
223
|
+
// Extract metadata if available, otherwise use defaults
|
|
224
|
+
const metadata = (confirmViolation.context.additionalInfo?.metadata || {});
|
|
225
|
+
// Generate enhanced default message with violation context
|
|
226
|
+
const additionalDetails = confirmViolation.context.additionalInfo?.message
|
|
227
|
+
? `\nDetails: ${confirmViolation.context.additionalInfo.message}`
|
|
228
|
+
: '';
|
|
229
|
+
const defaultMessage = `⚠️ Security Alert
|
|
230
|
+
|
|
231
|
+
Rule: ${confirmViolation.ruleName}
|
|
232
|
+
Severity: ${confirmViolation.severity.toUpperCase()}
|
|
233
|
+
Tool: ${confirmViolation.context.toolName}
|
|
234
|
+
|
|
235
|
+
Description: ${confirmViolation.description}${additionalDetails}
|
|
236
|
+
|
|
237
|
+
This operation requires your approval to proceed.`;
|
|
238
|
+
result.confirmationDetails = {
|
|
239
|
+
message: metadata.confirmationMessage || defaultMessage,
|
|
240
|
+
timeout: metadata.timeout || 30000,
|
|
241
|
+
defaultAction: metadata.defaultAction || 'block',
|
|
242
|
+
ruleId: confirmViolation.ruleId,
|
|
243
|
+
ruleName: confirmViolation.ruleName,
|
|
244
|
+
severity: confirmViolation.severity,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
217
248
|
this.reportScanComplete(result, currentSessionId);
|
|
218
249
|
return result;
|
|
219
250
|
}
|
|
@@ -229,9 +260,9 @@ class Wardn {
|
|
|
229
260
|
},
|
|
230
261
|
});
|
|
231
262
|
// Configurable fail mode
|
|
232
|
-
const
|
|
263
|
+
const action = failMode === "open" ? "allow" : "block";
|
|
233
264
|
return {
|
|
234
|
-
|
|
265
|
+
action,
|
|
235
266
|
violations: [],
|
|
236
267
|
latencyMs: Date.now() - startTime,
|
|
237
268
|
metadata: {
|
|
@@ -242,6 +273,40 @@ class Wardn {
|
|
|
242
273
|
};
|
|
243
274
|
}
|
|
244
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* v0.3.0: Determine final action based on all violations
|
|
278
|
+
* Priority: block > confirm > warn > log > allow
|
|
279
|
+
*
|
|
280
|
+
* Defensive design: Uses fail-closed approach - defaults to 'block' if
|
|
281
|
+
* recommendedAction is missing or invalid, ensuring security by default.
|
|
282
|
+
*/
|
|
283
|
+
determineAction(violations) {
|
|
284
|
+
if (violations.length === 0) {
|
|
285
|
+
return 'allow';
|
|
286
|
+
}
|
|
287
|
+
// Define action priority order
|
|
288
|
+
const actionPriority = ['block', 'confirm', 'warn', 'log', 'allow'];
|
|
289
|
+
const validActions = new Set(actionPriority);
|
|
290
|
+
// Find highest priority action from all violations
|
|
291
|
+
for (const action of actionPriority) {
|
|
292
|
+
const hasAction = violations.some(v => {
|
|
293
|
+
// Defensive: validate recommendedAction exists and is valid
|
|
294
|
+
const recommended = v.recommendedAction;
|
|
295
|
+
if (!recommended || !validActions.has(recommended)) {
|
|
296
|
+
// Fail-closed: log warning and treat as 'block'
|
|
297
|
+
logger_1.logger.warn(`Invalid recommendedAction '${recommended}' in violation ${v.id}, defaulting to 'block'`);
|
|
298
|
+
return action === 'block';
|
|
299
|
+
}
|
|
300
|
+
return recommended === action;
|
|
301
|
+
});
|
|
302
|
+
if (hasAction) {
|
|
303
|
+
return action;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Fallback: should never reach here, but fail-closed for safety
|
|
307
|
+
logger_1.logger.warn('determineAction fallback: no valid action found, defaulting to block');
|
|
308
|
+
return 'block';
|
|
309
|
+
}
|
|
245
310
|
/** Normalize request to ToolData format */
|
|
246
311
|
normalizeRequest(request) {
|
|
247
312
|
return {
|
|
@@ -287,8 +352,41 @@ class Wardn {
|
|
|
287
352
|
additionalInfo: { score },
|
|
288
353
|
},
|
|
289
354
|
timestamp: new Date().toISOString(),
|
|
355
|
+
// v0.4.0: Set recommended action (rule.action or default severity mapping)
|
|
356
|
+
recommendedAction: this.getRecommendedActionForSeverity(rule),
|
|
357
|
+
scope: 'Semantic analysis',
|
|
290
358
|
};
|
|
291
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* v0.4.0: Map severity to recommended action, with optional rule action override
|
|
362
|
+
*
|
|
363
|
+
* Default mapping:
|
|
364
|
+
* - critical → block (immediate threat)
|
|
365
|
+
* - high → confirm (needs user approval)
|
|
366
|
+
* - medium → warn (notify but allow)
|
|
367
|
+
* - low → log (record only)
|
|
368
|
+
*
|
|
369
|
+
* Rules can override default mapping via rule.action
|
|
370
|
+
*/
|
|
371
|
+
getRecommendedActionForSeverity(rule) {
|
|
372
|
+
// Rule action override takes precedence
|
|
373
|
+
if (rule.action) {
|
|
374
|
+
return rule.action;
|
|
375
|
+
}
|
|
376
|
+
// Default severity-to-action mapping
|
|
377
|
+
switch (rule.severity) {
|
|
378
|
+
case 'critical':
|
|
379
|
+
return 'block';
|
|
380
|
+
case 'high':
|
|
381
|
+
return 'confirm'; // v0.4.0: Changed from 'warn' to 'confirm'
|
|
382
|
+
case 'medium':
|
|
383
|
+
return 'warn';
|
|
384
|
+
case 'low':
|
|
385
|
+
return 'log';
|
|
386
|
+
default:
|
|
387
|
+
return 'block'; // Safe default
|
|
388
|
+
}
|
|
389
|
+
}
|
|
292
390
|
/** Report violation to telemetry */
|
|
293
391
|
reportViolation(violation, toolData, sessionId) {
|
|
294
392
|
// REDACTION: Create a safe copy for the cloud
|
|
@@ -311,7 +409,7 @@ class Wardn {
|
|
|
311
409
|
eventType: "scan_complete",
|
|
312
410
|
timestamp: new Date().toISOString(),
|
|
313
411
|
data: {
|
|
314
|
-
|
|
412
|
+
action: result.action,
|
|
315
413
|
latencyMs: result.latencyMs,
|
|
316
414
|
violationCount: result.violations.length,
|
|
317
415
|
},
|