Repository navigation
v0.3.0
Binaries for Linux, macOS, Windows and FreeBSD are attached below. The Docker image is
ghcr.io/mariuz/firebirdrest:0.3.0. See CHANGELOG.md for details.
Production readiness: Prometheus metrics, Server-Timing, JWKS keys from identity providers
with a token cache, single values and files in any media type, raw bodies for RPC, and the fixes
of a security review. A JavaScript client with the supabase-js API,
firebirdrest-js, checks compatibility in CI.
Behaviour changes to check before upgrading:
server-cors-allowed-origins = "*"no longer sendsAccess-Control-Allow-Credentials; list
the origins explicitly if browsers must send credentials.GET /describes only what the caller's role is granted; set
openapi-mode = "ignore-privileges"for the old document.- Requests are limited to 60 seconds and 16 MB bodies (
server-request-timeout,
server-max-body-size), andand=/or=nesting to 16 levels,in.()to 1000 values. - Permission errors no longer include Firebird's "Effective user is ..." line, and unexpected
errors return a generic message (the cause is logged). - Pool and statement cache metrics label roles in upper case.
- Responses carry
X-Content-Type-Options: nosniffand a sandboxingContent-Security-Policy.
Security
A review of the SQL generation, authentication and HTTP surface found no injection or
authentication bypass, and these weaknesses, all fixed here:
- A deeply nested
and=/or=filter or a longin.()list could take the Firebird connection
down or burn CPU for tens of seconds: nesting is limited to 16 levels andin.()to 1000
values (400beyond). - A
Rangeheader with a huge end overflowed the limit arithmetic and removeddb-max-rows;
such ranges are refused with416. - A single value requested as
text/html(or SVG, XML, script) was served as a page of the
API's origin: stored script in a column could run there. Every response now carries
X-Content-Type-Options: nosniffand a sandboxingContent-Security-Policy, and those types
are sent as downloads. - With the default
server-cors-allowed-origins = "*", any origin was reflected together with
Access-Control-Allow-Credentials: true; the wildcard now answers*without credentials, and
only listed origins get credentials (withVary: Origin). GET /described every table and routine to anonymous callers. The document now follows the
caller's privileges (openapi-mode, defaultfollow-privileges).- Permission errors named the database login ("Effective user is ..."), and unexpected errors
returned their internal text; both are now generic for the client and logged on the server. - Each case spelling of a role name (
web_user,WEB_USER...) opened its own connection pool;
names are folded to upper case, as Firebird does. - The HTTP server had no request timeout and read request bodies inside the transaction:
server-request-timeout(60 s),server-max-body-size(16 MB) and header limits are
enforced, and bodies are read before the transaction starts. - An empty boolean in the config file (
admin-pprof =) counted as true; it now keeps the
default. admin-server-hostbinds the admin server to its own address, e.g.127.0.0.1.
Added
-
Metrics in the Prometheus format at
GET /metricson the admin port: requests by method and
status, a request duration histogram, error codes, connection pool and statement cache usage
per role, schema cache loads, JWT cache and JWKS fetches, and Go runtime figures. See
Monitoring. -
server-timing-enabledadds aServer-Timingheader (jwt,parse,transaction,
response), as in PostgREST. -
jwt-secretaccepts a JWK or a JWKS as JSON, PEM certificates and PKCS#1 RSA keys, several PEM
keys at once, Ed25519 keys (EdDSA) and@/path/to/file;PS256/384/512tokens are accepted
with RSA keys. Tokens with akidare checked against the matching key. -
jwt-secret-is-base64for base64 encoded HMAC secrets. -
jwt-jwks-urlfetches the signing keys of an identity provider (Auth0, Keycloak, Okta, Azure
AD, ...) at startup and everyjwt-jwks-refreshseconds, and again when a token names an
unknownkid, so rotated keys work right away. -
A JWT validation cache (
jwt-cache-max-entries, default 1000) skips the signature check for
tokens already accepted, until they expire or for 5 minutes. -
Single values as is: any concrete
Accepttype that isn't a built-in format (text/plain,
application/octet-stream,image/png,application/geo+json...) returns the one selected
value of the one matching row as the body, with thatContent-Type. Binary columns are sent
as their bytes, so files can be served straight from the database. Works for reads, writes
withreturn=representationand RPC. -
Raw request bodies for RPC: a
POST /rpc/<name>whoseContent-Typeisn't JSON or a form puts
the body into the routine's only parameter, as text or bytes (FBRST133otherwise). -
The example database has a
coverstable with a binary BLOB and ablob_sizefunction. -
A raw single-value response sends a binary value as the driver returns it, without a base64
encode/decode round trip, so serving a file holds one copy of it in memory.
Fixed
-
Acceptentries with a parameter that has no value, such as the
application/vnd.pgrst.plan+text; for="application/json"; options=;that postgrest-js sends
forexplain(), were rejected with406. The media type is now read even when a parameter
is malformed. -
Responses are sent only after the transaction commits. A failed
COMMITnow returns an error
instead of a success response, and headers set before an error (such asContent-Rangeon a
416) no longer appear on the error response. -
Graceful shutdown waits for requests in flight (up to 10 seconds) before closing the database
connections; before, requests running atSIGTERMcould fail with500.