@vericto/sdk 0.0.0-stage → 0.2.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/CHANGELOG.md +83 -0
- package/LICENSE +93 -0
- package/README.md +377 -2
- package/SECURITY.md +58 -0
- package/dist/better-sqlite3.cjs +62 -0
- package/dist/better-sqlite3.cjs.map +1 -0
- package/dist/better-sqlite3.d.cts +64 -0
- package/dist/better-sqlite3.d.ts +64 -0
- package/dist/better-sqlite3.js +58 -0
- package/dist/better-sqlite3.js.map +1 -0
- package/dist/chunk-HFL7W4WC.cjs +600 -0
- package/dist/chunk-HFL7W4WC.cjs.map +1 -0
- package/dist/chunk-I4I35HUY.js +582 -0
- package/dist/chunk-I4I35HUY.js.map +1 -0
- package/dist/guard-BVpq8ytG.d.cts +175 -0
- package/dist/guard-BVpq8ytG.d.ts +175 -0
- package/dist/index.cjs +76 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +76 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/langchain.cjs +195 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +126 -0
- package/dist/langchain.d.ts +126 -0
- package/dist/langchain.js +186 -0
- package/dist/langchain.js.map +1 -0
- package/dist/snowflake.cjs +51 -0
- package/dist/snowflake.cjs.map +1 -0
- package/dist/snowflake.d.cts +47 -0
- package/dist/snowflake.d.ts +47 -0
- package/dist/snowflake.js +47 -0
- package/dist/snowflake.js.map +1 -0
- package/package.json +83 -4
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { VerictoConfigError, normalizeDialect, isMasked, sqlToExecute, Vericto, VerictoBlockedError, VerictoError } from './chunk-I4I35HUY.js';
|
|
2
|
+
import { SqlToolkit } from '@langchain/classic/agents/toolkits/sql';
|
|
3
|
+
import { SqlDatabase } from '@langchain/classic/sql_db';
|
|
4
|
+
import { QuerySqlTool } from '@langchain/classic/tools/sql';
|
|
5
|
+
import { Tool } from '@langchain/core/tools';
|
|
6
|
+
|
|
7
|
+
var TYPEORM_DIALECTS = {
|
|
8
|
+
postgres: "postgres",
|
|
9
|
+
"aurora-postgres": "postgres",
|
|
10
|
+
mysql: "mysql",
|
|
11
|
+
mariadb: "mysql",
|
|
12
|
+
"aurora-mysql": "mysql",
|
|
13
|
+
mssql: "mssql",
|
|
14
|
+
oracle: "oracle",
|
|
15
|
+
sqlite: "sqlite",
|
|
16
|
+
"better-sqlite3": "sqlite",
|
|
17
|
+
sqljs: "sqlite"
|
|
18
|
+
};
|
|
19
|
+
function dialectFromDataSource(dataSource) {
|
|
20
|
+
const type = dataSource.options.type;
|
|
21
|
+
const dialect = TYPEORM_DIALECTS[type];
|
|
22
|
+
if (!dialect) {
|
|
23
|
+
throw new VerictoConfigError(
|
|
24
|
+
`vericto: no Vericto dialect for the TypeORM type '${type}'. Supported: ${Object.keys(TYPEORM_DIALECTS).join(", ")}; or pass { dialect }.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return dialect;
|
|
28
|
+
}
|
|
29
|
+
function guardDataSource(dataSource, vericto, dialect) {
|
|
30
|
+
const name = normalizeDialect(dialect);
|
|
31
|
+
const query = async (sql, parameters, ...rest) => {
|
|
32
|
+
const final = await vericto.prepare(sql, name);
|
|
33
|
+
return dataSource.query.call(dataSource, final, parameters, ...rest);
|
|
34
|
+
};
|
|
35
|
+
return new Proxy(dataSource, {
|
|
36
|
+
get(target, prop) {
|
|
37
|
+
if (prop === "query") return query;
|
|
38
|
+
const value = Reflect.get(target, prop, target);
|
|
39
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function blockedMessage(verdict) {
|
|
44
|
+
const lines = ["Vericto blocked this SQL query; it was NOT executed."];
|
|
45
|
+
const rule = verdict.ruleCode ?? "a Vericto rule";
|
|
46
|
+
lines.push(`Rule: ${rule}${verdict.severity ? ` (severity ${verdict.severity})` : ""}`);
|
|
47
|
+
if (verdict.message) lines.push(`Reason: ${verdict.message}`);
|
|
48
|
+
const denied = verdict.accessDenied.length ? JSON.stringify(verdict.accessDenied) : void 0;
|
|
49
|
+
if (denied) lines.push(`Access denied: ${denied}`);
|
|
50
|
+
if (verdict.suggestedFix) {
|
|
51
|
+
lines.push(`Suggested fix: ${verdict.suggestedFix}`);
|
|
52
|
+
lines.push("Use the suggested fix, or rewrite the query so that it complies with the rule, and try again.");
|
|
53
|
+
} else {
|
|
54
|
+
lines.push("Rewrite the query so that it complies with the rule, and try again. Do not retry the same query.");
|
|
55
|
+
}
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
function describeVerdict(verdict) {
|
|
59
|
+
if (verdict.decision === "BLOCKED") return blockedMessage(verdict).replace("; it was NOT executed.", ".");
|
|
60
|
+
const rule = verdict.ruleCode ? ` by ${verdict.ruleCode}${verdict.severity ? ` (severity ${verdict.severity})` : ""}` : "";
|
|
61
|
+
let text;
|
|
62
|
+
switch (verdict.decision) {
|
|
63
|
+
case "FLAGGED":
|
|
64
|
+
text = `Vericto: FLAGGED${rule}. The query is allowed to run, but it was reported.`;
|
|
65
|
+
break;
|
|
66
|
+
case "MONITORED":
|
|
67
|
+
text = `Vericto: MONITORED${rule}. The query is allowed to run.`;
|
|
68
|
+
break;
|
|
69
|
+
case "PARSE_ERROR":
|
|
70
|
+
text = `Vericto could not parse the query${verdict.parseError ? ` (${verdict.parseError})` : ""}; the workspace policy allows it to run.`;
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
text = "Vericto: ALLOWED. The query can run.";
|
|
74
|
+
}
|
|
75
|
+
if (isMasked(verdict)) {
|
|
76
|
+
text += `
|
|
77
|
+
It reads sensitive columns, so it will run masked, as: ${sqlToExecute(verdict)}`;
|
|
78
|
+
}
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
function checkOnBlock(onBlock) {
|
|
82
|
+
const value = onBlock ?? "return";
|
|
83
|
+
if (value !== "return" && value !== "raise") {
|
|
84
|
+
throw new VerictoConfigError(`onBlock must be 'return' or 'raise', got ${JSON.stringify(value)}.`);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
var VerictoSqlDatabase = class _VerictoSqlDatabase extends SqlDatabase {
|
|
89
|
+
vericto;
|
|
90
|
+
dialect;
|
|
91
|
+
onBlock;
|
|
92
|
+
/** The unguarded data source: SQL run through it directly is not checked. */
|
|
93
|
+
rawDataSource;
|
|
94
|
+
constructor(fields) {
|
|
95
|
+
super(fields);
|
|
96
|
+
this.vericto = fields.vericto;
|
|
97
|
+
this.dialect = fields.dialect;
|
|
98
|
+
this.onBlock = checkOnBlock(fields.onBlock);
|
|
99
|
+
this.rawDataSource = fields.appDataSource;
|
|
100
|
+
this.appDataSource = guardDataSource(fields.appDataSource, this.vericto, this.dialect);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Same as `SqlDatabase.fromDataSourceParams`, plus the Vericto options. Fails fast
|
|
104
|
+
* when the server does not evaluate the dialect. LangChain's own schema
|
|
105
|
+
* introspection (the column catalogue read here) runs on the raw data source.
|
|
106
|
+
*/
|
|
107
|
+
static async fromDataSourceParams(fields) {
|
|
108
|
+
const vericto = fields.vericto ?? new Vericto();
|
|
109
|
+
const dialect = await vericto.requireDialect(fields.dialect ?? dialectFromDataSource(fields.appDataSource));
|
|
110
|
+
checkOnBlock(fields.onBlock);
|
|
111
|
+
const base = await SqlDatabase.fromDataSourceParams(fields);
|
|
112
|
+
const db = new _VerictoSqlDatabase({ ...fields, vericto, dialect });
|
|
113
|
+
db.allTables = base.allTables;
|
|
114
|
+
db.customDescription = base.customDescription;
|
|
115
|
+
return db;
|
|
116
|
+
}
|
|
117
|
+
/** Same as `SqlDatabase.fromOptionsParams`, plus the Vericto options. */
|
|
118
|
+
static async fromOptionsParams(fields) {
|
|
119
|
+
const { DataSource } = await SqlDatabase.imports();
|
|
120
|
+
const { appDataSourceOptions, ...rest } = fields;
|
|
121
|
+
return _VerictoSqlDatabase.fromDataSourceParams({ ...rest, appDataSource: new DataSource(appDataSourceOptions) });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Run `command` after Vericto checks it. Blocked: returns {@link blockedMessage}
|
|
125
|
+
* (or throws `VerictoBlockedError` with `onBlock: 'raise'`). Masked: runs the
|
|
126
|
+
* rewrite. Vericto unavailable or misconfigured: throws.
|
|
127
|
+
*/
|
|
128
|
+
async run(command, fetch = "all") {
|
|
129
|
+
try {
|
|
130
|
+
return await super.run(command, fetch);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err instanceof VerictoBlockedError && this.onBlock === "return") return blockedMessage(err.verdict);
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
var VerictoQuerySqlTool = class extends QuerySqlTool {
|
|
138
|
+
static lc_name() {
|
|
139
|
+
return "VerictoQuerySqlTool";
|
|
140
|
+
}
|
|
141
|
+
/** @ignore */
|
|
142
|
+
async _call(input) {
|
|
143
|
+
try {
|
|
144
|
+
return await this.db.run(input);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (err instanceof VerictoError) throw err;
|
|
147
|
+
return String(err);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var VerictoSqlToolkit = class extends SqlToolkit {
|
|
152
|
+
constructor(db, llm) {
|
|
153
|
+
super(db, llm);
|
|
154
|
+
this.tools = this.tools.map((tool) => tool instanceof QuerySqlTool ? new VerictoQuerySqlTool(db) : tool);
|
|
155
|
+
this.dialect = db.dialect;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var VerictoCheckSqlTool = class extends Tool {
|
|
159
|
+
static lc_name() {
|
|
160
|
+
return "VerictoCheckSqlTool";
|
|
161
|
+
}
|
|
162
|
+
name = "vericto-check-sql";
|
|
163
|
+
description = "Input is one SQL query. Checks it with the Vericto SQL firewall without running it and returns the verdict: ALLOWED, FLAGGED, MONITORED, or BLOCKED with the rule and a suggested fix. Use it before running a query; if it is blocked, fix the query and check again.";
|
|
164
|
+
vericto;
|
|
165
|
+
dialect;
|
|
166
|
+
onBlock;
|
|
167
|
+
constructor(options = {}) {
|
|
168
|
+
super();
|
|
169
|
+
const vericto = options.vericto ?? options.db?.vericto;
|
|
170
|
+
const dialect = options.dialect ?? options.db?.dialect;
|
|
171
|
+
if (!dialect) throw new VerictoConfigError("VerictoCheckSqlTool needs { dialect } or { db }.");
|
|
172
|
+
this.vericto = vericto ?? new Vericto();
|
|
173
|
+
this.dialect = normalizeDialect(dialect);
|
|
174
|
+
this.onBlock = checkOnBlock(options.onBlock ?? options.db?.onBlock);
|
|
175
|
+
}
|
|
176
|
+
/** @ignore */
|
|
177
|
+
async _call(input) {
|
|
178
|
+
const verdict = await this.vericto.check(input, this.dialect);
|
|
179
|
+
if (verdict.decision === "BLOCKED" && this.onBlock === "raise") throw new VerictoBlockedError(verdict, input);
|
|
180
|
+
return describeVerdict(verdict);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export { VerictoCheckSqlTool, VerictoQuerySqlTool, VerictoSqlDatabase, VerictoSqlToolkit, blockedMessage, describeVerdict, dialectFromDataSource, guardDataSource };
|
|
185
|
+
//# sourceMappingURL=langchain.js.map
|
|
186
|
+
//# sourceMappingURL=langchain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/langchain.ts"],"names":[],"mappings":"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","file":"langchain.js","sourcesContent":["/**\n * Vericto for LangChain.js SQL agents and chains (`@langchain/classic`).\n *\n * ```ts\n * import { DataSource } from 'typeorm';\n * import { VerictoSqlDatabase, VerictoSqlToolkit } from '@vericto/sdk/langchain';\n * import { createSqlAgent } from '@langchain/classic/agents/toolkits/sql';\n *\n * const db = await VerictoSqlDatabase.fromDataSourceParams({\n * appDataSource: new DataSource({ type: 'postgres', url: process.env.DATABASE_URL }),\n * });\n * const agent = createSqlAgent(llm, new VerictoSqlToolkit(db, llm));\n * ```\n *\n * Every statement LangChain sends through the database's TypeORM `DataSource` is\n * checked: the agent's queries (`run`, `QuerySqlTool`), the sample rows of\n * `getTableInfo`, and `SqlDatabaseChain`'s generated SQL. A blocked query is\n * answered with a message the model can act on (rule, reason, suggested fix)\n * instead of an exception, unless `onBlock: 'raise'`. Masked queries run as the\n * rewrite. Vericto being unavailable or misconfigured is never turned into a message\n * for the model: it propagates, so the fail mode stays in charge.\n *\n * Requires the optional peers `@langchain/classic`, `@langchain/core` and `typeorm`.\n */\nimport { SqlToolkit } from '@langchain/classic/agents/toolkits/sql';\nimport { SqlDatabase, type SqlDatabaseDataSourceParams } from '@langchain/classic/sql_db';\nimport { QuerySqlTool } from '@langchain/classic/tools/sql';\nimport type { BaseLanguageModelInterface } from '@langchain/core/language_models/base';\nimport { Tool } from '@langchain/core/tools';\nimport type { DataSource } from 'typeorm';\n\nimport { normalizeDialect } from './dialects.js';\nimport { VerictoBlockedError, VerictoConfigError, VerictoError } from './errors.js';\nimport { Vericto } from './guard.js';\nimport { isMasked, sqlToExecute, type Verdict } from './verdict.js';\n\n/** What a blocked query does: `return` a message for the model (default), or `raise`. */\nexport type OnBlock = 'return' | 'raise';\n\nexport interface VerictoLangChainOptions {\n /** The guard. Default: one built from the `VERICTO_*` environment variables. */\n vericto?: Vericto;\n /** The Vericto dialect. Default: read from the TypeORM `DataSource` type. */\n dialect?: string;\n /** Default `return`. */\n onBlock?: OnBlock;\n}\n\nexport type VerictoSqlDatabaseParams = SqlDatabaseDataSourceParams & VerictoLangChainOptions;\n\nconst TYPEORM_DIALECTS: Readonly<Record<string, string>> = {\n postgres: 'postgres',\n 'aurora-postgres': 'postgres',\n mysql: 'mysql',\n mariadb: 'mysql',\n 'aurora-mysql': 'mysql',\n mssql: 'mssql',\n oracle: 'oracle',\n sqlite: 'sqlite',\n 'better-sqlite3': 'sqlite',\n sqljs: 'sqlite',\n};\n\n/**\n * The Vericto dialect of a TypeORM `DataSource` type. Types Vericto does not\n * evaluate (cockroachdb, sap, spanner, mongodb...) throw `VerictoConfigError`: pass\n * `dialect` explicitly if you know what you are doing.\n */\nexport function dialectFromDataSource(dataSource: Pick<DataSource, 'options'>): string {\n const type: string = dataSource.options.type;\n const dialect = TYPEORM_DIALECTS[type];\n if (!dialect) {\n throw new VerictoConfigError(\n `vericto: no Vericto dialect for the TypeORM type '${type}'. ` +\n `Supported: ${Object.keys(TYPEORM_DIALECTS).join(', ')}; or pass { dialect }.`,\n );\n }\n return dialect;\n}\n\n/**\n * A `DataSource` whose `query()` is checked by Vericto first (and runs the masked\n * rewrite). Everything else is the original data source's.\n */\nexport function guardDataSource(dataSource: DataSource, vericto: Vericto, dialect: string): DataSource {\n const name = normalizeDialect(dialect);\n const query = async (sql: string, parameters?: unknown[], ...rest: unknown[]): Promise<unknown> => {\n const final = await vericto.prepare(sql, name);\n return (dataSource.query as (...args: unknown[]) => Promise<unknown>).call(dataSource, final, parameters, ...rest);\n };\n return new Proxy(dataSource, {\n get(target, prop) {\n if (prop === 'query') return query;\n const value: unknown = Reflect.get(target, prop, target);\n return typeof value === 'function' ? (value as (...a: unknown[]) => unknown).bind(target) : value;\n },\n });\n}\n\n/** The message a model gets for a blocked query: what was blocked, why, and the fix. */\nexport function blockedMessage(verdict: Verdict): string {\n const lines = ['Vericto blocked this SQL query; it was NOT executed.'];\n const rule = verdict.ruleCode ?? 'a Vericto rule';\n lines.push(`Rule: ${rule}${verdict.severity ? ` (severity ${verdict.severity})` : ''}`);\n if (verdict.message) lines.push(`Reason: ${verdict.message}`);\n const denied = verdict.accessDenied.length ? JSON.stringify(verdict.accessDenied) : undefined;\n if (denied) lines.push(`Access denied: ${denied}`);\n if (verdict.suggestedFix) {\n lines.push(`Suggested fix: ${verdict.suggestedFix}`);\n lines.push('Use the suggested fix, or rewrite the query so that it complies with the rule, and try again.');\n } else {\n lines.push('Rewrite the query so that it complies with the rule, and try again. Do not retry the same query.');\n }\n return lines.join('\\n');\n}\n\n/** A readable verdict for the check tool. */\nexport function describeVerdict(verdict: Verdict): string {\n if (verdict.decision === 'BLOCKED') return blockedMessage(verdict).replace('; it was NOT executed.', '.');\n const rule = verdict.ruleCode ? ` by ${verdict.ruleCode}${verdict.severity ? ` (severity ${verdict.severity})` : ''}` : '';\n let text: string;\n switch (verdict.decision) {\n case 'FLAGGED':\n text = `Vericto: FLAGGED${rule}. The query is allowed to run, but it was reported.`;\n break;\n case 'MONITORED':\n text = `Vericto: MONITORED${rule}. The query is allowed to run.`;\n break;\n case 'PARSE_ERROR':\n text = `Vericto could not parse the query${verdict.parseError ? ` (${verdict.parseError})` : ''}; the workspace policy allows it to run.`;\n break;\n default:\n text = 'Vericto: ALLOWED. The query can run.';\n }\n if (isMasked(verdict)) {\n text += `\\nIt reads sensitive columns, so it will run masked, as: ${sqlToExecute(verdict)}`;\n }\n return text;\n}\n\nfunction checkOnBlock(onBlock: OnBlock | undefined): OnBlock {\n // Checked at run time too: plain-JS callers get no type error.\n const value: string = onBlock ?? 'return';\n if (value !== 'return' && value !== 'raise') {\n throw new VerictoConfigError(`onBlock must be 'return' or 'raise', got ${JSON.stringify(value)}.`);\n }\n return value;\n}\n\n/**\n * A LangChain `SqlDatabase` whose statements are checked by Vericto. Accepted\n * wherever a `SqlDatabase` is: `QuerySqlTool`, `SqlToolkit`/`createSqlAgent`,\n * `SqlDatabaseChain`.\n */\nexport class VerictoSqlDatabase extends SqlDatabase {\n readonly vericto: Vericto;\n readonly dialect: string;\n readonly onBlock: OnBlock;\n /** The unguarded data source: SQL run through it directly is not checked. */\n readonly rawDataSource: DataSource;\n\n protected constructor(fields: VerictoSqlDatabaseParams & { vericto: Vericto; dialect: string }) {\n super(fields);\n this.vericto = fields.vericto;\n this.dialect = fields.dialect;\n this.onBlock = checkOnBlock(fields.onBlock);\n this.rawDataSource = fields.appDataSource;\n this.appDataSource = guardDataSource(fields.appDataSource, this.vericto, this.dialect);\n }\n\n /**\n * Same as `SqlDatabase.fromDataSourceParams`, plus the Vericto options. Fails fast\n * when the server does not evaluate the dialect. LangChain's own schema\n * introspection (the column catalogue read here) runs on the raw data source.\n */\n static override async fromDataSourceParams(fields: VerictoSqlDatabaseParams): Promise<VerictoSqlDatabase> {\n const vericto = fields.vericto ?? new Vericto();\n const dialect = await vericto.requireDialect(fields.dialect ?? dialectFromDataSource(fields.appDataSource));\n checkOnBlock(fields.onBlock);\n const base = await SqlDatabase.fromDataSourceParams(fields);\n const db = new VerictoSqlDatabase({ ...fields, vericto, dialect });\n db.allTables = base.allTables;\n db.customDescription = base.customDescription;\n return db;\n }\n\n /** Same as `SqlDatabase.fromOptionsParams`, plus the Vericto options. */\n static override async fromOptionsParams(\n fields: Parameters<typeof SqlDatabase.fromOptionsParams>[0] & VerictoLangChainOptions,\n ): Promise<VerictoSqlDatabase> {\n const { DataSource } = await SqlDatabase.imports();\n const { appDataSourceOptions, ...rest } = fields;\n return VerictoSqlDatabase.fromDataSourceParams({ ...rest, appDataSource: new DataSource(appDataSourceOptions) });\n }\n\n /**\n * Run `command` after Vericto checks it. Blocked: returns {@link blockedMessage}\n * (or throws `VerictoBlockedError` with `onBlock: 'raise'`). Masked: runs the\n * rewrite. Vericto unavailable or misconfigured: throws.\n */\n override async run(command: string, fetch: 'all' | 'one' = 'all'): Promise<string> {\n try {\n return await super.run(command, fetch);\n } catch (err) {\n if (err instanceof VerictoBlockedError && this.onBlock === 'return') return blockedMessage(err.verdict);\n throw err;\n }\n }\n}\n\n/**\n * `QuerySqlTool` that lets Vericto errors through. The stock tool turns every error\n * into text for the model, which would also hide \"Vericto unavailable\" (and, with\n * `onBlock: 'raise'`, the block) from your code.\n */\nexport class VerictoQuerySqlTool extends QuerySqlTool {\n static override lc_name(): string {\n return 'VerictoQuerySqlTool';\n }\n\n /** @ignore */\n override async _call(input: string): Promise<string> {\n try {\n return await this.db.run(input);\n } catch (err) {\n if (err instanceof VerictoError) throw err;\n return String(err);\n }\n }\n}\n\n/** `SqlToolkit` with {@link VerictoQuerySqlTool} and the Vericto dialect in the prompt. */\nexport class VerictoSqlToolkit extends SqlToolkit {\n constructor(db: VerictoSqlDatabase, llm: BaseLanguageModelInterface) {\n super(db, llm);\n this.tools = this.tools.map((tool) => (tool instanceof QuerySqlTool ? new VerictoQuerySqlTool(db) : tool));\n this.dialect = db.dialect;\n }\n}\n\nexport interface VerictoCheckSqlToolOptions extends VerictoLangChainOptions {\n /** Take the guard, dialect and onBlock from this database. */\n db?: VerictoSqlDatabase;\n}\n\n/**\n * A tool that only checks a query (nothing is executed) and answers with Vericto's\n * verdict, so an agent can validate SQL before it proposes or runs it. Each check is\n * a Runtime API evaluation (it counts against the runtime quota and is audited).\n */\nexport class VerictoCheckSqlTool extends Tool {\n static override lc_name(): string {\n return 'VerictoCheckSqlTool';\n }\n\n name = 'vericto-check-sql';\n description =\n 'Input is one SQL query. Checks it with the Vericto SQL firewall without running it and returns the ' +\n 'verdict: ALLOWED, FLAGGED, MONITORED, or BLOCKED with the rule and a suggested fix. ' +\n 'Use it before running a query; if it is blocked, fix the query and check again.';\n\n readonly vericto: Vericto;\n readonly dialect: string;\n readonly onBlock: OnBlock;\n\n constructor(options: VerictoCheckSqlToolOptions = {}) {\n super();\n const vericto = options.vericto ?? options.db?.vericto;\n const dialect = options.dialect ?? options.db?.dialect;\n if (!dialect) throw new VerictoConfigError('VerictoCheckSqlTool needs { dialect } or { db }.');\n this.vericto = vericto ?? new Vericto();\n this.dialect = normalizeDialect(dialect);\n this.onBlock = checkOnBlock(options.onBlock ?? options.db?.onBlock);\n }\n\n /** @ignore */\n async _call(input: string): Promise<string> {\n const verdict = await this.vericto.check(input, this.dialect);\n if (verdict.decision === 'BLOCKED' && this.onBlock === 'raise') throw new VerictoBlockedError(verdict, input);\n return describeVerdict(verdict);\n }\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkHFL7W4WC_cjs = require('./chunk-HFL7W4WC.cjs');
|
|
4
|
+
|
|
5
|
+
// src/snowflake.ts
|
|
6
|
+
var DIALECT = "snowflake";
|
|
7
|
+
var GuardedSnowflakeConnection = class {
|
|
8
|
+
/** The underlying connection. SQL run through it directly is **not** checked. */
|
|
9
|
+
raw;
|
|
10
|
+
vericto;
|
|
11
|
+
constructor(connection, vericto) {
|
|
12
|
+
this.raw = connection;
|
|
13
|
+
this.vericto = vericto;
|
|
14
|
+
}
|
|
15
|
+
/** Check `options.sqlText`, then `connection.execute()` the SQL to run. */
|
|
16
|
+
async execute(options) {
|
|
17
|
+
let sqlText;
|
|
18
|
+
try {
|
|
19
|
+
sqlText = await this.vericto.prepare(options.sqlText, DIALECT);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (typeof options.complete === "function") {
|
|
22
|
+
options.complete(err, void 0, void 0);
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
return this.raw.execute({ ...options, sqlText });
|
|
28
|
+
}
|
|
29
|
+
/** Promise style: resolves the rows (not for `streamResult`). */
|
|
30
|
+
query(sqlText, binds, options = {}) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const complete = (err, _statement, rows) => {
|
|
33
|
+
if (err) reject(err instanceof Error ? err : new Error(typeof err === "string" ? err : JSON.stringify(err)));
|
|
34
|
+
else resolve(rows ?? []);
|
|
35
|
+
};
|
|
36
|
+
const request = { ...options, sqlText, complete, ...binds === void 0 ? {} : { binds } };
|
|
37
|
+
this.execute(request).catch(reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
async function guardConnection(connection, options = {}) {
|
|
42
|
+
const vericto = options.vericto ?? new chunkHFL7W4WC_cjs.Vericto();
|
|
43
|
+
await vericto.requireDialect(DIALECT);
|
|
44
|
+
return new GuardedSnowflakeConnection(connection, vericto);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
exports.DIALECT = DIALECT;
|
|
48
|
+
exports.GuardedSnowflakeConnection = GuardedSnowflakeConnection;
|
|
49
|
+
exports.guardConnection = guardConnection;
|
|
50
|
+
//# sourceMappingURL=snowflake.cjs.map
|
|
51
|
+
//# sourceMappingURL=snowflake.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/snowflake.ts"],"names":["Vericto"],"mappings":";;;;;AAgBO,IAAM,OAAA,GAAU;AAiBhB,IAAM,6BAAN,MAAoE;AAAA;AAAA,EAEhE,GAAA;AAAA,EACA,OAAA;AAAA,EAET,WAAA,CAAY,YAAe,OAAA,EAAkB;AAC3C,IAAA,IAAA,CAAK,GAAA,GAAM,UAAA;AACX,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,QAAQ,OAAA,EAA4D;AACxE,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,SAAS,OAAO,CAAA;AAAA,IAC/D,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,OAAO,OAAA,CAAQ,QAAA,KAAa,UAAA,EAAY;AAC1C,QAAA,OAAA,CAAQ,QAAA,CAAS,GAAA,EAAK,MAAA,EAAW,MAAS,CAAA;AAC1C,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AACA,IAAA,OAAO,KAAK,GAAA,CAAI,OAAA,CAAQ,EAAE,GAAG,OAAA,EAAS,SAAS,CAAA;AAAA,EACjD;AAAA;AAAA,EAGA,KAAA,CACE,OAAA,EACA,KAAA,EACA,OAAA,GAAmC,EAAC,EACpB;AAChB,IAAA,OAAO,IAAI,OAAA,CAAe,CAAC,OAAA,EAAS,MAAA,KAAW;AAC7C,MAAA,MAAM,QAAA,GAAqB,CAAC,GAAA,EAAK,UAAA,EAAY,IAAA,KAAS;AACpD,QAAA,IAAI,GAAA,EAAK,MAAA,CAAO,GAAA,YAAe,KAAA,GAAQ,MAAM,IAAI,KAAA,CAAM,OAAO,GAAA,KAAQ,WAAW,GAAA,GAAM,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAC,CAAA;AAAA,aACtG,OAAA,CAAS,IAAA,IAAQ,EAAY,CAAA;AAAA,MACpC,CAAA;AACA,MAAA,MAAM,OAAA,GAAU,EAAE,GAAG,OAAA,EAAS,OAAA,EAAS,QAAA,EAAU,GAAI,KAAA,KAAU,MAAA,GAAY,EAAC,GAAI,EAAE,OAAM,EAAG;AAC3F,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAkC,CAAA,CAAE,KAAA,CAAM,MAAM,CAAA;AAAA,IAC/D,CAAC,CAAA;AAAA,EACH;AACF;AAGA,eAAsB,eAAA,CACpB,UAAA,EACA,OAAA,GAAwB,EAAC,EACe;AACxC,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,IAAIA,yBAAA,EAAQ;AAC/C,EAAA,MAAM,OAAA,CAAQ,eAAe,OAAO,CAAA;AACpC,EAAA,OAAO,IAAI,0BAAA,CAA2B,UAAA,EAAY,OAAO,CAAA;AAC3D","file":"snowflake.cjs","sourcesContent":["/**\n * Vericto for `snowflake-sdk`.\n *\n * `connection.execute()` returns its statement synchronously, but a Vericto check\n * is a network call, so the guarded `execute()` resolves the statement instead. The\n * options are the driver's own (`sqlText`, `binds`, `complete`, `streamResult`,\n * `parameters`...). When you pass `complete`, a Vericto rejection is delivered to it\n * as `err` (callback style) and the promise resolves `undefined`; without\n * `complete`, the promise rejects. `query()` is a promise-style shortcut.\n *\n * `sqlText` is sent to Vericto as written (`?` and `:1` binds included; bind values\n * are never sent). A multi-statement `sqlText` is evaluated as one request: a block\n * anywhere means nothing runs.\n */\nimport { Vericto } from './guard.js';\n\nexport const DIALECT = 'snowflake';\n\ntype Callback = (err: unknown, statement: unknown, rows?: unknown[]) => void;\n\n/** The part of a snowflake-sdk `Connection` this wrapper uses. */\nexport interface SnowflakeConnectionLike {\n execute(options: { sqlText: string; complete?: Callback }): unknown;\n}\n\ntype OptionsOf<C extends SnowflakeConnectionLike> = Parameters<C['execute']>[0];\ntype StatementOf<C extends SnowflakeConnectionLike> = ReturnType<C['execute']>;\n\nexport interface GuardOptions {\n /** The guard to use. Default: `new Vericto()` from the `VERICTO_*` environment. */\n vericto?: Vericto;\n}\n\nexport class GuardedSnowflakeConnection<C extends SnowflakeConnectionLike> {\n /** The underlying connection. SQL run through it directly is **not** checked. */\n readonly raw: C;\n readonly vericto: Vericto;\n\n constructor(connection: C, vericto: Vericto) {\n this.raw = connection;\n this.vericto = vericto;\n }\n\n /** Check `options.sqlText`, then `connection.execute()` the SQL to run. */\n async execute(options: OptionsOf<C>): Promise<StatementOf<C> | undefined> {\n let sqlText: string;\n try {\n sqlText = await this.vericto.prepare(options.sqlText, DIALECT);\n } catch (err) {\n if (typeof options.complete === 'function') {\n options.complete(err, undefined, undefined);\n return undefined;\n }\n throw err;\n }\n return this.raw.execute({ ...options, sqlText }) as StatementOf<C>;\n }\n\n /** Promise style: resolves the rows (not for `streamResult`). */\n query<Row = Record<string, unknown>>(\n sqlText: string,\n binds?: unknown,\n options: Record<string, unknown> = {},\n ): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n const complete: Callback = (err, _statement, rows) => {\n if (err) reject(err instanceof Error ? err : new Error(typeof err === 'string' ? err : JSON.stringify(err)));\n else resolve((rows ?? []) as Row[]);\n };\n const request = { ...options, sqlText, complete, ...(binds === undefined ? {} : { binds }) };\n this.execute(request as unknown as OptionsOf<C>).catch(reject);\n });\n }\n}\n\n/** Guard a snowflake-sdk connection (connected or not). */\nexport async function guardConnection<C extends SnowflakeConnectionLike>(\n connection: C,\n options: GuardOptions = {},\n): Promise<GuardedSnowflakeConnection<C>> {\n const vericto = options.vericto ?? new Vericto();\n await vericto.requireDialect(DIALECT);\n return new GuardedSnowflakeConnection(connection, vericto);\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Vericto } from './index.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vericto for `snowflake-sdk`.
|
|
5
|
+
*
|
|
6
|
+
* `connection.execute()` returns its statement synchronously, but a Vericto check
|
|
7
|
+
* is a network call, so the guarded `execute()` resolves the statement instead. The
|
|
8
|
+
* options are the driver's own (`sqlText`, `binds`, `complete`, `streamResult`,
|
|
9
|
+
* `parameters`...). When you pass `complete`, a Vericto rejection is delivered to it
|
|
10
|
+
* as `err` (callback style) and the promise resolves `undefined`; without
|
|
11
|
+
* `complete`, the promise rejects. `query()` is a promise-style shortcut.
|
|
12
|
+
*
|
|
13
|
+
* `sqlText` is sent to Vericto as written (`?` and `:1` binds included; bind values
|
|
14
|
+
* are never sent). A multi-statement `sqlText` is evaluated as one request: a block
|
|
15
|
+
* anywhere means nothing runs.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare const DIALECT = "snowflake";
|
|
19
|
+
type Callback = (err: unknown, statement: unknown, rows?: unknown[]) => void;
|
|
20
|
+
/** The part of a snowflake-sdk `Connection` this wrapper uses. */
|
|
21
|
+
interface SnowflakeConnectionLike {
|
|
22
|
+
execute(options: {
|
|
23
|
+
sqlText: string;
|
|
24
|
+
complete?: Callback;
|
|
25
|
+
}): unknown;
|
|
26
|
+
}
|
|
27
|
+
type OptionsOf<C extends SnowflakeConnectionLike> = Parameters<C['execute']>[0];
|
|
28
|
+
type StatementOf<C extends SnowflakeConnectionLike> = ReturnType<C['execute']>;
|
|
29
|
+
interface GuardOptions {
|
|
30
|
+
/** The guard to use. Default: `new Vericto()` from the `VERICTO_*` environment. */
|
|
31
|
+
vericto?: Vericto;
|
|
32
|
+
}
|
|
33
|
+
declare class GuardedSnowflakeConnection<C extends SnowflakeConnectionLike> {
|
|
34
|
+
/** The underlying connection. SQL run through it directly is **not** checked. */
|
|
35
|
+
readonly raw: C;
|
|
36
|
+
readonly vericto: Vericto;
|
|
37
|
+
constructor(connection: C, vericto: Vericto);
|
|
38
|
+
/** Check `options.sqlText`, then `connection.execute()` the SQL to run. */
|
|
39
|
+
execute(options: OptionsOf<C>): Promise<StatementOf<C> | undefined>;
|
|
40
|
+
/** Promise style: resolves the rows (not for `streamResult`). */
|
|
41
|
+
query<Row = Record<string, unknown>>(sqlText: string, binds?: unknown, options?: Record<string, unknown>): Promise<Row[]>;
|
|
42
|
+
}
|
|
43
|
+
/** Guard a snowflake-sdk connection (connected or not). */
|
|
44
|
+
declare function guardConnection<C extends SnowflakeConnectionLike>(connection: C, options?: GuardOptions): Promise<GuardedSnowflakeConnection<C>>;
|
|
45
|
+
|
|
46
|
+
export { DIALECT, GuardedSnowflakeConnection, guardConnection };
|
|
47
|
+
export type { GuardOptions, SnowflakeConnectionLike };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Vericto } from './index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vericto for `snowflake-sdk`.
|
|
5
|
+
*
|
|
6
|
+
* `connection.execute()` returns its statement synchronously, but a Vericto check
|
|
7
|
+
* is a network call, so the guarded `execute()` resolves the statement instead. The
|
|
8
|
+
* options are the driver's own (`sqlText`, `binds`, `complete`, `streamResult`,
|
|
9
|
+
* `parameters`...). When you pass `complete`, a Vericto rejection is delivered to it
|
|
10
|
+
* as `err` (callback style) and the promise resolves `undefined`; without
|
|
11
|
+
* `complete`, the promise rejects. `query()` is a promise-style shortcut.
|
|
12
|
+
*
|
|
13
|
+
* `sqlText` is sent to Vericto as written (`?` and `:1` binds included; bind values
|
|
14
|
+
* are never sent). A multi-statement `sqlText` is evaluated as one request: a block
|
|
15
|
+
* anywhere means nothing runs.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare const DIALECT = "snowflake";
|
|
19
|
+
type Callback = (err: unknown, statement: unknown, rows?: unknown[]) => void;
|
|
20
|
+
/** The part of a snowflake-sdk `Connection` this wrapper uses. */
|
|
21
|
+
interface SnowflakeConnectionLike {
|
|
22
|
+
execute(options: {
|
|
23
|
+
sqlText: string;
|
|
24
|
+
complete?: Callback;
|
|
25
|
+
}): unknown;
|
|
26
|
+
}
|
|
27
|
+
type OptionsOf<C extends SnowflakeConnectionLike> = Parameters<C['execute']>[0];
|
|
28
|
+
type StatementOf<C extends SnowflakeConnectionLike> = ReturnType<C['execute']>;
|
|
29
|
+
interface GuardOptions {
|
|
30
|
+
/** The guard to use. Default: `new Vericto()` from the `VERICTO_*` environment. */
|
|
31
|
+
vericto?: Vericto;
|
|
32
|
+
}
|
|
33
|
+
declare class GuardedSnowflakeConnection<C extends SnowflakeConnectionLike> {
|
|
34
|
+
/** The underlying connection. SQL run through it directly is **not** checked. */
|
|
35
|
+
readonly raw: C;
|
|
36
|
+
readonly vericto: Vericto;
|
|
37
|
+
constructor(connection: C, vericto: Vericto);
|
|
38
|
+
/** Check `options.sqlText`, then `connection.execute()` the SQL to run. */
|
|
39
|
+
execute(options: OptionsOf<C>): Promise<StatementOf<C> | undefined>;
|
|
40
|
+
/** Promise style: resolves the rows (not for `streamResult`). */
|
|
41
|
+
query<Row = Record<string, unknown>>(sqlText: string, binds?: unknown, options?: Record<string, unknown>): Promise<Row[]>;
|
|
42
|
+
}
|
|
43
|
+
/** Guard a snowflake-sdk connection (connected or not). */
|
|
44
|
+
declare function guardConnection<C extends SnowflakeConnectionLike>(connection: C, options?: GuardOptions): Promise<GuardedSnowflakeConnection<C>>;
|
|
45
|
+
|
|
46
|
+
export { DIALECT, GuardedSnowflakeConnection, guardConnection };
|
|
47
|
+
export type { GuardOptions, SnowflakeConnectionLike };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Vericto } from './chunk-I4I35HUY.js';
|
|
2
|
+
|
|
3
|
+
// src/snowflake.ts
|
|
4
|
+
var DIALECT = "snowflake";
|
|
5
|
+
var GuardedSnowflakeConnection = class {
|
|
6
|
+
/** The underlying connection. SQL run through it directly is **not** checked. */
|
|
7
|
+
raw;
|
|
8
|
+
vericto;
|
|
9
|
+
constructor(connection, vericto) {
|
|
10
|
+
this.raw = connection;
|
|
11
|
+
this.vericto = vericto;
|
|
12
|
+
}
|
|
13
|
+
/** Check `options.sqlText`, then `connection.execute()` the SQL to run. */
|
|
14
|
+
async execute(options) {
|
|
15
|
+
let sqlText;
|
|
16
|
+
try {
|
|
17
|
+
sqlText = await this.vericto.prepare(options.sqlText, DIALECT);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
if (typeof options.complete === "function") {
|
|
20
|
+
options.complete(err, void 0, void 0);
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
return this.raw.execute({ ...options, sqlText });
|
|
26
|
+
}
|
|
27
|
+
/** Promise style: resolves the rows (not for `streamResult`). */
|
|
28
|
+
query(sqlText, binds, options = {}) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const complete = (err, _statement, rows) => {
|
|
31
|
+
if (err) reject(err instanceof Error ? err : new Error(typeof err === "string" ? err : JSON.stringify(err)));
|
|
32
|
+
else resolve(rows ?? []);
|
|
33
|
+
};
|
|
34
|
+
const request = { ...options, sqlText, complete, ...binds === void 0 ? {} : { binds } };
|
|
35
|
+
this.execute(request).catch(reject);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
async function guardConnection(connection, options = {}) {
|
|
40
|
+
const vericto = options.vericto ?? new Vericto();
|
|
41
|
+
await vericto.requireDialect(DIALECT);
|
|
42
|
+
return new GuardedSnowflakeConnection(connection, vericto);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { DIALECT, GuardedSnowflakeConnection, guardConnection };
|
|
46
|
+
//# sourceMappingURL=snowflake.js.map
|
|
47
|
+
//# sourceMappingURL=snowflake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/snowflake.ts"],"names":[],"mappings":";;;AAgBO,IAAM,OAAA,GAAU;AAiBhB,IAAM,6BAAN,MAAoE;AAAA;AAAA,EAEhE,GAAA;AAAA,EACA,OAAA;AAAA,EAET,WAAA,CAAY,YAAe,OAAA,EAAkB;AAC3C,IAAA,IAAA,CAAK,GAAA,GAAM,UAAA;AACX,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,QAAQ,OAAA,EAA4D;AACxE,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,SAAS,OAAO,CAAA;AAAA,IAC/D,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,OAAO,OAAA,CAAQ,QAAA,KAAa,UAAA,EAAY;AAC1C,QAAA,OAAA,CAAQ,QAAA,CAAS,GAAA,EAAK,MAAA,EAAW,MAAS,CAAA;AAC1C,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AACA,IAAA,OAAO,KAAK,GAAA,CAAI,OAAA,CAAQ,EAAE,GAAG,OAAA,EAAS,SAAS,CAAA;AAAA,EACjD;AAAA;AAAA,EAGA,KAAA,CACE,OAAA,EACA,KAAA,EACA,OAAA,GAAmC,EAAC,EACpB;AAChB,IAAA,OAAO,IAAI,OAAA,CAAe,CAAC,OAAA,EAAS,MAAA,KAAW;AAC7C,MAAA,MAAM,QAAA,GAAqB,CAAC,GAAA,EAAK,UAAA,EAAY,IAAA,KAAS;AACpD,QAAA,IAAI,GAAA,EAAK,MAAA,CAAO,GAAA,YAAe,KAAA,GAAQ,MAAM,IAAI,KAAA,CAAM,OAAO,GAAA,KAAQ,WAAW,GAAA,GAAM,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAC,CAAA;AAAA,aACtG,OAAA,CAAS,IAAA,IAAQ,EAAY,CAAA;AAAA,MACpC,CAAA;AACA,MAAA,MAAM,OAAA,GAAU,EAAE,GAAG,OAAA,EAAS,OAAA,EAAS,QAAA,EAAU,GAAI,KAAA,KAAU,MAAA,GAAY,EAAC,GAAI,EAAE,OAAM,EAAG;AAC3F,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAkC,CAAA,CAAE,KAAA,CAAM,MAAM,CAAA;AAAA,IAC/D,CAAC,CAAA;AAAA,EACH;AACF;AAGA,eAAsB,eAAA,CACpB,UAAA,EACA,OAAA,GAAwB,EAAC,EACe;AACxC,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,IAAI,OAAA,EAAQ;AAC/C,EAAA,MAAM,OAAA,CAAQ,eAAe,OAAO,CAAA;AACpC,EAAA,OAAO,IAAI,0BAAA,CAA2B,UAAA,EAAY,OAAO,CAAA;AAC3D","file":"snowflake.js","sourcesContent":["/**\n * Vericto for `snowflake-sdk`.\n *\n * `connection.execute()` returns its statement synchronously, but a Vericto check\n * is a network call, so the guarded `execute()` resolves the statement instead. The\n * options are the driver's own (`sqlText`, `binds`, `complete`, `streamResult`,\n * `parameters`...). When you pass `complete`, a Vericto rejection is delivered to it\n * as `err` (callback style) and the promise resolves `undefined`; without\n * `complete`, the promise rejects. `query()` is a promise-style shortcut.\n *\n * `sqlText` is sent to Vericto as written (`?` and `:1` binds included; bind values\n * are never sent). A multi-statement `sqlText` is evaluated as one request: a block\n * anywhere means nothing runs.\n */\nimport { Vericto } from './guard.js';\n\nexport const DIALECT = 'snowflake';\n\ntype Callback = (err: unknown, statement: unknown, rows?: unknown[]) => void;\n\n/** The part of a snowflake-sdk `Connection` this wrapper uses. */\nexport interface SnowflakeConnectionLike {\n execute(options: { sqlText: string; complete?: Callback }): unknown;\n}\n\ntype OptionsOf<C extends SnowflakeConnectionLike> = Parameters<C['execute']>[0];\ntype StatementOf<C extends SnowflakeConnectionLike> = ReturnType<C['execute']>;\n\nexport interface GuardOptions {\n /** The guard to use. Default: `new Vericto()` from the `VERICTO_*` environment. */\n vericto?: Vericto;\n}\n\nexport class GuardedSnowflakeConnection<C extends SnowflakeConnectionLike> {\n /** The underlying connection. SQL run through it directly is **not** checked. */\n readonly raw: C;\n readonly vericto: Vericto;\n\n constructor(connection: C, vericto: Vericto) {\n this.raw = connection;\n this.vericto = vericto;\n }\n\n /** Check `options.sqlText`, then `connection.execute()` the SQL to run. */\n async execute(options: OptionsOf<C>): Promise<StatementOf<C> | undefined> {\n let sqlText: string;\n try {\n sqlText = await this.vericto.prepare(options.sqlText, DIALECT);\n } catch (err) {\n if (typeof options.complete === 'function') {\n options.complete(err, undefined, undefined);\n return undefined;\n }\n throw err;\n }\n return this.raw.execute({ ...options, sqlText }) as StatementOf<C>;\n }\n\n /** Promise style: resolves the rows (not for `streamResult`). */\n query<Row = Record<string, unknown>>(\n sqlText: string,\n binds?: unknown,\n options: Record<string, unknown> = {},\n ): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n const complete: Callback = (err, _statement, rows) => {\n if (err) reject(err instanceof Error ? err : new Error(typeof err === 'string' ? err : JSON.stringify(err)));\n else resolve((rows ?? []) as Row[]);\n };\n const request = { ...options, sqlText, complete, ...(binds === undefined ? {} : { binds }) };\n this.execute(request as unknown as OptionsOf<C>).catch(reject);\n });\n }\n}\n\n/** Guard a snowflake-sdk connection (connected or not). */\nexport async function guardConnection<C extends SnowflakeConnectionLike>(\n connection: C,\n options: GuardOptions = {},\n): Promise<GuardedSnowflakeConnection<C>> {\n const vericto = options.vericto ?? new Vericto();\n await vericto.requireDialect(DIALECT);\n return new GuardedSnowflakeConnection(connection, vericto);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,85 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vericto/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Official Vericto SDK for Node.js: check every SQL statement with Vericto before it runs. Wrappers for snowflake-sdk and better-sqlite3, LangChain.js SQL agents, and a client for the Vericto Runtime API.",
|
|
5
|
+
"license": "Elastic-2.0",
|
|
6
|
+
"author": "Vericto <security@vericto.com>",
|
|
7
|
+
"homepage": "https://vericto.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/vericto/vericto-node.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/vericto/vericto-node/issues",
|
|
13
|
+
"keywords": ["sql", "firewall", "security", "snowflake", "sqlite", "better-sqlite3", "ai-agents", "langchain", "vericto"],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
|
25
|
+
"require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
|
|
26
|
+
},
|
|
27
|
+
"./better-sqlite3": {
|
|
28
|
+
"import": { "types": "./dist/better-sqlite3.d.ts", "default": "./dist/better-sqlite3.js" },
|
|
29
|
+
"require": { "types": "./dist/better-sqlite3.d.cts", "default": "./dist/better-sqlite3.cjs" }
|
|
30
|
+
},
|
|
31
|
+
"./snowflake": {
|
|
32
|
+
"import": { "types": "./dist/snowflake.d.ts", "default": "./dist/snowflake.js" },
|
|
33
|
+
"require": { "types": "./dist/snowflake.d.cts", "default": "./dist/snowflake.cjs" }
|
|
34
|
+
},
|
|
35
|
+
"./langchain": {
|
|
36
|
+
"import": { "types": "./dist/langchain.d.ts", "default": "./dist/langchain.js" },
|
|
37
|
+
"require": { "types": "./dist/langchain.d.cts", "default": "./dist/langchain.cjs" }
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"files": ["dist", "README.md", "CHANGELOG.md", "SECURITY.md", "LICENSE"],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"lint": "eslint .",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"smoke": "node scripts/smoke.mjs",
|
|
48
|
+
"prepack": "tsup --silent"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@langchain/classic": ">=1.0.0 <2",
|
|
52
|
+
"@langchain/core": ">=1.0.0 <2",
|
|
53
|
+
"better-sqlite3": ">=9",
|
|
54
|
+
"snowflake-sdk": ">=1.9",
|
|
55
|
+
"typeorm": ">=0.3.20"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"@langchain/classic": { "optional": true },
|
|
59
|
+
"@langchain/core": { "optional": true },
|
|
60
|
+
"better-sqlite3": { "optional": true },
|
|
61
|
+
"snowflake-sdk": { "optional": true },
|
|
62
|
+
"typeorm": { "optional": true }
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@eslint/js": "10.0.1",
|
|
66
|
+
"@langchain/classic": "1.0.52",
|
|
67
|
+
"@langchain/core": "1.2.17",
|
|
68
|
+
"@types/better-sqlite3": "7.6.13",
|
|
69
|
+
"@types/node": "22.20.5",
|
|
70
|
+
"better-sqlite3": "13.0.3",
|
|
71
|
+
"eslint": "10.12.0",
|
|
72
|
+
"tsup": "9.0.0",
|
|
73
|
+
"typeorm": "1.1.2",
|
|
74
|
+
"typescript": "5.9.3",
|
|
75
|
+
"typescript-eslint": "8.71.2",
|
|
76
|
+
"vitest": "5.0.4"
|
|
77
|
+
},
|
|
78
|
+
"overrides": {
|
|
79
|
+
"typeorm": { "better-sqlite3": "$better-sqlite3" }
|
|
80
|
+
},
|
|
81
|
+
"publishConfig": {
|
|
82
|
+
"access": "public",
|
|
83
|
+
"provenance": true
|
|
84
|
+
}
|
|
85
|
+
}
|