@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this package are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-10-16
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The package is now **`@vericto/sdk`** (it was `@vericto/vericto`, never
|
|
14
|
+
published). Entry points: `@vericto/sdk`, `@vericto/sdk/snowflake`,
|
|
15
|
+
`@vericto/sdk/better-sqlite3`, `@vericto/sdk/langchain`.
|
|
16
|
+
|
|
17
|
+
- Documentation: the Vericto server now evaluates the `sqlite` dialect
|
|
18
|
+
(vericto-engine 4.1.0; `GET /api/v1/meta/config` lists it), so
|
|
19
|
+
`@vericto/sdk/better-sqlite3` and `@vericto/sdk/langchain` on SQLite work
|
|
20
|
+
against it. The 0.1.0 known limitation no longer applies to such servers.
|
|
21
|
+
Unchanged behaviour: `guardDatabase()` still asks `/meta/config` and rejects
|
|
22
|
+
with `VerictoUnsupportedDialectError`, before any SQL runs, against a server
|
|
23
|
+
that does not list `sqlite`.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `@vericto/sdk/langchain`, for LangChain.js 1.x (`@langchain/classic`):
|
|
28
|
+
- `VerictoSqlDatabase`, a `SqlDatabase` whose TypeORM data source checks every
|
|
29
|
+
`query()` (agent queries, `getTableInfo` sample rows, `SqlDatabaseChain`).
|
|
30
|
+
BLOCKED returns a message with the rule, reason and suggested fix for the
|
|
31
|
+
model (`onBlock: 'return'`, default) or throws `VerictoBlockedError`
|
|
32
|
+
(`onBlock: 'raise'`); masked queries run as the rewrite; Vericto
|
|
33
|
+
unavailable/config errors are thrown, never returned to the model. Dialect
|
|
34
|
+
from the TypeORM type, or `dialect`.
|
|
35
|
+
- `VerictoQuerySqlTool` and `VerictoSqlToolkit` (for `createSqlAgent`), which
|
|
36
|
+
let Vericto errors through instead of turning them into text.
|
|
37
|
+
- `VerictoCheckSqlTool`, a check-only tool that returns the verdict.
|
|
38
|
+
- `guardDataSource`, `dialectFromDataSource`, `blockedMessage`,
|
|
39
|
+
`describeVerdict` helpers.
|
|
40
|
+
- Optional peer dependencies `@langchain/classic`, `@langchain/core`, `typeorm`.
|
|
41
|
+
Tested with `@langchain/classic` 1.0.52, `@langchain/core` 1.2.17,
|
|
42
|
+
`typeorm` 1.1.2.
|
|
43
|
+
|
|
44
|
+
- `test/live/sqlite.live.test.ts`: an opt-in end-to-end test of
|
|
45
|
+
`@vericto/sdk/better-sqlite3` against a real Runtime API (allowed statements,
|
|
46
|
+
UPSERT, the SQLite rules VERICTO-098/099/100/101, DELETE without WHERE, scripts
|
|
47
|
+
with a blocked statement). Skipped unless `VERICTO_LIVE_SQLITE=1` and the
|
|
48
|
+
`VERICTO_*` variables are set; see "Live tests" in the README.
|
|
49
|
+
|
|
50
|
+
## [0.1.0] - unreleased (never published)
|
|
51
|
+
|
|
52
|
+
First release: Vericto checks in front of Node.js database drivers, evaluated by
|
|
53
|
+
the Vericto Runtime API (`POST /api/v1/proxy/query`).
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- `Vericto` guard: rejects with `VerictoBlockedError` on BLOCKED, runs the masked
|
|
58
|
+
`rewrittenQuery` for sensitive columns, logs FLAGGED / MONITORED / allowed
|
|
59
|
+
PARSE_ERROR verdicts and passes every verdict to `onVerdict`.
|
|
60
|
+
- Fail mode per guard: `closed` (default) rejects with `VerictoUnavailableError`
|
|
61
|
+
when Vericto cannot answer; `open` runs the statement and emits a
|
|
62
|
+
`VerictoFailOpenWarning` process warning. Configuration errors are never failed
|
|
63
|
+
open.
|
|
64
|
+
- `RuntimeApiEvaluator` on the global `fetch`: `VERICTO_*` environment
|
|
65
|
+
configuration as in vericto-cli, HTTPS-only key handling, timeouts, retries with
|
|
66
|
+
backoff on 5xx / 429 rate limits, `VerictoQuotaExceededError` for the monthly
|
|
67
|
+
quota, and dialect discovery through `GET /api/v1/meta/config`.
|
|
68
|
+
- `Evaluator` interface, so an embedded evaluator can be added later.
|
|
69
|
+
- `@vericto/vericto/snowflake`: `guardConnection()` for snowflake-sdk
|
|
70
|
+
(`execute` with the driver's options, callback or promise style, and `query`).
|
|
71
|
+
- `@vericto/vericto/better-sqlite3`: `guardDatabase()` (`prepare`, `exec`, `run`,
|
|
72
|
+
`all`, `get`).
|
|
73
|
+
- ESM and CommonJS builds with type declarations; Node.js 18+.
|
|
74
|
+
- Optional verdict cache (off by default).
|
|
75
|
+
|
|
76
|
+
### Known limitations
|
|
77
|
+
|
|
78
|
+
- The Vericto engine does not evaluate the `sqlite` dialect yet:
|
|
79
|
+
`guardDatabase()` rejects with `VerictoUnsupportedDialectError` until the server
|
|
80
|
+
lists it.
|
|
81
|
+
|
|
82
|
+
[0.2.0]: https://github.com/vericto/vericto-node/releases/tag/v0.2.0
|
|
83
|
+
[0.1.0]: https://github.com/vericto/vericto-node/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
URL: https://www.elastic.co/licensing/elastic-license
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
12
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
13
|
+
available, and prepare derivative works of the software, in each case subject to
|
|
14
|
+
the limitations and conditions below.
|
|
15
|
+
|
|
16
|
+
## Limitations
|
|
17
|
+
|
|
18
|
+
You may not provide the software to third parties as a hosted or managed
|
|
19
|
+
service, where the service provides users with access to any substantial set of
|
|
20
|
+
the features or functionality of the software.
|
|
21
|
+
|
|
22
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
23
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
24
|
+
software that is protected by the license key.
|
|
25
|
+
|
|
26
|
+
You may not alter, remove, or obscure any licensing, copyright, or other notices
|
|
27
|
+
of the licensor in the software. Any use of the licensor's trademarks is subject
|
|
28
|
+
to applicable law.
|
|
29
|
+
|
|
30
|
+
## Patents
|
|
31
|
+
|
|
32
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
33
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
34
|
+
sale, import and have imported the software, in each case subject to the
|
|
35
|
+
limitations and conditions in this license. This license does not cover any
|
|
36
|
+
patent claims that you cause to be infringed by modifications or additions to
|
|
37
|
+
the software. If you or your company make any written claim that the software
|
|
38
|
+
infringes or contributes to infringement of any patent, your patent license for
|
|
39
|
+
the software granted under these terms ends immediately. If your company makes
|
|
40
|
+
such a claim, your patent license ends immediately for work on behalf of your
|
|
41
|
+
company.
|
|
42
|
+
|
|
43
|
+
## Notices
|
|
44
|
+
|
|
45
|
+
You must ensure that anyone who gets a copy of any part of the software from you
|
|
46
|
+
also gets a copy of these terms.
|
|
47
|
+
|
|
48
|
+
If you modify the software, you must include in any modified copies of the
|
|
49
|
+
software prominent notices stating that you have modified the software.
|
|
50
|
+
|
|
51
|
+
## No Other Rights
|
|
52
|
+
|
|
53
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
54
|
+
these terms.
|
|
55
|
+
|
|
56
|
+
## Termination
|
|
57
|
+
|
|
58
|
+
If you use the software in violation of these terms, such use is not licensed,
|
|
59
|
+
and your licenses will automatically terminate. If the licensor provides you
|
|
60
|
+
with a notice of your violation, and you cease all violation of this license no
|
|
61
|
+
later than 30 days after you receive that notice, your licenses will be
|
|
62
|
+
reinstated retroactively. However, if you violate these terms after such
|
|
63
|
+
reinstatement, any additional violation of these terms will cause your licenses
|
|
64
|
+
to terminate automatically and permanently.
|
|
65
|
+
|
|
66
|
+
## No Liability
|
|
67
|
+
|
|
68
|
+
As far as the law allows, the software comes as is, without any warranty or
|
|
69
|
+
condition, and the licensor will not be liable to you for any damages arising
|
|
70
|
+
out of these terms or the use or nature of the software, under any kind of
|
|
71
|
+
legal claim.
|
|
72
|
+
|
|
73
|
+
## Definitions
|
|
74
|
+
|
|
75
|
+
The "licensor" is the entity offering these terms, and the "software" is the
|
|
76
|
+
software the licensor makes available under these terms, including any portion
|
|
77
|
+
of it.
|
|
78
|
+
|
|
79
|
+
"you" refers to the individual or entity agreeing to these terms.
|
|
80
|
+
|
|
81
|
+
"your company" is any legal entity, sole proprietorship, or other kind of
|
|
82
|
+
organization that you work for, plus all organizations that have control over,
|
|
83
|
+
are under the control of, or are under common control with that organization.
|
|
84
|
+
"control" means ownership of substantially all the assets of an entity, or the
|
|
85
|
+
power to direct its management and policies by vote, contract, or otherwise.
|
|
86
|
+
Control can be direct or indirect.
|
|
87
|
+
|
|
88
|
+
"your licenses" are all the licenses granted to you for the software under these
|
|
89
|
+
terms.
|
|
90
|
+
|
|
91
|
+
"use" means anything you do with the software requiring one of your licenses.
|
|
92
|
+
|
|
93
|
+
"trademark" means trademarks, service marks, and similar rights.
|
package/README.md
CHANGED
|
@@ -1,3 +1,378 @@
|
|
|
1
|
-
|
|
1
|
+
Official Vericto SDK for Node.js: check SQL before it runs (snowflake-sdk, better-sqlite3, LangChain.js SQL agents and chains, and any driver through the Runtime API client).
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @vericto/sdk
|
|
4
|
+
|
|
5
|
+
> Check every SQL statement with [Vericto](https://vericto.com) **before** it runs:
|
|
6
|
+
> thin wrappers for `snowflake-sdk` and `better-sqlite3`, a LangChain.js
|
|
7
|
+
> integration for SQL agents, and a client for the Vericto Runtime API you can put
|
|
8
|
+
> in front of any driver.
|
|
9
|
+
|
|
10
|
+
Vericto is a deterministic SQL firewall: it parses each statement into an AST and
|
|
11
|
+
evaluates it against your workspace's rules (destructive statements, missing
|
|
12
|
+
`WHERE`/`LIMIT`, risky DDL, data exposure, sensitive columns, agent allowlists).
|
|
13
|
+
This package checks SQL before your driver runs it: blocked statements reject
|
|
14
|
+
before they reach the database, flagged ones run and are reported, and statements
|
|
15
|
+
that read masked columns run as the masked rewrite Vericto returns.
|
|
16
|
+
|
|
17
|
+
It is a thin client of the **Vericto Runtime API** (`POST /api/v1/proxy/query`).
|
|
18
|
+
Evaluation happens in your Vericto workspace; nothing is evaluated locally in this
|
|
19
|
+
release (see [Evaluators](#evaluators)).
|
|
20
|
+
|
|
21
|
+
- TypeScript types, ESM and CommonJS builds, Node.js 18+ (uses the global `fetch`).
|
|
22
|
+
- No runtime dependencies. Drivers and LangChain are optional peer dependencies
|
|
23
|
+
and separate entry points: importing `@vericto/sdk` never loads them.
|
|
24
|
+
|
|
25
|
+
## Integrations
|
|
26
|
+
|
|
27
|
+
| Integration | Entry point | Peer dependencies | Status |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| snowflake-sdk | `@vericto/sdk/snowflake` | `snowflake-sdk` >= 1.9 | 0.1.0 |
|
|
30
|
+
| better-sqlite3 | `@vericto/sdk/better-sqlite3` | `better-sqlite3` >= 9 | 0.1.0 (see [SQLite status](#sqlite-status)) |
|
|
31
|
+
| LangChain.js SQL agents, tools and chains | `@vericto/sdk/langchain` | `@langchain/classic` 1.x, `@langchain/core` 1.x, `typeorm` | 0.2.0 |
|
|
32
|
+
| Any other driver | `@vericto/sdk` (`Vericto.enforce` / `prepare`) | none | 0.1.0 |
|
|
33
|
+
|
|
34
|
+
Python (snowflake-connector-python, sqlite3, SQLAlchemy, LangChain, LlamaIndex,
|
|
35
|
+
dbt and Airflow) lives in [vericto-python](https://github.com/vericto/vericto-python).
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @vericto/sdk
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configure
|
|
44
|
+
|
|
45
|
+
Create an API key with the **`proxy:evaluate`** scope in the Vericto dashboard
|
|
46
|
+
(API keys) and register the database you are protecting. Then:
|
|
47
|
+
|
|
48
|
+
| Variable | Option | Meaning | Default |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `VERICTO_API_KEY` | `apiKey` | API key (`vtro_...`), scope `proxy:evaluate` | required |
|
|
51
|
+
| `VERICTO_DATABASE_ID` | `databaseId` | The registered database (UUID): its rules, sensitive-column tags and agent allowlists apply | required |
|
|
52
|
+
| `VERICTO_API_URL` | `apiUrl` | API base URL | `https://api.vericto.com` |
|
|
53
|
+
| `VERICTO_FAIL_MODE` | `failMode` | `closed` or `open` (see [Fail mode](#fail-mode)) | `closed` |
|
|
54
|
+
| `VERICTO_TIMEOUT` | `timeoutMs` | Per-attempt timeout (env in seconds, option in ms) | 5 s |
|
|
55
|
+
|
|
56
|
+
The key is sent only in the `X-API-Key` header, only over HTTPS (plain HTTP is
|
|
57
|
+
accepted for localhost alone), and never appears in messages, `toString()` or
|
|
58
|
+
`JSON.stringify()`. To trust an extra CA, use Node's `NODE_EXTRA_CA_CERTS`.
|
|
59
|
+
|
|
60
|
+
## Quickstart
|
|
61
|
+
|
|
62
|
+
### snowflake-sdk
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import snowflake from 'snowflake-sdk';
|
|
66
|
+
import { VerictoBlockedError } from '@vericto/sdk';
|
|
67
|
+
import { guardConnection } from '@vericto/sdk/snowflake';
|
|
68
|
+
|
|
69
|
+
const raw = snowflake.createConnection({ account, username, authenticator: 'SNOWFLAKE_JWT', privateKeyPath });
|
|
70
|
+
await raw.connectAsync();
|
|
71
|
+
const conn = await guardConnection(raw);
|
|
72
|
+
|
|
73
|
+
const rows = await conn.query('SELECT id, email FROM customers WHERE id = ?', [42]);
|
|
74
|
+
|
|
75
|
+
// Callback style, with the driver's own options:
|
|
76
|
+
await conn.execute({
|
|
77
|
+
sqlText: 'DELETE FROM customers',
|
|
78
|
+
complete: (err, stmt, rows) => {
|
|
79
|
+
// err is a VerictoBlockedError: the statement never reached Snowflake
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`connection.execute()` returns its statement synchronously, but a Vericto check is
|
|
85
|
+
a network call, so the guarded `execute()` resolves the statement. With
|
|
86
|
+
`complete`, a Vericto rejection is delivered to it as `err` (and the promise
|
|
87
|
+
resolves `undefined`); without it, the promise rejects. A multi-statement
|
|
88
|
+
`sqlText` (`MULTI_STATEMENT_COUNT`) is evaluated as one request: a block anywhere
|
|
89
|
+
means nothing runs.
|
|
90
|
+
|
|
91
|
+
### better-sqlite3
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import Database from 'better-sqlite3';
|
|
95
|
+
import { guardDatabase } from '@vericto/sdk/better-sqlite3';
|
|
96
|
+
|
|
97
|
+
const db = await guardDatabase(new Database('app.db'));
|
|
98
|
+
|
|
99
|
+
const expire = await db.prepare('DELETE FROM sessions WHERE expires_at < ?'); // checked once
|
|
100
|
+
expire.run(Date.now()); // synchronous, as usual
|
|
101
|
+
|
|
102
|
+
await db.run('INSERT INTO audit (event) VALUES (?)', 'login');
|
|
103
|
+
const user = await db.get('SELECT * FROM users WHERE id = ?', 1);
|
|
104
|
+
await db.exec(migrationSql); // whole script checked first
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
better-sqlite3 is synchronous, so the SQL-carrying entry points (`prepare`,
|
|
108
|
+
`exec`, and the `run`/`all`/`get` shortcuts) become async. A prepared statement's
|
|
109
|
+
SQL cannot change, so its `run`/`all`/`get`/`iterate` stay synchronous: prepare
|
|
110
|
+
statements before `db.raw.transaction(...)` and use them inside it. See
|
|
111
|
+
[SQLite status](#sqlite-status).
|
|
112
|
+
|
|
113
|
+
### LangChain.js (SQL agents, tools and chains)
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install @vericto/sdk @langchain/classic @langchain/core typeorm
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { DataSource } from 'typeorm';
|
|
121
|
+
import { createSqlAgent } from '@langchain/classic/agents/toolkits/sql';
|
|
122
|
+
import { VerictoSqlDatabase, VerictoSqlToolkit, VerictoCheckSqlTool } from '@vericto/sdk/langchain';
|
|
123
|
+
|
|
124
|
+
const db = await VerictoSqlDatabase.fromDataSourceParams({
|
|
125
|
+
appDataSource: new DataSource({ type: 'postgres', url: process.env.DATABASE_URL }),
|
|
126
|
+
// vericto: new Vericto({ ... }), dialect: 'postgres', onBlock: 'return' | 'raise'
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const agent = createSqlAgent(llm, new VerictoSqlToolkit(db, llm));
|
|
130
|
+
await agent.invoke({ input: 'How many orders shipped last week?' });
|
|
131
|
+
|
|
132
|
+
// Or give an agent a check-only tool (nothing is executed):
|
|
133
|
+
const check = new VerictoCheckSqlTool({ db });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`VerictoSqlDatabase` is a LangChain `SqlDatabase` (`@langchain/classic/sql_db`;
|
|
137
|
+
`langchain/sql_db` no longer exists in LangChain.js 1.x), so it is accepted
|
|
138
|
+
wherever one is: `QuerySqlTool`, `SqlToolkit`/`createSqlAgent`,
|
|
139
|
+
`SqlDatabaseChain`. Its TypeORM data source is wrapped so that every `query()` is
|
|
140
|
+
checked: the agent's queries, the sample rows `getTableInfo` puts in the prompt,
|
|
141
|
+
and the SQL `SqlDatabaseChain` generates.
|
|
142
|
+
|
|
143
|
+
| Verdict | What the agent sees |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `BLOCKED` | `onBlock: 'return'` (default): the tool returns a message with the rule, severity, reason and suggested fix, and the query does not run, so the model can correct itself. `onBlock: 'raise'`: `VerictoBlockedError`. |
|
|
146
|
+
| `FLAGGED` / `MONITORED` | The query runs; the verdict is logged and passed to `onVerdict`. |
|
|
147
|
+
| mask | The masked rewrite runs, never the original: the model only sees masked values (sample rows included). |
|
|
148
|
+
| no verdict, config error | Thrown (`VerictoUnavailableError`, `VerictoConfigError`), never returned to the model; the [fail mode](#fail-mode) applies. |
|
|
149
|
+
|
|
150
|
+
- The dialect comes from the TypeORM type (`postgres`/`aurora-postgres`,
|
|
151
|
+
`mysql`/`mariadb`/`aurora-mysql`, `mssql`, `oracle`, `sqlite`/`better-sqlite3`/
|
|
152
|
+
`sqljs`); other types need `dialect`. The factory fails fast when the server does
|
|
153
|
+
not evaluate it.
|
|
154
|
+
- `VerictoSqlToolkit` is `SqlToolkit` with `VerictoQuerySqlTool`: the stock
|
|
155
|
+
`QuerySqlTool` turns every error into text for the model, which would also
|
|
156
|
+
hand it "Vericto unavailable" (and the block under `onBlock: 'raise'`).
|
|
157
|
+
`VerictoQuerySqlTool` rethrows Vericto errors and still returns SQL errors as text.
|
|
158
|
+
- A blocked sample-rows query is left out of the table info (LangChain logs the
|
|
159
|
+
error and carries on).
|
|
160
|
+
- `SqlDatabaseChain` swallows query errors itself (it logs them and answers from
|
|
161
|
+
an empty result), so there a block shows up as "no result" rather than as the
|
|
162
|
+
message.
|
|
163
|
+
- LangChain's schema introspection in `fromDataSourceParams` (its catalogue read
|
|
164
|
+
of tables and columns) runs on the raw data source. `db.rawDataSource` is the
|
|
165
|
+
unguarded data source: SQL run through it is not checked.
|
|
166
|
+
- `VerictoCheckSqlTool` is a Runtime API evaluation like any other: it counts
|
|
167
|
+
against the runtime quota and is in the audit trail.
|
|
168
|
+
|
|
169
|
+
### Any driver, by hand
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { Vericto, sqlToExecute } from '@vericto/sdk';
|
|
173
|
+
|
|
174
|
+
const vericto = new Vericto(); // one per process
|
|
175
|
+
const verdict = await vericto.enforce(sql, 'postgres'); // rejects with VerictoBlockedError
|
|
176
|
+
await pool.query(sqlToExecute(verdict)); // the masked rewrite, if any
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`vericto.prepare(sql, dialect)` resolves the SQL text directly; `vericto.check()`
|
|
180
|
+
resolves the verdict without applying it.
|
|
181
|
+
|
|
182
|
+
## What each decision does
|
|
183
|
+
|
|
184
|
+
| Verdict | What the wrapper does |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `ALLOWED` | Runs the statement. |
|
|
187
|
+
| `FLAGGED` | Runs it, logs a warning, calls `onVerdict`. |
|
|
188
|
+
| `MONITORED` | Runs it, logs at info, calls `onVerdict`. |
|
|
189
|
+
| `PARSE_ERROR` (allowed) | The workspace's `parse_error_action` let it through: runs it and logs a warning. When the workspace blocks unparseable SQL, the server answers `BLOCKED`. |
|
|
190
|
+
| `BLOCKED` | Rejects with `VerictoBlockedError` (`verdict`, `findings`, `suggestedFix`) **before** the driver is called. |
|
|
191
|
+
| mask (`rewrittenQuery`) | Runs `rewrittenQuery` **instead of** your SQL: the sensitive columns come back masked. Never the original. |
|
|
192
|
+
| no verdict | The [fail mode](#fail-mode) decides. |
|
|
193
|
+
|
|
194
|
+
`onVerdict` receives every verdict, blocked and unevaluated ones included. The
|
|
195
|
+
default logger is `console` for warnings and errors (`logger: false` silences it,
|
|
196
|
+
or pass your own `{ info, warn, error }`). Log lines carry the rule code, severity
|
|
197
|
+
and Vericto event id, **never the SQL text**. Every evaluation is recorded in the
|
|
198
|
+
Vericto audit trail.
|
|
199
|
+
|
|
200
|
+
## Fail mode
|
|
201
|
+
|
|
202
|
+
When Vericto cannot answer (network error, timeout, HTTP 5xx, rate limit after
|
|
203
|
+
retries, or the monthly quota `QUOTA_EXCEEDED`):
|
|
204
|
+
|
|
205
|
+
- `failMode: 'closed'` (default): reject with `VerictoUnavailableError`; the
|
|
206
|
+
statement does not run.
|
|
207
|
+
- `failMode: 'open'`: run the statement unevaluated, emit a process warning of type
|
|
208
|
+
`VerictoFailOpenWarning`, log it, and pass an `UNEVALUATED` verdict to `onVerdict`.
|
|
209
|
+
|
|
210
|
+
Configuration errors are **never** failed open: a missing or revoked key (401), a
|
|
211
|
+
key without the scope or for another database (403), a malformed request (400) or
|
|
212
|
+
an unsupported dialect reject with `VerictoConfigError` in both modes.
|
|
213
|
+
|
|
214
|
+
Retries: network errors, timeouts, 5xx and 429 `RATE_LIMIT_EXCEEDED` are retried
|
|
215
|
+
`maxRetries` times (default 2) with exponential backoff and jitter (capped at 2 s,
|
|
216
|
+
honouring `Retry-After`). `QUOTA_EXCEEDED` is not retried.
|
|
217
|
+
|
|
218
|
+
## Parameters
|
|
219
|
+
|
|
220
|
+
Bind values are **never** sent to Vericto. The SQL text goes as written,
|
|
221
|
+
placeholders included (`?`, `:1` for Snowflake; `?`, `:name`, `@name`, `$name` for
|
|
222
|
+
SQLite), and the driver binds the values afterwards.
|
|
223
|
+
|
|
224
|
+
## Caching
|
|
225
|
+
|
|
226
|
+
Off by default (`cacheTtlMs: 0`). With `cacheTtlMs: 30_000`, identical
|
|
227
|
+
`(sql, dialect)` pairs reuse a verdict for 30 seconds (LRU, `cacheSize: 1024`), and
|
|
228
|
+
the cache is dropped when a response reports a new `ruleset_version`. A cached hit
|
|
229
|
+
is not evaluated, so it is **not in the Vericto audit trail**, and rule changes
|
|
230
|
+
apply only when the entry expires (the Runtime API does not report the ruleset
|
|
231
|
+
version today). Enable it only for hot, repetitive statements, with a short TTL.
|
|
232
|
+
|
|
233
|
+
## SQLite status
|
|
234
|
+
|
|
235
|
+
SQLite has no server, so there is no Vericto proxy for it: this wrapper is the
|
|
236
|
+
enforcement point. The **Vericto server evaluates the `sqlite` dialect**
|
|
237
|
+
(vericto-engine 4.1.0+; `GET /api/v1/meta/config` lists `postgres`, `mysql`,
|
|
238
|
+
`oracle`, `mssql`, `snowflake`, `sqlite`), including SQLite-specific rules such
|
|
239
|
+
as `ATTACH DATABASE` (VERICTO-098), `PRAGMA foreign_keys = OFF` (VERICTO-099),
|
|
240
|
+
`load_extension()` (VERICTO-100) and `VACUUM INTO` (VERICTO-101).
|
|
241
|
+
|
|
242
|
+
`guardDatabase()` still asks the server first and rejects with
|
|
243
|
+
`VerictoUnsupportedDialectError`, before any SQL runs, if it does not list
|
|
244
|
+
`sqlite` (for example an older self-hosted server). There is deliberately no
|
|
245
|
+
fallback to another dialect. See [Live tests](#live-tests) for an end-to-end
|
|
246
|
+
check against a real server.
|
|
247
|
+
|
|
248
|
+
## Enforcement boundary
|
|
249
|
+
|
|
250
|
+
The wrapper protects the SQL that goes through it. `db.raw`, `conn.raw`, another
|
|
251
|
+
connection or another process with the same credentials are not checked. Pair the
|
|
252
|
+
wrapper with database-side least privilege (and, for Snowflake, a network policy)
|
|
253
|
+
when the guarantee matters.
|
|
254
|
+
|
|
255
|
+
## Errors
|
|
256
|
+
|
|
257
|
+
All extend `VerictoError`.
|
|
258
|
+
|
|
259
|
+
| Error | When |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `VerictoBlockedError` | Vericto blocked the statement. `.verdict`, `.findings`, `.suggestedFix`, `.sql`. |
|
|
262
|
+
| `VerictoUnavailableError` | No verdict and `failMode: 'closed'`. |
|
|
263
|
+
| `VerictoQuotaExceededError` | Extends `VerictoUnavailableError`: monthly quota used (`.resetsAt`). |
|
|
264
|
+
| `VerictoConfigError` | Key, scope, database id, URL or request problem. Thrown in both fail modes. |
|
|
265
|
+
| `VerictoUnsupportedDialectError` | Extends `VerictoConfigError`: the server does not evaluate that dialect. |
|
|
266
|
+
|
|
267
|
+
## Evaluators
|
|
268
|
+
|
|
269
|
+
`Vericto` takes an `evaluator`: any object with `evaluate(sql, dialect)` and
|
|
270
|
+
`supportedDialects()`. This release ships `RuntimeApiEvaluator`. An embedded
|
|
271
|
+
evaluator (the Vericto engine in-process, for example WASM, with synced rules) can
|
|
272
|
+
be added later behind the same interface without changing the wrappers.
|
|
273
|
+
|
|
274
|
+
## Integrations roadmap
|
|
275
|
+
|
|
276
|
+
Not implemented yet; each is meant to be a thin layer over `Vericto.enforce`:
|
|
277
|
+
|
|
278
|
+
- **LlamaIndex.TS** SQL tools.
|
|
279
|
+
- **`pg` and `mysql2`**: `pool.query` wrappers (the TCP proxy already covers
|
|
280
|
+
PostgreSQL and MySQL without code changes).
|
|
281
|
+
- **Cloudflare D1** Workers binding helper.
|
|
282
|
+
- **Embedded evaluator** (WASM).
|
|
283
|
+
|
|
284
|
+
## Development
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm ci
|
|
288
|
+
npm run lint && npm run typecheck
|
|
289
|
+
npm test # vitest: local Runtime API stand-in, real better-sqlite3, fake snowflake-sdk,
|
|
290
|
+
# real LangChain.js + TypeORM with a scripted LLM (no model calls)
|
|
291
|
+
npm run build # tsup → dist/ (ESM + CJS + .d.ts)
|
|
292
|
+
npm run smoke # the built package, ESM and CJS, on any Node >= 18
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Development needs Node 22+ (the tooling); the published package runs on Node 18+.
|
|
296
|
+
Nothing in the test suite connects to Snowflake.
|
|
297
|
+
|
|
298
|
+
### Live tests
|
|
299
|
+
|
|
300
|
+
`test/live/sqlite.live.test.ts` is an opt-in end-to-end test of
|
|
301
|
+
`@vericto/sdk/better-sqlite3` against a **real Vericto Runtime API** and a real
|
|
302
|
+
SQLite file in a temporary directory. It is skipped unless the variables below
|
|
303
|
+
are set, and never runs in CI. Benign statements (`CREATE TABLE`, `INSERT` with
|
|
304
|
+
`?` binds, UPSERT `ON CONFLICT ... DO UPDATE`, `SELECT`, `DELETE ... WHERE`) must
|
|
305
|
+
run; `DELETE` without `WHERE` (VERICTO-001), `ATTACH DATABASE` (VERICTO-098),
|
|
306
|
+
`load_extension()` (VERICTO-100) and `VACUUM INTO` (VERICTO-101) must reject with
|
|
307
|
+
`VerictoBlockedError` before SQLite sees them (checked with better-sqlite3's
|
|
308
|
+
`verbose` trace, the file's SHA-256 and the absence of the attached/copy files);
|
|
309
|
+
`PRAGMA foreign_keys = OFF` (VERICTO-099) must run and log a FLAGGED warning; an
|
|
310
|
+
`exec` script with a blocked statement in the middle must run none of it. The
|
|
311
|
+
database's workspace must use the default rules; a run makes about 25
|
|
312
|
+
evaluations, counted against the workspace's monthly quota.
|
|
313
|
+
|
|
314
|
+
| Variable | Value |
|
|
315
|
+
|---|---|
|
|
316
|
+
| `VERICTO_LIVE_SQLITE` | `1` |
|
|
317
|
+
| `VERICTO_API_KEY` | a `vtro_...` key with the `proxy:evaluate` scope |
|
|
318
|
+
| `VERICTO_API_URL` | `https://api-staging.vericto.com` (or another server that lists `sqlite`) |
|
|
319
|
+
| `VERICTO_DATABASE_ID` | the id of a database of dialect `sqlite` in that workspace |
|
|
320
|
+
|
|
321
|
+
On the host (Node 22+):
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
export VERICTO_LIVE_SQLITE=1 VERICTO_API_URL=https://api-staging.vericto.com
|
|
325
|
+
export VERICTO_API_KEY=vtro_... VERICTO_DATABASE_ID=...
|
|
326
|
+
npm ci && npx vitest run test/live
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Or in a throwaway `node:22-slim` container (the repository is mounted read-only
|
|
330
|
+
and copied; better-sqlite3 installs its prebuilt linux binary; the SQLite file
|
|
331
|
+
never leaves the container):
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
docker run --rm -v "$PWD":/src:ro \
|
|
335
|
+
-e VERICTO_LIVE_SQLITE=1 -e VERICTO_API_KEY -e VERICTO_API_URL -e VERICTO_DATABASE_ID \
|
|
336
|
+
node:22-slim sh -c 'mkdir /work && cd /src && tar --exclude=node_modules --exclude=dist -cf - . | tar -xf - -C /work \
|
|
337
|
+
&& cd /work && npm ci --no-audit --no-fund && npx vitest run test/live'
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
A skipped run (`7 skipped`) means a variable is missing.
|
|
341
|
+
|
|
342
|
+
## Security
|
|
343
|
+
|
|
344
|
+
See [SECURITY.md](SECURITY.md). Report vulnerabilities to security@vericto.com,
|
|
345
|
+
not in public issues.
|
|
346
|
+
|
|
347
|
+
## License
|
|
348
|
+
|
|
349
|
+
[Elastic License 2.0](LICENSE).
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## En español
|
|
354
|
+
|
|
355
|
+
`@vericto/sdk` (antes `@vericto/vericto`, nunca publicado) valida cada sentencia
|
|
356
|
+
SQL con Vericto **antes** de ejecutarla: wrappers para `snowflake-sdk` y
|
|
357
|
+
`better-sqlite3`, una integración con LangChain.js (`@vericto/sdk/langchain`) y un
|
|
358
|
+
cliente de la Runtime API para cualquier otro driver. En LangChain.js, una consulta
|
|
359
|
+
bloqueada devuelve al agente un mensaje con la regla y la corrección sugerida (o
|
|
360
|
+
lanza `VerictoBlockedError` con `onBlock: 'raise'`), y las consultas con columnas
|
|
361
|
+
enmascaradas se ejecutan reescritas.
|
|
362
|
+
|
|
363
|
+
- **BLOCKED**: rechaza con `VerictoBlockedError` (hallazgos y corrección sugerida) y
|
|
364
|
+
la sentencia no llega a la base de datos.
|
|
365
|
+
- **FLAGGED / MONITORED**: se ejecuta y se registra (`onVerdict`, logger).
|
|
366
|
+
- **Máscara de columnas sensibles**: se ejecuta `rewrittenQuery`, nunca la original.
|
|
367
|
+
- **Sin respuesta de Vericto**: `failMode: 'closed'` (por defecto) bloquea; `'open'`
|
|
368
|
+
ejecuta y emite el aviso `VerictoFailOpenWarning`. Los errores de configuración
|
|
369
|
+
nunca se dejan pasar.
|
|
370
|
+
|
|
371
|
+
Configuración: `VERICTO_API_KEY` (clave `vtro_...` con el scope `proxy:evaluate`),
|
|
372
|
+
`VERICTO_DATABASE_ID` y, opcionalmente, `VERICTO_API_URL`, `VERICTO_FAIL_MODE`,
|
|
373
|
+
`VERICTO_TIMEOUT`. Los valores de los parámetros nunca se envían a Vericto.
|
|
374
|
+
|
|
375
|
+
SQLite: el servidor de Vericto evalúa el dialecto `sqlite` (vericto-engine
|
|
376
|
+
4.1.0+). `guardDatabase()` igual consulta `GET /api/v1/meta/config` y, si un
|
|
377
|
+
servidor no lo anuncia, rechaza con `VerictoUnsupportedDialectError` sin ejecutar
|
|
378
|
+
nada.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
Vericto is a security product. We take vulnerabilities seriously and
|
|
4
|
+
appreciate responsible disclosure.
|
|
5
|
+
|
|
6
|
+
## Reporting a vulnerability
|
|
7
|
+
|
|
8
|
+
**Do not open a public GitHub issue for security vulnerabilities.**
|
|
9
|
+
|
|
10
|
+
Report it privately, by either channel:
|
|
11
|
+
|
|
12
|
+
- Email **security@vericto.com**.
|
|
13
|
+
- Open a private report on GitHub:
|
|
14
|
+
[Report a vulnerability](https://github.com/vericto/vericto-node/security/advisories/new)
|
|
15
|
+
(Security tab → "Report a vulnerability"). Only the maintainers can see it.
|
|
16
|
+
|
|
17
|
+
Include:
|
|
18
|
+
|
|
19
|
+
- A description of the vulnerability and its impact.
|
|
20
|
+
- Steps to reproduce.
|
|
21
|
+
- The affected version (`npm ls @vericto/sdk`)
|
|
22
|
+
and the driver involved.
|
|
23
|
+
- Any suggested remediation.
|
|
24
|
+
|
|
25
|
+
You will receive an acknowledgement within **48 hours** and a status update
|
|
26
|
+
within **5 business days**.
|
|
27
|
+
|
|
28
|
+
## Scope
|
|
29
|
+
|
|
30
|
+
High-priority issues for this package include:
|
|
31
|
+
|
|
32
|
+
- **Credential exposure**: the API key written, logged or sent anywhere other
|
|
33
|
+
than the configured Vericto API over TLS.
|
|
34
|
+
- **Guard bypass**: a path through a wrapped connection that executes SQL Vericto
|
|
35
|
+
did not evaluate, or blocked, outside an explicit `failMode: 'open'`; or a masked
|
|
36
|
+
statement that runs the original SQL instead of `rewrittenQuery`.
|
|
37
|
+
- **Data leakage to Vericto**: bind values sent to the API.
|
|
38
|
+
|
|
39
|
+
- **Supply chain**: a published package that was not built by this repository's
|
|
40
|
+
release workflow (every release carries npm provenance).
|
|
41
|
+
|
|
42
|
+
Issues in the SQL evaluation itself belong to
|
|
43
|
+
[vericto-engine](https://github.com/vericto/vericto-engine/security).
|
|
44
|
+
|
|
45
|
+
## Disclosure process
|
|
46
|
+
|
|
47
|
+
1. Report received and acknowledged (within 48h).
|
|
48
|
+
2. We confirm and assess severity.
|
|
49
|
+
3. We develop and test a fix.
|
|
50
|
+
4. We release the fix and credit you (unless you prefer to remain anonymous).
|
|
51
|
+
5. Public disclosure after users have had reasonable time to update.
|
|
52
|
+
|
|
53
|
+
## Supported versions
|
|
54
|
+
|
|
55
|
+
Security fixes are applied to the latest released version. Older versions are
|
|
56
|
+
patched at the maintainers' discretion.
|
|
57
|
+
|
|
58
|
+
Thank you for helping keep Vericto and its users safe.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkHFL7W4WC_cjs = require('./chunk-HFL7W4WC.cjs');
|
|
4
|
+
|
|
5
|
+
// src/better-sqlite3.ts
|
|
6
|
+
var DIALECT = "sqlite";
|
|
7
|
+
var GuardedDatabase = class {
|
|
8
|
+
/** The underlying database. SQL run through it directly is **not** checked. */
|
|
9
|
+
raw;
|
|
10
|
+
vericto;
|
|
11
|
+
constructor(db, vericto) {
|
|
12
|
+
this.raw = db;
|
|
13
|
+
this.vericto = vericto;
|
|
14
|
+
}
|
|
15
|
+
/** Check `sql`, then `db.prepare()` the SQL to run (the masked rewrite, if any). */
|
|
16
|
+
async prepare(sql) {
|
|
17
|
+
const final = await this.vericto.prepare(sql, DIALECT);
|
|
18
|
+
return this.raw.prepare(final);
|
|
19
|
+
}
|
|
20
|
+
/** Like `prepare`, also resolving the verdict (decision, rule, event id). */
|
|
21
|
+
async prepareWithVerdict(sql) {
|
|
22
|
+
const verdict = await this.vericto.enforce(sql, DIALECT);
|
|
23
|
+
return { statement: this.raw.prepare(chunkHFL7W4WC_cjs.sqlToExecute(verdict)), verdict };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check a script, then `db.exec()` it. The script is evaluated as one request (the
|
|
27
|
+
* engine evaluates every statement in it); a block anywhere means nothing runs.
|
|
28
|
+
*/
|
|
29
|
+
async exec(sql) {
|
|
30
|
+
const final = await this.vericto.prepare(sql, DIALECT);
|
|
31
|
+
this.raw.exec(final);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
/** `prepare(sql)` then `statement.run(...params)`. */
|
|
35
|
+
async run(sql, ...params) {
|
|
36
|
+
return call(await this.prepare(sql), "run", params);
|
|
37
|
+
}
|
|
38
|
+
/** `prepare(sql)` then `statement.all(...params)`. */
|
|
39
|
+
async all(sql, ...params) {
|
|
40
|
+
return call(await this.prepare(sql), "all", params);
|
|
41
|
+
}
|
|
42
|
+
/** `prepare(sql)` then `statement.get(...params)`. */
|
|
43
|
+
async get(sql, ...params) {
|
|
44
|
+
return call(await this.prepare(sql), "get", params);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function call(statement, method, params) {
|
|
48
|
+
const fn = statement[method];
|
|
49
|
+
if (typeof fn !== "function") throw new TypeError(`vericto: the prepared statement has no ${method}()`);
|
|
50
|
+
return fn.apply(statement, params);
|
|
51
|
+
}
|
|
52
|
+
async function guardDatabase(db, options = {}) {
|
|
53
|
+
const vericto = options.vericto ?? new chunkHFL7W4WC_cjs.Vericto();
|
|
54
|
+
await vericto.requireDialect(DIALECT);
|
|
55
|
+
return new GuardedDatabase(db, vericto);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
exports.DIALECT = DIALECT;
|
|
59
|
+
exports.GuardedDatabase = GuardedDatabase;
|
|
60
|
+
exports.guardDatabase = guardDatabase;
|
|
61
|
+
//# sourceMappingURL=better-sqlite3.cjs.map
|
|
62
|
+
//# sourceMappingURL=better-sqlite3.cjs.map
|