NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
High-performance AppStream XML parser with C++23 FFI bridge. Streams catalog metadata into SQLite with Drift ORM and FTS5 search.
Last release 1 months ago
14 Aug 2026
Too new to tell
only 2 release windows
Nearly every release is documented
notes for 5 of 5 stable releases
Nothing withdrawn
no release was ever pulled
6 months old
5 releases · first in 2026
One column per month.
Security and reliability fix. Recommended for all users. pana 160/160.
Security and reliability fix. Recommended for all users. pana 160/160.
The digit accumulators in AppStreamParser computed result * 10 + digit with no bound, so a long enough run of digits overflowed. Signed overflow is undefined behavior. UBSan confirmed it on the real code path:
AppStreamParser.cpp:57 runtime error: signed integer overflow:
999999999999999999 * 10 cannot be represented in type 'long long int'
AppStreamParser.cpp:37 runtime error: signed integer overflow:
999999999 * 10 cannot be represented in type 'int'
Both are reached from attribute values in a catalog downloaded over the network — <release timestamp="999…"> and <icon width="999…">. The same helper also parses priority, image and video dimensions, and icon scale.
Accumulation now saturates, so a hostile value is clamped rather than wrapped, and the parse stays total. Three regression tests cover it.
This was not caught earlier despite CI running a Debug/Release x asan/ubsan matrix, because no test fed an oversized number.
gmtime_rgmtime_r returns null for a time_t it cannot represent, which a saturated epoch reaches. The return value was ignored, leaving the std::tm zero-initialized and silently producing a 1900-01-01T00:00:00Z timestamp. An unrepresentable epoch now yields no timestamp.
scripts/tidy.sh pins clang-tidy the way scripts/format.sh pins clang-format, and the CI clang-tidy job is no longer advisory.
Unpinned, results depended on which LLVM happened to be first on PATH. The version CI installed could not parse a current libstdc++ — it emitted 20 parse errors and stopped early — while also reporting a bugprone-use-after-move false positive on x = {} immediately after std::move(x), which is the documented way to restore a moved-from object. The pinned version is what found the gmtime_r defect above.
| Version | Parses host libstdc++ | use-after-move false positive |
Found gmtime_r defect |
|---|---|---|---|
| 18 | ✗ bails early | yes | no |
| 20 (pinned) | ✓ | no | ✓ |
| 22 | ✓ | no | no |
The gate passes --warnings-as-errors, because plain clang-tidy exits 0 even when it reports diagnostics.
152/152 C++ tests (three new) including asan and ubsan, 45/45 Dart, UBSan clean on both hostile inputs after the fix, clang-tidy clean at the pinned version, clang-format applied last, dart analyze --fatal-infos clean, dart pub publish --dry-run 0 warnings.
No API changes.
Full changelog: https://github.com/flatpak-minimal/appstream_dart/blob/v0.4.2/CHANGELOG.md
AppStreamParser computed result * 10 + digit with no
bound, so a long enough run of digits overflowed. Signed overflow is
undefined behavior, and UBSan confirmed it on two reachable paths:
<release timestamp="999..."> overflowing long long, and
<icon width="999..."> overflowing int. Both values come from an
attribute in a catalog fetched over the network. The same helper also
feeds priority, image and video dimensions, and icon scale.
Accumulation now saturates, so a hostile value is clamped rather than
wrapped. Regression tests cover both inputs.gmtime_r return value in unixEpochToISO8601. It returns null
for a time_t it cannot represent, which a saturated epoch reaches;
ignoring it left the std::tm zero-initialized and silently produced a
1900-01-01T00:00:00Z timestamp. An unrepresentable epoch now yields no
timestamp.scripts/tidy.sh, pinning clang-tidy the way scripts/format.sh pins
clang-format, and make the CI clang-tidy job blocking rather than
advisory. The unpinned checker gave results that depended on which LLVM
happened to be first on PATH: the version CI installed could not parse a
current libstdc++ and bailed early, while also emitting a
bugprone-use-after-move false positive on x = {} immediately after
std::move(x). The pinned version is what found the gmtime_r defect
above.Recommended upgrade for all users, required for Flutter users. pana 160/160.
Recommended upgrade for all users, required for Flutter users. pana 160/160.
hooks >=2.1.0 depends on meta ^1.19.0
flutter_test from sdk depends on meta 1.18.0
→ version solving failed
Every Flutter app depends on flutter_test, so no Flutter project on an SDK pinning meta 1.18.0 (3.44.x) could depend on appstream_dart 0.4.0 at all. The bundled example was one of them, which is how this surfaced. CI had missed it because its Flutter job runs 3.47.0.
Constraints are now hooks: '>=1.0.2 <3.0.0' and code_assets: '>=1.0.0 <2.0.0', which resolve to 2.1.0/1.2.1 standalone and 1.0.2/1.0.0 under an older Flutter. The build hook is source-compatible with both majors, so the pana score is unaffected.
hook/build.dart published libappstream.so as a code asset, but the Dart VM consults its asset table only for @Native declarations — a plain DynamicLibrary.open(name) never sees it. The asset was therefore built and never used, and bindings.dart compensated with a seven-step runtime search.
Symbols are now @Native externals bound to @DefaultAsset, and that search is gone (~180 lines): a /proc/self/maps scan, a glob through .dart_tool/hooks_runner/ internals, and candidates derived from Platform.script, the executable, and the current directory.
Security: those CWD-relative candidates meant the process would load libappstream.so from ./lib, ./build, or ./src/build. Running an application from a directory an attacker can write to was enough to get a library of their choosing loaded.
The public API is unchanged — AppstreamBindings is not exported.
Resolution goes through NativeAssetsManifest.json, which maps the asset to the plain soname libappstream.so, so the final dlopen uses the system loader. The standard Flutter Linux runner sets RPATH=$ORIGIN/lib and bundles the library, so it just works. Embedders whose binary lives outside the bundle (ivi-homescreen, for example) need it on LD_LIBRARY_PATH:
LD_LIBRARY_PATH=/path/to/bundle/lib homescreen -b /path/to/bundleNote that flutter build bundle does not perform Linux native-asset packaging — use flutter build linux. The README documents this.
Exercised on three runtimes against the real 48 MB Flathub catalog:
dart run / dart test — 45/45Throughput is unchanged (416/411 ms with @Native vs 414/409 ms before): the FFI boundary is crossed twice per parse and components stream over the Dart port, so the binding mechanism cannot move it.
149/149 C++ tests (including asan and ubsan), 45/45 Dart, clang-tidy clean, dart analyze --fatal-infos clean, dart pub publish --dry-run 0 warnings.
Full changelog: https://github.com/flatpak-minimal/appstream_dart/blob/v0.4.1/CHANGELOG.md
hooks ^2.1.0 requires
meta ^1.19.0, but flutter_test from the Flutter SDK pins meta 1.18.0
on 3.44.x, so any Flutter app failed version solving against
appstream_dart 0.4.0 — the bundled example included. The constraints are
now hooks: '>=1.0.2 <3.0.0' and code_assets: '>=1.0.0 <2.0.0', which
resolve to 2.1.0/1.2.1 standalone and to 1.0.2/1.0.0 under an older
Flutter. The build hook is source-compatible with both majors.@Native externals against @DefaultAsset
instead of resolving them through DynamicLibrary.open. hook/build.dart
already emitted the library as a code asset, but the VM consults its asset
table only for @Native declarations, so that asset was built and then
never used; the loader compensated with a seven-step search. The public
API is unchanged./proc/self/maps scan, a glob
through .dart_tool/hooks_runner/ internals, and candidate paths derived
from Platform.script, the executable, and the current directory. The
last of those made the process load libappstream.so from a
CWD-relative lib/, build/, or src/build/ directory, so running an
application from a directory an attacker could write to was enough to get
a library of their choosing loaded.hook/build.dart and lib/src/appstream_native.dart,
which described the @Native mechanism that did not yet exist.^0.2.2, the status line and test count were
three releases old (194 tests, not 185), the project tree was rooted at
the pre-rename appstream/ and listed a dart_api_dl.c that is now
.cpp, and the prerequisites claimed Clang 17+ while the std::expected
polyfill targets Clang 18.Consumers pinned to hooks 1.x will no longer resolve. This is why the release is 0.4.0 rather than 0.3.1 — in 0.x semver a breaking change bumps the m…
First release from flatpak-minimal/appstream_dart. Scores 160/160 on pana.
hooks ^1.0.2 → ^2.1.0 and code_assets ^1.0.0 → ^1.2.1. Consumers pinned to hooks 1.x will no longer resolve. This is why the release is 0.4.0 rather than 0.3.1 — in 0.x semver a breaking change bumps the minor.
The public Dart API is unchanged. The build hook API is identical across the hooks major bump, so hook/build.dart needed no edits, and the SDK constraint stays ^3.10.0. The old bound was pinning code_assets to 1.0.0 and holding native_toolchain_c and record_use at 1.x-era versions.
repository and issue_tracker now point at github.com/flatpak-minimal/appstream_dart. The previously published 0.3.0 still advertises the old meta-flutter/appstream URL on pub.dev; this release corrects the listing.
scripts/test.sh never built the C++ suite. It passed -DBUILD_TESTING=ON, but the gate has been -DAPPSTREAM_BUILD_TESTS=ON since 0.2.2. CMake was warning Manually-specified variables were not used by the project, and ctest silently ran whatever stale binary was left in the build directory — locally, one still linked against a since-removed gtest 1.15.2. CI already passed the correct flag, so only local runs were affected.AppStreamParser and XmlScanner, all semantics-preserving: explicit parentheses in mixed */+ accumulator arithmetic, contains() in place of a find() != npos membership test, and consistent braces across the provides if/else chain. No bugprone-*, cert-*, clang-analyzer-*, or performance-* findings.dart analyze --fatal-infos clean, both format gates clean against CI's toolchain, 149/149 C++ tests (including asan and ubsan), 45/45 Dart tests, dart pub publish --dry-run reports 0 warnings.
Full changelog: https://github.com/flatpak-minimal/appstream_dart/blob/v0.4.0/CHANGELOG.md
hooks ^1.0.2 → ^2.1.0 and
code_assets ^1.0.0 → ^1.2.1. Consumers pinned to hooks 1.x will no
longer resolve. The build hook API is unchanged between hooks 1.x and
2.x, so hook/build.dart needed no edits and the public Dart API is
untouched; the bump also unpins native_toolchain_c and record_use
from their 1.x-era versions. The SDK constraint stays ^3.10.0.github.com/flatpak-minimal/appstream_dart;
repository and issue_tracker updated to match.scripts/test.sh passing -DBUILD_TESTING=ON, which the CMake build
ignores — the gate has been -DAPPSTREAM_BUILD_TESTS=ON since 0.2.2. The
C++ suite was therefore never configured or rebuilt, and ctest silently
ran whatever stale binary was left in the build directory. CI already
passed the correct flag, so only local runs were affected.AppStreamParser and XmlScanner: explicit
parentheses in mixed */+ accumulator arithmetic, contains() in place
of a find() != npos membership test, and consistent braces across the
provides if/else chain.Bump version to 0.3.0 and add changelog entry covering:
Bump version to 0.3.0 and add changelog entry covering:
Signed-off-by: Joel Winarske joel.winarske@gmail.com
SPDX-License-Identifier /
SPDX-FileCopyrightText) across all source files; add
THIRD_PARTY_LICENSES cataloging every direct dependency.sqlite3 ^2.4.0 → ^3.3.1, lints ^4.0.0 → ^6.1.0
(applies to both the main package and the Flutter example).lib/appstream.dart,
lib/src/database/database.dart, and lib/src/database/tables.dart.Reliability and security fixes surfaced by clang-tidy:
lib/appstream_dart.dart re-export so the primary library name
matches the package name. The original lib/appstream.dart import
continues to work.docs/ → doc/ and tests/ → native_tests/ to match the
pub package layout (singular doc/, no clash with the Dart test/
directory)..pubignore to keep build artifacts, the cached
appstream.xml/catalog.db, the Flutter example sub-package, and
legacy/dev shell scripts out of the published archive.-DAPPSTREAM_BUILD_TESTS=ON so the package:hooks build hook and
downstream consumers no longer fetch GoogleTest or build the test
executable by default.AppStreamParser (member key strings
were re-checked via .empty() after being moved).SqliteWriter::~SqliteWriter noexcept and wrap its body in a
try/catch so a logging failure during teardown can no longer
std::terminate the parsing process.postString (FFI) now returns success/failure and a stack-allocated
OOM sentinel (-2) is posted if the malloc fails, instead of the
progress message being silently dropped.appstream_parse_to_sqlite:
paths are passed directly to open(2)/SQLite with no normalization
or sandboxing, so callers accepting them from untrusted input must
validate first..clang-format and .clang-tidy at the repo root so
formatting and lint runs are deterministic.example/flathub_catalog): drive the package's
CMake build via ExternalProject_Add so libappstream.so is always
built and bundled before the runner is linked.Your coding agent can read these notes before it upgrades. Set up the MCP server →