Skip to main content

Security roadmap shipped — MCP RBAC, prepared statements, PBKDF2, audit log

· 4 min read
DataZoo Team
flAPI Development Team

flAPI v26.05.17 is out. This release answers a recurring question from operators evaluating flAPI for AI-agent access to real data warehouses: "Is this safe to expose to MCP-driven agents against production?"

The answer is now: yes, with single-line opt-insflapii project init demos still work without auth, and every production control is one YAML key away.

What's new

MCP hardening (the headline)

FeatureYAMLPurpose
Per-tool RBACmcp-tool.allowed-roles: [analyst]Restrict each tool to JWT/OIDC principals carrying one of the named roles. Deny-by-default when mcp.auth.enabled: true — a tool without allowed-roles refuses every call.
Shadow / dry-run_dryRun: true in tools/call argsRun validators + template expansion + EXPLAIN and return the rendered SQL + plan, without executing. Lets operators audit every tool definition before production.
Tool-description hygienemcp.strict-descriptions: trueRefuses to start if any mcp-tool.description contains control characters, JSON-breakout patterns, or role-override phrases ("ignore previous instructions").
Response shapingmcp-tool.response: { max-rows, redact-columns, sample }Hard row caps, per-column redaction, or summary-only sampling — applied before the result reaches the agent.
Per-tool rate limitmcp-tool.rate-limit: { enabled, max, interval }Per-principal budget independent of the global rate limiter.

SQL-injection defense

flAPI's SQL templates use Mustache. Typed double-brace references are now rewritten to DuckDB prepared-statement placeholders:

-- Field has `validators: [{type: int}]`. The renderer emits `?`
-- and binds via duckdb_bind_int64. SQL injection at this site is
-- structurally impossible.
WHERE id = {{ params.id }}

-- Triple-brace still flows through Mustache as text — use for LIKE
-- patterns and other text-mode use sites.
WHERE name LIKE '%{{{ params.name }}}%'

This applies to int, double, boolean, date, time, uuid, enum, email, and string types. The end-to-end injection corpus at test/integration/test_sql_injection_corpus.py exercises 37 classic payloads — every one returns zero rows.

General production wins

  • PBKDF2-SHA256 password hashingauth.users[*].password now accepts $pbkdf2-sha256$<iter>$<b64-salt>$<b64-hash> MCF strings (OpenSSL, 600 k iterations). Compatible with Python passlib. Plaintext and MD5 still work but the startup auditor warns about them.
  • Config-driven CORS allowlistcors.allow-origins: [...]. The historic wildcard is gone; the auditor warns if it's combined with auth.enabled: true.
  • JSONL request audit logaudit: { enabled, sink, path, redact: [...] } emits one JSON line per REST + MCP request with principal, target, params, status, row count, latency.
  • Per-user rate limitrate-limit.key: ip | user | user-or-ip. user-or-ip is the recommended setting for share-NAT scenarios.
  • TLS in the embedded serverhttps: { enabled, ssl_cert_file, ssl_key_file } wires Crow's OpenSSL chain. Reverse-proxy termination is still recommended; direct TLS is supported for self-contained deployments.
  • Startup security auditor — at boot, flAPI scans the loaded config and emits structured warnings for plaintext passwords, MD5 passwords, MCP exposed without auth on a non-loopback bind, and CORS wildcard + auth combinations.

Honest docs

The misleading claim that {{{ }}} "prevents SQL injection" is gone from the docs. The actual layered defense (validators → prepared bind → regex fallback for triple-brace and untyped fields) is documented in CONFIG_REFERENCE.md.

Migration notes

Existing configs keep working unchanged:

  • MCP without auth stays simple — no allowed-roles required.
  • REST endpoints with typed validators automatically benefit from the prepared-statement defense; no template changes needed.
  • MD5 and plaintext passwords still verify; you'll see a deprecation warning at boot.
  • CORS wildcard is still accepted, with a warning when paired with auth.

To opt into the production hardening, see docs/getting-started/configuration.md for end-to-end YAML examples.

License

This release coincides with the relicense from Apache 2.0 to the Business Source License (BSL) v1.1. The BSL is source-available; non-production use is permitted without a commercial agreement. The Change License (MPL 2.0) takes effect five years after first publication of each version. Full text in LICENSE.


Full release notes: CHANGELOG.md

🍪 Cookie Settings