跳过主要内容
H

dsh-plugin-sql-security-validate

huangzuting/dsh-plugin-sql-security-validate

拒绝智能体生成的越权 SQL:读取的表或字段不在黑白名单策略内时直接拒绝,返回结构化权限原因,不改写查询。

安装

dsh plugin --profile web add github:huangzuting/dsh-plugin-sql-security-validate

README

dsh-plugin-sql-security-validate

English | 简体中文

AI SQL permission guard for DeepSeek Harness — a general-purpose permission layer for agent-driven database access.

It intercepts SQL an agent generates that exceeds its permissions and refuses to execute it rather than silently rewriting it, returning a structured permission reason to the model. The agent can then drop the offending column, switch to an allowed aggregation, or explain the limitation to the user in plain language.

  • Plugin ID: dsh-plugin-sql-security-validate
  • Framework: DeepSeek Harness (Cordis)
  • Tested against: DSH 0.2.x
  • License: MIT

1. What it solves

ProblemWhat this plugin does
An agent writes SQL that reads tables or columns it should notParses every resource through a real AST; a policy hit refuses execution
The error text is vague, so the question-answering loop breaksThe refusal carries SQL_PERMISSION_DENIED plus the exact refused resources
Silent rewriting makes the agent believe the data is completeNever rewrites SQL; a refusal is explicit
Light personal use versus strict enterprise deploymentEvery hardening option is off by default; enable what production needs
Another plugin overrides the refusal later in the chainOptional monotonic guard ctx.tools.guard() makes the refusal final

2. Scope

  1. This plugin guards only SQL generated by an AI agent, reaching the database through a registered tool.
  2. Scope discovery is a first-class concern: an agent that cannot see which tables exist cannot write a valid query, so DESCRIBE / SHOW CREATE TABLE are governed per table rather than refused outright. You can inspect the shape of a table you are allowed to read, and not one you are not.
  3. It does not handle MCP service-layer, network-layer, or database-account authentication — those belong to service security.
  4. It does not defend against platform-level routes that reach the database without passing through a registered tool.

3. Installation

From the plugin market

Install the bundle from the DSH Web GUI under Plugin Market or Settings → Plugins, or ask an agent in a session:

Install the bundle from C:\path\to\dsh-plugin-sql-security-validate

The bundle ships a cordis.patch.yml that inserts one row named sql-security-validate; no other configuration is required to start.

From source

git clone <repository-url>
cd dsh-plugin-sql-security-validate
node tests/unit.mjs          # verify the checkout is intact

Then install the directory as a bundle in DSH.

4. Quick start

  1. Enable the plugin (it is enabled on install).
  2. Open Settings → SQL 权限校验.
  3. Pick a mode:
    • Blacklist (default): everything runs except what you list. Nothing is needed to get started; add the tables and columns that must never be read.
    • Whitelist: only what you list runs; everything else is refused. No external service required — the list lives in this panel.
  4. Point the agent at your database and watch the per-turn card at the end of each turn: it reports what was refused in that turn and which policy entry matched.

Whitelist example

EntryMeaning
demo.userThe user table in demo — recommend always qualifying
demo.ordersSame, another table
demo.user_private.phoneA single column, when only that column may be read

Write database.table, not a bare user. A bare name matches the same-named table in every database. See §7.

5. Configuration

All fields are editable from the settings panel; the same values can be written into cordis.patch.yml.

Core

FieldDefaultMeaning
enabletrueMaster switch
securityModeblacklistblacklist or whitelist_emulate
enableStrictGuardModefalseInstall the monotonic guard so a refusal cannot be overridden by another plugin
allowSchemaDiscoverytruePermit table-less discovery (SHOW TABLES, SHOW DATABASES). Turn off where table names are confidential
failOnParseErrorfalseRefuse statements the parser cannot interpret. Safer, but rejects valid legacy MySQL forms
strictDatabaseQualificationfalseWhether a bare whitelist grant may reach a qualified resource (see §7)

Policy

FieldMeaning
localBlacklist.tables / .columnsEntries refused in blacklist mode
localWhitelist.tables / .columnsGrants allowed in whitelist mode
targetToolPrefixesOnly tools starting with these prefixes are inspected; MCP tool names are mcp__<server>__<tool>
includeNonMcpSqlToolsAlso inspect tools outside those prefixes
sqlParamKeysWhich argument keys are parsed as SQL

Remote policy (optional)

Whitelist mode works fully offline. A central policy service is optional:

FieldDefaultMeaning
remotePolicy.enabledfalseFetch policy from a service
remotePolicy.policyApiUrl''Endpoint
remotePolicy.policyApiToken''Bearer token
remotePolicy.cacheTtlMs30000Cache lifetime
remotePolicy.apiTimeoutMs3000Request timeout; a failure fails closed

Audit

FieldDefaultMeaning
auditEnabletrueRecord decisions
audit.maxRecords5000In-memory ring size
audit.redactSqlfalseReplace string literals with '***'
audit.logPath''Append-only JSONL path; empty means $DSH_HOME/sql-security-validate/audit.jsonl

6. How a decision is reached

Agent emits a tool call
  → pre-execute hook (or the guard, in strict mode)
    → collect candidate SQL from the configured argument keys
      → parse each through the AST, collecting every table and column
        → merge local policy (and remote, if enabled)
          → a refused resource produces SQL_PERMISSION_DENIED and the call does not run

Two properties are deliberate:

  • Nothing is rewritten. The SQL either runs as written or is refused. A rewritten query would let the agent believe its result set is complete.
  • The refusal is informative. The reason lists the refused tables and columns, the security mode, and the policy source, so the model can correct itself instead of retrying blindly.

On what a statement is judged by:

  • A * projection is refused when a column rule governs one of the tables it covers. There is no column list to compare, so the statement cannot be shown to exclude the forbidden column; the refusal names it as <table>.*. COUNT(*) is not this case — it is an aggregate and reveals no column value.
  • Leading comments are stripped before judging. A statement may open with --, # or /* */; the comment carries no semantics and must not change the verdict.

7. Known boundaries

These are design decisions, not defects. SECURITY.md explains the reasoning.

  1. Enforcement scope — registered tools only; no MCP/network/account-layer authentication.
  2. Schema discovery is on by default — SHOW TABLES / SHOW DATABASES reveal names. DESCRIBE / SHOW CREATE TABLE remain per-table regardless.
  3. Unparseable statements pass through by default (failOnParseError: false).
  4. Audit SQL is plaintext by default (audit.redactSql: false).
  5. A bare whitelist grant spans databases — user matches demo.user and mysql.user. Write database.table, or set strictDatabaseQualification: true to restrict a bare grant to bare references.

8. Vendored parser

node-sql-parser 5.4.0 lives under vendor/ (2.77 MB, Apache-2.0) instead of being an npm dependency. The published package is 88 MB, of which about 87 MB is browser bundles, alternate builds, and source maps that never load at runtime. Vendoring keeps the plugin at 2.76 MB, pins the exact parser version inside the repository so it is auditable, and lets the plugin install offline.

The trade-off: upstream security fixes do not arrive automatically. See SECURITY.md for the re-vendoring procedure.

9. Development

pnpm install                         # only needed for the engine and panel suites
node tests/unit.mjs                  # parser, policy, remote client (49)
node tests/report-vectors.mjs        # first-round security vectors (15)
node tests/subquery-vectors.mjs      # subquery positions (26)
node tests/qualification-vectors.mjs # grant qualification (10)
node tests/engine.mjs                # apply() wiring, dual engines (28)
node tests/panel.mjs                 # settings panel and turn card (15)

143 assertions total. npm test runs all six in order.

The parser, policy, and vector suites have no dependencies — they run on a bare clone. engine.mjs imports src/index.js, which needs @deepseek-ai/schemastery, so pnpm install is required before the last two.

Layout

PathPurpose
src/index.jsHost half: config, dual engines, audit route, turn tracking
src/sql-parse.jsAST-driven resource extraction
src/policy.jsBlacklist/whitelist matching and grant semantics
src/audit.jsBounded ring buffer plus append-only JSONL
src/client.jsSettings panel and per-turn interception card
vendor/node-sql-parser/Vendored parser (see its README)

10. License

MIT — see LICENSE.

The vendored node-sql-parser is Apache-2.0 and big-integer is MIT; both licenses ship alongside their code under vendor/ and are not relicensed by this project.

相关插件