docs: document NL2SQL read-only enforcement and its limits

The page claimed read-only mode blocked "multi-statement queries containing
semicolons" and said nothing about CTEs, EXPLAIN ANALYZE, or the fact that a
SELECT can still reach the database server's filesystem. Both gaps matter now
that those routes are enforced.

- Replace the semicolon sentence with a table of every indirect write route
  blocked in read-only mode: multi-statement, writable CTEs (including
  AS MATERIALIZED), a write after a CTE, EXPLAIN ANALYZE, INTO OUTFILE,
  server-filesystem functions, and unparseable WITH statements.
- Explain that analysis masks literals and comments, so `SELECT 'DROP TABLE
  users'` is allowed while `EXPLAIN /*x*/ ANALYZE DELETE ...` is blocked, and
  that a semicolon inside a literal no longer splits statements.
- Document the new SET TRANSACTION READ ONLY backstop and which backends
  fall back without it.
- Add a warning that these checks are defence in depth and a least-privileged
  read-only database role is the only complete control.

Every example in the new table was verified against the implementation.
Applied to en/ar/ko/pt-BR under docs/edge; the ko and pt-BR pages carry the
warning inline as they have no Hardening Recommendations section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rip&Tear
2026-07-31 08:47:04 +08:00
parent 7f6367a21c
commit 338662201f
4 changed files with 76 additions and 4 deletions

View File

@@ -24,7 +24,25 @@ O `NL2SQLTool` opera em **modo somente leitura por padrão**. Apenas os seguinte
Qualquer tentativa de executar uma operação de escrita (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `CREATE`, `ALTER`, `TRUNCATE`, etc.) resultará em erro, a menos que o DML seja habilitado explicitamente.
Consultas com múltiplas instruções contendo ponto e vírgula (ex.: `SELECT 1; DROP TABLE users`) também são bloqueadas no modo somente leitura para prevenir ataques de injeção.
O modo somente leitura também bloqueia os caminhos indiretos para uma escrita:
| Bloqueado no modo somente leitura | Exemplo |
| --- | --- |
| Consultas com múltiplas instruções | `SELECT 1; DROP TABLE users` |
| CTEs de escrita, incluindo a forma `MATERIALIZED` | `WITH d AS MATERIALIZED (DELETE FROM users RETURNING *) SELECT * FROM d` |
| Uma escrita após uma CTE | `WITH d AS (SELECT 1) DELETE FROM users` |
| `EXPLAIN ANALYZE`, que executa seu argumento | `EXPLAIN ANALYZE DELETE FROM users` |
| Escrita no sistema de arquivos do servidor de banco de dados | `SELECT * FROM users INTO OUTFILE '/var/www/shell.php'` |
| Funções que alcançam o sistema de arquivos do servidor ou abrem uma nova conexão | `SELECT pg_read_file('/etc/passwd')`, `dblink_exec(...)` |
| Uma instrução `WITH` que não pode ser confirmada como somente leitura | `WITH d AS DELETE FROM users` |
As instruções são analisadas com literais de texto e comentários mascarados, de modo que uma palavra-chave escondida em um literal não é confundida com um comando (`SELECT 'DROP TABLE users'` é permitido) e um comentário colocado entre palavras-chave não esconde nenhum (`EXPLAIN /*x*/ ANALYZE DELETE ...` é bloqueado). Um ponto e vírgula dentro de um literal não conta como separador de instruções, portanto `SELECT ';'` é uma única instrução válida.
No modo somente leitura, a ferramenta também marca a transação como `SET TRANSACTION READ ONLY`, de forma que PostgreSQL e MySQL rejeitam escritas no próprio banco de dados, independentemente de como a instrução foi escrita. Backends sem essa sintaxe (SQLite, SQL Server, Snowflake) registram uma mensagem de depuração e voltam a depender apenas da validação de instruções — mais um motivo para confiar em um papel somente leitura em vez da análise textual.
<Warning>
As verificações internas de somente leitura são defesa em profundidade, não uma fronteira completa. Elas inspecionam o texto da instrução, e SQL é específico de cada banco: uma instrução que começa com `SELECT` ainda pode alcançar o sistema de arquivos do servidor (`SELECT ... INTO OUTFILE`, `pg_read_file()`) ou chamar uma função com efeitos colaterais. Os caminhos conhecidos são bloqueados explicitamente, mas o único controle completo são os privilégios concedidos em `db_uri`. **Aponte a ferramenta para um papel de banco de dados somente leitura e com privilégio mínimo.**
</Warning>
### Habilitando Operações de Escrita