@eamonboyle/mssql-mcp 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -22
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +29 -24
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +144 -112
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +382 -122
- package/dist/index.js.map +1 -1
- package/dist/mcpResults.d.ts +49 -0
- package/dist/mcpResults.d.ts.map +1 -0
- package/dist/mcpResults.js +246 -0
- package/dist/mcpResults.js.map +1 -0
- package/dist/promptRegistry.d.ts +16 -0
- package/dist/promptRegistry.d.ts.map +1 -0
- package/dist/promptRegistry.js +112 -0
- package/dist/promptRegistry.js.map +1 -0
- package/dist/resourceRegistry.d.ts +14 -0
- package/dist/resourceRegistry.d.ts.map +1 -0
- package/dist/resourceRegistry.js +311 -0
- package/dist/resourceRegistry.js.map +1 -0
- package/dist/schema.d.ts +57 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +390 -0
- package/dist/schema.js.map +1 -0
- package/dist/serverState.d.ts +28 -0
- package/dist/serverState.d.ts.map +1 -0
- package/dist/serverState.js +99 -0
- package/dist/serverState.js.map +1 -0
- package/dist/sql.d.ts +7 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +73 -0
- package/dist/sql.js.map +1 -0
- package/dist/toolRegistry.d.ts +20 -0
- package/dist/toolRegistry.d.ts.map +1 -0
- package/dist/toolRegistry.js +547 -0
- package/dist/toolRegistry.js.map +1 -0
- package/dist/tools/AnalyzeTableTool.d.ts +37 -0
- package/dist/tools/AnalyzeTableTool.d.ts.map +1 -0
- package/dist/tools/AnalyzeTableTool.js +81 -0
- package/dist/tools/AnalyzeTableTool.js.map +1 -0
- package/dist/tools/CreateIndexTool.d.ts +30 -23
- package/dist/tools/CreateIndexTool.d.ts.map +1 -1
- package/dist/tools/CreateIndexTool.js +47 -77
- package/dist/tools/CreateIndexTool.js.map +1 -1
- package/dist/tools/CreateTableTool.d.ts +18 -11
- package/dist/tools/CreateTableTool.d.ts.map +1 -1
- package/dist/tools/CreateTableTool.js +37 -61
- package/dist/tools/CreateTableTool.js.map +1 -1
- package/dist/tools/DeleteDataTool.d.ts +22 -0
- package/dist/tools/DeleteDataTool.d.ts.map +1 -0
- package/dist/tools/DeleteDataTool.js +38 -0
- package/dist/tools/DeleteDataTool.js.map +1 -0
- package/dist/tools/DescribeObjectTool.d.ts +21 -0
- package/dist/tools/DescribeObjectTool.d.ts.map +1 -0
- package/dist/tools/DescribeObjectTool.js +31 -0
- package/dist/tools/DescribeObjectTool.js.map +1 -0
- package/dist/tools/DescribeRelationshipsTool.d.ts +20 -0
- package/dist/tools/DescribeRelationshipsTool.d.ts.map +1 -0
- package/dist/tools/DescribeRelationshipsTool.js +24 -0
- package/dist/tools/DescribeRelationshipsTool.js.map +1 -0
- package/dist/tools/DescribeTableTool.d.ts +19 -20
- package/dist/tools/DescribeTableTool.d.ts.map +1 -1
- package/dist/tools/DescribeTableTool.js +23 -44
- package/dist/tools/DescribeTableTool.js.map +1 -1
- package/dist/tools/DropTableTool.d.ts +13 -11
- package/dist/tools/DropTableTool.d.ts.map +1 -1
- package/dist/tools/DropTableTool.js +30 -44
- package/dist/tools/DropTableTool.js.map +1 -1
- package/dist/tools/ExplainQueryTool.d.ts +32 -0
- package/dist/tools/ExplainQueryTool.d.ts.map +1 -0
- package/dist/tools/ExplainQueryTool.js +102 -0
- package/dist/tools/ExplainQueryTool.js.map +1 -0
- package/dist/tools/InsertDataTool.d.ts +20 -16
- package/dist/tools/InsertDataTool.d.ts.map +1 -1
- package/dist/tools/InsertDataTool.js +91 -108
- package/dist/tools/InsertDataTool.js.map +1 -1
- package/dist/tools/ListDatabasesTool.d.ts +16 -0
- package/dist/tools/ListDatabasesTool.d.ts.map +1 -0
- package/dist/tools/ListDatabasesTool.js +26 -0
- package/dist/tools/ListDatabasesTool.js.map +1 -0
- package/dist/tools/ListForeignKeysTool.d.ts +19 -0
- package/dist/tools/ListForeignKeysTool.d.ts.map +1 -0
- package/dist/tools/ListForeignKeysTool.js +24 -0
- package/dist/tools/ListForeignKeysTool.js.map +1 -0
- package/dist/tools/ListObjectsTool.d.ts +20 -0
- package/dist/tools/ListObjectsTool.d.ts.map +1 -0
- package/dist/tools/ListObjectsTool.js +26 -0
- package/dist/tools/ListObjectsTool.js.map +1 -0
- package/dist/tools/ListTableTool.d.ts +20 -16
- package/dist/tools/ListTableTool.d.ts.map +1 -1
- package/dist/tools/ListTableTool.js +32 -51
- package/dist/tools/ListTableTool.js.map +1 -1
- package/dist/tools/PreviewDeleteTool.d.ts +28 -0
- package/dist/tools/PreviewDeleteTool.d.ts.map +1 -0
- package/dist/tools/PreviewDeleteTool.js +31 -0
- package/dist/tools/PreviewDeleteTool.js.map +1 -0
- package/dist/tools/PreviewUpdateTool.d.ts +30 -0
- package/dist/tools/PreviewUpdateTool.d.ts.map +1 -0
- package/dist/tools/PreviewUpdateTool.js +34 -0
- package/dist/tools/PreviewUpdateTool.js.map +1 -0
- package/dist/tools/ReadDataTool.d.ts +35 -33
- package/dist/tools/ReadDataTool.d.ts.map +1 -1
- package/dist/tools/ReadDataTool.js +92 -105
- package/dist/tools/ReadDataTool.js.map +1 -1
- package/dist/tools/SearchDataTool.d.ts +27 -0
- package/dist/tools/SearchDataTool.d.ts.map +1 -0
- package/dist/tools/SearchDataTool.js +61 -0
- package/dist/tools/SearchDataTool.js.map +1 -0
- package/dist/tools/UpdateDataTool.d.ts +22 -16
- package/dist/tools/UpdateDataTool.d.ts.map +1 -1
- package/dist/tools/UpdateDataTool.js +48 -65
- package/dist/tools/UpdateDataTool.js.map +1 -1
- package/dist/validation.d.ts +13 -13
- package/dist/validation.js +133 -133
- package/dist/validation.js.map +1 -1
- package/dist/writePreview.d.ts +20 -0
- package/dist/writePreview.d.ts.map +1 -0
- package/dist/writePreview.js +27 -0
- package/dist/writePreview.js.map +1 -0
- package/dist/writePreviewGrant.d.ts +18 -0
- package/dist/writePreviewGrant.d.ts.map +1 -0
- package/dist/writePreviewGrant.js +71 -0
- package/dist/writePreviewGrant.js.map +1 -0
- package/dist/writePreviewGrantStore.d.ts +10 -0
- package/dist/writePreviewGrantStore.d.ts.map +1 -0
- package/dist/writePreviewGrantStore.js +47 -0
- package/dist/writePreviewGrantStore.js.map +1 -0
- package/dist/writeSafety.d.ts +21 -0
- package/dist/writeSafety.d.ts.map +1 -0
- package/dist/writeSafety.js +169 -0
- package/dist/writeSafety.js.map +1 -0
- package/package.json +6 -5
- package/dist/__tests__/db.test.d.ts +0 -2
- package/dist/__tests__/db.test.d.ts.map +0 -1
- package/dist/__tests__/db.test.js +0 -204
- package/dist/__tests__/db.test.js.map +0 -1
- package/dist/__tests__/validation.test.d.ts +0 -2
- package/dist/__tests__/validation.test.d.ts.map +0 -1
- package/dist/__tests__/validation.test.js +0 -111
- package/dist/__tests__/validation.test.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdateDataTool.js","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"UpdateDataTool.js","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAkB,MAAM,mBAAmB,CAAC;AAUlF,MAAM,OAAO,cAAc;IAA3B;QACE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GACT,4EAA4E,CAAC;IAmDjF,CAAC;IAjDC,KAAK,CAAC,GAAG,CAAC,MAAwB;QAChC,IAAI,KAAyB,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;YAEzE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;YAC7D,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAC5C,CAAC;YAED,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3C,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,SAAS,GAAG,aAAa;iBAC5B,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;gBAClB,MAAM,SAAS,GAAG,UAAU,KAAK,EAAE,CAAC;gBACpC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvC,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,SAAS,EAAE,CAAC;YACnD,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,MAAM,WAAW,GAAG,6BAA6B,CAC/C,OAAO,EACP,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAC3C,eAAe,CAChB,CAAC;YAEF,KAAK,GAAG,UAAU,kBAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,QAAQ,SAAS,UAAU,WAAW,EAAE,CAAC;YACpG,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAChC,8BAA8B,KAAK,mBAAmB,CACvD,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,kCAAkC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,kBAAkB;gBACnF,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;aACrC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,yBAAyB,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE;aAC9E,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
package/dist/validation.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQL query validation for read-only operations.
|
|
3
|
-
* Extracted for testability and reuse.
|
|
4
|
-
*/
|
|
5
|
-
export interface ValidationResult {
|
|
6
|
-
isValid: boolean;
|
|
7
|
-
error?: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Validates a SQL query for read-only (SELECT) operations.
|
|
11
|
-
* Rejects dangerous keywords, injection patterns, and multi-statement queries.
|
|
12
|
-
*/
|
|
13
|
-
export declare function validateReadQuery(query: string): ValidationResult;
|
|
1
|
+
/**
|
|
2
|
+
* SQL query validation for read-only operations.
|
|
3
|
+
* Extracted for testability and reuse.
|
|
4
|
+
*/
|
|
5
|
+
export interface ValidationResult {
|
|
6
|
+
isValid: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Validates a SQL query for read-only (SELECT) operations.
|
|
11
|
+
* Rejects dangerous keywords, injection patterns, and multi-statement queries.
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateReadQuery(query: string): ValidationResult;
|
|
14
14
|
//# sourceMappingURL=validation.d.ts.map
|
package/dist/validation.js
CHANGED
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQL query validation for read-only operations.
|
|
3
|
-
* Extracted for testability and reuse.
|
|
4
|
-
*/
|
|
5
|
-
const DANGEROUS_KEYWORDS = [
|
|
6
|
-
"DELETE",
|
|
7
|
-
"DROP",
|
|
8
|
-
"UPDATE",
|
|
9
|
-
"INSERT",
|
|
10
|
-
"ALTER",
|
|
11
|
-
"CREATE",
|
|
12
|
-
"TRUNCATE",
|
|
13
|
-
"EXEC",
|
|
14
|
-
"EXECUTE",
|
|
15
|
-
"MERGE",
|
|
16
|
-
"REPLACE",
|
|
17
|
-
"GRANT",
|
|
18
|
-
"REVOKE",
|
|
19
|
-
"COMMIT",
|
|
20
|
-
"ROLLBACK",
|
|
21
|
-
"TRANSACTION",
|
|
22
|
-
"BEGIN",
|
|
23
|
-
"DECLARE",
|
|
24
|
-
"SET",
|
|
25
|
-
"USE",
|
|
26
|
-
"BACKUP",
|
|
27
|
-
"RESTORE",
|
|
28
|
-
"KILL",
|
|
29
|
-
"SHUTDOWN",
|
|
30
|
-
"WAITFOR",
|
|
31
|
-
"OPENROWSET",
|
|
32
|
-
"OPENDATASOURCE",
|
|
33
|
-
"OPENQUERY",
|
|
34
|
-
"OPENXML",
|
|
35
|
-
"BULK",
|
|
36
|
-
"INTO",
|
|
37
|
-
];
|
|
38
|
-
const DANGEROUS_PATTERNS = [
|
|
39
|
-
/SELECT\s+.*?\s+INTO\s+/i,
|
|
40
|
-
/;\s*(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE|MERGE|REPLACE|GRANT|REVOKE)/i,
|
|
41
|
-
/UNION\s+(?:ALL\s+)?SELECT.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
|
|
42
|
-
/--.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
|
|
43
|
-
/\/\*.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE).*?\*\//i,
|
|
44
|
-
/EXEC\s*\(/i,
|
|
45
|
-
/EXECUTE\s*\(/i,
|
|
46
|
-
/sp_/i,
|
|
47
|
-
/xp_/i,
|
|
48
|
-
/BULK\s+INSERT/i,
|
|
49
|
-
/OPENROWSET/i,
|
|
50
|
-
/OPENDATASOURCE/i,
|
|
51
|
-
/@@/,
|
|
52
|
-
/SYSTEM_USER/i,
|
|
53
|
-
/USER_NAME/i,
|
|
54
|
-
/DB_NAME/i,
|
|
55
|
-
/HOST_NAME/i,
|
|
56
|
-
/WAITFOR\s+DELAY/i,
|
|
57
|
-
/WAITFOR\s+TIME/i,
|
|
58
|
-
/;\s*\w/,
|
|
59
|
-
/\+\s*CHAR\s*\(/i,
|
|
60
|
-
/\+\s*NCHAR\s*\(/i,
|
|
61
|
-
/\+\s*ASCII\s*\(/i,
|
|
62
|
-
];
|
|
63
|
-
/**
|
|
64
|
-
* Validates a SQL query for read-only (SELECT) operations.
|
|
65
|
-
* Rejects dangerous keywords, injection patterns, and multi-statement queries.
|
|
66
|
-
*/
|
|
67
|
-
export function validateReadQuery(query) {
|
|
68
|
-
if (!query || typeof query !== "string") {
|
|
69
|
-
return {
|
|
70
|
-
isValid: false,
|
|
71
|
-
error: "Query must be a non-empty string",
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
const cleanQuery = query
|
|
75
|
-
.replace(/--.*$/gm, "")
|
|
76
|
-
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
77
|
-
.replace(/\s+/g, " ")
|
|
78
|
-
.trim();
|
|
79
|
-
if (!cleanQuery) {
|
|
80
|
-
return {
|
|
81
|
-
isValid: false,
|
|
82
|
-
error: "Query cannot be empty after removing comments",
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
const upperQuery = cleanQuery.toUpperCase();
|
|
86
|
-
if (!upperQuery.startsWith("SELECT")) {
|
|
87
|
-
return {
|
|
88
|
-
isValid: false,
|
|
89
|
-
error: "Query must start with SELECT for security reasons",
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
for (const keyword of DANGEROUS_KEYWORDS) {
|
|
93
|
-
const keywordRegex = new RegExp(`(^|\\s|[^A-Za-z0-9_])${keyword}($|\\s|[^A-Za-z0-9_])`, "i");
|
|
94
|
-
if (keywordRegex.test(upperQuery)) {
|
|
95
|
-
return {
|
|
96
|
-
isValid: false,
|
|
97
|
-
error: `Dangerous keyword '${keyword}' detected in query. Only SELECT operations are allowed.`,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
for (const pattern of DANGEROUS_PATTERNS) {
|
|
102
|
-
if (pattern.test(query)) {
|
|
103
|
-
return {
|
|
104
|
-
isValid: false,
|
|
105
|
-
error: "Potentially malicious SQL pattern detected. Only simple SELECT queries are allowed.",
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const statements = cleanQuery
|
|
110
|
-
.split(";")
|
|
111
|
-
.filter((stmt) => stmt.trim().length > 0);
|
|
112
|
-
if (statements.length > 1) {
|
|
113
|
-
return {
|
|
114
|
-
isValid: false,
|
|
115
|
-
error: "Multiple SQL statements are not allowed. Use only a single SELECT statement.",
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
if (query.includes("CHAR(") ||
|
|
119
|
-
query.includes("NCHAR(") ||
|
|
120
|
-
query.includes("ASCII(")) {
|
|
121
|
-
return {
|
|
122
|
-
isValid: false,
|
|
123
|
-
error: "Character conversion functions are not allowed as they may be used for obfuscation.",
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
if (query.length > 10000) {
|
|
127
|
-
return {
|
|
128
|
-
isValid: false,
|
|
129
|
-
error: "Query is too long. Maximum allowed length is 10,000 characters.",
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
return { isValid: true };
|
|
133
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* SQL query validation for read-only operations.
|
|
3
|
+
* Extracted for testability and reuse.
|
|
4
|
+
*/
|
|
5
|
+
const DANGEROUS_KEYWORDS = [
|
|
6
|
+
"DELETE",
|
|
7
|
+
"DROP",
|
|
8
|
+
"UPDATE",
|
|
9
|
+
"INSERT",
|
|
10
|
+
"ALTER",
|
|
11
|
+
"CREATE",
|
|
12
|
+
"TRUNCATE",
|
|
13
|
+
"EXEC",
|
|
14
|
+
"EXECUTE",
|
|
15
|
+
"MERGE",
|
|
16
|
+
"REPLACE",
|
|
17
|
+
"GRANT",
|
|
18
|
+
"REVOKE",
|
|
19
|
+
"COMMIT",
|
|
20
|
+
"ROLLBACK",
|
|
21
|
+
"TRANSACTION",
|
|
22
|
+
"BEGIN",
|
|
23
|
+
"DECLARE",
|
|
24
|
+
"SET",
|
|
25
|
+
"USE",
|
|
26
|
+
"BACKUP",
|
|
27
|
+
"RESTORE",
|
|
28
|
+
"KILL",
|
|
29
|
+
"SHUTDOWN",
|
|
30
|
+
"WAITFOR",
|
|
31
|
+
"OPENROWSET",
|
|
32
|
+
"OPENDATASOURCE",
|
|
33
|
+
"OPENQUERY",
|
|
34
|
+
"OPENXML",
|
|
35
|
+
"BULK",
|
|
36
|
+
"INTO",
|
|
37
|
+
];
|
|
38
|
+
const DANGEROUS_PATTERNS = [
|
|
39
|
+
/SELECT\s+.*?\s+INTO\s+/i,
|
|
40
|
+
/;\s*(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE|MERGE|REPLACE|GRANT|REVOKE)/i,
|
|
41
|
+
/UNION\s+(?:ALL\s+)?SELECT.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
|
|
42
|
+
/--.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
|
|
43
|
+
/\/\*.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE).*?\*\//i,
|
|
44
|
+
/EXEC\s*\(/i,
|
|
45
|
+
/EXECUTE\s*\(/i,
|
|
46
|
+
/sp_/i,
|
|
47
|
+
/xp_/i,
|
|
48
|
+
/BULK\s+INSERT/i,
|
|
49
|
+
/OPENROWSET/i,
|
|
50
|
+
/OPENDATASOURCE/i,
|
|
51
|
+
/@@/,
|
|
52
|
+
/SYSTEM_USER/i,
|
|
53
|
+
/USER_NAME/i,
|
|
54
|
+
/DB_NAME/i,
|
|
55
|
+
/HOST_NAME/i,
|
|
56
|
+
/WAITFOR\s+DELAY/i,
|
|
57
|
+
/WAITFOR\s+TIME/i,
|
|
58
|
+
/;\s*\w/,
|
|
59
|
+
/\+\s*CHAR\s*\(/i,
|
|
60
|
+
/\+\s*NCHAR\s*\(/i,
|
|
61
|
+
/\+\s*ASCII\s*\(/i,
|
|
62
|
+
];
|
|
63
|
+
/**
|
|
64
|
+
* Validates a SQL query for read-only (SELECT) operations.
|
|
65
|
+
* Rejects dangerous keywords, injection patterns, and multi-statement queries.
|
|
66
|
+
*/
|
|
67
|
+
export function validateReadQuery(query) {
|
|
68
|
+
if (!query || typeof query !== "string") {
|
|
69
|
+
return {
|
|
70
|
+
isValid: false,
|
|
71
|
+
error: "Query must be a non-empty string",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const cleanQuery = query
|
|
75
|
+
.replace(/--.*$/gm, "")
|
|
76
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
77
|
+
.replace(/\s+/g, " ")
|
|
78
|
+
.trim();
|
|
79
|
+
if (!cleanQuery) {
|
|
80
|
+
return {
|
|
81
|
+
isValid: false,
|
|
82
|
+
error: "Query cannot be empty after removing comments",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const upperQuery = cleanQuery.toUpperCase();
|
|
86
|
+
if (!upperQuery.startsWith("SELECT")) {
|
|
87
|
+
return {
|
|
88
|
+
isValid: false,
|
|
89
|
+
error: "Query must start with SELECT for security reasons",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
for (const keyword of DANGEROUS_KEYWORDS) {
|
|
93
|
+
const keywordRegex = new RegExp(`(^|\\s|[^A-Za-z0-9_])${keyword}($|\\s|[^A-Za-z0-9_])`, "i");
|
|
94
|
+
if (keywordRegex.test(upperQuery)) {
|
|
95
|
+
return {
|
|
96
|
+
isValid: false,
|
|
97
|
+
error: `Dangerous keyword '${keyword}' detected in query. Only SELECT operations are allowed.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
102
|
+
if (pattern.test(query)) {
|
|
103
|
+
return {
|
|
104
|
+
isValid: false,
|
|
105
|
+
error: "Potentially malicious SQL pattern detected. Only simple SELECT queries are allowed.",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const statements = cleanQuery
|
|
110
|
+
.split(";")
|
|
111
|
+
.filter((stmt) => stmt.trim().length > 0);
|
|
112
|
+
if (statements.length > 1) {
|
|
113
|
+
return {
|
|
114
|
+
isValid: false,
|
|
115
|
+
error: "Multiple SQL statements are not allowed. Use only a single SELECT statement.",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (query.includes("CHAR(") ||
|
|
119
|
+
query.includes("NCHAR(") ||
|
|
120
|
+
query.includes("ASCII(")) {
|
|
121
|
+
return {
|
|
122
|
+
isValid: false,
|
|
123
|
+
error: "Character conversion functions are not allowed as they may be used for obfuscation.",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (query.length > 10000) {
|
|
127
|
+
return {
|
|
128
|
+
isValid: false,
|
|
129
|
+
error: "Query is too long. Maximum allowed length is 10,000 characters.",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return { isValid: true };
|
|
133
|
+
}
|
|
134
134
|
//# sourceMappingURL=validation.js.map
|
package/dist/validation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,kBAAkB,GAAG;IACzB,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,UAAU;IACV,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;IACT,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,aAAa;IACb,OAAO;IACP,SAAS;IACT,KAAK;IACL,KAAK;IACL,QAAQ;IACR,SAAS;IACT,MAAM;IACN,UAAU;IACV,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,WAAW;IACX,SAAS;IACT,MAAM;IACN,MAAM;CACP,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,yBAAyB;IACzB,gGAAgG;IAChG,6FAA6F;IAC7F,sEAAsE;IACtE,+EAA+E;IAC/E,YAAY;IACZ,eAAe;IACf,MAAM;IACN,MAAM;IACN,gBAAgB;IAChB,aAAa;IACb,iBAAiB;IACjB,IAAI;IACJ,cAAc;IACd,YAAY;IACZ,UAAU;IACV,YAAY;IACZ,kBAAkB;IAClB,iBAAiB;IACjB,QAAQ;IACR,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;CACnB,CAAC;AAOF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type SqlFilter } from "./writeSafety.js";
|
|
2
|
+
export interface WritePreviewResult {
|
|
3
|
+
query: string;
|
|
4
|
+
countQuery: string;
|
|
5
|
+
rows: unknown[];
|
|
6
|
+
affectedRowCount: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function previewFilteredRows(params: {
|
|
9
|
+
tableName: string;
|
|
10
|
+
schemaName?: string;
|
|
11
|
+
filters: SqlFilter[];
|
|
12
|
+
databaseName?: string;
|
|
13
|
+
limit: number;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
query: string;
|
|
16
|
+
countQuery: string;
|
|
17
|
+
rows: import("mssql").IRecordSet<any>;
|
|
18
|
+
affectedRowCount: any;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=writePreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writePreview.d.ts","sourceRoot":"","sources":["../src/writePreview.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;;;;;GA6BA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getSqlRequest } from "./db.js";
|
|
2
|
+
import { buildQualifiedName } from "./sql.js";
|
|
3
|
+
import { buildParameterizedWhereClause } from "./writeSafety.js";
|
|
4
|
+
export async function previewFilteredRows(params) {
|
|
5
|
+
const { tableName, schemaName, filters, databaseName, limit } = params;
|
|
6
|
+
const { request, error } = await getSqlRequest(databaseName);
|
|
7
|
+
if (error) {
|
|
8
|
+
throw new Error(error);
|
|
9
|
+
}
|
|
10
|
+
request.input("previewLimit", limit);
|
|
11
|
+
const whereClause = buildParameterizedWhereClause(filters, (name, value) => request.input(name, value), "preview_filter");
|
|
12
|
+
const tableRef = buildQualifiedName(tableName, schemaName);
|
|
13
|
+
const query = `SELECT TOP (@previewLimit) * FROM ${tableRef} WHERE ${whereClause}`;
|
|
14
|
+
const countQuery = `SELECT COUNT(*) AS affectedRowCount FROM ${tableRef} WHERE ${whereClause}`;
|
|
15
|
+
const result = await request.query(`
|
|
16
|
+
${query};
|
|
17
|
+
${countQuery};
|
|
18
|
+
`);
|
|
19
|
+
const recordsets = Array.isArray(result.recordsets) ? result.recordsets : [];
|
|
20
|
+
return {
|
|
21
|
+
query,
|
|
22
|
+
countQuery,
|
|
23
|
+
rows: recordsets[0] ?? [],
|
|
24
|
+
affectedRowCount: recordsets[1]?.[0]?.affectedRowCount ?? 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=writePreview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writePreview.js","sourceRoot":"","sources":["../src/writePreview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,6BAA6B,EAAkB,MAAM,kBAAkB,CAAC;AASjF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAMzC;IACC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IACvE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;IAC7D,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,6BAA6B,CAC/C,OAAO,EACP,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAC3C,gBAAgB,CACjB,CAAC;IACF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,qCAAqC,QAAQ,UAAU,WAAW,EAAE,CAAC;IACnF,MAAM,UAAU,GAAG,4CAA4C,QAAQ,UAAU,WAAW,EAAE,CAAC;IAE/F,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;MAC/B,KAAK;MACL,UAAU;GACb,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IAE7E,OAAO;QACL,KAAK;QACL,UAAU;QACV,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE;QACzB,gBAAgB,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,IAAI,CAAC;KAC/B,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SqlFilter } from "./writeSafety.js";
|
|
2
|
+
export type WriteDataToolName = "update_data" | "delete_data";
|
|
3
|
+
export declare function fingerprintForUpdateOp(params: {
|
|
4
|
+
databaseName?: string;
|
|
5
|
+
schemaName?: string;
|
|
6
|
+
tableName: string;
|
|
7
|
+
filters: SqlFilter[];
|
|
8
|
+
updates: Record<string, unknown>;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function fingerprintForDeleteOp(params: {
|
|
11
|
+
databaseName?: string;
|
|
12
|
+
schemaName?: string;
|
|
13
|
+
tableName: string;
|
|
14
|
+
filters: SqlFilter[];
|
|
15
|
+
}): string;
|
|
16
|
+
export type PreviewOrWriteName = "preview_update" | "preview_delete" | "update_data" | "delete_data";
|
|
17
|
+
export declare function fingerprintForWriteTool(toolName: PreviewOrWriteName, requestArgs: Record<string, unknown>): string;
|
|
18
|
+
//# sourceMappingURL=writePreviewGrant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writePreviewGrant.d.ts","sourceRoot":"","sources":["../src/writePreviewGrant.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,aAAa,CAAC;AAqC9D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,GAAG,MAAM,CAUT;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB,GAAG,MAAM,CAST;AAED,MAAM,MAAM,kBAAkB,GAC1B,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,aAAa,CAAC;AAElB,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,kBAAkB,EAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAiBR"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
function stableStringify(value) {
|
|
3
|
+
return JSON.stringify(value, (_key, v) => {
|
|
4
|
+
if (v instanceof Date) {
|
|
5
|
+
return v.toISOString();
|
|
6
|
+
}
|
|
7
|
+
return v;
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function normalizeFilters(filters) {
|
|
11
|
+
return [...filters]
|
|
12
|
+
.map((f) => ({
|
|
13
|
+
column: f.column,
|
|
14
|
+
operator: f.operator,
|
|
15
|
+
value: f.value,
|
|
16
|
+
values: f.values ? [...f.values] : undefined,
|
|
17
|
+
}))
|
|
18
|
+
.sort((a, b) => {
|
|
19
|
+
const byCol = a.column.localeCompare(b.column);
|
|
20
|
+
if (byCol !== 0) {
|
|
21
|
+
return byCol;
|
|
22
|
+
}
|
|
23
|
+
return a.operator.localeCompare(b.operator);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function normalizeUpdates(updates) {
|
|
27
|
+
const keys = Object.keys(updates).sort((a, b) => a.localeCompare(b));
|
|
28
|
+
const out = {};
|
|
29
|
+
for (const key of keys) {
|
|
30
|
+
out[key] = updates[key];
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
export function fingerprintForUpdateOp(params) {
|
|
35
|
+
const payload = {
|
|
36
|
+
kind: "update",
|
|
37
|
+
database: params.databaseName ?? "",
|
|
38
|
+
schema: params.schemaName ?? "",
|
|
39
|
+
table: params.tableName,
|
|
40
|
+
filters: normalizeFilters(params.filters),
|
|
41
|
+
updates: normalizeUpdates(params.updates),
|
|
42
|
+
};
|
|
43
|
+
return createHash("sha256").update(stableStringify(payload)).digest("hex");
|
|
44
|
+
}
|
|
45
|
+
export function fingerprintForDeleteOp(params) {
|
|
46
|
+
const payload = {
|
|
47
|
+
kind: "delete",
|
|
48
|
+
database: params.databaseName ?? "",
|
|
49
|
+
schema: params.schemaName ?? "",
|
|
50
|
+
table: params.tableName,
|
|
51
|
+
filters: normalizeFilters(params.filters),
|
|
52
|
+
};
|
|
53
|
+
return createHash("sha256").update(stableStringify(payload)).digest("hex");
|
|
54
|
+
}
|
|
55
|
+
export function fingerprintForWriteTool(toolName, requestArgs) {
|
|
56
|
+
const filters = requestArgs.filters ?? [];
|
|
57
|
+
const databaseName = typeof requestArgs.databaseName === "string" ? requestArgs.databaseName : undefined;
|
|
58
|
+
const schemaName = typeof requestArgs.schemaName === "string" ? requestArgs.schemaName : undefined;
|
|
59
|
+
const tableName = String(requestArgs.tableName ?? "");
|
|
60
|
+
if (toolName === "preview_delete" || toolName === "delete_data") {
|
|
61
|
+
return fingerprintForDeleteOp({ databaseName, schemaName, tableName, filters });
|
|
62
|
+
}
|
|
63
|
+
return fingerprintForUpdateOp({
|
|
64
|
+
databaseName,
|
|
65
|
+
schemaName,
|
|
66
|
+
tableName,
|
|
67
|
+
filters,
|
|
68
|
+
updates: requestArgs.updates ?? {},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=writePreviewGrant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writePreviewGrant.js","sourceRoot":"","sources":["../src/writePreviewGrant.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WriteDataToolName } from "./writePreviewGrant.js";
|
|
2
|
+
/**
|
|
3
|
+
* Process-global store so preview tokens remain valid across HTTP round-trips
|
|
4
|
+
* (each request uses a fresh ServerState for query plans / results).
|
|
5
|
+
*/
|
|
6
|
+
export declare const writePreviewGrantStore: {
|
|
7
|
+
issue(fingerprint: string, writeToolName: WriteDataToolName): string;
|
|
8
|
+
consume(token: string, fingerprint: string, writeToolName: WriteDataToolName): boolean;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=writePreviewGrantStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writePreviewGrantStore.d.ts","sourceRoot":"","sources":["../src/writePreviewGrantStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAoBhE;;;GAGG;AACH,eAAO,MAAM,sBAAsB;uBACd,MAAM,iBAAiB,iBAAiB,GAAG,MAAM;mBAa3D,MAAM,eACA,MAAM,iBACJ,iBAAiB,GAC/B,OAAO;CAqBX,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
const WRITE_PREVIEW_GRANT_TTL_MS = 10 * 60 * 1000;
|
|
3
|
+
const grants = new Map();
|
|
4
|
+
function purgeExpiredGrants(now) {
|
|
5
|
+
for (const [token, grant] of grants) {
|
|
6
|
+
if (grant.expiresAt < now) {
|
|
7
|
+
grants.delete(token);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Process-global store so preview tokens remain valid across HTTP round-trips
|
|
13
|
+
* (each request uses a fresh ServerState for query plans / results).
|
|
14
|
+
*/
|
|
15
|
+
export const writePreviewGrantStore = {
|
|
16
|
+
issue(fingerprint, writeToolName) {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
purgeExpiredGrants(now);
|
|
19
|
+
const token = randomBytes(24).toString("base64url");
|
|
20
|
+
grants.set(token, {
|
|
21
|
+
fingerprint,
|
|
22
|
+
writeToolName,
|
|
23
|
+
expiresAt: now + WRITE_PREVIEW_GRANT_TTL_MS,
|
|
24
|
+
});
|
|
25
|
+
return token;
|
|
26
|
+
},
|
|
27
|
+
consume(token, fingerprint, writeToolName) {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
purgeExpiredGrants(now);
|
|
30
|
+
if (!token) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const grant = grants.get(token);
|
|
34
|
+
if (!grant) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (grant.expiresAt < now ||
|
|
38
|
+
grant.fingerprint !== fingerprint ||
|
|
39
|
+
grant.writeToolName !== writeToolName) {
|
|
40
|
+
grants.delete(token);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
grants.delete(token);
|
|
44
|
+
return true;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=writePreviewGrantStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writePreviewGrantStore.js","sourceRoot":"","sources":["../src/writePreviewGrantStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAS1C,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAElD,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEpD,SAAS,kBAAkB,CAAC,GAAW;IACrC,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC;YAC1B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,KAAK,CAAC,WAAmB,EAAE,aAAgC;QACzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE;YAChB,WAAW;YACX,aAAa;YACb,SAAS,EAAE,GAAG,GAAG,0BAA0B;SAC5C,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CACL,KAAa,EACb,WAAmB,EACnB,aAAgC;QAEhC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IACE,KAAK,CAAC,SAAS,GAAG,GAAG;YACrB,KAAK,CAAC,WAAW,KAAK,WAAW;YACjC,KAAK,CAAC,aAAa,KAAK,aAAa,EACrC,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const SQL_FILTER_OPERATORS: readonly ["=", "!=", ">", ">=", "<", "<=", "LIKE", "IN", "IS NULL", "IS NOT NULL"];
|
|
2
|
+
export type SqlFilterOperator = (typeof SQL_FILTER_OPERATORS)[number];
|
|
3
|
+
export interface SqlFilter {
|
|
4
|
+
column: string;
|
|
5
|
+
operator: SqlFilterOperator;
|
|
6
|
+
value?: unknown;
|
|
7
|
+
values?: unknown[];
|
|
8
|
+
}
|
|
9
|
+
export interface SqlTypeDeclaration {
|
|
10
|
+
type: string;
|
|
11
|
+
nullable?: boolean;
|
|
12
|
+
isPrimaryKey?: boolean;
|
|
13
|
+
isUnique?: boolean;
|
|
14
|
+
isIdentity?: boolean;
|
|
15
|
+
identitySeed?: number;
|
|
16
|
+
identityIncrement?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function normalizeSqlType(rawType: string): string;
|
|
19
|
+
export declare function buildCreateColumnDefinition(columnName: string, declaration: SqlTypeDeclaration): string;
|
|
20
|
+
export declare function buildParameterizedWhereClause(filters: SqlFilter[], input: (name: string, value: unknown) => unknown, parameterPrefix?: string): string;
|
|
21
|
+
//# sourceMappingURL=writeSafety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeSafety.d.ts","sourceRoot":"","sources":["../src/writeSafety.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,oFAWvB,CAAC;AAGX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAqFD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA6BxD;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CAwBR;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,SAAS,EAAE,EACpB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,EAChD,eAAe,SAAW,GACzB,MAAM,CA0CR"}
|