@solongate/proxy 0.47.7 → 0.48.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.
- package/dist/cli-utils.d.ts +27 -0
- package/dist/config.d.ts +105 -0
- package/dist/core/capability-token.d.ts +46 -0
- package/dist/core/constants.d.ts +47 -0
- package/dist/core/context-boundary.d.ts +19 -0
- package/dist/core/context.d.ts +24 -0
- package/dist/core/errors.d.ts +51 -0
- package/dist/core/execution.d.ts +35 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/input-guard.d.ts +66 -0
- package/dist/core/mcp-types.d.ts +35 -0
- package/dist/core/permissions.d.ts +27 -0
- package/dist/core/policy.d.ts +399 -0
- package/dist/core/response-scanner.d.ts +27 -0
- package/dist/core/schema-validator.d.ts +33 -0
- package/dist/core/tool.d.ts +23 -0
- package/dist/core/trust.d.ts +28 -0
- package/dist/create.d.ts +13 -0
- package/dist/global-install.d.ts +17 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +211 -158
- package/dist/init.d.ts +18 -0
- package/dist/init.js +0 -0
- package/dist/inject.d.ts +19 -0
- package/dist/lib.d.ts +18 -0
- package/dist/lib.js +103 -48
- package/dist/login.d.ts +2 -0
- package/dist/policy-engine/command-matcher.d.ts +34 -0
- package/dist/policy-engine/defaults.d.ts +23 -0
- package/dist/policy-engine/engine.d.ts +30 -0
- package/dist/policy-engine/evaluator.d.ts +13 -0
- package/dist/policy-engine/filename-matcher.d.ts +20 -0
- package/dist/policy-engine/index.d.ts +12 -0
- package/dist/policy-engine/matcher.d.ts +18 -0
- package/dist/policy-engine/opa/index.d.ts +4 -0
- package/dist/policy-engine/opa/json-to-rego.d.ts +2 -0
- package/dist/policy-engine/opa/opa-evaluator.d.ts +10 -0
- package/dist/policy-engine/opa/rego-compiler.d.ts +2 -0
- package/dist/policy-engine/opa/request-adapter.d.ts +17 -0
- package/dist/policy-engine/path-matcher.d.ts +41 -0
- package/dist/policy-engine/policy-store.d.ts +66 -0
- package/dist/policy-engine/url-matcher.d.ts +29 -0
- package/dist/policy-engine/validator.d.ts +7 -0
- package/dist/policy-engine/warnings.d.ts +10 -0
- package/dist/proxy.d.ts +89 -0
- package/dist/pull-push.d.ts +17 -0
- package/dist/sdk/config.d.ts +26 -0
- package/dist/sdk/expiring-set.d.ts +18 -0
- package/dist/sdk/index.d.ts +10 -0
- package/dist/sdk/interceptor.d.ts +45 -0
- package/dist/sdk/logger.d.ts +16 -0
- package/dist/sdk/rate-limiter.d.ts +59 -0
- package/dist/sdk/secure-server.d.ts +68 -0
- package/dist/sdk/server-verifier.d.ts +53 -0
- package/dist/sdk/solongate.d.ts +105 -0
- package/dist/sdk/token-issuer.d.ts +37 -0
- package/dist/sync.d.ts +82 -0
- package/hooks/audit.mjs +3 -1
- package/hooks/guard.bundled.mjs +63 -7
- package/hooks/guard.mjs +75 -14
- package/package.json +15 -13
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { PolicySet, PolicyRule } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* A versioned snapshot of a policy set.
|
|
4
|
+
* Immutable once created - modifications create new versions.
|
|
5
|
+
*/
|
|
6
|
+
export interface PolicyVersion {
|
|
7
|
+
readonly version: number;
|
|
8
|
+
readonly policySet: PolicySet;
|
|
9
|
+
readonly hash: string;
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
readonly createdBy: string;
|
|
12
|
+
readonly createdAt: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Diff between two policy versions.
|
|
16
|
+
*/
|
|
17
|
+
export interface PolicyDiff {
|
|
18
|
+
readonly added: readonly PolicyRule[];
|
|
19
|
+
readonly removed: readonly PolicyRule[];
|
|
20
|
+
readonly modified: readonly {
|
|
21
|
+
readonly old: PolicyRule;
|
|
22
|
+
readonly new: PolicyRule;
|
|
23
|
+
}[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* In-memory versioned policy store.
|
|
27
|
+
* Stores complete history of policy changes with cryptographic hashes.
|
|
28
|
+
*
|
|
29
|
+
* Security properties:
|
|
30
|
+
* - Immutable versions: once saved, a version cannot be modified
|
|
31
|
+
* - Hash chain: each version includes SHA256 of the policy content
|
|
32
|
+
* - Full history: no version is ever deleted
|
|
33
|
+
*/
|
|
34
|
+
export declare class PolicyStore {
|
|
35
|
+
private readonly versions;
|
|
36
|
+
/**
|
|
37
|
+
* Saves a new version of a policy set.
|
|
38
|
+
* The version number auto-increments.
|
|
39
|
+
*/
|
|
40
|
+
saveVersion(policySet: PolicySet, reason: string, createdBy: string): PolicyVersion;
|
|
41
|
+
/**
|
|
42
|
+
* Gets a specific version of a policy set.
|
|
43
|
+
*/
|
|
44
|
+
getVersion(id: string, version: number): PolicyVersion | null;
|
|
45
|
+
/**
|
|
46
|
+
* Gets the latest version of a policy set.
|
|
47
|
+
*/
|
|
48
|
+
getLatest(id: string): PolicyVersion | null;
|
|
49
|
+
/**
|
|
50
|
+
* Gets the full version history of a policy set.
|
|
51
|
+
*/
|
|
52
|
+
getHistory(id: string): readonly PolicyVersion[];
|
|
53
|
+
/**
|
|
54
|
+
* Rolls back to a previous version by creating a new version
|
|
55
|
+
* with the same content as the target version.
|
|
56
|
+
*/
|
|
57
|
+
rollback(id: string, toVersion: number): PolicyVersion;
|
|
58
|
+
/**
|
|
59
|
+
* Computes a diff between two policy versions.
|
|
60
|
+
*/
|
|
61
|
+
diff(v1: PolicyVersion, v2: PolicyVersion): PolicyDiff;
|
|
62
|
+
/**
|
|
63
|
+
* Computes SHA256 hash of a policy set for integrity verification.
|
|
64
|
+
*/
|
|
65
|
+
computeHash(policySet: PolicySet): string;
|
|
66
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PolicyRule } from '../core/index.js';
|
|
2
|
+
type UrlConstraints = NonNullable<PolicyRule['urlConstraints']>;
|
|
3
|
+
/**
|
|
4
|
+
* Extracts URL arguments from tool call arguments.
|
|
5
|
+
* Scans ALL string values — not just known field names.
|
|
6
|
+
* Any string containing http:// or https:// is treated as a URL.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractUrlArguments(args: Readonly<Record<string, unknown>>): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Glob-style URL pattern matching.
|
|
11
|
+
* Normalizes both URL and pattern to lowercase for comparison.
|
|
12
|
+
*
|
|
13
|
+
* Patterns:
|
|
14
|
+
* '*instagram.com*' → URL contains instagram.com
|
|
15
|
+
* 'https://api.example.com/*' → URL starts with prefix
|
|
16
|
+
* '*.onion' → URL ends with .onion
|
|
17
|
+
* '*' → matches everything
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchUrlPattern(url: string, pattern: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a URL is allowed by the given constraints.
|
|
22
|
+
*
|
|
23
|
+
* Evaluation order:
|
|
24
|
+
* 1. If denied list exists, URL must NOT match any denied pattern
|
|
25
|
+
* 2. If allowed list exists, URL must match at least one allowed pattern
|
|
26
|
+
* 3. If neither list exists, URL is allowed
|
|
27
|
+
*/
|
|
28
|
+
export declare function isUrlAllowed(url: string, constraints: UrlConstraints): boolean;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
readonly valid: boolean;
|
|
3
|
+
readonly errors: readonly string[];
|
|
4
|
+
readonly warnings: readonly string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function validatePolicyRule(input: unknown): ValidationResult;
|
|
7
|
+
export declare function validatePolicySet(input: unknown): ValidationResult;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PolicySet } from '../core/index.js';
|
|
2
|
+
export interface SecurityWarning {
|
|
3
|
+
readonly level: 'WARNING' | 'CRITICAL';
|
|
4
|
+
readonly code: string;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
readonly ruleId?: string;
|
|
7
|
+
readonly recommendation: string;
|
|
8
|
+
}
|
|
9
|
+
/** Analyzes a policy set and returns security warnings. Pure function. */
|
|
10
|
+
export declare function analyzeSecurityWarnings(policySet: PolicySet): readonly SecurityWarning[];
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ProxyConfig } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* SolonGate MCP Proxy.
|
|
4
|
+
*
|
|
5
|
+
* Sits between an MCP client (Claude) and an upstream MCP server.
|
|
6
|
+
* Intercepts all tool calls through the SolonGate security pipeline.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* Claude ──(stdio|http)──> Proxy Server ──(stdio|sse|http)──> Upstream MCP Server
|
|
10
|
+
* │
|
|
11
|
+
* [SolonGate]
|
|
12
|
+
* rate limit
|
|
13
|
+
* policy eval
|
|
14
|
+
* audit log
|
|
15
|
+
*/
|
|
16
|
+
export declare class SolonGateProxy {
|
|
17
|
+
private config;
|
|
18
|
+
private readonly gate;
|
|
19
|
+
private client;
|
|
20
|
+
private server;
|
|
21
|
+
private readonly toolMutexes;
|
|
22
|
+
private syncManager;
|
|
23
|
+
private upstreamTools;
|
|
24
|
+
/** Agent identity for trust map — resolved from CLI flag, HTTP headers, or MCP clientInfo */
|
|
25
|
+
private agentId;
|
|
26
|
+
private agentName;
|
|
27
|
+
/** Per-session agent info for HTTP mode (keyed by session ID) */
|
|
28
|
+
private httpAgentInfo;
|
|
29
|
+
/** Per-request sub-agent info from HTTP headers (transient, overwritten per request) */
|
|
30
|
+
private httpSubAgent;
|
|
31
|
+
constructor(config: ProxyConfig);
|
|
32
|
+
/** Normalize well-known MCP client names to display-friendly agent identities */
|
|
33
|
+
private normalizeAgentName;
|
|
34
|
+
/** Extract sub-agent identity from MCP _meta field */
|
|
35
|
+
private extractSubAgent;
|
|
36
|
+
/**
|
|
37
|
+
* Start the proxy: connect to upstream, then serve downstream.
|
|
38
|
+
*/
|
|
39
|
+
start(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Connect to the upstream MCP server.
|
|
42
|
+
* Supports stdio (child process), SSE, and StreamableHTTP transports.
|
|
43
|
+
*/
|
|
44
|
+
private connectUpstream;
|
|
45
|
+
/**
|
|
46
|
+
* Discover tools from the upstream server.
|
|
47
|
+
*/
|
|
48
|
+
private discoverTools;
|
|
49
|
+
/**
|
|
50
|
+
* Create the downstream MCP server with proxied handlers.
|
|
51
|
+
*/
|
|
52
|
+
private createServer;
|
|
53
|
+
/**
|
|
54
|
+
* Register discovered tools to the SolonGate Cloud API.
|
|
55
|
+
* This makes tools visible on the Dashboard (/tools page).
|
|
56
|
+
*/
|
|
57
|
+
private registerToolsToCloud;
|
|
58
|
+
/**
|
|
59
|
+
* Guess tool permissions from tool name.
|
|
60
|
+
*/
|
|
61
|
+
private guessPermissions;
|
|
62
|
+
/**
|
|
63
|
+
* Register the upstream MCP server to the SolonGate Cloud API.
|
|
64
|
+
* This makes it visible on the Dashboard MCP Servers page.
|
|
65
|
+
*/
|
|
66
|
+
private registerServerToCloud;
|
|
67
|
+
/**
|
|
68
|
+
* Start bidirectional policy sync between local JSON file and cloud dashboard.
|
|
69
|
+
*
|
|
70
|
+
* - Watches local policy.json for changes → pushes to cloud API
|
|
71
|
+
* - Polls cloud API for dashboard changes → writes to local policy.json
|
|
72
|
+
* - Version number determines which is newer (higher wins, cloud wins on tie)
|
|
73
|
+
*/
|
|
74
|
+
/**
|
|
75
|
+
* Extract protected filenames from policy DENY rules (filenameConstraints.denied).
|
|
76
|
+
*/
|
|
77
|
+
private extractProtectedFiles;
|
|
78
|
+
/**
|
|
79
|
+
* Extract protected paths from policy DENY rules (pathConstraints.denied).
|
|
80
|
+
*/
|
|
81
|
+
private extractProtectedPaths;
|
|
82
|
+
private startPolicySync;
|
|
83
|
+
/**
|
|
84
|
+
* Start serving downstream.
|
|
85
|
+
* If --port is set, serves via StreamableHTTP on that port.
|
|
86
|
+
* Otherwise, serves on stdio (default for Claude Code / etc).
|
|
87
|
+
*/
|
|
88
|
+
private serve;
|
|
89
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* solongate-proxy pull — Pull a policy from dashboard to local file
|
|
4
|
+
* solongate-proxy push — Push a local policy file to dashboard
|
|
5
|
+
* solongate-proxy list — List all policies with details
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* solongate-proxy list
|
|
9
|
+
* solongate-proxy list --policy-id <ID>
|
|
10
|
+
* solongate-proxy pull --policy-id <ID>
|
|
11
|
+
* solongate-proxy push --policy-id <ID>
|
|
12
|
+
* solongate-proxy pull --policy-id <ID> --file my-policy.json
|
|
13
|
+
*
|
|
14
|
+
* The --policy-id flag determines WHICH cloud policy to work with.
|
|
15
|
+
* API key is read from .env file (SOLONGATE_API_KEY) or environment variable.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PolicySet } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for the SolonGate SDK.
|
|
4
|
+
* All fields have secure defaults. Weakening requires explicit opt-in.
|
|
5
|
+
*/
|
|
6
|
+
export interface SolonGateConfig {
|
|
7
|
+
readonly policySet?: PolicySet;
|
|
8
|
+
readonly validateSchemas: boolean;
|
|
9
|
+
readonly enableLogging: boolean;
|
|
10
|
+
readonly logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
11
|
+
readonly evaluationTimeoutMs: number;
|
|
12
|
+
readonly verboseErrors: boolean;
|
|
13
|
+
readonly globalRateLimitPerMinute: number;
|
|
14
|
+
readonly rateLimitPerTool: number;
|
|
15
|
+
readonly tokenSecret?: string;
|
|
16
|
+
readonly tokenTtlSeconds: number;
|
|
17
|
+
readonly tokenIssuer?: string;
|
|
18
|
+
readonly gatewaySecret?: string;
|
|
19
|
+
readonly enableVersionedPolicies: boolean;
|
|
20
|
+
readonly apiUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const DEFAULT_CONFIG: Readonly<SolonGateConfig>;
|
|
23
|
+
export declare function resolveConfig(userConfig?: Partial<SolonGateConfig>): {
|
|
24
|
+
config: SolonGateConfig;
|
|
25
|
+
warnings: string[];
|
|
26
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Set that automatically evicts entries after a configurable TTL.
|
|
3
|
+
* Prevents unbounded memory growth in long-running processes.
|
|
4
|
+
*
|
|
5
|
+
* Uses a sweep-on-access strategy: expired entries are purged every
|
|
6
|
+
* `sweepIntervalMs` when add() is called, keeping overhead minimal.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ExpiringSet {
|
|
9
|
+
private readonly entries;
|
|
10
|
+
private readonly ttlMs;
|
|
11
|
+
private readonly sweepIntervalMs;
|
|
12
|
+
private lastSweep;
|
|
13
|
+
constructor(ttlMs: number, sweepIntervalMs?: number);
|
|
14
|
+
add(value: string): void;
|
|
15
|
+
has(value: string): boolean;
|
|
16
|
+
get size(): number;
|
|
17
|
+
private maybeSweep;
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { SolonGate, LicenseError } from './solongate.js';
|
|
2
|
+
export { SecureMcpServer, type SecureMcpServerOptions } from './secure-server.js';
|
|
3
|
+
export { interceptToolCall, ExfiltrationChainTracker, type InterceptorOptions } from './interceptor.js';
|
|
4
|
+
export { resolveConfig, DEFAULT_CONFIG, type SolonGateConfig } from './config.js';
|
|
5
|
+
export { SecurityLogger } from './logger.js';
|
|
6
|
+
export { TokenIssuer } from './token-issuer.js';
|
|
7
|
+
export { ServerVerifier, type SignedMcpRequest, type SignatureValidationResult } from './server-verifier.js';
|
|
8
|
+
export { RateLimiter, type RateLimitResult } from './rate-limiter.js';
|
|
9
|
+
export { TrustLevel, Permission, PolicyEffect, type PolicyRule, type PolicySet, type PolicyDecision, type SecurityContext, type ExecutionRequest, type ExecutionResult, type ToolCapability, type McpCallToolParams, type McpCallToolResult, type CapabilityToken, type TokenConfig, type InputGuardConfig, type ThreatType, type DetectedThreat, type SanitizationResult, SolonGateError, PolicyDeniedError, SchemaValidationError, RateLimitError as CoreRateLimitError, InputGuardError, NetworkError, createSecurityContext, createDeniedToolResult, validateToolInput, sanitizeInput, detectPathTraversal, detectShellInjection, detectWildcardAbuse, detectSSRF, detectSQLInjection, detectExfiltration, detectBoundaryEscape, checkLengthLimits, checkEntropyLimits, scanResponse, RESPONSE_WARNING_MARKER, BOUNDARY_PREFIX, BOUNDARY_SUFFIX, tagUserInput, stripBoundaryTags, type ResponseScanConfig, type ResponseThreatType, type ResponseThreat, type ResponseScanResult, type TaggedArguments, } from '../core/index.js';
|
|
10
|
+
export { PolicyEngine, PolicyStore, type PolicyVersion, type PolicyDiff, createDefaultDenyPolicySet, createPermissivePolicySet, createReadOnlyPolicySet, } from '../policy-engine/index.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ExecutionResult, McpCallToolParams, McpCallToolResult, ResponseScanConfig } from '../core/index.js';
|
|
2
|
+
import type { PolicyEngine } from '../policy-engine/index.js';
|
|
3
|
+
import type { TokenIssuer } from './token-issuer.js';
|
|
4
|
+
import type { ServerVerifier } from './server-verifier.js';
|
|
5
|
+
import type { RateLimiter } from './rate-limiter.js';
|
|
6
|
+
export declare class ExfiltrationChainTracker {
|
|
7
|
+
private readonly recentCalls;
|
|
8
|
+
private writeIndex;
|
|
9
|
+
private count;
|
|
10
|
+
record(toolName: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Check if a data sink tool call follows a recent data source tool call,
|
|
13
|
+
* which may indicate a read-then-exfiltrate chain.
|
|
14
|
+
*/
|
|
15
|
+
detectChain(currentTool: string): boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface InterceptorOptions {
|
|
18
|
+
readonly policyEngine: PolicyEngine;
|
|
19
|
+
readonly validateSchemas: boolean;
|
|
20
|
+
readonly verboseErrors: boolean;
|
|
21
|
+
readonly onDecision?: (result: ExecutionResult) => void;
|
|
22
|
+
readonly tokenIssuer?: TokenIssuer;
|
|
23
|
+
readonly serverVerifier?: ServerVerifier;
|
|
24
|
+
readonly rateLimiter?: RateLimiter;
|
|
25
|
+
readonly rateLimitPerTool?: number;
|
|
26
|
+
readonly globalRateLimitPerMinute?: number;
|
|
27
|
+
readonly exfiltrationTracker?: ExfiltrationChainTracker;
|
|
28
|
+
readonly responseScanConfig?: ResponseScanConfig;
|
|
29
|
+
readonly blockUnsafeResponses?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Intercepts an MCP tool call and runs the full security pipeline:
|
|
33
|
+
*
|
|
34
|
+
* 1. Rate limit check → RateLimitError if exceeded
|
|
35
|
+
* 2. Exfiltration chain check → deny if data-source → data-sink pattern
|
|
36
|
+
* 3. Policy evaluation (path + command constraints) → PolicyDeniedError if denied
|
|
37
|
+
* 4. Issue capability token (if TokenIssuer configured)
|
|
38
|
+
* 5. Sign request (if ServerVerifier configured)
|
|
39
|
+
* 6. Call upstream
|
|
40
|
+
* 7. Scan response for indirect prompt injection
|
|
41
|
+
* 8. Record rate limit usage
|
|
42
|
+
* 9. Log to audit trail
|
|
43
|
+
* 10. Return result
|
|
44
|
+
*/
|
|
45
|
+
export declare function interceptToolCall(params: McpCallToolParams, upstreamCall: (params: McpCallToolParams) => Promise<McpCallToolResult>, options: InterceptorOptions): Promise<McpCallToolResult>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExecutionResult } from '../core/index.js';
|
|
2
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
3
|
+
/**
|
|
4
|
+
* Structured security event logger.
|
|
5
|
+
* Outputs JSON-formatted log entries for machine consumption.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SecurityLogger {
|
|
8
|
+
private readonly minLevel;
|
|
9
|
+
private readonly enabled;
|
|
10
|
+
constructor(options: {
|
|
11
|
+
level: LogLevel;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
});
|
|
14
|
+
logDecision(result: ExecutionResult): void;
|
|
15
|
+
private log;
|
|
16
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of a rate limit check.
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimitResult {
|
|
5
|
+
readonly allowed: boolean;
|
|
6
|
+
readonly remaining: number;
|
|
7
|
+
readonly resetAt: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Sliding window rate limiter for tool calls.
|
|
11
|
+
*
|
|
12
|
+
* Uses circular buffers for O(1) push and O(log n) count operations
|
|
13
|
+
* instead of array filtering. Window size defaults to 1 minute.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RateLimiter {
|
|
16
|
+
private readonly windowMs;
|
|
17
|
+
private readonly maxEntries;
|
|
18
|
+
private readonly buffers;
|
|
19
|
+
private globalBuffer;
|
|
20
|
+
constructor(options?: {
|
|
21
|
+
windowMs?: number;
|
|
22
|
+
maxEntries?: number;
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Checks if a tool call is within the rate limit.
|
|
26
|
+
* Does NOT record the call - use recordCall() after successful execution.
|
|
27
|
+
*/
|
|
28
|
+
checkLimit(toolName: string, limitPerWindow: number): RateLimitResult;
|
|
29
|
+
/**
|
|
30
|
+
* Checks the global rate limit across all tools.
|
|
31
|
+
*/
|
|
32
|
+
checkGlobalLimit(limitPerWindow: number): RateLimitResult;
|
|
33
|
+
/**
|
|
34
|
+
* Atomically checks and records a tool call.
|
|
35
|
+
* Prevents TOCTOU race conditions between check and record.
|
|
36
|
+
* Returns the rate limit result; if allowed, the call is already recorded.
|
|
37
|
+
*/
|
|
38
|
+
checkAndRecord(toolName: string, limitPerWindow: number, globalLimit?: number): RateLimitResult;
|
|
39
|
+
/**
|
|
40
|
+
* Records a tool call for rate limiting.
|
|
41
|
+
* Call this after successful execution.
|
|
42
|
+
*/
|
|
43
|
+
recordCall(toolName: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Gets usage stats for a tool.
|
|
46
|
+
*/
|
|
47
|
+
getUsage(toolName: string): {
|
|
48
|
+
count: number;
|
|
49
|
+
windowStart: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Resets rate tracking for a specific tool.
|
|
53
|
+
*/
|
|
54
|
+
resetTool(toolName: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Resets all rate tracking.
|
|
57
|
+
*/
|
|
58
|
+
resetAll(): void;
|
|
59
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecureMcpServer — Drop-in replacement for McpServer with SolonGate protection.
|
|
3
|
+
*
|
|
4
|
+
* Extends the standard McpServer and automatically wraps every tool handler
|
|
5
|
+
* with SolonGate's security pipeline (rate limiting, input guard, policy eval,
|
|
6
|
+
* audit logging). No manual wrapping of individual tool handlers needed.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { SecureMcpServer } from '@solongate/proxy';
|
|
11
|
+
*
|
|
12
|
+
* // Just replace `new McpServer(...)` with `new SecureMcpServer(...)`
|
|
13
|
+
* const server = new SecureMcpServer({
|
|
14
|
+
* name: 'my-server',
|
|
15
|
+
* version: '1.0.0',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Register tools as normal — they're automatically protected
|
|
19
|
+
* server.tool('file_read', { path: z.string() }, async ({ path }) => {
|
|
20
|
+
* return { content: [{ type: 'text', text: readFileSync(path, 'utf-8') }] };
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // API key comes from env: SOLONGATE_API_KEY=sg_live_xxx
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
27
|
+
import type { Implementation } from '@modelcontextprotocol/sdk/types.js';
|
|
28
|
+
import type { PolicySet } from '../core/index.js';
|
|
29
|
+
import { SolonGate } from './solongate.js';
|
|
30
|
+
import type { SolonGateConfig } from './config.js';
|
|
31
|
+
/**
|
|
32
|
+
* Options for SecureMcpServer that control SolonGate behavior.
|
|
33
|
+
*/
|
|
34
|
+
export interface SecureMcpServerOptions {
|
|
35
|
+
/** SolonGate Cloud API key. Defaults to process.env.SOLONGATE_API_KEY */
|
|
36
|
+
apiKey?: string;
|
|
37
|
+
/** Policy set to enforce. If omitted, uses cloud policy or default. */
|
|
38
|
+
policySet?: PolicySet;
|
|
39
|
+
/** SolonGate configuration overrides. */
|
|
40
|
+
config?: Partial<SolonGateConfig>;
|
|
41
|
+
}
|
|
42
|
+
export declare class SecureMcpServer extends McpServer {
|
|
43
|
+
private readonly gate;
|
|
44
|
+
/**
|
|
45
|
+
* Create a secure MCP server.
|
|
46
|
+
*
|
|
47
|
+
* @param serverInfo - MCP server info (name, version)
|
|
48
|
+
* @param solongateOptions - SolonGate security options
|
|
49
|
+
* @param mcpOptions - Standard McpServer options (capabilities, etc.)
|
|
50
|
+
*/
|
|
51
|
+
constructor(serverInfo: Implementation, solongateOptions?: SecureMcpServerOptions, mcpOptions?: ConstructorParameters<typeof McpServer>[1]);
|
|
52
|
+
/**
|
|
53
|
+
* Override tool() to auto-wrap handlers with SolonGate security pipeline.
|
|
54
|
+
*
|
|
55
|
+
* Supports all McpServer.tool() overloads — the handler (always the last
|
|
56
|
+
* argument) is transparently wrapped. Tool name, description, schema, and
|
|
57
|
+
* annotations pass through unchanged.
|
|
58
|
+
*/
|
|
59
|
+
tool(name: string, ...rest: unknown[]): ReturnType<McpServer['tool']>;
|
|
60
|
+
/**
|
|
61
|
+
* Override registerTool() to auto-wrap handlers with SolonGate security pipeline.
|
|
62
|
+
*
|
|
63
|
+
* This is the modern (non-deprecated) API for registering tools.
|
|
64
|
+
*/
|
|
65
|
+
registerTool(name: string, config: Parameters<McpServer['registerTool']>[1], cb: unknown): ReturnType<McpServer['registerTool']>;
|
|
66
|
+
/** Get the underlying SolonGate instance for direct access. */
|
|
67
|
+
getSolonGate(): SolonGate;
|
|
68
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { McpCallToolParams } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* A signed MCP request that includes capability token and integrity signature.
|
|
4
|
+
* Requests without valid gateway signature should be rejected by MCP servers.
|
|
5
|
+
*/
|
|
6
|
+
export interface SignedMcpRequest {
|
|
7
|
+
readonly params: McpCallToolParams;
|
|
8
|
+
readonly capabilityToken: string;
|
|
9
|
+
readonly signature: string;
|
|
10
|
+
readonly timestamp: string;
|
|
11
|
+
readonly nonce: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Result of validating a signed request.
|
|
15
|
+
*/
|
|
16
|
+
export interface SignatureValidationResult {
|
|
17
|
+
readonly valid: boolean;
|
|
18
|
+
readonly reason?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Signs and verifies MCP requests to ensure they originate from the gateway.
|
|
22
|
+
*
|
|
23
|
+
* Security properties:
|
|
24
|
+
* - HMAC-SHA256 signature of request params + token
|
|
25
|
+
* - Timestamp to prevent old request replays
|
|
26
|
+
* - Nonce for uniqueness
|
|
27
|
+
* - Configurable max age for timestamp validation
|
|
28
|
+
*/
|
|
29
|
+
export declare class ServerVerifier {
|
|
30
|
+
private readonly gatewaySecret;
|
|
31
|
+
private readonly maxAgeMs;
|
|
32
|
+
private readonly usedNonces;
|
|
33
|
+
constructor(config: {
|
|
34
|
+
gatewaySecret: string;
|
|
35
|
+
maxAgeMs?: number;
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Computes HMAC signature for request data.
|
|
39
|
+
*/
|
|
40
|
+
signRequest(params: McpCallToolParams, capabilityToken: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Verifies the HMAC signature of request data.
|
|
43
|
+
*/
|
|
44
|
+
verifySignature(params: McpCallToolParams, capabilityToken: string, signature: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Creates a complete signed request including timestamp and nonce.
|
|
47
|
+
*/
|
|
48
|
+
createSignedRequest(params: McpCallToolParams, capabilityToken: string): SignedMcpRequest;
|
|
49
|
+
/**
|
|
50
|
+
* Validates a complete signed request including timestamp, nonce, and signature.
|
|
51
|
+
*/
|
|
52
|
+
validateSignedRequest(request: SignedMcpRequest): SignatureValidationResult;
|
|
53
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { PolicySet, McpCallToolParams, McpCallToolResult } from '../core/index.js';
|
|
2
|
+
import { PolicyEngine } from '../policy-engine/index.js';
|
|
3
|
+
import { type SolonGateConfig } from './config.js';
|
|
4
|
+
import { TokenIssuer } from './token-issuer.js';
|
|
5
|
+
import { RateLimiter } from './rate-limiter.js';
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when a valid SolonGate license (API key) is missing or invalid.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LicenseError extends Error {
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* SolonGate - Security Gateway for MCP Tool Servers.
|
|
14
|
+
*
|
|
15
|
+
* Requires a valid API key. Get one at https://solongate.com
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const gate = new SolonGate({ name: 'my-gateway', apiKey: 'sg_live_xxx' });
|
|
20
|
+
*
|
|
21
|
+
* // Intercept a tool call
|
|
22
|
+
* const result = await gate.executeToolCall(
|
|
23
|
+
* { name: 'file.read', arguments: { path: '/etc/passwd' } },
|
|
24
|
+
* async (params) => upstreamMcpServer.callTool(params),
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Architecture:
|
|
29
|
+
* [LLM] -> [SolonGate.executeToolCall] -> [Security Pipeline] -> [Upstream MCP Server]
|
|
30
|
+
*
|
|
31
|
+
* Pipeline:
|
|
32
|
+
* Rate Limit → Policy Eval (path + command constraints) → Token Issue → Sign → Call → Audit
|
|
33
|
+
*/
|
|
34
|
+
export declare class SolonGate {
|
|
35
|
+
private readonly policyEngine;
|
|
36
|
+
private readonly config;
|
|
37
|
+
private readonly logger;
|
|
38
|
+
private readonly configWarnings;
|
|
39
|
+
private readonly tokenIssuer;
|
|
40
|
+
private readonly serverVerifier;
|
|
41
|
+
private readonly rateLimiter;
|
|
42
|
+
private readonly exfiltrationTracker;
|
|
43
|
+
private readonly apiKey;
|
|
44
|
+
private licenseValidated;
|
|
45
|
+
private pollingTimer;
|
|
46
|
+
constructor(options: {
|
|
47
|
+
name: string;
|
|
48
|
+
version?: string;
|
|
49
|
+
apiKey?: string;
|
|
50
|
+
config?: Partial<SolonGateConfig>;
|
|
51
|
+
policySet?: PolicySet;
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Validate the API key against the SolonGate cloud API.
|
|
55
|
+
* Called once on first executeToolCall. Throws LicenseError if invalid.
|
|
56
|
+
* Test keys (sg_test_) skip online validation.
|
|
57
|
+
*/
|
|
58
|
+
private validateLicense;
|
|
59
|
+
/**
|
|
60
|
+
* Fetch policy from SolonGate Cloud API (fire once, non-blocking).
|
|
61
|
+
* TODO: extract cloud policy parsing to shared module with packages/proxy/src/config.ts
|
|
62
|
+
*/
|
|
63
|
+
private fetchCloudPolicyOnce;
|
|
64
|
+
/**
|
|
65
|
+
* Fetch the compiled OPA WASM bundle for a policy and load it into the engine.
|
|
66
|
+
* The proxy does not compile policies itself — the cloud API compiles every
|
|
67
|
+
* policy version to WASM on save and serves it from /policies/:id/wasm.
|
|
68
|
+
*/
|
|
69
|
+
private loadCloudWasm;
|
|
70
|
+
/**
|
|
71
|
+
* Poll for policy updates from dashboard every 60 seconds.
|
|
72
|
+
*/
|
|
73
|
+
private startPolicyPolling;
|
|
74
|
+
/**
|
|
75
|
+
* Send audit log to SolonGate Cloud API (fire-and-forget).
|
|
76
|
+
*/
|
|
77
|
+
private sendAuditLog;
|
|
78
|
+
/**
|
|
79
|
+
* Intercept and evaluate a tool call against the full security pipeline.
|
|
80
|
+
* If denied at any stage, returns an error result without calling upstream.
|
|
81
|
+
* If allowed, calls upstream and returns the result.
|
|
82
|
+
*/
|
|
83
|
+
executeToolCall(params: McpCallToolParams, upstreamCall: (params: McpCallToolParams) => Promise<McpCallToolResult>): Promise<McpCallToolResult>;
|
|
84
|
+
/** Load a new policy set at runtime. */
|
|
85
|
+
loadPolicy(policySet: PolicySet, options?: {
|
|
86
|
+
reason?: string;
|
|
87
|
+
createdBy?: string;
|
|
88
|
+
}): import("../policy-engine/validator.js").ValidationResult;
|
|
89
|
+
/**
|
|
90
|
+
* Load a pre-compiled OPA WASM bundle for evaluation. OPA WASM is the sole
|
|
91
|
+
* evaluation backend — until a bundle is loaded, evaluate() fails closed
|
|
92
|
+
* (default DENY). The bundle is fetched from the cloud /policies/:id/wasm.
|
|
93
|
+
*/
|
|
94
|
+
loadWasmBundle(wasmBundle: BufferSource): Promise<void>;
|
|
95
|
+
/** Get current security warnings. */
|
|
96
|
+
getWarnings(): readonly string[];
|
|
97
|
+
/** Get the policy engine for direct access. */
|
|
98
|
+
getPolicyEngine(): PolicyEngine;
|
|
99
|
+
/** Get the rate limiter for direct access. */
|
|
100
|
+
getRateLimiter(): RateLimiter;
|
|
101
|
+
/** Get the token issuer (null if not configured). */
|
|
102
|
+
getTokenIssuer(): TokenIssuer | null;
|
|
103
|
+
/** Stop policy polling and release resources. */
|
|
104
|
+
destroy(): void;
|
|
105
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TokenConfig, TokenVerificationResult, Permission } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Issues and verifies capability tokens using HMAC-SHA256.
|
|
4
|
+
*
|
|
5
|
+
* Security properties:
|
|
6
|
+
* - Short-lived TTL (default 30 seconds)
|
|
7
|
+
* - Single-use nonces (replay prevention)
|
|
8
|
+
* - Revocation support
|
|
9
|
+
* - No external JWT library dependency
|
|
10
|
+
*/
|
|
11
|
+
export declare class TokenIssuer {
|
|
12
|
+
private readonly secret;
|
|
13
|
+
private readonly ttlSeconds;
|
|
14
|
+
private readonly issuer;
|
|
15
|
+
private readonly usedNonces;
|
|
16
|
+
private readonly revokedTokens;
|
|
17
|
+
constructor(config: TokenConfig);
|
|
18
|
+
/**
|
|
19
|
+
* Issues a signed capability token.
|
|
20
|
+
*/
|
|
21
|
+
issue(requestId: string, permissions: readonly Permission[], toolScope: readonly string[], serverScope?: readonly string[], pathScope?: readonly string[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* Verifies a capability token and consumes the nonce (single-use).
|
|
24
|
+
*/
|
|
25
|
+
verify(token: string): TokenVerificationResult;
|
|
26
|
+
/**
|
|
27
|
+
* Revokes a token by its ID.
|
|
28
|
+
*/
|
|
29
|
+
revoke(jti: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a token ID has been revoked.
|
|
32
|
+
*/
|
|
33
|
+
isRevoked(jti: string): boolean;
|
|
34
|
+
private sign;
|
|
35
|
+
private parseAndVerify;
|
|
36
|
+
private computeSignature;
|
|
37
|
+
}
|