Skip to content

0.13.5

Choose a tag to compare

@cevheri cevheri released this 29 Aug 20:03
· 23 commits to main since this release
Immutable release. Only release title and notes can be modified.

LibreDB Studio 0.13.5 adds two database engines, closes three security defects in the
connection and command paths, and finishes the campaign that stopped the product drawing
a reading it never took as a confident zero.

Security

Three fixes an operator should weigh. None of them moves a chart template or a value -
they are all in the application image, so upgrading the image is the whole of the fix.

The SSH tunnel verified no host key. createSSHTunnel passed no hostVerifier to
ssh2, and the library has no default: read out of its own source, it takes the branch that
logs "Host accepted by default (no verification)" and completes the handshake. The tunnel
therefore connected to whatever answered on the bastion's address, and everything it
carried - the database password among it - was readable to anything that could occupy that
address. Host keys are now trust-on-first-use, pinned per connection: the first contact
records the fingerprint, a matching key connects, and a changed key is refused naming both
fingerprints. Requiring a pasted fingerprint up front was rejected as the policy because it
makes tunnelling unusable for someone reaching their own bastion, and TOFU is both what
every SSH client does on first contact and strictly better than verifying nothing. Driven
against a real openssh-server container on all three arms; the reported fingerprint is
byte-identical to ssh-keyscan | ssh-keygen -lf -.

The destructive-command confirmation gate spoke SQL only. For redis and mongodb it
returned false outright, so FLUSHALL and a deleteMany with an empty filter ran with no
confirmation while DELETE FROM on every SQL engine asked. The vocabulary now lives in one
per-type table grounded in what each provider can actually dispatch, and a payload the
reader cannot parse asks rather than staying silent.

POST /api/db/maintenance validated the wrong thing. It checked that an operation
exists, not that it could take the target it was given. Both UI surfaces already gated on
the per-provider declaration and the route did not, so every mismatch stayed reachable by a
direct request: {type:"vacuum", target:"users"} against SQLite vacuumed the whole file.
The route now derives the placement from that same declaration.

Two new engines

DuckDB ships as an embedded file engine. The read-only profile is enforced by the
engine - enable_external_access:'false' - rather than by a name denylist, because a
denylist is not a boundary: the live probe is what settled that, and it refuted three
claims the plan document had made.

libSQL ships over the Hrana protocol as a single connection type serving both a
self-hosted sqld and Turso Cloud, with no added runtime dependency. It is a separate
driver from sqlite rather than a relative of it - the two share a dialect and nothing
else, since one holds a file handle and the other speaks HTTP.

That brings the published set to sixteen engines beside the embedded LibreDB sample.

An unknown reading is absent, not zero

The largest thread in this release. In every case a reading that could not be taken, or a
list that had been cut, reached the reader as a confident figure.

  • SQL Server, Oracle and MongoDB each initialised a connection count to 0 and swallowed the
    read's failure into it, so a denied DMV, an unprivileged V$SESSION or a serverStatus
    without a connections section drew "0 connections" on a busy server. All three omit the
    key now, and a real zero still reads as zero.
  • The Queries tab summed a list every provider caps at ten and labelled the sum the
    database's query count. Measured against a MySQL server holding 59 digests for one
    schema, both cards were the ceiling wearing a measurement's label.
  • Trino paired an "unavailable" size string with a zero byte count from a single input; the
    search seam did the same.
  • The Storage tab divided three figures by a total it did not have, rendering shares of
    -36.68% / -63.82% / -99.4975% and a negative remainder. A share now requires a positive
    total, which excludes the absent size and the measured zero alike.
  • The agent's curated health reading projected slow-query and session counts off those
    same capped lists, so the figure was the limit, permanently, on any busy server. Both
    counts are gone from the projection; the slow-queries and sessions kinds carry those
    facts with their rows visible, which is the only form in which they mean anything.

One test was pinning the defect rather than measuring: the SQL Server assertion read
expect(typeof overview.activeConnections).toBe("number"), which the fabricated 0 satisfied.

Query, schema and editor fixes

  • The row limiter ran its Oracle ROWNUM, UNION and subquery probes over the whole
    statement text, so a bound merely mentioned inside a comment or a string literal read as a
    bound the statement already carried and nothing was injected. With /* ROWNUM <= 10 */
    mid-statement the grid returned every row of a 9.5k table; it now returns 500 with
    AUTO-LIMITED, while a real trailing LIMIT 10 still returns 10.
  • Schema diff no longer wraps generated migrations in BEGIN;/COMMIT; for dialects that
    cannot run DDL in a transaction, which had made the script fail on the very engines it was
    generated for.
  • A commented-out DROP stopped becoming a statement.
  • An abandoned Monaco load is expected during navigation and is no longer reported as a fault.
  • Chart series and pie slices are capped at the palette size instead of repeating colours.
  • The built-in LibreDB sample could not be edited, and ?ssl=true in a connection string
    verified nothing.

Connections and the agent

  • Redis connections carry their ACL username through to the driver, and the active session's
    user is read from CLIENT LIST's own user field rather than inferred.
  • The query agent carries measured settings for more models rather than one default, records
    which model drove a run and where its settings came from, and binds each run to the
    conversation it belongs to. A conversation used to be single-connection by record rather
    than by database, so a connection edited to address another server kept its id and a
    follow-up was handed the earlier steps' claims about the old database while reading the
    new one. Each run now records a connection identity that includes the far end of an SSH
    hop, and a follow-up whose database does not match declines instead of carrying.

Compatibility registry

Apache Doris, Garnet, both Percona distributions, ParadeDB and OrioleDB are recorded as
measured relatives; Databend is query-only and QuestDB is refused a row, each for a reason
that was probed rather than assumed.

Mobile and accessibility

Four defects at 390px, starting with a light-theme toggle that could not be reached on a
phone. The React Compiler rules that oxlint 1.80 promoted to errors are answered across the
component tree.

Helm chart: 0.1.55

Tracks app release 0.13.5 and carries artifacthub.io/containsSecurityUpdates: "true" for
the three fixes above. No packaged template and no value changes - the chart moves only its
appVersion and default image tag.

Full changelog: 0.13.4...0.13.5