claudeunmask 1.0.28 → 1.0.29

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /* v 1.0.28 */
1
+ /* v 1.0.29 */
2
2
  function getMessageEncoding(message) {
3
3
  return new TextEncoder().encode(message);
4
4
  }
@@ -56,33 +56,27 @@
56
56
  );
57
57
  return getMessageDecoding(new Uint8Array(plaintextBuffer));
58
58
  } catch (error) {
59
- console.log ("failed", key, bytesToBase64(ciphertext), error);
60
59
  throw Error ("Failed to decrypt");
61
60
  }
62
61
  return "";
63
62
  }
64
63
  async function decrypt(inData, decryptionKey) {
65
64
  var dailyKey = await window.prompt("Enter the daily key","crazy_string");
66
- console.log ("decrypt: daily key", dailyKey);
67
65
  var retVal = [];
68
66
  var enc = new TextEncoder();
69
67
  var [tmpPassword, salt, theIV] = decryptionKey.split("-");
70
- console.log ("from MCP", decryptionKey, tmpPassword, salt, theIV);
71
68
  const dailyKeyBinary = await deriveKeyFromPassword(dailyKey, base64ToBytes(salt));
72
69
  try {
73
70
  tmpPassword = await decryptThisData (base64ToBytes(tmpPassword), dailyKeyBinary, base64ToBytes(theIV));
74
71
  } catch (error) {
75
- console.log ("failed to decrypt the key");
76
72
  throw Error ("Failed to decrypt the key");
77
73
  }
78
- console.log ("secret:", tmpPassword);
79
74
  const password = tmpPassword;
80
75
  for (const curRec of inData) {
81
76
  var tmpRec = {};
82
77
  for (const [key, value] of Object.entries(curRec)) {
83
78
  try {
84
79
  if (typeof value === "string" && value.includes("m$k_")) {
85
- console.log ("masked", value, curRec[key]);
86
80
  var theData = base64ToBytes(curRec[key].replace ("m$k_", ""));
87
81
  const encryptionKey = await deriveKeyFromPassword(password, base64ToBytes(salt));
88
82
  tmpRec[key] = await decryptThisData (theData, encryptionKey, base64ToBytes(theIV));
@@ -90,7 +84,6 @@ console.log ("masked", value, curRec[key]);
90
84
  tmpRec[key] = value;
91
85
  }
92
86
  } catch (error) {
93
- console.log ("error", error);
94
87
  tmpRec[key] = "failed";
95
88
  }
96
89
  }
@@ -99,17 +92,14 @@ console.log ("error", error);
99
92
  return retVal;
100
93
  }
101
94
  async function unmaskData (inData, decryptionKey, dailyKey) {
102
- console.log ("unmask: daily key", dailyKey);
103
95
  var retVal = {};
104
96
  retVal.data = [];
105
97
  retVal.errorCount = 0;
106
98
  retVal.unmaskCount = 0;
107
99
  var enc = new TextEncoder();
108
100
  var [tmpPassword, salt, theIV] = decryptionKey.split("-");
109
- console.log ("from MCP", decryptionKey, tmpPassword, salt, theIV);
110
101
  var haveError = 0;
111
102
  for (var ii= 0; ii<=2; ii++) { // try to decrypt the daily key and the add = until it works or we've added 2
112
- console.log ("testing daily key: ", dailyKey);
113
103
  const dailyKeyBinary = await deriveKeyFromPassword(dailyKey, base64ToBytes(salt));
114
104
  try {
115
105
  tmpPassword = await decryptThisData (base64ToBytes(tmpPassword), dailyKeyBinary, base64ToBytes(theIV));
@@ -124,7 +114,6 @@ console.log ("testing daily key: ", dailyKey);
124
114
  if (haveError) {
125
115
  throw Error ("Failed to decrypt the key");
126
116
  }
127
- console.log ("secret:", tmpPassword);
128
117
  const password = tmpPassword;
129
118
  const encryptionKey = await deriveKeyFromPassword(password, base64ToBytes(salt));
130
119
  for (const curRec of inData) {
@@ -132,7 +121,6 @@ console.log ("secret:", tmpPassword);
132
121
  for (const [key, value] of Object.entries(curRec)) {
133
122
  try {
134
123
  if (typeof value === "string" && value.includes("m$k_")) {
135
- console.log ("masked", value, curRec[key]);
136
124
  retVal.unmaskCount++;
137
125
  var theData = base64ToBytes(curRec[key].replace ("m$k_", ""));
138
126
  tmpRec[key] = await decryptThisData (theData, encryptionKey, base64ToBytes(theIV));
@@ -140,7 +128,6 @@ console.log ("masked", value, curRec[key]);
140
128
  tmpRec[key] = value;
141
129
  }
142
130
  } catch (error) {
143
- console.log ("error", error);
144
131
  tmpRec[key] = "failed";
145
132
  retVal.errorCount++;
146
133
  }
@@ -149,8 +136,7 @@ console.log ("error", error);
149
136
  }
150
137
 
151
138
  if (retVal.errorCount) {
152
- console.log ("an error happened", (100 * retVal.errorCount / retVal.unmaskCount));
153
-
139
+ console.log ("an error happened", (100 * retVal.errorCount / retVal.unmaskCount));
154
140
  }
155
141
  return retVal;
156
142
  }
package/index.min.js CHANGED
@@ -1 +1 @@
1
- function getMessageEncoding(e){return new TextEncoder().encode(e)}function getMessageDecoding(e){return new TextDecoder().decode(e)}function base64ToBytes(e){let t=atob(e),a=new Uint8Array(t.length);for(let r=0;r<t.length;r++)a[r]=t.charCodeAt(r);return a}function bytesToBase64(e){let t=String.fromCharCode(...e);return btoa(t)}async function deriveKeyFromPassword(e,t){let a=getMessageEncoding(e),r=await crypto.subtle.importKey("raw",a,"PBKDF2",!1,["deriveKey"]),o=await crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},r,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);return o}async function decryptThisData(e,t,a){try{let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:a},t,e);return getMessageDecoding(new Uint8Array(r))}catch(o){throw console.log("failed",t,bytesToBase64(e),o),Error("Failed to decrypt")}return""}async function decrypt(e,t){var a=await window.prompt("Enter the daily key","crazy_string");console.log("decrypt: daily key",a);var r=[];new TextEncoder;var[o,s,n]=t.split("-");console.log("from MCP",t,o,s,n);let i=await deriveKeyFromPassword(a,base64ToBytes(s));try{o=await decryptThisData(base64ToBytes(o),i,base64ToBytes(n))}catch(y){throw console.log("failed to decrypt the key"),Error("Failed to decrypt the key")}console.log("secret:",o);let l=o;for(let d of e){var c={};for(let[u,g]of Object.entries(d))try{if("string"==typeof g&&g.includes("m$k_")){console.log("masked",g,d[u]);var f=base64ToBytes(d[u].replace("m$k_",""));let h=await deriveKeyFromPassword(l,base64ToBytes(s));c[u]=await decryptThisData(f,h,base64ToBytes(n))}else c[u]=g}catch(p){console.log("error",p),c[u]="failed"}r.push(c)}return r}async function unmaskData(e,t,a){console.log("unmask: daily key",a);var r={};r.data=[],r.errorCount=0,r.unmaskCount=0,new TextEncoder;var[o,s,n]=t.split("-");console.log("from MCP",t,o,s,n);let i=await deriveKeyFromPassword(a,base64ToBytes(s));try{o=await decryptThisData(base64ToBytes(o),i,base64ToBytes(n))}catch(y){throw console.log("failed to decrypt the key"),Error("Failed to decrypt the key")}console.log("secret:",o);let l=o,d=await deriveKeyFromPassword(l,base64ToBytes(s));for(let c of e){var u={};for(let[g,f]of Object.entries(c))try{if("string"==typeof f&&f.includes("m$k_")){console.log("masked",f,c[g]),r.unmaskCount++;var h=base64ToBytes(c[g].replace("m$k_",""));u[g]=await decryptThisData(h,d,base64ToBytes(n))}else u[g]=f}catch(p){console.log("error",p),u[g]="failed",r.errorCount++}r.data.push(u)}return r.errorCount&&console.log("an error happened",100*r.errorCount/r.unmaskCount),r}
1
+ function getMessageEncoding(e){return new TextEncoder().encode(e)}function getMessageDecoding(e){return new TextDecoder().decode(e)}function base64ToBytes(e){let t=atob(e),a=new Uint8Array(t.length);for(let r=0;r<t.length;r++)a[r]=t.charCodeAt(r);return a}function bytesToBase64(e){let t=String.fromCharCode(...e);return btoa(t)}async function deriveKeyFromPassword(e,t){let a=getMessageEncoding(e),r=await crypto.subtle.importKey("raw",a,"PBKDF2",!1,["deriveKey"]),s=await crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},r,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"]);return s}async function decryptThisData(e,t,a){try{let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:a},t,e);return getMessageDecoding(new Uint8Array(r))}catch(s){throw Error("Failed to decrypt")}return""}async function decrypt(e,t){var a=await window.prompt("Enter the daily key","crazy_string"),r=[];new TextEncoder;var[s,o,n]=t.split("-");let i=await deriveKeyFromPassword(a,base64ToBytes(o));try{s=await decryptThisData(base64ToBytes(s),i,base64ToBytes(n))}catch(y){throw Error("Failed to decrypt the key")}let c=s;for(let d of e){var l={};for(let[u,w]of Object.entries(d))try{if("string"==typeof w&&w.includes("m$k_")){var f=base64ToBytes(d[u].replace("m$k_",""));let h=await deriveKeyFromPassword(c,base64ToBytes(o));l[u]=await decryptThisData(f,h,base64ToBytes(n))}else l[u]=w}catch(p){l[u]="failed"}r.push(l)}return r}async function unmaskData(e,t,a){var r={};r.data=[],r.errorCount=0,r.unmaskCount=0,new TextEncoder;for(var[s,o,n]=t.split("-"),i=0,y=0;y<=2;y++){let c=await deriveKeyFromPassword(a,base64ToBytes(o));try{s=await decryptThisData(base64ToBytes(s),c,base64ToBytes(n)),i=0;break}catch(d){i=1,a+="="}}if(i)throw Error("Failed to decrypt the key");let l=s,u=await deriveKeyFromPassword(l,base64ToBytes(o));for(let w of e){var f={};for(let[h,p]of Object.entries(w))try{if("string"==typeof p&&p.includes("m$k_")){r.unmaskCount++;var g=base64ToBytes(w[h].replace("m$k_",""));f[h]=await decryptThisData(g,u,base64ToBytes(n))}else f[h]=p}catch(m){f[h]="failed",r.errorCount++}r.data.push(f)}return r.errorCount&&console.log("an error happened",100*r.errorCount/r.unmaskCount),r}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeunmask",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
4
4
  "description": "claude unmask test",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/index - Copy.js DELETED
@@ -1,19 +0,0 @@
1
-
2
- function unmask (theData) {
3
- var retVal = [];
4
- for (curRow of theData) {
5
- var retRow = {};
6
- for (const [key, value] of Object.entries(curRow)) {
7
- let newValue = value;
8
-
9
- if (newValue.startsWith ("m$k_")) {
10
- newValue = newValue.replace ("m$k_","");
11
- newValue = newValue.split('').reverse().join('');
12
- }
13
- retRow[key] = newValue;
14
- }
15
- retVal.push (retRow);
16
- }
17
- console.log ("hello from unmask", retVal);
18
- return retVal;
19
- }
package/junk.js DELETED
@@ -1,676 +0,0 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import {
4
- CallToolRequestSchema,
5
- ListToolsRequestSchema,
6
- } from "@modelcontextprotocol/sdk/types.js";
7
- import { DefaultAzureCredential } from "@azure/identity";
8
- import { SqlManagementClient } from "@azure/arm-sql";
9
- import sql from "mssql";
10
- import * as fs from "fs";
11
- import * as path from "path";
12
-
13
- import { webcrypto } from 'crypto';
14
- const subtle = webcrypto.subtle;
15
-
16
- // Azure MCP Server
17
- class AzureMCPServer {
18
- private server: Server;
19
- private credential: DefaultAzureCredential;
20
- private subscriptionId: string;
21
- private sqlPools: Map<string, sql.ConnectionPool>;
22
-
23
- constructor() {
24
- this.server = new Server(
25
- {
26
- name: "azure-mcp-server",
27
- version: "1.0.0",
28
- },
29
- {
30
- capabilities: {
31
- tools: {},
32
- },
33
- }
34
- );
35
-
36
- // Get subscription ID from environment
37
- this.subscriptionId = process.env.AZURE_SUBSCRIPTION_ID || "";
38
- if (!this.subscriptionId) {
39
- throw new Error("AZURE_SUBSCRIPTION_ID environment variable is required");
40
- }
41
-
42
- // Initialize Azure credential
43
- this.credential = new DefaultAzureCredential();
44
-
45
- // Initialize SQL connection pool cache
46
- this.sqlPools = new Map();
47
-
48
- this.setupHandlers();
49
- }
50
-
51
- private setupHandlers() {
52
- // List available tools
53
- this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
54
- tools: [
55
- {
56
- name: "list_resource_groups",
57
- description: "List all resource groups in the Azure subscription",
58
- inputSchema: {
59
- type: "object",
60
- properties: {},
61
- },
62
- },
63
- {
64
- name: "create_resource_group",
65
- description: "Create a new resource group",
66
- inputSchema: {
67
- type: "object",
68
- properties: {
69
- name: { type: "string", description: "Resource group name" },
70
- location: { type: "string", description: "Azure region (e.g., eastus, westus2)" },
71
- },
72
- required: ["name", "location"],
73
- },
74
- },
75
- {
76
- name: "list_storage_accounts",
77
- description: "List all storage accounts in a resource group",
78
- inputSchema: {
79
- type: "object",
80
- properties: {
81
- resourceGroup: { type: "string", description: "Resource group name" },
82
- },
83
- required: ["resourceGroup"],
84
- },
85
- },
86
- {
87
- name: "create_storage_account",
88
- description: "Create a new storage account",
89
- inputSchema: {
90
- type: "object",
91
- properties: {
92
- resourceGroup: { type: "string", description: "Resource group name" },
93
- accountName: { type: "string", description: "Storage account name (lowercase, alphanumeric)" },
94
- location: { type: "string", description: "Azure region" },
95
- sku: { type: "string", description: "SKU name (e.g., Standard_LRS, Premium_LRS)", default: "Standard_LRS" },
96
- },
97
- required: ["resourceGroup", "accountName", "location"],
98
- },
99
- },
100
- {
101
- name: "list_vms",
102
- description: "List all virtual machines in a resource group",
103
- inputSchema: {
104
- type: "object",
105
- properties: {
106
- resourceGroup: { type: "string", description: "Resource group name" },
107
- },
108
- required: ["resourceGroup"],
109
- },
110
- },
111
- {
112
- name: "get_vm_status",
113
- description: "Get the power state of a virtual machine",
114
- inputSchema: {
115
- type: "object",
116
- properties: {
117
- resourceGroup: { type: "string", description: "Resource group name" },
118
- vmName: { type: "string", description: "Virtual machine name" },
119
- },
120
- required: ["resourceGroup", "vmName"],
121
- },
122
- },
123
- {
124
- name: "list_blob_containers",
125
- description: "List all blob containers in a storage account",
126
- inputSchema: {
127
- type: "object",
128
- properties: {
129
- accountName: { type: "string", description: "Storage account name" },
130
- },
131
- required: ["accountName"],
132
- },
133
- },
134
- {
135
- name: "upload_blob",
136
- description: "Upload content to a blob in Azure Storage",
137
- inputSchema: {
138
- type: "object",
139
- properties: {
140
- accountName: { type: "string", description: "Storage account name" },
141
- containerName: { type: "string", description: "Container name" },
142
- blobName: { type: "string", description: "Blob name/path" },
143
- content: { type: "string", description: "Content to upload" },
144
- },
145
- required: ["accountName", "containerName", "blobName", "content"],
146
- },
147
- },
148
- {
149
- name: "list_sql_servers",
150
- description: "List all SQL servers in a resource group",
151
- inputSchema: {
152
- type: "object",
153
- properties: {
154
- resourceGroup: { type: "string", description: "Resource group name" },
155
- },
156
- required: ["resourceGroup"],
157
- },
158
- },
159
- {
160
- name: "list_databases",
161
- description: "List all databases on a SQL server",
162
- inputSchema: {
163
- type: "object",
164
- properties: {
165
- resourceGroup: { type: "string", description: "Resource group name" },
166
- serverName: { type: "string", description: "SQL server name" },
167
- },
168
- required: ["resourceGroup", "serverName"],
169
- },
170
- },
171
- {
172
- name: "execute_query",
173
- description: "Execute a SQL query on an Azure SQL Database",
174
- inputSchema: {
175
- type: "object",
176
- properties: {
177
- server: { type: "string", description: "SQL server FQDN (e.g., myserver.database.windows.net)" },
178
- database: { type: "string", description: "Database name" },
179
- query: { type: "string", description: "SQL query to execute" },
180
- username: { type: "string", description: "Username for audit logging (required)" },
181
- password: { type: "string", description: "SQL password (optional, uses Azure AD if not provided)" },
182
- },
183
- required: ["server", "database", "query", "username"],
184
- },
185
- },
186
- {
187
- name: "execute_stored_procedure",
188
- description: "Execute a stored procedure on an Azure SQL Database",
189
- inputSchema: {
190
- type: "object",
191
- properties: {
192
- server: { type: "string", description: "SQL server FQDN" },
193
- database: { type: "string", description: "Database name" },
194
- procedureName: { type: "string", description: "Stored procedure name" },
195
- username: { type: "string", description: "Username for audit logging (required)" },
196
- parameters: { type: "object", description: "Procedure parameters as key-value pairs" },
197
- password: { type: "string", description: "SQL password (optional)" },
198
- },
199
- required: ["server", "database", "procedureName", "username"],
200
- },
201
- },
202
- {
203
- name: "get_database_info",
204
- description: "Get detailed information about a specific database",
205
- inputSchema: {
206
- type: "object",
207
- properties: {
208
- resourceGroup: { type: "string", description: "Resource group name" },
209
- serverName: { type: "string", description: "SQL server name" },
210
- databaseName: { type: "string", description: "Database name" },
211
- },
212
- required: ["resourceGroup", "serverName", "databaseName"],
213
- },
214
- },
215
- {
216
- name: "get_version",
217
- description: "Get the version of the Azure MCP Server",
218
- inputSchema: {
219
- type: "object",
220
- properties: {},
221
- },
222
- },
223
- ],
224
- }));
225
-
226
- // Handle tool calls
227
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
228
- try {
229
- switch (request.params.name) {
230
- case "list_resource_groups":
231
- return await this.listResourceGroups();
232
-
233
- case "create_resource_group":
234
- return await this.createResourceGroup(
235
- request.params.arguments as { name: string; location: string }
236
- );
237
-
238
- case "list_storage_accounts":
239
- return await this.listStorageAccounts(
240
- request.params.arguments as { resourceGroup: string }
241
- );
242
-
243
- case "create_storage_account":
244
- return await this.createStorageAccount(
245
- request.params.arguments as { resourceGroup: string; accountName: string; location: string; sku?: string }
246
- );
247
-
248
- case "list_vms":
249
- return await this.listVMs(
250
- request.params.arguments as { resourceGroup: string }
251
- );
252
-
253
- case "get_vm_status":
254
- return await this.getVMStatus(
255
- request.params.arguments as { resourceGroup: string; vmName: string }
256
- );
257
-
258
- case "list_blob_containers":
259
- return await this.listBlobContainers(
260
- request.params.arguments as { accountName: string }
261
- );
262
-
263
- case "upload_blob":
264
- return await this.uploadBlob(
265
- request.params.arguments as { accountName: string; containerName: string; blobName: string; content: string }
266
- );
267
-
268
- case "list_sql_servers":
269
- return await this.listSqlServers(
270
- request.params.arguments as { resourceGroup: string }
271
- );
272
-
273
- case "list_databases":
274
- return await this.listDatabases(
275
- request.params.arguments as { resourceGroup: string; serverName: string }
276
- );
277
-
278
- case "execute_query":
279
- return await this.executeQuery(
280
- request.params.arguments as { server: string; database: string; query: string; username: string; password?: string }
281
- );
282
-
283
- case "execute_stored_procedure":
284
- return await this.executeStoredProcedure(
285
- request.params.arguments as { server: string; database: string; procedureName: string; username: string; parameters?: any; password?: string }
286
- );
287
-
288
- case "get_database_info":
289
- return await this.getDatabaseInfo(
290
- request.params.arguments as { resourceGroup: string; serverName: string; databaseName: string }
291
- );
292
-
293
- case "get_version":
294
- return await this.getVersion();
295
-
296
- default:
297
- throw new Error(`Unknown tool: ${request.params.name}`);
298
- }
299
- } catch (error: any) {
300
- return {
301
- content: [
302
- {
303
- type: "text",
304
- text: `Error: ${error.message}`,
305
- },
306
- ],
307
- };
308
- }
309
- });
310
- }
311
-
312
- private async listSqlServers(args: { resourceGroup: string }) {
313
- const client = new SqlManagementClient(this.credential, this.subscriptionId);
314
- const servers = [];
315
-
316
- for await (const server of client.servers.listByResourceGroup(args.resourceGroup)) {
317
- servers.push({
318
- name: server.name,
319
- location: server.location,
320
- fullyQualifiedDomainName: server.fullyQualifiedDomainName,
321
- version: server.version,
322
- administratorLogin: server.administratorLogin,
323
- });
324
- }
325
-
326
- return {
327
- content: [
328
- {
329
- type: "text",
330
- text: JSON.stringify(servers, null, 2),
331
- },
332
- ],
333
- };
334
- }
335
-
336
- private async listDatabases(args: { resourceGroup: string; serverName: string }) {
337
- const client = new SqlManagementClient(this.credential, this.subscriptionId);
338
- const databases = [];
339
-
340
- for await (const db of client.databases.listByServer(args.resourceGroup, args.serverName)) {
341
- databases.push({
342
- name: db.name,
343
- location: db.location,
344
- status: db.status,
345
- sku: db.sku,
346
- maxSizeBytes: db.maxSizeBytes,
347
- creationDate: db.creationDate,
348
- });
349
- }
350
-
351
- return {
352
- content: [
353
- {
354
- type: "text",
355
- text: JSON.stringify(databases, null, 2),
356
- },
357
- ],
358
- };
359
- }
360
-
361
- private async getDatabaseInfo(args: { resourceGroup: string; serverName: string; databaseName: string }) {
362
- const client = new SqlManagementClient(this.credential, this.subscriptionId);
363
-
364
- const database = await client.databases.get(
365
- args.resourceGroup,
366
- args.serverName,
367
- args.databaseName
368
- );
369
-
370
- return {
371
- content: [
372
- {
373
- type: "text",
374
- text: JSON.stringify({
375
- name: database.name,
376
- location: database.location,
377
- status: database.status,
378
- sku: database.sku,
379
- maxSizeBytes: database.maxSizeBytes,
380
- collation: database.collation,
381
- creationDate: database.creationDate,
382
- earliestRestoreDate: database.earliestRestoreDate,
383
- elasticPoolId: database.elasticPoolId,
384
- kind: database.kind,
385
- }, null, 2),
386
- },
387
- ],
388
- };
389
- }
390
-
391
- private async getSqlPool(server: string, database: string, username: string, password?: string): Promise<sql.ConnectionPool> {
392
- const poolKey = `${server}:${database}`;
393
-
394
- // Return existing pool if available
395
- if (this.sqlPools.has(poolKey)) {
396
- const pool = this.sqlPools.get(poolKey)!;
397
- if (pool.connected) {
398
- return pool;
399
- }
400
- }
401
-
402
- const DBusername = "pprappdevusermcp";
403
- password = "ZuX?c5%+=6!c?}5z)n73"
404
-
405
-
406
- // Create new connection pool
407
- let config: sql.config;
408
-
409
- // Use SQL auth if credentials provided, otherwise use Azure AD
410
- if (DBusername && password) {
411
- config = {
412
- server: server,
413
- database: database,
414
- user: DBusername,
415
- password: password,
416
- options: {
417
- encrypt: true,
418
- trustServerCertificate: false,
419
- },
420
- pool: {
421
- max: 10,
422
- min: 0,
423
- idleTimeoutMillis: 30000,
424
- },
425
- };
426
- } else {
427
- // Use Azure AD authentication with managed identity or DefaultAzureCredential
428
- const token = await this.credential.getToken("https://database.windows.net/.default");
429
- config = {
430
- server: server,
431
- database: database,
432
- options: {
433
- encrypt: true,
434
- trustServerCertificate: false,
435
- },
436
- pool: {
437
- max: 10,
438
- min: 0,
439
- idleTimeoutMillis: 30000,
440
- },
441
- authentication: {
442
- type: 'azure-active-directory-access-token',
443
- options: {
444
- token: token.token,
445
- },
446
- } as any,
447
- };
448
- }
449
-
450
- const pool = await sql.connect(config);
451
- this.sqlPools.set(poolKey, pool);
452
-
453
- return pool;
454
- }
455
-
456
- private async logRequest(queryString: string, server: string, username: string) {
457
- const homeDir = process.env.USERPROFILE || process.env.HOME || '';
458
- const logDir = path.join(homeDir, 'source', 'repos', 'mcp_server');
459
-
460
- // Create directory if it doesn't exist
461
- if (!fs.existsSync(logDir)) {
462
- fs.mkdirSync(logDir, { recursive: true });
463
- }
464
-
465
- const logFile = path.join(logDir, 'sql_query_log.txt');
466
- const logEntry = `[${new Date().toISOString()}] Server: ${server}\tUsername: ${username}\tQuery:\n ${queryString}\n`;
467
- fs.appendFileSync(logFile, logEntry);
468
-
469
- }
470
-
471
- private getMessageEncoding(message: string): Uint8Array {
472
- return new TextEncoder().encode(message);
473
- }
474
-
475
- private getMessageDecoding(message: Uint8Array): string {
476
- return new TextDecoder().decode(message);
477
- }
478
-
479
- private bytesToBase64(bytes: Uint8Array): string {
480
- const binary = String.fromCharCode(...bytes);
481
- return btoa(binary);
482
- }
483
- private base64ToBytes(base64: string): Uint8Array {
484
- const binary = atob(base64);
485
- const bytes = new Uint8Array(binary.length);
486
- for (let i = 0; i < binary.length; i++) {
487
- bytes[i] = binary.charCodeAt(i);
488
- }
489
- return bytes;
490
- }
491
-
492
- private async deriveKeyFromPassword(password: string, salt: Uint8Array): Promise<CryptoKey> {
493
- const pwUtf8 = this.getMessageEncoding(password);
494
- // Use PBKDF2 for key derivation from a password
495
-
496
- // importKey(format, keyData, algorithm, extractable, keyUsages)
497
- const keyMaterial = await crypto.subtle.importKey(
498
- 'raw',
499
- pwUtf8 as BufferSource,
500
- 'PBKDF2',
501
- false,
502
- ['deriveKey']
503
- );
504
-
505
- const derivedKey = await crypto.subtle.deriveKey(
506
- {
507
- name: 'PBKDF2',
508
- salt: salt as BufferSource,
509
- iterations: 100000, // High iterations make it more secure
510
- hash: 'SHA-256',
511
- },
512
- keyMaterial,
513
- { name: 'AES-GCM', length: 256 }, // Use AES-GCM for encryption
514
- true, // Key can be extracted if needed
515
- ['encrypt', 'decrypt']
516
- );
517
-
518
- return derivedKey;
519
- }
520
-
521
- private async encryptData(plaintext: string, key: CryptoKey, iv: BufferSource): Promise<{ ciphertext: string, iv: string }> {
522
- const encoded = this.getMessageEncoding(plaintext);
523
-
524
- const ciphertextBuffer = await crypto.subtle.encrypt(
525
- { name: 'AES-GCM', iv},
526
- key,
527
- encoded as BufferSource
528
- );
529
- const cipherBase64 = this.bytesToBase64(new Uint8Array(ciphertextBuffer));
530
- const ivBase64 = this.bytesToBase64(new Uint8Array(iv as ArrayBuffer));
531
-
532
- return {
533
- ciphertext: cipherBase64,
534
- iv: ivBase64
535
- };
536
- }
537
-
538
- private async findDataToEncrypt(theData: any[], secretKey: string, salt:Uint8Array, ivIn: BufferSource): Promise<any[]> {
539
- const retVal: any[] = [];
540
- const algorithm: string = 'SHA-256';
541
-
542
- const server: string = "no server v1";
543
- const username: string = "no username";
544
-
545
- const encryptionKey = await this.deriveKeyFromPassword(secretKey, salt);
546
-
547
- for (const curRow of theData) {
548
- const retRow: any = {};
549
-
550
- for (const [key, value] of Object.entries(curRow)) {
551
- let newValue: string = <any>value;
552
-
553
- if (typeof newValue === 'string' && newValue.startsWith("m$k_")) {
554
- this.logRequest(`\t in encrypt -> encrypting ${JSON.stringify(newValue)}`, server, username);
555
- var tmpValue: any = newValue.replace("m$k_", "");
556
-
557
- const { ciphertext, iv } = await this.encryptData(tmpValue, encryptionKey, ivIn);
558
- // newValue = "m$k_" + ciphertext + "-" + iv + "-" + this.bytesToBase64(salt) + "-" + this.bytesToBase64(ivIn as Uint8Array);
559
- newValue = "m$k_" + ciphertext;
560
- this.logRequest(`\tin encrypt -> result ${JSON.stringify(newValue)}`, server, username);
561
- }
562
-
563
- retRow[key] = newValue;
564
- }
565
-
566
- retVal.push(retRow);
567
- }
568
-
569
- return retVal;
570
- }
571
-
572
- private async executeQuery(args: { server: string; database: string; query: string; username: string; password?: string }) {
573
- try {
574
- const pool = await this.getSqlPool(args.server, args.database, args.username, args.password);
575
-
576
- this.logRequest(args.query, args.server, args.username);
577
-
578
- const result = await pool.request().query(args.query);
579
- const resultCount = result.recordset ? result.recordset.length : 0;
580
- this.logRequest(`\t-> Query returned ${JSON.stringify(result.recordset)} rows`, args.server, args.username);
581
- const secretKey = this.bytesToBase64(crypto.getRandomValues(new Uint8Array(16)));
582
- const salt = crypto.getRandomValues(new Uint8Array(16));
583
- const ivIn = crypto.getRandomValues(new Uint8Array(16));
584
-
585
- var retval: any = await this.findDataToEncrypt(result.recordset, secretKey, salt, ivIn);
586
- const junkBits = new Date().getSeconds().toString(16).padStart(2, 'P');
587
- const secretKey_modified = junkBits + secretKey;
588
- this.logRequest(`\t-> encryption returned ${JSON.stringify(retval)}`, args.server, args.username);
589
- return {
590
- content: [
591
- {
592
- type: "text",
593
- text: JSON.stringify({
594
- rowsAffected: result.rowsAffected,
595
- key: secretKey_modified + "-" + this.bytesToBase64(salt) + "-" + this.bytesToBase64(ivIn as Uint8Array),
596
- recordset: retval,
597
- }, null, 2),
598
- },
599
- ],
600
- };
601
- } catch (error: any) {
602
- this.logRequest(`\t**** error occurred: ${error.message}`, args.server, args.username);
603
- return {
604
- content: [
605
- {
606
- type: "text",
607
- text: `Query execution failed: ${error.message}`,
608
- },
609
- ],
610
- };
611
- }
612
- }
613
-
614
- private getVersion() {
615
- return {
616
- content: [
617
- {
618
- type: "text",
619
- text: JSON.stringify({
620
- version: "1.0.4",
621
- name: "azure-mcp-server",
622
- }, null, 2),
623
- },
624
- ],
625
- };
626
- }
627
-
628
- private async executeStoredProcedure(args: { server: string; database: string; procedureName: string; username: string; parameters?: any; password?: string }) {
629
- try {
630
- const pool = await this.getSqlPool(args.server, args.database, args.username, args.password);
631
- const request = pool.request();
632
-
633
- // Add parameters if provided
634
- if (args.parameters) {
635
- for (const [key, value] of Object.entries(args.parameters)) {
636
- request.input(key, value);
637
- }
638
- }
639
-
640
- const result = await request.execute(args.procedureName);
641
-
642
- return {
643
- content: [
644
- {
645
- type: "text",
646
- text: JSON.stringify({
647
- rowsAffected: result.rowsAffected,
648
- recordset: result.recordset,
649
- recordsets: result.recordsets,
650
- returnValue: result.returnValue,
651
- }, null, 2),
652
- },
653
- ],
654
- };
655
- } catch (error: any) {
656
- return {
657
- content: [
658
- {
659
- type: "text",
660
- text: `Stored procedure execution failed: ${error.message}`,
661
- },
662
- ],
663
- };
664
- }
665
- }
666
-
667
- async run() {
668
- const transport = new StdioServerTransport();
669
- await this.server.connect(transport);
670
- console.error("Azure MCP Server running on stdio");
671
- }
672
- }
673
-
674
- // Start the server
675
- const server = new AzureMCPServer();
676
- server.run().catch(console.error);