@blacksandscyber/mcp-server-bursar 0.5.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 (68) hide show
  1. package/README.md +230 -0
  2. package/build/config.d.ts +45 -0
  3. package/build/config.js +177 -0
  4. package/build/http-transport.d.ts +16 -0
  5. package/build/http-transport.js +191 -0
  6. package/build/index.d.ts +16 -0
  7. package/build/index.js +31 -0
  8. package/build/server.d.ts +41 -0
  9. package/build/server.js +902 -0
  10. package/build/shared/errors.d.ts +50 -0
  11. package/build/shared/errors.js +69 -0
  12. package/build/shared/linkBuilder.d.ts +93 -0
  13. package/build/shared/linkBuilder.js +148 -0
  14. package/build/shared/logger.d.ts +10 -0
  15. package/build/shared/logger.js +28 -0
  16. package/build/shield/bootRole.d.ts +60 -0
  17. package/build/shield/bootRole.js +145 -0
  18. package/build/shield/client.d.ts +265 -0
  19. package/build/shield/client.js +656 -0
  20. package/build/shield/deploy/index.d.ts +69 -0
  21. package/build/shield/deploy/index.js +569 -0
  22. package/build/shield/discovery/dataStoreDetector.d.ts +3 -0
  23. package/build/shield/discovery/dataStoreDetector.js +125 -0
  24. package/build/shield/discovery/dockerScanner.d.ts +34 -0
  25. package/build/shield/discovery/dockerScanner.js +543 -0
  26. package/build/shield/discovery/endpointScanner.d.ts +3 -0
  27. package/build/shield/discovery/endpointScanner.js +306 -0
  28. package/build/shield/discovery/environmentScanner.d.ts +86 -0
  29. package/build/shield/discovery/environmentScanner.js +545 -0
  30. package/build/shield/discovery/externalServiceDetector.d.ts +3 -0
  31. package/build/shield/discovery/externalServiceDetector.js +98 -0
  32. package/build/shield/discovery/frameworkDetector.d.ts +3 -0
  33. package/build/shield/discovery/frameworkDetector.js +114 -0
  34. package/build/shield/discovery/manifestGenerator.d.ts +12 -0
  35. package/build/shield/discovery/manifestGenerator.js +124 -0
  36. package/build/shield/discovery/piiDetector.d.ts +5 -0
  37. package/build/shield/discovery/piiDetector.js +203 -0
  38. package/build/shield/discovery/severity.d.ts +47 -0
  39. package/build/shield/discovery/severity.js +138 -0
  40. package/build/shield/discovery/topologyNormalizer.d.ts +109 -0
  41. package/build/shield/discovery/topologyNormalizer.js +416 -0
  42. package/build/shield/identity.d.ts +53 -0
  43. package/build/shield/identity.js +70 -0
  44. package/build/shield/install/configMerge.d.ts +91 -0
  45. package/build/shield/install/configMerge.js +324 -0
  46. package/build/shield/install/keystore.d.ts +25 -0
  47. package/build/shield/install/keystore.js +156 -0
  48. package/build/shield/install/orchestrator.d.ts +33 -0
  49. package/build/shield/install/orchestrator.js +404 -0
  50. package/build/shield/install/transports/awsSsm.d.ts +43 -0
  51. package/build/shield/install/transports/awsSsm.js +378 -0
  52. package/build/shield/install/transports/bootstrapToken.d.ts +39 -0
  53. package/build/shield/install/transports/bootstrapToken.js +117 -0
  54. package/build/shield/install/transports/ssh.d.ts +50 -0
  55. package/build/shield/install/transports/ssh.js +569 -0
  56. package/build/shield/install/types.d.ts +139 -0
  57. package/build/shield/install/types.js +10 -0
  58. package/build/shield/protocol-walkthrough.d.ts +65 -0
  59. package/build/shield/protocol-walkthrough.js +392 -0
  60. package/build/shield/provision/appProvisioner.d.ts +15 -0
  61. package/build/shield/provision/appProvisioner.js +25 -0
  62. package/build/shield/types.d.ts +261 -0
  63. package/build/shield/types.js +4 -0
  64. package/build/shield/verify/postureReporter.d.ts +4 -0
  65. package/build/shield/verify/postureReporter.js +31 -0
  66. package/dxt/blacksands-ca.crt +67 -0
  67. package/dxt/scripts/setup.js +520 -0
  68. package/package.json +76 -0
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Shield API Client — Broker mode only.
3
+ *
4
+ * Every request flows through the Blacksands Broker:
5
+ * Step 1: POST {authorizer}/api/agent/auth with { password, serviceId }
6
+ * — the Authorizer validates the mTLS client cert (presented by our
7
+ * httpsAgent), authenticates the password, and resolves the serviceId
8
+ * to a Receiver URL — all in one round trip. Returns a sessionToken
9
+ * plus { service: { serviceUrl } }.
10
+ * Step 2: All subsequent Shield API calls go to service.serviceUrl (the
11
+ * Receiver), which authenticates us and proxies to Shield API.
12
+ *
13
+ * The mTLS client certificate and auth password are issued by a human
14
+ * administrator through Overwatch or SysAdmin (or by the Phase-2 setup-token
15
+ * bootstrap flow, which is just a thin wrapper around the same issuance).
16
+ */
17
+ import * as https from "https";
18
+ import type { Organization, App, Certificate, Endpoint, Policy, PolicyRule, DnsRule, Manifest, Operation, VerifyResult, ComplianceReport, ComplianceFramework, Session, ListResponse } from "./types";
19
+ export interface ShieldClientOptions {
20
+ timeout?: number;
21
+ maxRetries?: number;
22
+ /** PEM-encoded client certificate for mTLS authentication */
23
+ clientCert: string;
24
+ /** PEM-encoded client private key for mTLS authentication */
25
+ clientKey: string;
26
+ /** Optional PEM-encoded CA certificate to pin the Authorizer/Receiver server cert */
27
+ caCert?: string | null;
28
+ /**
29
+ * Broker connection settings. Required for client-side (stdio) mode where
30
+ * the MCP server connects to Shield API via the full Authorizer handshake.
31
+ * Mutually exclusive with `directUrl`.
32
+ */
33
+ broker?: {
34
+ authorizerUrl: string;
35
+ serviceId: string;
36
+ authPassword: string;
37
+ };
38
+ /**
39
+ * Direct Shield API base URL. Used when the MCP server is deployed as an
40
+ * internal HTTP service behind a Receiver and authenticates to Shield API
41
+ * using a service-level mTLS certificate (no broker handshake).
42
+ * Mutually exclusive with `broker`.
43
+ */
44
+ directUrl?: string;
45
+ }
46
+ export declare class BrokerConnector {
47
+ private authorizerUrl;
48
+ private serviceId;
49
+ private authPassword;
50
+ private httpsAgent;
51
+ private sessionToken;
52
+ private receiverUrl;
53
+ private connectedAt;
54
+ private sessionTtlMs;
55
+ private heartbeatTimer;
56
+ private readonly requestedTtlSec?;
57
+ constructor(authorizerUrl: string, serviceId: string, authPassword: string, httpsAgent: https.Agent, requestedTtlSec?: number | undefined);
58
+ get isConnected(): boolean;
59
+ get url(): string;
60
+ connect(): Promise<string>;
61
+ ensureConnected(): Promise<void>;
62
+ reconnect(): Promise<string>;
63
+ /**
64
+ * Tear down state and stop the heartbeat. Tests/teardown call this; runtime
65
+ * normally stays connected for the process lifetime.
66
+ */
67
+ close(): void;
68
+ private startHeartbeat;
69
+ private stopHeartbeat;
70
+ }
71
+ export declare class ShieldClient {
72
+ private http;
73
+ private maxRetries;
74
+ private broker;
75
+ private httpsAgent;
76
+ private options;
77
+ private mode;
78
+ constructor(options: ShieldClientOptions);
79
+ /** Complete the Broker handshake. No-op in direct mode. */
80
+ connect(): Promise<void>;
81
+ get authMethod(): "broker" | "direct";
82
+ request<T = unknown>(method: string, path: string, data?: unknown): Promise<T>;
83
+ pollOperation(operationId: string, opts?: {
84
+ interval?: number;
85
+ timeout?: number;
86
+ }): Promise<Operation>;
87
+ listMcpCerts(): Promise<{
88
+ data: Array<{
89
+ id: string;
90
+ name: string;
91
+ cn: string;
92
+ fingerprint: string;
93
+ expires: string;
94
+ }>;
95
+ }>;
96
+ revokeMcpCert(clientName: string): Promise<void>;
97
+ /**
98
+ * Issue a full MCP client certificate bundle. Internal use only —
99
+ * consumed by bursar_install_agent_remotely's SSH and aws-ssm transports,
100
+ * which need the key material in-process to ship to the target.
101
+ *
102
+ * The returned `key_pem` is SENSITIVE: callers MUST treat it as tainted
103
+ * and MUST NOT persist it. See bursar_install_agent_remotely §FR-1.
104
+ */
105
+ issueMcpCert(clientName: string, orgId: string): Promise<{
106
+ cn: string;
107
+ cert_pem: string;
108
+ key_pem: string;
109
+ ca_pem: string | null;
110
+ auth_password: string;
111
+ fingerprint: string;
112
+ expires: string;
113
+ client_id: string;
114
+ }>;
115
+ /**
116
+ * Mint a short-lived single-use setup token bound to {clientName, orgId}.
117
+ * Consumed by bursar_install_agent_remotely's bootstrap-token transport —
118
+ * the token is handed to the target, which calls POST /mcp/setup-tokens/redeem
119
+ * to exchange it for a cert bundle. The private key is returned to the
120
+ * redeemer; the MCP host holding this call never sees it.
121
+ *
122
+ * `expiresIn` accepts a number of seconds or a shorthand like "1h"/"30m".
123
+ *
124
+ * `role` ('master' | 'consumer') stamps the role on the token row so the
125
+ * redeem flow propagates it to the issued McpClient.role, and the redeem
126
+ * response echoes it back so setup.js can write
127
+ * ~/.blacksands/mcp-certs/.role for boot-time tool filtering. Defaults
128
+ * to 'master' on the server side.
129
+ */
130
+ mintSetupToken(clientName: string, orgId: string, expiresIn?: string | number, role?: "master" | "consumer"): Promise<{
131
+ token: string;
132
+ token_id: string;
133
+ token_prefix: string;
134
+ client_name: string;
135
+ org_id: string;
136
+ role: "master" | "consumer";
137
+ expires_at: string;
138
+ ttl_seconds: number;
139
+ url: string;
140
+ }>;
141
+ /**
142
+ * Revoke a pending (unconsumed) setup token by ID. Internal rollback
143
+ * helper for bursar_install_agent_remotely — NOT exposed as a tool.
144
+ * A 404 from the server means the token was already consumed or
145
+ * expired; callers should treat that as success for rollback purposes.
146
+ */
147
+ revokeSetupToken(tokenId: string): Promise<void>;
148
+ /**
149
+ * Check whether the named MCP client has completed its first broker
150
+ * handshake since issuance. Polled by the install orchestrator with
151
+ * exponential backoff until waitForHandshake timeout.
152
+ */
153
+ getHandshakeStatus(clientName: string): Promise<{
154
+ success: boolean;
155
+ verified: boolean;
156
+ client_name?: string;
157
+ cn?: string;
158
+ fingerprint?: string;
159
+ expires?: string;
160
+ first_handshake_at?: string | null;
161
+ last_handshake_at?: string | null;
162
+ receiver_url?: string | null;
163
+ reason?: string | null;
164
+ }>;
165
+ /**
166
+ * Acquire the per-clientName advisory lock. Throws on 409 (conflict).
167
+ */
168
+ acquireInstallLock(clientName: string, opts?: {
169
+ ttlSeconds?: number;
170
+ auditId?: string;
171
+ }): Promise<{
172
+ owner_id: string;
173
+ expires_at: string;
174
+ ttl_seconds: number;
175
+ }>;
176
+ /** Release the advisory lock. Owner-match enforced server-side. */
177
+ releaseInstallLock(clientName: string): Promise<void>;
178
+ /** Append an audit row for a bursar_install_agent_remotely invocation. */
179
+ recordInstallAudit(row: {
180
+ auditId: string;
181
+ clientName: string;
182
+ transportType: string;
183
+ agentType?: string;
184
+ status: string;
185
+ phase?: string;
186
+ dryRun?: boolean;
187
+ target?: Record<string, unknown>;
188
+ errorMessage?: string;
189
+ }): Promise<{
190
+ id: string;
191
+ audit_id: string;
192
+ }>;
193
+ createOrg(name: string, plan?: string): Promise<Organization>;
194
+ getOrg(orgId: string): Promise<Organization>;
195
+ listOrgs(page?: number, pageSize?: number): Promise<ListResponse<Organization>>;
196
+ createApp(orgId: string, name: string, opts?: {
197
+ type?: "web" | "mobile" | "api" | "iot" | "service";
198
+ framework?: string;
199
+ runtime?: string;
200
+ environment?: "development" | "staging" | "production";
201
+ domain?: string;
202
+ }): Promise<App>;
203
+ getApp(appId: string): Promise<App>;
204
+ listApps(orgId: string): Promise<ListResponse<App>>;
205
+ submitManifest(manifest: unknown, orgId: string): Promise<Manifest>;
206
+ getManifest(manifestId: string): Promise<Manifest>;
207
+ provisionManifest(manifestId: string): Promise<{
208
+ operationId: string;
209
+ }>;
210
+ listCerts(appId: string): Promise<ListResponse<Certificate>>;
211
+ rotateCert(appId: string, certId: string): Promise<Certificate>;
212
+ revokeCert(appId: string, certId: string, reason?: string): Promise<void>;
213
+ listEndpoints(appId: string): Promise<ListResponse<Endpoint>>;
214
+ listPolicies(appId: string): Promise<ListResponse<Policy>>;
215
+ createPolicy(appId: string, name: string, type: string, rules: PolicyRule[]): Promise<Policy>;
216
+ updateDnsRules(appId: string, rules: Array<{
217
+ domain: string;
218
+ action: "allow" | "block";
219
+ reason?: string;
220
+ }>): Promise<DnsRule[]>;
221
+ triggerVerify(appId: string, scanType?: "full" | "quick" | "compliance" | "penetration"): Promise<{
222
+ operationId: string;
223
+ }>;
224
+ getLatestPosture(appId: string): Promise<VerifyResult>;
225
+ getComplianceReport(appId: string, framework: ComplianceFramework): Promise<ComplianceReport>;
226
+ getComplianceControls(framework: ComplianceFramework): Promise<{
227
+ controls: Array<{
228
+ id: string;
229
+ name: string;
230
+ description: string;
231
+ }>;
232
+ }>;
233
+ listSessions(appId: string): Promise<ListResponse<Session>>;
234
+ revokeSession(appId: string, sessionId: string): Promise<void>;
235
+ emergencyLockdown(appId: string, reason: string): Promise<{
236
+ locked: boolean;
237
+ timestamp: string;
238
+ }>;
239
+ liftLockdown(appId: string): Promise<void>;
240
+ getReceiver(receiverUID: string): Promise<Record<string, unknown>>;
241
+ listReceivers(filters?: {
242
+ status?: string;
243
+ organizationId?: string;
244
+ }): Promise<Record<string, unknown>[]>;
245
+ initializeReceiver(installerEmail: string, organizationId: string, notes?: string): Promise<Record<string, unknown>>;
246
+ getReceiverStatus(receiverUID: string): Promise<Record<string, unknown>>;
247
+ activateReceiver(receiverUID: string): Promise<Record<string, unknown>>;
248
+ resendReceiverEmail(receiverUID: string): Promise<Record<string, unknown>>;
249
+ cancelReceiverInit(receiverUID: string): Promise<Record<string, unknown>>;
250
+ getReceiverStatistics(): Promise<Record<string, unknown>>;
251
+ onboardReceiverService(receiverUID: string, svc: {
252
+ name: string;
253
+ host: string;
254
+ port: number;
255
+ protocol: "http" | "https" | "ssh" | "rdp";
256
+ path?: string;
257
+ }): Promise<Record<string, unknown>>;
258
+ removeReceiverService(receiverUID: string, serviceName: string): Promise<Record<string, unknown>>;
259
+ listReceiverServices(receiverUID: string): Promise<Record<string, unknown>[]>;
260
+ pauseService(receiverUID: string, serviceName: string): Promise<Record<string, unknown>>;
261
+ resumeService(receiverUID: string, serviceName: string): Promise<Record<string, unknown>>;
262
+ disableService(receiverUID: string, serviceName: string): Promise<Record<string, unknown>>;
263
+ getServiceStatus(receiverUID: string, serviceName: string): Promise<Record<string, unknown>>;
264
+ }
265
+ //# sourceMappingURL=client.d.ts.map