Commit Graph
279 Commits
Author SHA1 Message Date
Dylan M. Taylor 6c5f5ea2f5 Merge pull request #898 from storytold/chore/deps-safe-refresh
chore(deps): refresh serde_json, winresource, plist, ureq, tray-icon
2026-10-25 19:03:58 -04:00
skylar 53edc87ac2 style: rustfmt tray.rs 2026-10-25 16:52:55 -06:00
skylar c77c63dce4 chore(deps): bump serde_json, winresource, plist, ureq, tray-icon 2026-10-25 16:36:25 -06:00
Dylan M. TaylorandCodebuff a9ac66106e build: pin craft-update to craft-libs main (495e597) (#896)
Picks up the update prompt fix that draws its close mark and version pill
without glyphs a font may lack, so the prompt no longer takes the app down
when a bundled face has no ✕ or →.

Part of tracking issue storytold/craft-libs#319

🤖 Generated with Codebuff

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-25 17:37:28 -04:00
Dylan M. Taylor e2b1b73a08 refactor: delegate self-update functionality to craft-update (#871)
* refactor: delegate self_update to craft-update

* ci: point craft-update patch to dylanmtaylor/craft-libs branch

* nix: set allowBuiltinFetchGit for git dependencies

* chore(deps): update craft-update commit

* build: pin craft-update to storytold/craft-libs fe61243 and drop the temporary patch

* fix(update): share craft_update::Stop so pause and cancel reach the self-update

* build: pin craft-update to the dirs-free, zip 9 build

Satisfies cargo-deny: no MPL-2.0 option-ext, no zip 8 advisories, and a versioned git dependency instead of a wildcard.

* build: pin craft-update to craft-libs main (b9ba47f)
2026-10-25 13:40:46 -04:00
skylar taylor-barrick 4ab652afd8 Migrate Craft Launcher to egui 0.37 (#873)
* Migrate Launcher to egui 0.37 and preserve current-main behavior

* egui037: record final published pins and local qualification

* fix(ui): gate CJK language drawing until fonts are installed

* Document migrated launcher UI contracts and regression invariants
2026-10-25 10:49:00 -04:00
Brandon ThomasandClaude Opus 5.5 932e718bbc Release 0.8.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.8.1
2026-10-25 09:52:58 -04:00
Brandon ThomasandClaude Opus 5.5 1da85f9f61 macOS packaging: retry stapler when Apple's CloudKit drops the connection
The 0.8.0 release's macOS job failed with 'The validate action failed! Error 68' (NSURLErrorDomain -1005 from api.apple-cloudkit.com) after both notarizations were Accepted. Same helper as VectorCraft's packaging/macos/package.sh (craftrules learnings/ci-and-release.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:52:35 -04:00
Brandon ThomasandClaude Opus 5.5 75274994e7 Release 0.8.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:44:58 -04:00
Dylan M. Taylor f0cb3b0439 Merge pull request #870 from dylanmtaylor/security/xtask-mirror-hardening
fix(security): enforce bounded reads, deadlines, and terminal escaping in local mirror
2026-10-24 22:37:29 -04:00
Dylan M. Taylor 2d122457d1 Merge pull request #869 from dylanmtaylor/security/single-instance-challenge-response
fix(security): implement mutual challenge-response for single-instance IPC
2026-10-24 22:37:26 -04:00
Dylan M. Taylor 44cbaeedce Merge pull request #868 from dylanmtaylor/security/proxy-loopback-credential-logging
security: bypass proxy for loopback and suppress HTTP wire credential logging (F-003, F-005)
2026-10-24 22:37:23 -04:00
Dylan M. Taylor 5f8cfd3c9a Merge pull request #867 from dylanmtaylor/security/artifact-identity-binding
security: bind artifact URL identity in install and self-update (F-010, F-011, F-015)
2026-10-24 22:37:18 -04:00
Dylan M. Taylor ca67541cbd Merge pull request #865 from dylanmtaylor/security/decoded-payload-limits
security: bound decoded HTTP responses and screenshot decoding (F-006, F-008, F-009)
2026-10-24 22:37:15 -04:00
Dylan M. Taylor 3f84a88967 Merge pull request #864 from dylanmtaylor/security/release-url-confinement
security: confine release HTML URLs and sanitize browser open links (F-004, F-007)
2026-10-24 22:37:12 -04:00
Dylan M. Taylor fab99e46e8 Merge pull request #866 from dylanmtaylor/security/tar-archive-filesystem-safety
security: harden archive extraction, layout normalization, and file enumeration (F-001, F-002, F-012, F-013, F-014)
2026-10-24 22:36:58 -04:00
Dylan M. Taylor 248db064eb security(install): skip records under symlinked directories in remove_files 2026-10-24 22:36:35 -04:00
Dylan M. Taylor 6bd7efa57c install(freebsd): record non-traversed symlink entries in tree manifest 2026-10-24 22:25:12 -04:00
Dylan M. Taylor ad40581d64 fix: implement constant-time equality in pure Rust without modifying Cargo.lock 2026-10-24 22:23:33 -04:00
Dylan M. Taylor a9ac09060a fix(install): parse release tag and filename from release download URLs
- Validates tag and artifact filename from /releases/download/ URLs without breaking mock/loopback URLs.
2026-10-24 22:21:43 -04:00
Dylan M. Taylor f8c691e55c fix(clippy): remove redundant let binding in rogue-server test 2026-10-24 22:18:58 -04:00
Dylan M. Taylor 72b9418c05 fix(security): address review comments on proxy and logging
- Redact Bearer tokens with multiple spaces and add regression test.
- Explicitly disable proxies on direct and direct_download ureq agents.
- Add direct_download agent with download timeout profile for loopback downloads.
- Preserve proxy username, password, and NO_PROXY exclusions in proxy::pick().
2026-10-24 22:18:27 -04:00
Dylan M. Taylor df0b2e4d96 fix(install): enforce URL filename and tag identity checks when release.version is absent
- Retains download URL filename and release tag checks in bind_artifact_version even when semantic version parsing is unavailable.
2026-10-24 22:15:21 -04:00
Dylan M. Taylor d31ee123b5 fix(install): defer directory unpacking, use effective entry size, and fix FreeBSD paths
- Uses entry.size() rather than header.size() to account for PAX size headers in cumulative unpack limits.
- Defers directory extraction until child entries are written to avoid restrictive directory modes preventing child file creation.
- Returns error when tree file enumeration exceeds limits or fails to read directory.
- Supports both /usr/local and ~/.local install prefixes based on needs_root.
2026-10-24 22:14:22 -04:00
Dylan M. Taylor 6f2c7e7b44 fix(http): restrict oversized check to resuming partial downloads
Allows a full 200 response to replace an existing oversized partial file instead of erroneously rejecting it.
2026-10-24 22:12:07 -04:00
Dylan M. Taylor 07cd50b3f0 fix(security): validate fallback release URL and reject dot segments in feed repos
- Validates constructed fallback release URLs with safe_release_html_url to prevent command injection from malformed tags.
- Rejects empty, '.' and '..' path components in RawApp::into_app and RawRepoRename::into_rename.
2026-10-24 22:11:28 -04:00
Dylan M. Taylor ec40f2e1cd fix(security): enforce bounded reads, deadlines, and terminal escaping in local mirror
Resolves F-019 (CWE-400), F-020 (CWE-117), and F-021 (CWE-117).
- Enforces per-byte read deadlines and socket timeouts during header parsing.
- Bounds maximum line length (8 KiB) and maximum total headers size (64 KiB) to prevent slowloris/DoS.
- Sanitizes request method and target before logging to stderr to prevent ANSI escape sequence injection.
2026-10-24 22:10:07 -04:00
Dylan M. Taylor 886fa562d3 fix(security): implement mutual challenge-response for single-instance IPC
Resolves F-016 (CWE-522) and F-017 (CWE-287).
- Replaces cleartext transmission of persistent bearer token with ephemeral mutual challenge-response.
- Client initiates handshake with ephemeral client nonce without disclosing token.
- Server proves possession of per-user token via SHA-256 HMAC-style proof before client discloses client proof.
- Uses constant-time equality check to prevent timing leaks.
- Replayed or stale auth proofs across handshakes are rejected via fresh server nonces.
2026-10-24 22:07:59 -04:00
Dylan M. Taylor 004fe617af security: bypass proxy for loopback and suppress HTTP wire credential logging (F-003, F-005) 2026-10-24 21:58:20 -04:00
Dylan M. Taylor 0594ec7f03 security: bind artifact URL identity in install and self-update (F-010, F-011, F-015) 2026-10-24 21:52:05 -04:00
Dylan M. Taylor 5ec7596b61 security: harden archive extraction, layout normalization, and file enumeration (F-001, F-002, F-012, F-013, F-014) 2026-10-24 21:49:55 -04:00
Dylan M. Taylor ab5e83ffd1 security: bound decoded HTTP responses and screenshot decoding (F-006, F-008, F-009) 2026-10-24 21:47:13 -04:00
Dylan M. Taylor 416a3ff148 security: confine release HTML URLs and sanitize browser open links (F-004, F-007) 2026-10-24 21:42:38 -04:00
Brandon ThomasandClaude Opus 5.5 c0fa1c69f4 Tests: start the tray-sleep clock before the waker thread (#850)
with_no_window_it_sleeps_until_woken read Instant::now() after spawning
the thread whose 200 ms pause it measures, so a runner that held the
test thread back between the two failed started.elapsed() >= pause
(seen on main's ubuntu CI). Read the clock first.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:54:12 -04:00
Brandon ThomasandClaude Opus 5.5 fdcd6fb631 Tests: retry the version probe of a script a test just wrote (ETXTBSY) (#848)
reads_binary_version_when_the_cli_uses_the_display_name_or_a_former_id
failed now and then: another test thread that forks while the script is
still open for writing holds that descriptor until it execs, and running
the script in that window fails with ETXTBSY. Both binary_version tests
now retry a miss for up to a second.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:17:58 -04:00
Brandon ThomasandClaude Opus 5.5 517786fc81 Tests: pick Simplified Chinese through AccessKit now that it sorts last (#720) (#849)
#720 lists the languages by their own name, so 简体中文 is now the last
row of the language picker, below the fold of the popup's scroll area.
The pointer click that kittest's click() simulates landed outside the
visible part, nothing was chosen, and the Chinese test failed on main on
both Linux (no font: language stayed "en") and Windows. Clicking via
AccessKit doesn't need the row on screen; the picker itself is fine,
people scroll it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:17:47 -04:00
c8870ccf8c Say once, the first time, that closing the window keeps the launcher in the tray (#376)
The close button parks the launcher in the tray by design, but to
someone who doesn't know that, a window that vanishes with nothing to
show for it looks hung, and the reports end with killing the process
(#45, #215).

The first time the window closes to the tray, a system notification
says the launcher is still running, that it's in the tray, and that
Settings can turn this off. `LauncherUi::hidden_to_tray` sends it
through the host and records it in `Settings::tray_hint_shown`, so it
happens once per install. The desktop shell calls it from the
close-request path only, for both ways of going to the tray; a window
hidden from the tray icon, or started hidden at login, gets no hint.

Two strings, with rows in all ten complete catalogs. A UI test checks
the hint is sent once and recorded.

Co-authored-by: YangXZ2008 <141027290+YangXZ2008@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-24 01:44:17 -04:00
803d5f57a4 fix(windows): launch system helpers by absolute path (#815)
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-24 01:44:10 -04:00
478ba4e4e2 fix(ui): ask wgpu for no more than the GPU offers (#458)
egui requests wgpu's default limits on every backend but GL, and device
creation fails when the adapter falls short of any one of them. The
Raspberry Pi 5's V3D driver (Mesa V3DV) allows 15 inter-stage shader
variables against the default 16, so the launcher exited before the first
frame with "Limit 'max_inter_stage_shader_variables' value 16 is better
than allowed 15".

Wrap egui's device descriptor so each requested limit is lowered to the
adapter's where it is below the request. GPUs that meet the defaults get
the same request as before.

Includes regression coverage with the V3D limit.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-24 01:44:04 -04:00
CapriandGPT-6 Luna 75a1ed5167 Backend: Clear busy state when a worker panics (#780) (#845)
Co-authored-by: GPT-6 Luna <noreply@openai.com>
2026-10-24 01:43:57 -04:00
smittie2000andClaude Opus 5.5 b5333a6247 refactor(core): one table of bundled app icons (#789)
native's Linux menu integration and ui-egui's icons each kept the id -> PNG table;
adding FigCraft had to edit both. core::catalog::app_icon_png is the one table now,
with a test that every app in SUITE has a PNG.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:43:51 -04:00
XianZheYangandYangXZ2008 37fff03c1e Chinese: look for a hand-installed MiSans too, in the user's font folders (#438)
Some systems have no Chinese face the launcher knew about but do have
MiSans (Xiaomi's free one), installed by hand into the user's font
folder, so the interface stayed in English (#378).

`MiSans-Regular.ttf` (then Normal, Medium) is now looked for after the
system's own faces, in the places hand-installed fonts go: the user's
font folder and the system-wide one on macOS and Windows, and the XDG
and legacy user folders plus the usual packaged locations on Linux and
the BSDs. The "none found" log line names it. A test checks it is on
the list on every system, after the system's own fonts.

Fixes #378

Co-authored-by: YangXZ2008 <141027290+YangXZ2008@users.noreply.github.com>
2026-10-24 01:43:45 -04:00
XianZheYangandYangXZ2008 e7448607f4 Title bar: maximized, the close button takes the screen's corner (#375)
The window buttons kept the resize band (5 px along the top, and the
right edge for Close) out of their clickable area, even while the window
was maximized and `window_edges` offered no resizing there. At the
screen's top-right corner that band belonged to nobody, so a click there
did nothing until the cursor came a few pixels in.

`widgets::resize_band` is the band's width right now: nothing while the
window is maximized or full screen. The buttons and the title bar's drag
area inset themselves by that, so maximized they reach the edges and the
close button takes the corner, as Windows' own caption buttons do.

A UI test reads the close button's area windowed and maximized, clicks
its corner in both states, and checks the window's own corner still
begins a resize.

Fixes #374

Co-authored-by: YangXZ2008 <141027290+YangXZ2008@users.noreply.github.com>
2026-10-24 01:43:30 -04:00
Hamza Naveed 00a7394fe1 An argument that is not UTF-8 is ignored, not the end of the start-up (#730)
`std::env::args` panics on an argument that is not valid UTF-8, and it panics
while collecting them, before the launcher looks at what it was given. Any
start-up with such an argument ended at

    internal error: panicked at library/std/src/env.rs:878:51:
    called `Result::unwrap()` on an `Err` value: "/tmp/caf\xE9.png"

with exit 101 and the launcher's own panic report, even for `--version`, which
the packaging smoke test runs on the staged binary.

The launcher looks for two flags and ignores everything else a desktop start-up
brings along (`-psn_0_…` from Finder, a file dropped on the icon, an old login
entry's leftovers), so an argument it cannot read is one it would have ignored
anyway: `args_os`, and a warning in the log naming it.

tests/arguments.rs covers it, and `--version` with the arguments a desktop
adds. Without the change the first test fails with exit 101 and that report.
2026-10-24 01:43:24 -04:00
JustWirelessandClaude Opus 5.5 1368d7c673 Windows: keep pinned taskbar icons across updates (#813)
The MSI's shortcuts took their icon from a WiX <Icon>, which Windows
Installer caches under %APPDATA%\Microsoft\Installer\{ProductCode}.
Every update is a major upgrade with a new ProductCode that deletes the
old folder. The Start Menu shortcut is reinstalled, but a taskbar pin is
a copy of it that no update refreshes, so its icon went blank.

The shortcuts now use the icon embedded in artcraft-launcher.exe, whose
path every update keeps. check-icons.ps1 fails on a shortcut that names
an MSI <Icon>, and checks ICE50 on ARPPRODUCTICON instead.

Fixes #643

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:43:18 -04:00
Nikolai Nyegaard f58b237275 Settings: list the languages alphabetically by their own name (#720)
- English stays first as the source language and fallback; the rest sort by native name with accents folded, so Čeština is under C and Magyar under M; Cyrillic and CJK names follow the Latin ones
- Lang::for_picker holds the order; the picker used the registry order, which is the order the languages were added
2026-10-24 01:43:13 -04:00
smittie2000andClaude Opus 5.5 16285cd62e xtask local: order pre-releases by semver (rc.10 after rc.9) (#820)
compare_versions compared pre-release tags as plain strings, so
1.0.0-rc.10 sorted before 1.0.0-rc.9. `cargo xtask local` uses it to pick
the newest mirrored build for `status` and to choose what `prune` keeps,
so a tenth release candidate would have been treated as older than the
ninth and pruned first.

Parse with the workspace's semver crate and compare by precedence, which
orders numeric pre-release parts as numbers.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:43:05 -04:00
.CooCoo e84fa1fa70 macOS: skip wgpu's indirect-call validation on Intel Macs (#569)
wgpu 30 turns on InstanceFlags::VALIDATION_INDIRECT_CALL by default and
compiles its indirect-validation compute shaders in request_device. On
Intel Iris and Iris Pro Macs, Metal's compiler fails on them with an
internal error, the device is lost and the launcher exits at startup.

wgpu_options() on macOS now removes the flag when the build is x86_64,
then applies with_env() so WGPU_VALIDATION_INDIRECT_CALL=1 turns it back
on. The launcher issues no indirect draws or dispatches. Apple silicon,
Windows and Linux keep wgpu's default. This follows PhotoCraft's fix in
storytold/photocraft#3171 (apps/photocraft/src/gpu_startup.rs).

Fixes #344
2026-10-24 01:42:59 -04:00
smittie2000andClaude Opus 5.5 baa61c0967 refactor(core): one truncate helper for release and feed text (#783)
feed.rs and release.rs each had the same `truncate` (cut to a byte budget on a char
boundary, then an ellipsis). The feed now uses release's, which gains a test for the
multi-byte case.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:42:47 -04:00
smittie2000andClaude Opus 5.5 ea86e195c7 fix(web): read ?lang= from the query, not the #fragment too (#786)
`?lang=de#apps` gave the tag `de#apps`, so the page fell back to the browser's
languages. The parsing is a small tested function now.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:42:41 -04:00