Releases: DataZooDE/flapi
v26.05.18 — Prepared-statement coverage sweep
v26.05.18 — Prepared-statement coverage swept across every code path
Follow-up to v26.05.17. After v26.05.17 shipped, an internal audit found that the prepared-statement path was only wired into the GET endpoint executor — POST/PUT/PATCH writes and the Arrow-streaming endpoint still rendered Mustache templates as strings. This release closes that gap.
Coverage extension
- POST/PUT/PATCH writes now take the prepared path.
executeWritecalls the rewriter first, splits the rewritten SQL into statements (quote-aware), distributes the binding plan across statements by counting?placeholders per statement, and prepares + binds + executes each one. Multi-statement INSERT…;SELECT…RETURNING templates keep working — each statement is its own prepared statement with the right slice of the binding plan. - Arrow streaming (
executeQueryRaw) now routes through the prepared path with the same fall-back-to-string behaviour when the binding plan is empty. countSqlPlaceholdershelper insrc/sql_utils.cpp— quote-aware?counter (skips placeholders inside'…',"…",$tag$…$tag$) used by the write-path distributor. Covered by 6 new Catch2 cases.
Coverage extension — tests
- Read-path corpus extended from 37 → 99 parameterised payloads (
test/integration/test_sql_injection_corpus.py). Adds endpoints fordouble,boolean,date,time,uuid,enum,emailso every validator type is exercised end-to-end. Plus a/lookup-int-pagedendpoint that proves pagination + prepared bindings work together (count + paginated wraps both bind correctly). - New write-path corpus (
test/integration/test_sql_injection_write_corpus.py, 19 cases). Fires the classic injection payloads at aPOST /widgets/endpoint and asserts the payload lands as a literal string column value, never as a side-effect that drops the table or smuggles extra rows. Includes a multi-statement INSERT-then-SELECT-RETURNING endpoint to exercise binding-plan slicing.
Validator hardening (defense in depth)
validateDateandvalidateTimenow demand the entire input string be consumed —2024-03-15' OR 1=1no longer parses to2024-03-15and silently drops the suffix. Same fix asvalidateIntin v26.05.17.
HTTP status correctness
- New
flapi::BadRequestErrorexception class.QueryExecutor::executeWithBindingsthrows it on bind-conversion failure (caller supplied an invalid value for a typed param);RequestHandlercatches it and returns HTTP 400 with a JSON body, instead of the previous500 Internal Server Error. Server-side prepare/execute failures still return 500 (they're not client errors).
Tests
- 586 C++ unit assertions (Catch2; +6 for
countSqlPlaceholders). - 483 integration tests passing (+81 from the corpus extensions). 21 skip in environments without the relevant fixtures.
v26.05.17
v26.05.17 — Security roadmap (Waves 0–3) + BSL relicense
Headline: in-product MCP + general security hardening — per-tool RBAC, shadow/dry-run, response shaping, description hygiene, prepared-statement SQL-injection defense, PBKDF2 password hashing, audit log, per-user rate limit, CORS allowlist, TLS wire-up, startup auditor. Simple things stay simple — every new control is opt-in via single-line YAML.
License
- Apache 2.0 → BSL v1.1 (e1b465e). The Business Source License 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. See
LICENSEfor the full text.
MCP hardening — Wave 2 (#24)
- Per-tool RBAC (8886cd2, #27).
mcp-tool.allowed-roles: [admin, analyst]in the endpoint YAML restricts a tool to JWT/OIDC principals carrying one of those roles. Deny-by-default: whenmcp.auth.enabled: true, every tool MUST declareallowed-roles— a tool without one refuses every call. Endpoints withmcp.auth.enabledunset keep working role-free forflapii project initdemos. - Dry-run / shadow mode (385f793, #29). Pass
"_dryRun": trueintools/callarguments. flAPI runs validators + template expansion + EXPLAIN and returns the rendered SQL + plan as JSON, but never executes the query. The same controls that gate a real call (RBAC, rate limit) gate a dry-run too. - Tool-description hygiene scanner (63a1af7, #28). At config-load time, descriptions are scanned for control characters, JSON-breakout patterns, and known role-override phrases ("ignore previous instructions"). Strict-mode opt-in via
mcp.strict-descriptions: true— refuses to start when any tool fails the scan. - Per-tool response shaping (9c9cd55, #30). New
mcp-tool.responseblock:max-rowscaps the result-set size,redact-columns: [...]replaces listed columns with a redaction sentinel,sample: truereturns only summary metadata (row_count,columns,sampled: true). - Per-tool rate limit (0a7d69c, #34). New
mcp-tool.rate-limit: { enabled, max, interval }keyed on the authenticated principal (with an anonymous fallback bucket per tool).
General security wins — Wave 1 (#23)
- PBKDF2-SHA256 password hashing (db87b8e, #36).
auth.users[*].passwordaccepts the MCF string$pbkdf2-sha256$<iter>$<b64-salt>$<b64-hash>(OpenSSLPKCS5_PBKDF2_HMACwith 600 k iterations, 16-byte salt, 32-byte key — OWASP 2023 minimum). Compatible with Pythonpassliband any other PBKDF2-SHA256 generator. Plaintext and 32-char-hex MD5 hashes still verify, but the startup auditor emits a deprecation warning. - Config-driven CORS allowlist (f1a6751, #32). The legacy wildcard
Access-Control-Allow-Origin: *is gone — default is same-origin only. Opt into specific origins viacors.allow-origins: [...].flapii project initstill ships["*"]so first-run demos work; the auditor warns when*meetsauth.enabled: true. - JSONL request audit log (1c762d4, #31).
audit: { enabled, sink: stdout|file, path, redact: [...] }emits one JSON line per request (REST and MCP) with principal, method/target, params (redacted per config), status, row count, latency. Off by default, one-line to enable. - Per-user rate limit (b44c92d, #33). New
rate-limit.key: ip | user | user-or-ip. The default staysipfor backward compatibility;user-or-ipis the recommended setting for share-NAT scenarios where many users share a single egress IP. - TLS in embedded server (e38c715, #35). The
HTTPSConfigstruct is now wired into Crow'sssl_file()chain. Reverse-proxy termination is still recommended for production, but direct TLS is supported for self-contained deployments.
SQL-injection defense — Wave 3 (#25)
- Prepared-statement path for typed scalar params (8bf073d + ca16217, #37).
{{ params.X }}(double-brace) references on fields with typed validators (int,double,boolean,date,time,uuid,enum,email,string) are now rewritten to?and bound viaduckdb_bind_*. The value travels as a primitive, not text — SQL injection becomes structurally impossible for those sites. Triple-brace{{{ params.X }}}is unchanged (forLIKEpatterns and other text-mode use sites). The integer validator was also tightened:1; DROP TABLEno longer slips through as1. - W3.3: SQL-keyword regex demotion (ca16217). For numeric/temporal bindable fields, the historic keyword regex is demoted to a debug-level log line — the prepared bind is the hard defense, and the regex's false positives (
latitude=1.111) are gone. Varchar-classified fields keep the regex because flAPI templates routinely embed them via triple-brace. - 37-payload integration corpus at
test/integration/test_sql_injection_corpus.py— every classic injection pattern (UNION, OR 1=1, comment-evasion, xkcd 327) returns zero rows; legitimate values still match.
Honest defaults & honest docs — Wave 0 (#22)
- Startup security auditor (655d61f, #26). 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 combined with
auth.enabled. - Documentation correctness. The misleading claim that
{{{ }}}"prevents SQL injection" is gone fromdocs/CONFIG_REFERENCE.md. The actual layered defense (validators → prepared bind → regex fallback for triple-brace and untyped fields) is documented in § 9 SQL Templates.
Fixes
- Windows release link (4619687).
mcp_authorization_policy.hppforward-declaredEndpointConfigasclasswhile the actual type isstruct; MSVC encodes that keyword into mangled symbols, so the call site and definition emitted different names. Fixed by aligning the forward decl. - Auth-context param leak (4619687).
RequestValidator::validateRequestFieldsrejected every authenticated write request as containing five phantom unknown fields (__auth_username/_email/_roles/_type/_authenticated). The reserved__auth_*prefix is now silently skipped. - Release linker fix (1116f25). Explicit
safeGet<int>template instantiation inconfig_manager.cppfor cross-TU release linking (debug inlined; release with-Wl,--no-undefinedexposed the missing definition). - Cross-platform smoke tests in CI (2f25366, 822ea3e, b3c9744). Each platform binary (linux-amd64, linux-arm64, macos-arm64, windows-amd64) is now booted in CI before release; the four smoke jobs gate
create-release. - Auth template variable names (8b2b8d8). Doc fix: it's
auth.username, notcontext.auth.username.
Tests
- 580 C++ unit assertions (Catch2).
- 402 integration tests passing (37 of them parameterised SQL-injection payloads). 21 skip in environments without the relevant fixtures (AWS Secrets Manager, OIDC issuers).
v26.04.22
fix: OIDC endpoint auth parsing and auth.* template variables (#17)
- parseEndpointAuth(): add oidc block parsing (was silently ignored)
- parseAuthConfig(): store parsed config in global_auth_config member
instead of discarding into a local variable - loadConfig(): propagate global OIDC config to endpoints with
type:oidc that have no local oidc block - ConfigManager: add getGlobalOIDCConfig() accessor
- createTemplateContext(): extract _auth* reserved params and expose
as auth.username, auth.roles, auth.email, auth.type in Mustache context - AuthMiddleware::context: add email and auth_type fields
- handleDynamicRequest(): inject auth context as _auth* params
- RequestHandler: thread authParams through handleRequest/Get/Write
All 425 unit tests pass.
v26.04.21
v26.04.03
fix: Enable macOS tests and fix concurrent cache race
- Remove Debug-only restriction for macOS C++ tests (aligns with Linux)
- Fix CachingFileProvider race: use emplace to avoid double-counting cache entries on concurrent misses
- Add setuptools build-system config to pyproject.toml
- Replace hardcoded absolute paths in test_mcp_instructions.py with PROJECT_ROOT
- Add checks: write permission to build.yaml
Closes #16
v26.03.26
v26.02.17.1
Quick Start
pip install flapi-io
# or try instantly:
uvx --from flapi-io flapi --help
uvx --from flapi-io flapii --helpAvailable on Linux (x86_64, arm64), macOS (arm64), and Windows (x64).
What's Changed since v25.03.22
This release includes 159 commits covering major new features, a new CLI tool, and PyPI distribution.
- PyPI distribution —
pip install flapi-ioinstalls bothflapi(C++ server) andflapii(TypeScript interactive CLI) as standalone binaries. No compiler or runtime dependencies needed. - flapii interactive CLI — New TypeScript CLI built with Bun for project scaffolding, endpoint wizards, and configuration management
- Apache Arrow IPC streaming — HTTP streaming endpoint with LZ4/ZSTD compression, metrics, and health monitoring
- BigQuery support — Quote-aware SQL statement splitting for BigQuery stored procedures
- Cloud storage (VFS) — S3, GCS, and Azure blob storage integration via DuckDB VFS, with security-focused path validation
- MCP config tools — JSON schema-validated configuration tooling for endpoint mutation, template management, cache operations, and discovery
- OIDC authentication — Provider presets for identity-based API access control
- Docker — Multi-arch image (amd64 + arm64) at
ghcr.io/datazoode/flapi - DuckDB upgrades — Updated from v1.3.1 to v1.4.4
- Integration test suite — Comprehensive CI test coverage with published results across all platforms
- Windows support — Full MSVC build with static CRT
Bugfixes in v26.02.17.1
- Fix flapii binary corruption caused by stripping Bun standalone executables
- Fix wheel compression (65% smaller downloads vs v26.02.15)
- Fix artifact download conflicts in CI release workflow
- Fix Arrow server hang by adding Content-Length header
- Fix heap-use-after-free in environment variable substitution
Full Changelog: v25.03.22...v26.02.17.1
v26.02.17
Note: This release has been superseded by v26.02.17.1, which fixes flapii binary corruption caused by stripping Bun standalone executables.
Stripped debug symbols and minified flapii JS bundle to reduce wheel download size. The binary stripping was applied too broadly and broke flapii — fixed in v26.02.17.1.
Full Changelog: v26.02.15...v26.02.17
v26.02.15
Note: This release has been superseded by v26.02.17.1, which includes smaller wheels and working flapii binaries.
First release with PyPI wheel distribution. Published flapi-io and flapii as separate packages (later combined into a single flapi-io package in v26.02.17).
Full Changelog: v25.03.22...v26.02.15
flAPI v25.03.22
What's Changed
- Initial version with basic features.
- Tests activated
- Integration test suite for manual runs
Full Changelog: https://github.com/DataZooDE/flapi/commits/v25.03.22
New Contributors
- @NithinKumaraNT made their first contribution in #1