@wardnmesh/sdk-node 0.2.1

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 (42) hide show
  1. package/LICENSE +47 -0
  2. package/README.md +178 -0
  3. package/bin/setup.js +119 -0
  4. package/dist/agent-guard.d.ts +28 -0
  5. package/dist/agent-guard.js +206 -0
  6. package/dist/config/security-limits.d.ts +42 -0
  7. package/dist/config/security-limits.js +52 -0
  8. package/dist/detectors/base.d.ts +22 -0
  9. package/dist/detectors/base.js +51 -0
  10. package/dist/detectors/pattern.d.ts +7 -0
  11. package/dist/detectors/pattern.js +76 -0
  12. package/dist/detectors/semantic-detector.d.ts +16 -0
  13. package/dist/detectors/semantic-detector.js +96 -0
  14. package/dist/detectors/sequence.d.ts +11 -0
  15. package/dist/detectors/sequence.js +182 -0
  16. package/dist/detectors/state.d.ts +8 -0
  17. package/dist/detectors/state.js +86 -0
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.js +31 -0
  20. package/dist/integrations/vercel.d.ts +7 -0
  21. package/dist/integrations/vercel.js +34 -0
  22. package/dist/middleware/express.d.ts +36 -0
  23. package/dist/middleware/express.js +54 -0
  24. package/dist/middleware/nextjs.d.ts +3 -0
  25. package/dist/middleware/nextjs.js +40 -0
  26. package/dist/state/session-manager.d.ts +13 -0
  27. package/dist/state/session-manager.js +22 -0
  28. package/dist/telemetry/reporter.d.ts +32 -0
  29. package/dist/telemetry/reporter.js +86 -0
  30. package/dist/types.d.ts +206 -0
  31. package/dist/types.js +14 -0
  32. package/dist/update-checker.d.ts +21 -0
  33. package/dist/update-checker.js +218 -0
  34. package/dist/utils/logger.d.ts +40 -0
  35. package/dist/utils/logger.js +79 -0
  36. package/dist/utils/rule-validator.d.ts +15 -0
  37. package/dist/utils/rule-validator.js +143 -0
  38. package/dist/utils/safe-regex.d.ts +53 -0
  39. package/dist/utils/safe-regex.js +220 -0
  40. package/dist/wardn.d.ts +67 -0
  41. package/dist/wardn.js +443 -0
  42. package/package.json +47 -0
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createExpressMiddleware = createExpressMiddleware;
4
+ const logger_1 = require("../utils/logger");
5
+ function createExpressMiddleware(guard, config) {
6
+ return async (req, res, next) => {
7
+ try {
8
+ let wardnRequest;
9
+ if (config?.extractRequest) {
10
+ wardnRequest = await config.extractRequest(req);
11
+ }
12
+ else {
13
+ // Default extraction logic: assumes standard JSON body
14
+ if (!req.body) {
15
+ logger_1.logger.warn("req.body is undefined. Ensure express.json() middleware is used before Wardn.");
16
+ }
17
+ const sessionIdHeader = req.headers?.["x-session-id"];
18
+ wardnRequest = {
19
+ sessionId: (typeof sessionIdHeader === "string" ? sessionIdHeader : undefined) ||
20
+ req.body?.sessionId,
21
+ prompt: (req.body?.prompt || req.body?.message),
22
+ messages: req.body?.messages,
23
+ toolName: req.body?.tool,
24
+ parameters: (req.body?.parameters || req.body?.args),
25
+ };
26
+ }
27
+ // If nothing to scan, skip
28
+ if (!wardnRequest.prompt && !wardnRequest.toolName) {
29
+ return next();
30
+ }
31
+ const result = await guard.scan(wardnRequest);
32
+ if (!result.allowed) {
33
+ if (config?.onBlock) {
34
+ return config.onBlock(req, res, result);
35
+ }
36
+ return res.status(403).json({
37
+ error: "Wardn Blocked Request",
38
+ code: "WARDN_BLOCK",
39
+ violations: result.violations,
40
+ });
41
+ }
42
+ // Attach result to request for downstream use
43
+ req.wardnResult = result;
44
+ next();
45
+ }
46
+ catch (error) {
47
+ logger_1.logger.error("Middleware Error:", error);
48
+ // Fail open or closed? Defaulting to Fail Open to not break app on internal error,
49
+ // but typically security should Fail Closed.
50
+ // For now, logging and continuing.
51
+ next();
52
+ }
53
+ };
54
+ }
@@ -0,0 +1,3 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { WardnConfig } from "../types";
3
+ export declare function createNextMiddleware(config: WardnConfig): (req: NextRequest) => Promise<NextResponse<unknown>>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNextMiddleware = createNextMiddleware;
4
+ const wardn_1 = require("../wardn");
5
+ const server_1 = require("next/server");
6
+ function createNextMiddleware(config) {
7
+ // Initialize the singleton if not already initialized
8
+ try {
9
+ wardn_1.Wardn.getInstance();
10
+ }
11
+ catch {
12
+ wardn_1.Wardn.init(config);
13
+ }
14
+ const guard = wardn_1.Wardn.getInstance();
15
+ return async function middleware(req) {
16
+ // Clone the request to read body without consuming the stream
17
+ const clone = req.clone();
18
+ const body = await clone.json().catch(() => ({}));
19
+ // Extract prompt from common locations
20
+ const prompt = body.prompt ||
21
+ body.messages?.[body.messages.length - 1]?.content ||
22
+ JSON.stringify(body);
23
+ const result = await guard.scan({
24
+ prompt,
25
+ metadata: {
26
+ path: req.nextUrl.pathname,
27
+ method: req.method,
28
+ ip: req.ip || req.headers.get("x-forwarded-for") || "unknown",
29
+ },
30
+ });
31
+ if (!result.allowed) {
32
+ return server_1.NextResponse.json({
33
+ error: "Request blocked by Wardn Security Policy",
34
+ code: "WARDN_BLOCK",
35
+ violations: result.violations,
36
+ }, { status: 403 });
37
+ }
38
+ return server_1.NextResponse.next();
39
+ };
40
+ }
@@ -0,0 +1,13 @@
1
+ export interface SessionStateProvider {
2
+ getState(sessionId: string): Promise<Record<string, unknown>>;
3
+ setState(sessionId: string, state: Record<string, unknown>): Promise<void>;
4
+ updateState(sessionId: string, updates: Record<string, unknown>): Promise<void>;
5
+ clearState(sessionId: string): Promise<void>;
6
+ }
7
+ export declare class InMemorySessionStateProvider implements SessionStateProvider {
8
+ private store;
9
+ getState(sessionId: string): Promise<Record<string, unknown>>;
10
+ setState(sessionId: string, state: Record<string, unknown>): Promise<void>;
11
+ updateState(sessionId: string, updates: Record<string, unknown>): Promise<void>;
12
+ clearState(sessionId: string): Promise<void>;
13
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemorySessionStateProvider = void 0;
4
+ class InMemorySessionStateProvider {
5
+ constructor() {
6
+ this.store = new Map();
7
+ }
8
+ async getState(sessionId) {
9
+ return this.store.get(sessionId) || {};
10
+ }
11
+ async setState(sessionId, state) {
12
+ this.store.set(sessionId, state);
13
+ }
14
+ async updateState(sessionId, updates) {
15
+ const current = await this.getState(sessionId);
16
+ this.store.set(sessionId, { ...current, ...updates });
17
+ }
18
+ async clearState(sessionId) {
19
+ this.store.delete(sessionId);
20
+ }
21
+ }
22
+ exports.InMemorySessionStateProvider = InMemorySessionStateProvider;
@@ -0,0 +1,32 @@
1
+ export interface TelemetryEvent {
2
+ eventType: "scan_complete" | "violation_detected" | "error" | "agent_started" | "agent_shutdown" | "rules_updated";
3
+ timestamp: string;
4
+ data: Record<string, unknown>;
5
+ metadata?: Record<string, unknown>;
6
+ }
7
+ export interface TelemetryReporter {
8
+ emit(event: TelemetryEvent): void | Promise<void>;
9
+ flush(): Promise<void>;
10
+ stop?(): void;
11
+ }
12
+ export declare class ConsoleReporter implements TelemetryReporter {
13
+ emit(event: TelemetryEvent): void;
14
+ flush(): Promise<void>;
15
+ }
16
+ export declare class WardnReporter implements TelemetryReporter {
17
+ private buffer;
18
+ private endpoint;
19
+ private apiKey;
20
+ private batchSize;
21
+ private flushInterval;
22
+ private timer;
23
+ private serviceName;
24
+ constructor(endpoint: string, apiKey: string, serviceName?: string);
25
+ emit(event: TelemetryEvent): void;
26
+ private startAutoFlush;
27
+ flush(): Promise<void>;
28
+ /**
29
+ * Stop the auto-flush timer. Call this on shutdown.
30
+ */
31
+ stop(): void;
32
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WardnReporter = exports.ConsoleReporter = void 0;
4
+ const logger_1 = require("../utils/logger");
5
+ class ConsoleReporter {
6
+ emit(event) {
7
+ if (event.eventType === "violation_detected") {
8
+ logger_1.logger.warn("Violation Detected:", event.data);
9
+ }
10
+ else if (event.eventType === "error") {
11
+ logger_1.logger.error("Error:", event.data);
12
+ }
13
+ // debug logging for scan_complete?
14
+ }
15
+ async flush() {
16
+ // No-op for console
17
+ }
18
+ }
19
+ exports.ConsoleReporter = ConsoleReporter;
20
+ class WardnReporter {
21
+ constructor(endpoint, apiKey, serviceName = "unknown_service") {
22
+ this.buffer = [];
23
+ this.batchSize = 10;
24
+ this.flushInterval = 5000;
25
+ this.timer = null;
26
+ this.endpoint = endpoint;
27
+ this.apiKey = apiKey;
28
+ this.serviceName = serviceName;
29
+ this.startAutoFlush();
30
+ }
31
+ emit(event) {
32
+ // Add service metadata
33
+ event.metadata = { ...event.metadata, service: this.serviceName };
34
+ this.buffer.push(event);
35
+ if (this.buffer.length >= this.batchSize) {
36
+ this.flush();
37
+ }
38
+ }
39
+ startAutoFlush() {
40
+ this.timer = setInterval(() => this.flush(), this.flushInterval);
41
+ // Unref to not hold process open
42
+ this.timer.unref();
43
+ }
44
+ async flush() {
45
+ if (this.buffer.length === 0)
46
+ return;
47
+ const batch = [...this.buffer];
48
+ this.buffer = [];
49
+ try {
50
+ // Mock HTTP call for now if no endpoint, or real fetch
51
+ if (!this.endpoint.startsWith("http")) {
52
+ // Mock flush
53
+ return;
54
+ }
55
+ const apiUrl = this.endpoint.endsWith("/")
56
+ ? this.endpoint.slice(0, -1)
57
+ : this.endpoint;
58
+ await fetch(`${apiUrl}/api/telemetry/batch`, {
59
+ method: "POST",
60
+ headers: {
61
+ "Content-Type": "application/json",
62
+ Authorization: `Bearer ${this.apiKey}`,
63
+ },
64
+ body: JSON.stringify({ events: batch }),
65
+ });
66
+ }
67
+ catch (error) {
68
+ logger_1.logger.error("Failed to flush telemetry:", error);
69
+ // Retry logic? For now, drop to avoid memory leak or infinitely growing buffer
70
+ }
71
+ }
72
+ /**
73
+ * Stop the auto-flush timer. Call this on shutdown.
74
+ */
75
+ stop() {
76
+ if (this.timer) {
77
+ clearInterval(this.timer);
78
+ this.timer = null;
79
+ }
80
+ // Final flush attempt
81
+ this.flush().catch(() => {
82
+ // Ignore errors on final flush
83
+ });
84
+ }
85
+ }
86
+ exports.WardnReporter = WardnReporter;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Rule Schema - Core type definitions for WardnMesh rules
3
+ */
4
+ export type RuleCategory = "workflow" | "quality" | "safety" | "network_boundary" | "supply_chain";
5
+ export type Severity = "critical" | "major" | "minor";
6
+ export type DetectorType = "sequence" | "state" | "pattern" | "content_analysis" | "semantic";
7
+ export type EscalationLevel = "none" | "warning" | "critical" | "block";
8
+ export interface SequenceDetectorConfig {
9
+ lookback: number;
10
+ pattern: SequencePattern[];
11
+ advancedChecks?: {
12
+ multipleEditsThreshold?: number;
13
+ requireSuccessfulRead?: boolean;
14
+ };
15
+ }
16
+ export interface SequencePattern {
17
+ tool: string;
18
+ extractPath: string;
19
+ storeAs?: string;
20
+ mustMatch?: string;
21
+ maxTimeSinceMatch?: number;
22
+ requireSuccess?: boolean;
23
+ matchesPattern?: string;
24
+ }
25
+ export interface StateDetectorConfig {
26
+ requiredState: string;
27
+ targetStateValue: unknown;
28
+ trigger: {
29
+ tool: string;
30
+ parameterMatch?: {
31
+ key: string;
32
+ valuePattern: string;
33
+ };
34
+ };
35
+ stateDerivation?: {
36
+ fromTool: string;
37
+ setState: string;
38
+ setValue: unknown;
39
+ validityDurationMs?: number;
40
+ };
41
+ }
42
+ export interface StateCondition {
43
+ eventType: "scan_complete" | "violation_detected" | "error" | "agent_started";
44
+ value?: unknown;
45
+ predicate?: (state: Record<string, unknown>) => boolean;
46
+ }
47
+ export interface PatternDetectorConfig {
48
+ targetTool: string;
49
+ targetParameter: string;
50
+ patterns: {
51
+ name: string;
52
+ regex: string;
53
+ description: string;
54
+ }[];
55
+ exceptions?: string[];
56
+ patternType?: "excessive_usage" | "slow_execution" | "high_failure_rate";
57
+ threshold?: number;
58
+ timeWindow?: number;
59
+ }
60
+ export interface ContentAnalysisDetectorConfig {
61
+ suspiciousPatterns?: RegExp[];
62
+ validPatterns?: RegExp[];
63
+ logic: "suspicious_without_valid" | "contains_suspicious" | "missing_valid";
64
+ minMatches?: number;
65
+ }
66
+ export interface SemanticDetectorConfig {
67
+ type: "semantic";
68
+ embeddingMatch?: string;
69
+ threshold?: number;
70
+ }
71
+ export type DetectorConfig = SequenceDetectorConfig | StateDetectorConfig | PatternDetectorConfig | ContentAnalysisDetectorConfig | SemanticDetectorConfig;
72
+ export type EscalationConfig = Record<number, EscalationLevel>;
73
+ export interface AutofixConfig {
74
+ enabled: boolean;
75
+ agent: string;
76
+ strategy: string;
77
+ params?: Record<string, unknown>;
78
+ }
79
+ export interface Rule {
80
+ id: string;
81
+ name: string;
82
+ category: RuleCategory;
83
+ severity: Severity;
84
+ description: string;
85
+ detector: {
86
+ type: DetectorType;
87
+ config: DetectorConfig;
88
+ };
89
+ escalation: EscalationConfig;
90
+ autofix?: AutofixConfig;
91
+ }
92
+ export interface ToolData {
93
+ toolName: string;
94
+ parameters: Record<string, unknown>;
95
+ result: ToolResult;
96
+ duration: number;
97
+ timestamp: string;
98
+ }
99
+ export interface ToolResult {
100
+ success: boolean;
101
+ output?: string;
102
+ error?: string;
103
+ [key: string]: unknown;
104
+ }
105
+ export interface Violation {
106
+ id: string;
107
+ ruleId: string;
108
+ ruleName: string;
109
+ severity: Severity;
110
+ description: string;
111
+ context: ViolationContext;
112
+ timestamp: string;
113
+ }
114
+ export interface ViolationContext {
115
+ toolName: string;
116
+ toolData: ToolData;
117
+ filePath?: string;
118
+ additionalInfo?: Record<string, unknown>;
119
+ }
120
+ export interface ViolationRecord {
121
+ ruleId: string;
122
+ count: number;
123
+ firstViolation: string;
124
+ lastViolation: string;
125
+ history: ViolationEvent[];
126
+ escalationLevel: EscalationLevel;
127
+ }
128
+ export interface ViolationEvent {
129
+ violation: Violation;
130
+ timestamp: string;
131
+ action: "detected" | "warned" | "critical" | "blocked";
132
+ }
133
+ export interface SessionState {
134
+ startTime: string;
135
+ toolCalls: ToolData[];
136
+ recentTools: ToolData[];
137
+ detectedViolations: Violation[];
138
+ currentFile?: string;
139
+ customState: Record<string, unknown>;
140
+ }
141
+ /**
142
+ * Interface for State Provider
143
+ */
144
+ export interface StateProvider {
145
+ getRecentTools(count?: number): ToolData[];
146
+ setCustomState(key: string, value: unknown): void;
147
+ getCustomState(key: string): unknown;
148
+ }
149
+ export interface Detector {
150
+ detect(toolData: ToolData, rule: Rule, sessionState: StateProvider): Violation | null;
151
+ getType?(): DetectorType | string;
152
+ }
153
+ export declare enum RiskLevel {
154
+ NONE = "none",
155
+ WARNING = "warning",
156
+ CRITICAL = "critical",
157
+ BLOCK = "block"
158
+ }
159
+ export interface WardnConfig {
160
+ rules: Rule[];
161
+ enabled?: boolean;
162
+ /**
163
+ * Maximum number of recent tool calls to keep in session state.
164
+ * Defaults to 50.
165
+ */
166
+ maxHistorySize?: number;
167
+ telemetry?: {
168
+ enabled?: boolean;
169
+ serviceName?: string;
170
+ };
171
+ /**
172
+ * URL to poll for rule updates (e.g., "http://localhost:3000/api/rules").
173
+ * If provided, the SDK will periodically fetch rules from this endpoint.
174
+ */
175
+ remoteRulesUrl?: string;
176
+ /**
177
+ * API Key for WardnMesh Cloud (optional if WARDN_API_KEY env var is set)
178
+ */
179
+ apiKey?: string;
180
+ /**
181
+ * Polling interval in milliseconds. Defaults to 60000 (1 minute).
182
+ */
183
+ pollingIntervalMs?: number;
184
+ /**
185
+ * Behavior when scan encounters an error.
186
+ * - 'open': Allow request through on error (default, fail-open)
187
+ * - 'closed': Block request on error (fail-closed, more secure)
188
+ */
189
+ failMode?: "open" | "closed";
190
+ }
191
+ export interface WardnRequest {
192
+ sessionId?: string;
193
+ toolName?: string;
194
+ parameters?: Record<string, unknown>;
195
+ prompt?: string;
196
+ messages?: unknown[];
197
+ context?: Record<string, unknown>;
198
+ metadata?: Record<string, unknown>;
199
+ }
200
+ export interface ScanResult {
201
+ allowed: boolean;
202
+ violations: Violation[];
203
+ latencyMs: number;
204
+ metadata: Record<string, unknown>;
205
+ }
206
+ export type PatternConfig = PatternDetectorConfig;
package/dist/types.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Rule Schema - Core type definitions for WardnMesh rules
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RiskLevel = void 0;
7
+ // --- Public SDK API Types ---
8
+ var RiskLevel;
9
+ (function (RiskLevel) {
10
+ RiskLevel["NONE"] = "none";
11
+ RiskLevel["WARNING"] = "warning";
12
+ RiskLevel["CRITICAL"] = "critical";
13
+ RiskLevel["BLOCK"] = "block";
14
+ })(RiskLevel || (exports.RiskLevel = RiskLevel = {}));
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Update Checker - Check for new SDK versions on GitHub Releases
3
+ * Caches results to avoid excessive API calls
4
+ */
5
+ export interface VersionInfo {
6
+ currentVersion: string;
7
+ latestVersion: string;
8
+ updateAvailable: boolean;
9
+ releaseUrl?: string;
10
+ publishedAt?: string;
11
+ }
12
+ /**
13
+ * Check for SDK updates (uses cache if available)
14
+ * @param forceCheck - Force check even if cache is valid
15
+ * @returns Version information including update availability
16
+ */
17
+ export declare function checkForUpdates(forceCheck?: boolean): Promise<VersionInfo>;
18
+ /**
19
+ * Log update notification if update is available
20
+ */
21
+ export declare function notifyIfUpdateAvailable(): Promise<void>;