Skip to content

Trim release bundle size - #7589

Merged
lawrencecchen merged 6 commits into
mainfrom
feat-release-size-trim
Jul 11, 2026
Merged

lawrencecchen merged 6 commits into
mainfrom
feat-release-size-trim

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • compress all bundled markdown-viewer JS/MJS assets recursively, including diff viewer chunks and webview app assets
  • serve copied .deflate diff-viewer assets through the CLI HTTP server and WKURLSchemeHandler with Content-Encoding: deflate
  • strip cmux-owned release binaries before codesigning in nightly and stable release workflows

Size impact

  • Resources/markdown-viewer JS/MJS: 721 assets, 35.23 MiB raw -> 7.32 MiB zlib deflate, saving 27.91 MiB before DMG compression
  • Debug tagged bundle check: 0 raw JS/MJS files, 721 .deflate files under Contents/Resources/markdown-viewer
  • Release/nightly workflows now run scripts/strip-release-bundle.sh before signing, covering Contents/MacOS/cmux, Contents/Resources/bin/cmux, cmux plugins, and libcmux_*.dylib

Verification

Local Swift test execution was blocked by the cmuxterm-hq guard against local xcodebuild test; PR CI should run the focused XCTest coverage added here.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Touches release signing prep and diff/markdown asset HTTP delivery; regressions could break the diff viewer or signed release binaries, though CI guards and smoke tests mitigate this.

Overview
Shrinks shipped macOS bundles by deflating bundled markdown/diff viewer JS and stripping cmux-owned Mach-O binaries before release signing.

Compressed web assets: The markdown-viewer compress script now walks all nested .js/.mjs files (not only top-level), writes sibling *.deflate blobs, and deletes the raw sources. Runtime paths stay the same: the CLI diff-viewer HTTP server, BrowserPanel custom URL scheme, and bundled asset copy/hash logic prefer .deflate on disk, map URLs without the suffix, and set Content-Encoding: deflate. In-app markdown loading uses explicit zlib inflate for deflated text assets. Diff-viewer asset helpers move into CMUXCLI+DiffViewerBundledAssets.swift.

Release pipeline: New scripts/strip-release-bundle.sh runs in nightly and stable release workflows immediately before codesign, stripping local symbols from the app binary, bundled CLI, cmux plug-ins, and libcmux_*.dylib (not third-party frameworks). CI adds guard tests for compression and stripping behavior.

Reviewed by Cursor Bugbot for commit cbabd7b. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Compresses all markdown/diff viewer JS modules and strips macOS binaries to shrink the app bundle, saving ~27.91 MiB before DMG compression. Prefers .deflate assets end-to-end while keeping URLs stable and serving them with the correct Content-Encoding in both the CLI server and the webview.

  • New Features

    • Recursively compress and ship .deflate for all markdown-viewer .js/.mjs (skip existing .deflate); serve with Content-Encoding: deflate while keeping original URLs.
    • Add scripts/strip-release-bundle.sh in nightly/release to strip cmux-owned Mach-O binaries before codesigning; CI runs guard tests for compression and stripping.
  • Bug Fixes

    • Centralize diff-viewer asset discovery/copying/hashing; prefer .deflate, dedupe raw vs deflated, and keep the on-disk suffix without changing request paths.
    • Improve deflated asset handling: robust zlib inflate in-app/webview; tests use failable decoding and cover recursive compression, ensuring existing .deflate files aren’t rewritten.

Written for commit cbabd7b. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Enhanced support for compressed (“deflated”) bundled diff/markdown viewer assets, including consistent viewer URL mapping and proper client decoding.
  • Bug Fixes
    • Improved deterministic asset staging and allowlist behavior for deflated variants.
    • Improved markdown viewer deflated text decompression for better correctness.
  • CI / Release
    • macOS release and nightly builds now strip local symbols from release binaries before signing.
    • CI adds validations for markdown viewer asset compression and release bundle stripping.
  • Tests
    • Updated tests to cover deflated module fixtures and validate the new stripping behavior.

@vercel

vercel Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 11, 2026 11:02am

@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds deflate-based compression and serving for markdown and diff viewer JavaScript assets, including asset resolution, staging, decompression, and HTTP headers. It also adds release bundle Mach-O stripping before codesigning, with CI, nightly, release, shell, and Swift test coverage.

Changes

Deflated Asset Compression and Serving

Layer / File(s) Summary
Compression script and tests
scripts/compress-markdown-viewer-assets.sh, tests/test_compress_markdown_viewer_assets.sh
Recursively compresses .js/.mjs files into .deflate outputs via zlib and validates round-trip decompression.
Manual zlib inflate and BrowserPanel fixtures
Sources/Panels/MarkdownViewerAssets.swift, cmuxTests/BrowserPanelTests.swift
Uses a manual z_stream inflater for deflated text assets and updates scheme fixtures to register compressed modules.
CLI asset resolution and staging
CLI/CMUXCLI+DiffViewerBundledAssets.swift, CLI/cmux_open.swift
Resolves raw or .deflate variants, hashes resolved contents, and collects exact staged asset URLs.
Content-Encoding headers
CLI/cmux_open.swift, Sources/Panels/BrowserPanel.swift
Adds Content-Encoding: deflate when serving compressed files.
CLI compressed-asset tests and project wiring
cmuxTests/CMUXOpenCommandTests.swift, cmuxTests/DeflatedAssetTestSupport.swift, cmux.xcodeproj/project.pbxproj
Updates fixtures and assertions for deflated assets, adds zlib test helpers, and registers the new Swift sources in the Xcode project.

Release Bundle Symbol Stripping

Layer / File(s) Summary
Bundle stripping implementation and test
scripts/strip-release-bundle.sh, tests/test_strip_release_bundle.sh
Strips eligible Mach-O binaries from app bundles and validates the exact target paths.
Workflow integration
.github/workflows/nightly.yml, .github/workflows/release.yml, .github/workflows/ci.yml
Runs stripping before codesigning and adds CI guards for both scripts.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DiffViewerServer as diff-viewer-server
  participant AssetStore as Staged .deflate Assets

  Client->>DiffViewerServer: GET /assets/mod.mjs
  DiffViewerServer->>AssetStore: Resolve mod.mjs to mod.mjs.deflate
  AssetStore-->>DiffViewerServer: Return compressed bytes
  DiffViewerServer-->>Client: 200 response with Content-Encoding: deflate
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error Non-test Swift adds DispatchSemaphore.wait/queue.sync in RemotePTYBridgeServer and Task.sleep in BrowserFileDropNavigationGuard and BrowserPrewarmedWebViewPool. Replace the bridge join with an async callback/state transition, and the expiry sweeps with a cancellation-aware timer or async sequence instead of Task.sleep.
Cmux Full Internationalization ❌ Error New production CLIError text (“Failed to enumerate…” / “Bundled diff viewer asset not found…”) is added without localization API or xcstrings entries. Route those errors through String(localized:defaultValue:) (or equivalent) and add matching Resources/Localizable.xcstrings entries for every supported locale.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (22 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PASS: the PR adds only pure helpers and header/compression logic; no new implicit MainActor value models, unsafe Sendable refs, or background UI-store access.
Cmux Browser Automation Off-Main ✅ Passed PR only changes viewer asset serving/strip/compress code; the browser socket router and policy tests already keep waiting browser commands on the worker lane.
Cmux Expensive Synchronous Load ✅ Passed PASS: The Swift changes only adjust asset compression/serving; no new main-actor or interactive agent-history/session/JSONL load was added, and agent-turn-diff-baselines remains path-only.
Cmux Cache Substitution Correctness ✅ Passed No persistence/history/undo/snapshot path swaps in a stale cache; the refs cache has cold-miss compute and TTL refresh, and asset copying checks freshness.
Cmux No Hacky Sleeps ✅ Passed Changed shell scripts/tests add no sleeps, timers, or polling; workflow YAML waits are explicitly out of scope here.
Cmux Algorithmic Complexity ✅ Passed No new nested scans in scalable user-data paths; added loops/sorts are over bounded bundled assets or tiny fixed sets, with no hot-path rescans introduced.
Cmux Swift Concurrency ✅ Passed The PR’s touched Swift hunks only add deflate asset handling, header changes, and safer test decoding; no new DispatchQueue/Task/completion-handler patterns were introduced or expanded.
Cmux Swift @Concurrent ✅ Passed PASS: The PR’s Swift changes are synchronous file/response-header updates; no new nonisolated async or @concurrent misuse appears in the diff.
Cmux Swift File And Package Boundaries ✅ Passed Changes are small glue/focused fixes: a 40-line CLI helper, a 193-line app asset loader, and incidental edits in already oversized files; no clear package-boundary breach.
Cmux Swiftpm Lockfiles ✅ Passed The only package-adjacent file changed is cmux.xcodeproj/project.pbxproj, and its diff only adds source files; no SwiftPM package refs or Package.resolved/.gitignore changes appear.
Cmux Swift Logging ✅ Passed No production Swift logging was added or changed; the only current code change is a test helper with no logging, and existing app logs are outside the diff.
Cmux User-Facing Error Privacy ✅ Passed PASS: touched production user-facing text is generic; the only vendor mention is Sentry in an operational build-script usage banner, not end-user copy.
Cmux Swiftui State Layout ✅ Passed PASS: the diff only adds deflate-serving, zlib inflation, and tests; no new SwiftUI state/layout patterns appear, and BrowserPanel’s change is just a response header tweak.
Cmux Architecture Rethink ✅ Passed PASS — the Swift changes are localized asset-resolution/serving correctness fixes; they add no new timing, observer, lock, or split-ownership paths.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed Touched Swift files are asset-serving/tests only; no new NSWindow/NSPanel/WindowGroup or cmux.* identifier changes, so the auxiliary-window shortcut rule isn't implicated.
Cmux Source Artifacts ✅ Passed Only changed path is a hand-written test helper (cmuxTests/DeflatedAssetTestSupport.swift); no logs/tmp/build/artifact paths were added, so it fits the pass category.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PASS: the touched Sources files add deflate serving/zlib inflation only; no new #if DEBUG test/debug accessor or ForTesting/TestHook-style seam was introduced.
Cmux No Ambient Global State ✅ Passed Only changed file is test-only DeflatedAssetTestSupport.swift; no new production top-level funcs, globals, or singletons were added.
Title check ✅ Passed The title is concise and accurately reflects the main change: shrinking the release bundle by trimming assets and binaries.
Description check ✅ Passed The description covers the summary, size impact, and verification steps; it is mostly complete even though some template sections are absent.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-release-size-trim

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR trims the macOS release bundle by compressing viewer assets and stripping bundled binaries. The main changes are:

  • Compress recursive markdown and diff-viewer JavaScript assets into .deflate files.
  • Keep logical module URLs unchanged while serving deflated files with Content-Encoding: deflate.
  • Prefer deflated bundled assets when raw and compressed variants both exist.
  • Strip cmux-owned Release Mach-O binaries before codesigning.
  • Add focused tests for asset compression, deflated serving, and release-bundle stripping.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
CLI/CMUXCLI+DiffViewerBundledAssets.swift Adds shared diff-viewer asset helpers that collapse .deflate variants to logical paths and prefer compressed files.
CLI/cmux_open.swift Updates diff-viewer asset staging, manifest generation, and HTTP serving for deflated assets.
Sources/Panels/BrowserPanel.swift Adds deflate response headers for WKURLSchemeHandler asset responses backed by compressed files.
Sources/Panels/MarkdownViewerAssets.swift Adds direct zlib inflation for compressed markdown-viewer text assets.
scripts/compress-markdown-viewer-assets.sh Compresses recursive JS and MJS markdown-viewer assets and removes the raw copies.
scripts/strip-release-bundle.sh Adds a release helper that strips cmux-owned Mach-O binaries before signing.

Reviews (5): Last reviewed commit: "Use failable deflated fixture decoding" | Re-trigger Greptile

Comment thread CLI/cmux_open.swift Outdated
Comment on lines +7761 to +7768
let rawURL = sourceDirectory.appendingPathComponent(relativePath, isDirectory: false)
if FileManager.default.fileExists(atPath: rawURL.path) {
return rawURL
}
let deflatedURL = sourceDirectory.appendingPathComponent(relativePath + ".deflate", isDirectory: false)
if FileManager.default.fileExists(atPath: deflatedURL.path) {
return deflatedURL
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Raw Variant Shadows Deflate

When an incremental bundle contains both foo.mjs and foo.mjs.deflate, the enumeration collapses them to one logical path and this resolver returns the raw file first. The copied asset then serves stale or uncompressed bytes for the same request path instead of the compressed artifact the release bundle expects.

Suggested change
let rawURL = sourceDirectory.appendingPathComponent(relativePath, isDirectory: false)
if FileManager.default.fileExists(atPath: rawURL.path) {
return rawURL
}
let deflatedURL = sourceDirectory.appendingPathComponent(relativePath + ".deflate", isDirectory: false)
if FileManager.default.fileExists(atPath: deflatedURL.path) {
return deflatedURL
}
let rawURL = sourceDirectory.appendingPathComponent(relativePath, isDirectory: false)
let deflatedURL = sourceDirectory.appendingPathComponent(relativePath + ".deflate", isDirectory: false)
let hasRaw = FileManager.default.fileExists(atPath: rawURL.path)
let hasDeflated = FileManager.default.fileExists(atPath: deflatedURL.path)
if hasRaw && hasDeflated {
throw CLIError(message: "Bundled diff viewer asset has both raw and deflated variants: \(relativePath)")
}
if hasDeflated {
return deflatedURL
}
if hasRaw {
return rawURL
}

Comment thread CLI/cmux_open.swift Outdated
Comment on lines +7761 to +7768
let rawURL = sourceDirectory.appendingPathComponent(relativePath, isDirectory: false)
if FileManager.default.fileExists(atPath: rawURL.path) {
return rawURL
}
let deflatedURL = sourceDirectory.appendingPathComponent(relativePath + ".deflate", isDirectory: false)
if FileManager.default.fileExists(atPath: deflatedURL.path) {
return deflatedURL
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Prefer deflated variant When a bundle contains both foo.mjs and foo.mjs.deflate, the logical path is collapsed to foo.mjs, but this resolver still returns the raw file first. That can copy, hash, and serve stale or uncompressed bytes for the same module URL even though the compressed artifact is present. Prefer the .deflate file when both variants exist so the logical asset path resolves to the release artifact.

Suggested change
let rawURL = sourceDirectory.appendingPathComponent(relativePath, isDirectory: false)
if FileManager.default.fileExists(atPath: rawURL.path) {
return rawURL
}
let deflatedURL = sourceDirectory.appendingPathComponent(relativePath + ".deflate", isDirectory: false)
if FileManager.default.fileExists(atPath: deflatedURL.path) {
return deflatedURL
}
let deflatedURL = sourceDirectory.appendingPathComponent(relativePath + ".deflate", isDirectory: false)
if FileManager.default.fileExists(atPath: deflatedURL.path) {
return deflatedURL
}
let rawURL = sourceDirectory.appendingPathComponent(relativePath, isDirectory: false)
if FileManager.default.fileExists(atPath: rawURL.path) {
return rawURL
}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cmuxTests/BrowserPanelTests.swift (1)

793-800: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider removing the redundant evaluateJavaScript assertion.

The moduleLoaded message-handler assertion at line 792 already validates the full module/worker/WASM execution result ("module-ok:js-ok:wasm-ok"). The follow-up evaluateJavaScript("document.body.dataset.loaded || ''") block at lines 793-799 re-checks the same value through a second round-trip, adding test latency without increasing coverage. If this was intended to be removed per the change plan, it should be; if intentionally kept as a belt-and-suspenders check, consider adding a brief comment explaining why both assertions are needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmuxTests/BrowserPanelTests.swift` around lines 793 - 800, Remove the
redundant evaluateJavaScript assertion and its evaluated expectation from the
test, keeping the existing moduleLoaded message-handler assertion as the sole
validation of the full module/worker/WASM result.
CLI/cmux_open.swift (1)

7602-7644: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider content-keying the main "pierre-diffs" asset directory like the app asset directory already is.

The app asset directory is content-hashed (diffViewerAppAssetContentKey, lines 7658-7683) specifically so different webview bundles never clobber each other in the shared per-uid /tmp/cmux-diff-viewer-<uid> cache. The main asset directory name ("pierre-diffs-1.2.7-trees-1.0.0-beta.4", line 7604) is still a fixed literal, not content-keyed. Now that assets can transition between raw and .deflate representations under the same pinned version string (this PR's whole premise), a future release that recompresses the same vendor bundle version without bumping that string can leave a stale raw variant sitting alongside the new .deflate variant in the same shared directory indefinitely (pruneDiffViewerFiles only sweeps .html/.patch/manifest/session/lock/cache files, never assets/ subdirectories). This doesn't break correctness today (the manifest is built from fresh copy results, not a directory rescan), but it's an unbounded, never-cleaned disk-growth path across upgrades.

Reusing the existing diffViewerAppAssetContentKey-style hashing for the main asset directory name would close this gap with the same pattern already proven for the app assets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLI/cmux_open.swift` around lines 7602 - 7644, The main diff viewer asset
directory in ensureDiffViewerAssets should be content-keyed rather than using
the fixed assetDirectoryName literal. Reuse the existing
diffViewerAppAssetContentKey-style hashing for the bundled main asset source,
use the resulting name consistently for targetDirectory and all returned module
URLs, and preserve the existing asset-copy and validation behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/DeflatedAssetTestSupport.swift`:
- Around line 10-14: Update DeflatedAssetTestSupport.loadText to construct the
string with failable String(bytes:encoding:) using the decompressed data, and
propagate a decoding failure through the existing throws behavior instead of
silently replacing invalid UTF-8.

---

Outside diff comments:
In `@CLI/cmux_open.swift`:
- Around line 7602-7644: The main diff viewer asset directory in
ensureDiffViewerAssets should be content-keyed rather than using the fixed
assetDirectoryName literal. Reuse the existing
diffViewerAppAssetContentKey-style hashing for the bundled main asset source,
use the resulting name consistently for targetDirectory and all returned module
URLs, and preserve the existing asset-copy and validation behavior.

In `@cmuxTests/BrowserPanelTests.swift`:
- Around line 793-800: Remove the redundant evaluateJavaScript assertion and its
evaluated expectation from the test, keeping the existing moduleLoaded
message-handler assertion as the sole validation of the full module/worker/WASM
result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6205722-5261-4c79-8f87-a4848c7c0a20

📥 Commits

Reviewing files that changed from the base of the PR and between b8d2746 and 06105df.

📒 Files selected for processing (7)
  • CLI/CMUXCLI+DiffViewerBundledAssets.swift
  • CLI/cmux_open.swift
  • Sources/Panels/BrowserPanel.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/BrowserPanelTests.swift
  • cmuxTests/CMUXOpenCommandTests.swift
  • cmuxTests/DeflatedAssetTestSupport.swift

Comment on lines +10 to +14
static func loadText(path: String) throws -> String {
let data = try Data(contentsOf: URL(fileURLWithPath: path))
let decompressed = try (data as NSData).decompressed(using: .zlib) as Data
return String(decoding: decompressed, as: UTF8.self)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer failable String(bytes:encoding:) over String(decoding:as:).

String(decoding:as:) never fails; invalid UTF-8 silently becomes replacement characters instead of surfacing a decode error, which could mask a real decompression/corruption bug in the deflated fixture.

♻️ Proposed fix
-        return String(decoding: decompressed, as: UTF8.self)
+        guard let text = String(bytes: decompressed, encoding: .utf8) else {
+            throw CocoaError(.fileReadCorruptFile)
+        }
+        return text
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static func loadText(path: String) throws -> String {
let data = try Data(contentsOf: URL(fileURLWithPath: path))
let decompressed = try (data as NSData).decompressed(using: .zlib) as Data
return String(decoding: decompressed, as: UTF8.self)
}
static func loadText(path: String) throws -> String {
let data = try Data(contentsOf: URL(fileURLWithPath: path))
let decompressed = try (data as NSData).decompressed(using: .zlib) as Data
guard let text = String(bytes: decompressed, encoding: .utf8) else {
throw CocoaError(.fileReadCorruptFile)
}
return text
}
🧰 Tools
🪛 ast-grep (0.44.1)

[error] 10-10: A file is read from a path built from runtime/request input via FileManager.contents(atPath:), Data(contentsOf:), or String(contentsOfFile:). An attacker can supply '../' sequences or absolute paths to read files outside the intended directory (path traversal). Validate and canonicalize the path, reject '..' components, and confine reads to an allow-listed base directory (e.g. resolve with URL(fileURLWithPath:relativeTo:) and verify the resolved path is still inside the base) before reading.
Context: Data(contentsOf: URL(fileURLWithPath: path))
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(path-traversal-file-read-request-input-swift)

🪛 SwiftLint (0.65.0)

[Warning] 13-13: Prefer failable String(bytes:encoding:) initializer when converting Data to String

(optional_data_string_conversion)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmuxTests/DeflatedAssetTestSupport.swift` around lines 10 - 14, Update
DeflatedAssetTestSupport.loadText to construct the string with failable
String(bytes:encoding:) using the decompressed data, and propagate a decoding
failure through the existing throws behavior instead of silently replacing
invalid UTF-8.

Source: Linters/SAST tools

# Conflicts:
#	cmux.xcodeproj/project.pbxproj
@lawrencecchen
lawrencecchen merged commit 5463a5f into main Jul 11, 2026
30 checks passed
@lawrencecchen
lawrencecchen deleted the feat-release-size-trim branch July 11, 2026 11:17
@lawrencecchen
lawrencecchen restored the feat-release-size-trim branch July 18, 2026 10:18

This branch was successfully deployed

1 active deployment
Preview – cmux — cbabd7bb Deployed Jul 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant