Repository navigation
0.13.3
Six provider defects and one distribution defect. The provider work is the reason to
upgrade: a MySQL user was told every DDL and DML statement had failed while the
server was applying it, and eight providers were reporting a cache hit ratio they
had never measured.
Every MySQL DDL and DML statement reported a failure for work it had done
query() called .map on the driver's first return value, which for anything that
returns no rows is a ResultSetHeader object rather than an array. Measured through
the provider against MySQL 26.7.0, with an interleaved SELECT proving the server
had already applied each statement:
FAIL DROP TABLE ... -> TypeError: result.rows.map is not a function
FAIL CREATE TABLE ... -> TypeError: result.rows.map is not a function
FAIL INSERT ... -> TypeError: result.rows.map is not a function
OK SELECT * ... -> rowCount=1 <- the "failed" work landed
This is a SQL IDE, and reporting an error for a statement the server ran is the worst
available answer, because the user retries it. Both sites now report affectedRows
in rowCount, the same answer PostgreSQL, SQL Server and SQLite already gave. The
tests could not see it: they mocked the driver as returning an array, and the mock's
type made the real shape unexpressible. That type is now wide enough, which is the
part that keeps it fixed.
A cache hit ratio that was never measured, in eight providers
SQLite claimed a fixed 95 percent, derived from a configuration value. Couchbase
published 0 percent when the stats read was denied - the ordinary case there, and 0
rates as critical, so the panel invented a cache fault the cluster never reported.
PostgreSQL, Oracle and SQL Server defaulted to 100 or 0 inside our own SQL. All
eight are honest now: an absent metric reads as not measured. Buffer pool usage went
with it in three providers, because it was the same quantity wearing a second
name, drawn and rated as two independent gauges.
MySQL also lost its entire health panel, not one metric, on a tenant with no
performance_schema database, because that query ran uncaught.
Two of these fabrications were pinned by tests, one of them named "reports zero
rather than a perfect score when denied". The suite was asserting the invention.
A pasted rediss:// URL connected in the clear
It landed on SSL mode disable, so the scheme a user pasted specifically to get
encryption was dropped. couchbases:// did the same, and the Couchbase transport
was posting plain HTTP to the TLS management port. Both arrive as require now.
mongodb+srv:// is deliberately left alone: the driver turns TLS on with chain
verification, and handing it our require would have stopped an Atlas certificate
being verified.
Exports carried the wrong types, and binary as JSON
A result exported as DDL typed numeric and timestamp columns as TEXT - not because
inference was wrong, but because it never saw a type. PostgreSQL, MySQL, Oracle and
SQL Server now report the engine's own column spelling. A binary value was written
as {"type":"Buffer","data":[...]}, which replayed without error and stored 46
bytes of JSON text where six bytes belonged; every dialect now writes a literal its
own engine accepts, verified by replaying the export into seven engines.
The AppImage shipped a launcher only its builder could execute
linuxdeploy writes the wrapped launcher into the AppDir as 0770 root:root. On the
0.13.2 artifact:
-rwxr-xr-x root/root 274 AppRun
-rwxrwx--- root/root 31552 AppRun.wrapped
Read that with real permission checks and AppRun cannot exec AppRun.wrapped:
AppRun: line 12: AppRun.wrapped: Permission denied
No window, no log, nothing else to go on.
Why it never showed up
A type-2 AppImage mounts its squashfs through FUSE privately to the user who
launched it, and a private FUSE mount skips the kernel's permission check. So the
ordinary case - download, chmod +x, double-click - works no matter what the
recorded mode says. Measured rather than assumed: the shipped 0.13.2 image, mounted
and run as a normal user, opens its window in about a second exactly as it always
did.
What breaks is every reader that does apply the check:
- an extracted AppDir owned by a different user (
--appimage-extract, then run it
as someone else, or from a shared location) - a container or sandbox running the AppDir as a non-root user
- firejail's
--appimagemount, which mounts as root and drops to the user
The third is how this surfaced: AppImageHub's review CI runs a submitted AppImage
exactly that way, and it failed on that line.
The fix
The build now audits the bundled file modes and repacks the image when the audit
fails, reusing the original runtime bytes and the original squashfs parameters.
That constraint is the interesting part: the runtime is what decides whether the
image mounts on a user's machine at all, so regenerating it to correct a file mode
would have traded this defect for a worse one. Compression and block size are read
back from the bundler's own output rather than hardcoded.
The audit is a small script (scripts/check-appimage-perms.mjs) so it is unit
tested rather than asserted in shell, and it runs twice per build: once on the
bundler's output to decide, once on the repacked image to verify, because a repack
that dropped the modes would otherwise pass on the strength of its input.
Verified on Ubuntu 22.04 against the released artifact and the repacked one: mounted
and run as a normal user, both open a window in about a second; with the AppDir
root-owned, only the repacked one does.
Helm chart: 0.1.47
No template change: its content is the appVersion move to 0.13.3. It carries
containsSecurityUpdates: true for the rediss:// fix above - no CVE and no
dependency moved, but a connection a user asked to encrypt was made in the clear,
and an operator deciding whether to upgrade should see that.
Full changelog: 0.13.2...0.13.3