Commit Graph
222 Commits
Author SHA1 Message Date
Ian Holloway f1fe597eca Resolve Unix restart test helpers through PATH (#424)
Assisted-by: OpenAI Codex (GPT-6)

Signed-off-by: IanHollow <72767437+IanHollow@users.noreply.github.com>
2026-10-24 01:41:50 -04:00
Justin JohnandCodex 07f4f2959c test: avoid comparing different Windows catalog signers (#814)
Co-authored-by: Codex <codex@openai.com>
2026-10-24 01:41:44 -04:00
dependabot[bot] 03783ea980 ci(deps): bump Swatinem/rust-cache (#818)
Bumps the actions group with 1 update: [Swatinem/rust-cache](https://github.com/swatinem/rust-cache).


Updates `Swatinem/rust-cache` from 2.9.2 to 2.9.3
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/6323deb102c322ba6fcbdcafc7e3dddab59af2b6...d1527c8f67ef9af357290475d4af4b0fab8f12e0)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-version: 2.9.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-24 01:41:39 -04:00
Dylan M. Taylor 5752c9d881 chore: resolve SonarCloud maintainability issues (#844)
* chore: resolve SonarCloud maintainability issues

- Refactor table_header in crates/ui-egui/src/views.rs to reduce cognitive complexity (rust:S3776)
- Define SHA256_LINE_FMT constant in packaging/linux/package.sh (shelldre:S1192)

* fix(ci): fix shellcheck SC2059, clippy needless borrow, and cargo fmt

* fix(packaging): assign positional parameters to local variables in check_sha256
2026-10-23 22:33:18 -04:00
Dylan M. TaylorandCodebuff 674b893f0e Refactor the complex fns, expand wildcard imports, tidy closures and lifetimes (SonarCloud) (#836)
* Refactor the complex fns, expand wildcard imports, tidy closures and lifetimes (SonarCloud)

- S3776: split the twelve flagged functions (portable install, backend check
  sweep, app details drawer/hero/identity, toolbar and its banner rows, table
  header/rows/cells, action menu and its phases, settings install panels,
  widgets cells, narrow-header test) into focused helpers
- S2208: replace the nine `use super::*` in platform test modules with the
  exact imports each module needs
- S1612: call clone() on the iterator directly instead of in a closure
- S8833: drop the redundant reference in iterator-based loops
- shellcheck: use [[ ]] division guard in packaging/linux/package.sh

* Lint: pass a fixed printf format to sha256sum, not a variable (shellcheck SC2059)

The shellcheck gate flags printf with a variable format string; inline
'%s  %s' at the four sha256 check sites so the format is a literal. No
behavior change.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Fix: a Windows build needs CraftApp in the windows module's scope

uninstall_msi references CraftApp but the module's super import omitted
it, breaking the Windows clippy job only (caught by CI, not by the
Linux check the branch was verified with).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Address the CodeRabbit review: no missing-entry assert, and the paused
row's resume button measures against the full row width again

- details: return early when the shown detail has no matching entry
  (non-test code never panics); the assert only fired in debug builds
- actions_in: paused_actions gets the full row width, as the branch
  subtracted the discard and menu buttons from it itself; passing
  main_w sized the resume button one CONTROL_H + gap too narrow

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-23 17:14:44 -04:00
Dylan M. Taylor 7ad6ae1c95 ci(sonar): don't cancel superseded runs on main, so coverage is published (#837) 2026-10-23 17:00:55 -04:00
aiongg 2d3992cf0f Linux and FreeBSD install: run pkexec and package tools from the system directories (#689)
* Linux install: run package tools by absolute path

* Linux install: run pkexec from the system directories

* FreeBSD install: look for pkexec under /usr/local too
2026-10-23 13:03:01 -04:00
aiongg 9256915b76 Packaging: pin the signing client and appimagetool, and check their hashes (#688)
* Packaging: pin the Windows signing client and check its hash

* Packaging: pin appimagetool and check its hash

* Fail clearly when no appimagetool is pinned for the architecture

* Pin the AppImage runtime
2026-10-23 12:59:57 -04:00
Dylan M. Taylor ce3b29f4f2 packaging: [[ ]] tests, named positional parameters and explicit returns (SonarCloud S7688, S7679, S7682) (#808)
* packaging: [[ ]] tests, named positional parameters and explicit returns (SonarCloud S7688, S7679, S7682)

Every packaging script is bash (macOS ships bash 3.2, which has [[ ]]), so the
single-bracket tests become [[ ]]: no word splitting or globbing of unquoted
operands. Every right-hand side of = is quoted or a plain literal, so no test
turns into a pattern match.

Functions that read positional parameters copy them into named locals, and the
env.sh helpers end with a bare return, which keeps the last command's status.

* fix: use POSIX test in generated /bin/sh helper
2026-10-23 11:28:35 -04:00
Dylan M. Taylor 3829b32c3b Merge pull request #824 from dylanmtaylor/sonar/nosonar-placement
fix: put NOSONAR markers on the flagged lines (SonarCloud follow-up)
2026-10-23 09:53:42 -04:00
Dylan M. Taylor a010a50d73 fix: put NOSONAR markers on the flagged lines (SonarCloud)
Sonar applies NOSONAR to the line that carries the marker, so the
explanations above the calls did not suppress anything.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-10-23 09:44:36 -04:00
Dylan M. Taylor 9d5b411c0a Merge pull request #823 from dylanmtaylor/sonar/security-reliability
fix: document install permissions and keep recovery backups (SonarCloud)
2026-10-23 09:37:41 -04:00
Dylan M. Taylor d861df547a fix: spell out why installs use 0755 and keep recovery backups (SonarCloud)
The AppImage and unpacked binary must be executable, and 0755 is not
group/world-writable; the staging directory is handed to the operator
when a rollback fails, so its Drop must not delete the only copy left.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-10-23 09:29:39 -04:00
skylartaylor 9419d8977b Merge pull request #756: install wrapped portable ZIP releases
Accept validated flat and version-folder-wrapped archives. Adapt the newer self-update replacement caller to the extractor result so the combined Windows source builds.
2026-10-23 02:40:45 +02:00
skylar taylor-barrick 49a8ba5dd9 Merge pull request #377: refresh past a lagging release feed
Check the latest GitHub release redirect during manual refresh before trusting older feed metadata.
2026-10-22 18:39:02 -06:00
skylar taylor-barrick 89c5ca017a Merge pull request #524: keep settings inside a portable suite
Store portable settings and window state in ArtCraftLauncherData/data/app.ron.
2026-10-22 18:38:50 -06:00
Brandon ThomasandClaude Opus 5.5 9f93806e1e Merge the release branch (0.6.2) into main, keeping 0.7.0
The 0.6.2 version commit landed on release but never on main, so release
could not fast-forward. Both sides only changed version numbers; main's
0.7.0 wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.7.0
2026-10-22 20:13:15 -04:00
Dylan M. Taylor c190a590bc Merge pull request #750 from dylanmtaylor/security/ipc-local-service-hardening
security: authenticate single-instance replace and bound the local mirror server
2026-10-22 19:41:10 -04:00
Dylan M. Taylor 7cf50d7a10 security: address CodeRabbit review on IPC token CSPRNG, retry, and mirror timeouts 2026-10-22 19:28:40 -04:00
Dylan M. Taylor 329e9c2fb7 Merge pull request #749 from dylanmtaylor/security/install-filesystem-safety
security: harden local install and packaging against filesystem attacks
2026-10-22 19:28:01 -04:00
Dylan M. Taylor d75fd3b2d1 security: authenticate single-instance replace and bound the local mirror server
Two local-service hardening findings:

- Single-instance replacement authority (CWE-862): the listener on the fixed loopback
  port 127.0.0.1:47413 granted a Replace (which can make an idle launcher quit) on a
  peer's claimed version alone, so a different local OS account could end the victim's
  launcher. A per-user token now gates Replace: it lives in the user's own data folder
  (created owner-only, 0600 on Unix), the second start echoes it in its hello, and the
  primary only honours Replace when the token matches. An unauthenticated peer may
  still bring the window forward (Show, harmless), never quit the launcher. The token
  is derived without a new RNG crate, hashing OS-seeded RandomState with sha2.

- Local mirror server DoS (xtask local serve, CWE-770): a thread was spawned per
  connection with no admission limit, request reads had no deadline (idle clients held
  threads forever), and a failed thread spawn could panic the listener. Add a bounded
  handler cap (64; excess connections get 503), a 15s request-header read deadline
  lifted before streaming the body, and graceful handling of spawn failure.

Addresses findings: single_instance.rs CWE-862, xtask/local.rs CWE-770.
2026-10-22 19:22:00 -04:00
Dylan M. Taylor ceffdd6d5d style: rustfmt 2026-10-22 19:16:10 -04:00
Dylan M. Taylor 398dd79332 Merge pull request #748 from dylanmtaylor/security/resource-limits
security: bound decoded feed size, screenshot dimensions, and locale tag matching
2026-10-22 17:54:15 -04:00
Dylan M. Taylor cc68b9a2d4 http: cap the decoded text body, not the compressed wire bytes
CodeRabbit caught that ureq's body limit sits *below* its transparent gzip
decoder, so limit(max + 1) bounded the compressed bytes on the wire, not the
decoded text: a few hundred gzipped bytes still expanded past the cap into
truncated-but-parseable-looking data (CWE-409). read_text_capped now caps the
fully decoded reader with take(max + 1) and still refuses an oversized answer
outright, before parsing; a new test serves a real gzip bomb and a small gzip
answer to pin both halves.
2026-10-22 17:41:03 -04:00
Brandon ThomasandClaude Opus 5.5 5f3b7aaf4b Release 0.7.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:37:28 -04:00
skylar taylor-barrick 26a3462624 Merge pull request #742: replace PowerShell in Windows self-updates
Validate the portable launcher before replacement and retain rollback backups until installed files pass validation.
2026-10-22 12:11:21 -06:00
skylartaylor 20715c1eed Merge current main and fix the CI formatting failures 2026-10-22 19:59:21 +02:00
Dylan M. Taylor d24cd0e1ba Merge pull request #751 from dylanmtaylor/security/url-opener-injection
security: reject shell metacharacters in opened web links (WSL cmd.exe injection)
2026-10-22 13:51:20 -04:00
dotCooCoo bc2a9fbe65 fix(windows): keep running when the update helper would use other folders
When the launcher can't find its per-user folders, it keeps its files in
the temporary folder. The update helper finds the folders again from the
installed exe. It then failed to find them, or found a cache other than
the one holding the update, and stopped after the launcher had quit
without starting the launcher again.

Before the launcher quits, the update now also checks that the helper
finds the cache holding the download, the staging folder and the
helper's copy. Otherwise the update stops with an error and the launcher
keeps running.
2026-10-22 10:24:22 -07:00
Dylan M. Taylor af4270fb6d security: reject shell metacharacters in opened web links
Feed notice links and release-note hyperlinks are forwarded to the system opener
when clicked. The pinned webbrowser crate's WSL fallback passes the URL to
cmd.exe /c start while escaping only ^ and &, so a pipe (or other metacharacter) in
an otherwise valid https URL becomes a command separator and runs a Windows command
(CWE-78).

Rather than depend on the third-party opener to neutralise the URL, refuse any link
containing a shell/cmd metacharacter at the validation boundary, where a real https
web link never needs one:

- release::safe_web_link (release notes) now rejects shell metacharacters via the
  shared release::is_shell_meta, on top of the existing scheme/host/whitespace checks.
- The feed notice link filter routes through the same rule and also rejects control
  characters.

Addresses finding: widgets.rs/webbrowser WSL fallback CWE-78.
2026-10-22 13:02:34 -04:00
Dylan M. Taylor 693ad64275 Merge pull request #726 from dylanmtaylor/time/chrono-1785
Use jiff instead of hand-rolled UTC conversion
2026-10-22 12:43:55 -04:00
Dylan M. Taylor 9301052571 Merge pull request #747 from dylanmtaylor/security/feed-release-url-confinement
security: confine release/feed download URLs and bind installed artifact version
2026-10-22 12:41:02 -04:00
Hasan Tan b34a123da5 style: rustfmt wrapper directory-entry regression test 2026-10-23 01:54:43 +11:00
Hasan Tan d7e33b9c8b fix(windows): validate ZIP directory entries in wrapper layout
Ensure explicit empty ZIP directories cannot bypass the single-wrapper validation. Keep file-only ownership tracking and add a regression test.
2026-10-23 01:52:35 +11:00
Hasan Tanandkbpal 1d16d8ddf8 test(windows): cover wrapped portable install lifecycle
Add the three regression tests contributed by kbpal, without changing production code.
Original: https://github.com/kbpal/craft-launcher/commit/090cce42a454ffe6cfbd54b2cb0df618d6875c6e

Co-authored-by: kbpal <11362533+kbpal@users.noreply.github.com>
2026-10-23 01:39:26 +11:00
skylartaylor 123032ea07 style: wrap portable update signatures and documentation 2026-10-22 15:59:32 +02:00
skylartaylor b9b24da3d2 Merge main and retain both Windows API features 2026-10-22 15:58:43 +02:00
skylartaylor c868893f30 fix: validate the portable launcher before committing its update 2026-10-22 15:58:28 +02:00
skylar taylor-barrick d405a75016 Merge pull request #743: read Windows versions without PowerShell
Read ProductVersion directly from Windows VERSIONINFO, including fallback string tables.

Validated by Windows and Linux tests, WASM checks, Nix evaluation, and SonarCloud.
2026-10-22 07:33:17 -06:00
BFlat baf0e33eb3 Merge pull request #759 from storytold/readme/app-icon
README: the app icon above the title
2026-10-22 18:01:16 +07:00
bflatastic eb3fbfadc2 README: the app icon above the title 2026-10-22 17:57:26 +07:00
Hasan Tan 6fbc078422 fix(windows): install version-folder wrapped portable ZIP releases 2026-10-22 16:27:11 +11:00
Dylan M. Taylor bf39e92f30 Merge pull request #745 from storytold/sonar-fix/pin-actions-sha
ci: pin GitHub Actions to full commit SHA (SonarCloud S7637)
2026-10-21 19:34:28 -04:00
Dylan M. Taylor 78cbc6e149 Merge pull request #746 from storytold/sonar-fix/enforce-https
security: enforce HTTPS on curl downloads (SonarCloud S6506)
2026-10-21 19:34:11 -04:00
Dylan M. Taylor 12b5064631 Say the library computes month and day; time-of-day is plain arithmetic
Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-21 19:07:04 -04:00
Dylan M. Taylor 1ef6b1a53e Name the 400-year range extension as the one exception to the time rule
The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past jiff's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.
2026-10-21 18:29:19 -04:00
Dylan M. Taylor 44ea2ecd0d security: bound decoded feed, screenshot dimensions, and locale tags
Three resource-exhaustion findings in the same untrusted-input handling:

- HTTP feed/API/text (CWE-409): read_to_string(limit(MAX_SMALL)) silently truncates,
  and ureq's transparent gzip means a small compressed response can decode far past
  the compressed-byte count. Add read_text_capped, which reads one byte past the cap
  and rejects an oversized *decoded* body before it is parsed. Used by get_api,
  get_feed and get_text.

- Screenshot decode (CWE-789): bytes were accepted on the JPEG signature and a 2 MiB
  byte cap only, but a tiny JPEG can declare 65535x65535 and egui_extras/image
  allocate width*height*channels from the header before decoding. Add
  details::jpeg_dimensions and screenshot_within_budget (8192x8192 budget), enforced
  in screenshots::download and saved_copy, and re-checked in Picture::from_result
  before bytes reach egui.

- Locale matching (CWE-400): candidates() built every shorter prefix of an unbounded
  URL lang value, allocating quadratically. Bound tag length (64) and subtag count (8)
  before building candidates.

Addresses findings: http.rs CWE-409, details.rs CWE-789, lang.rs CWE-400.
2026-10-21 18:00:49 -04:00
Dylan M. Taylor a483ae81f6 security: confine release/feed download URLs to the repository and bind artifact version
Download URLs were accepted on a raw starts_with(prefix) check. A value like
.../releases/download/../../../ATTACKER/REPO/releases/download/v99/app shares the
prefix textually but its interpreted path escapes into another repository (CWE-180),
and the Linux AppImage self-update then downloads and executes those bytes.

- Add release::download_url_under / download_url_under_mirror, which require the text
  after the prefix to be exactly <tag>/<name>: two non-empty segments, no traversal
  (./..), no extra slashes, no query/fragment/backslash, and no percent-encoded
  separators (%2f/%5c). release_from_value and FeedRelease::to_release share it.
- Bind the installed artifact to the release that authorized it: before download,
  bind_artifact_version rejects an artifact whose filename-encoded version or URL
  release tag disagrees with release.version, so a feed cannot advertise a new
  version while shipping an older official build and persist a false version.

Addresses findings: release.rs CWE-180, feed.rs CWE-180, platform/install version binding.
2026-10-21 17:51:41 -04:00
Dylan M. Taylor 0091a46d3b time: keep old UTC output past jiff's narrow civil range
`civil_from_unix` (core) and `rfc3339` (xtask) took `i64` Unix seconds, which
the old hand-rolled Hinnant code converted across the whole `i64` range. The
jiff rewrite built the date with `epoch.checked_add(whole_days).unwrap_or(epoch)`,
but jiff's civil calendar only spans years -9999..=9999 — far narrower than
chrono's — so any instant past year 9999 silently became the epoch, changing
existing output. `i64::MAX` used to read `Dec 4, 292277026596` /
`292277026596-12-04T15:30:07Z`, and `i64::MIN` `Jan 27, -292277022657`, not 1970.

Fix: move the instant into jiff's range by whole 400-year Gregorian cycles
(146_097 days) before converting, then restore the real year afterwards. `rest`
is always under one cycle, so the in-range date lands in 1970..=2369 (well inside
jiff) and `rest * 86_400` can't overflow; the year is restored through `i128` so
`cycles * 400` is exact at the `i64` extremes. jiff stays the only calendar logic
(it does the month/day); the cycle count just shifts the year. No epoch fallback
for any value the old code converted. core still builds for wasm32.

Tests now pin the old output instead of the epoch:
- core civil_from_unix(i64::MAX) == (292277026596, 12, 4),
  civil_from_unix(i64::MIN) == (-292277022657, 1, 27),
  civil_from_unix(253_402_300_800) == (10000, 1, 1)
- xtask rfc3339(i64::MAX) == "292277026596-12-04T15:30:07Z",
  rfc3339(i64::MIN) == "-292277022657-01-27T08:29:52Z",
  rfc3339(253_402_300_800) == "10000-01-01T00:00:00Z"
2026-10-21 17:22:06 -04:00
Dylan M. Taylor 89ff63e6fc security: enforce HTTPS on curl downloads (SonarCloud S6506)
Add --proto '=https' to curl invocations so redirects cannot downgrade to
plaintext HTTP. Resolves SonarCloud S6506 (shell/docker/githubactions).
2026-10-21 17:06:36 -04:00