Skip to content

File Browser command allowlist, share, archive, symlink, Fleet, Fabric.js, and Cordova callback boundary checks

Source: hourly offensive-security scan, 2026-06-12. Primary entries: GitHub advisories GHSA-8c9q-7855-wfxq / CVE-2026-54090, GHSA-3q2p-72cj-682c / CVE-2026-54096, GHSA-5ww9-jg6q-38r7 / CVE-2026-54097, GHSA-j9jx-hp4c-ghhh / CVE-2026-54091, GHSA-gxjx-7m74-hcq8 / CVE-2026-54093, GHSA-239w-m3h6-ch8v / CVE-2026-54094, GHSA-vxm7-9x8v-8gm4 / CVE-2026-46370, GHSA-x4qr-qw6h-wvxq / CVE-2026-46371, GHSA-w22m-hvvm-xmwx / CVE-2026-44311, and updated Cordova Plugin InAppBrowser advisory GHSA-q42j-x8rq-pjg6 / CVE-2026-47430.

This batch is durable because each item exposes a reusable operator boundary: command allowlists that validate the first token but hand the full raw string to a configured shell, future public-share grants bound to paths that do not exist yet, prefix-based cleanup that crosses user share records, public directory shares rebasing rule checks away from the owner's original scope, archive generation preserving Windows-style traversal separators, symlink traversal from scoped roots into server-reachable files, cursor pagination used as a database sort oracle, canvas-to-SVG serialization that crosses into trusted DOM rendering, and mobile in-app browser messages that can dispatch predictable native callback IDs.

What changed

  • File Browser command allowlist shell boundary — when command execution is explicitly enabled and a shell such as /bin/sh -c is configured, File Browser's parser checks only the first token against the user's allowed command list but passes the complete raw string to the shell. Shell metacharacters after an allowed command can therefore cross from an allowlisted helper into arbitrary process execution as the server user.
  • File Browser future-share grant boundary — POST /api/share/<path> could create a public share record for a path before the target file existed. When a file later appeared at the same logical path, the old public share became valid and exposed it through the public download route.
  • File Browser share cleanup prefix boundary — a low-privileged user deleting an owned file could trigger share-record deletion by unbounded logical-path prefix match, crossing into other users' share links when paths shared byte prefixes.
  • File Browser public directory rule-rebase boundary — public directory-share handlers rebased the owner's filesystem root to the shared directory, then checked descendant paths against deny rules using the rebased relative path. A known public directory share could therefore expose blocked descendants under that share.
  • File Browser archive separator boundary — download-as-zip/tar archive names preserved literal backslashes from Linux filenames. A crafted filename such as a Windows-style traversal could become an archive entry that Windows extractors write outside the chosen extraction directory.
  • File Browser scoped-root symlink boundary — per-user scopes rooted with a base-path filesystem did not stop handlers from following symlinks inside the scope to files or directories outside it, affecting raw reads, resource metadata, overwrites, TUS uploads, share creation, and public-share serving.
  • Fleet ORDER BY pagination oracle boundary — Observer-level users could supply non-allowlisted order_key values on host/MDM listing endpoints and combine cursor pagination with binary search to recover joined-table secrets such as host enrollment keys.
  • Fabric.js SVG serialization boundary — user-controlled gradient color-stop values were emitted into SVG attributes without escaping. Applications that insert toSVG() output into the DOM can turn stored drawing/canvas data into script execution.
  • Cordova InAppBrowser callback-dispatch boundary — iOS InAppBrowser web content could post arbitrary Cordova callback IDs to the host app. Predictable plugin callback IDs make this a mobile bridge confused-deputy issue when attacker-controlled content is opened in the in-app browser.

Operator triage

  1. Start from role and rendering preconditions. File Browser checks vary by primitive: command allowlist bypass requires command execution to be re-enabled, a configured shell interpreter, Execute permission, and at least one allowed command; future-share/prefix/symlink/archive planting generally require authenticated share, delete, create, write, or existing symlink conditions; public directory rule rebasing requires only knowledge of a public directory share URL once a share exists. Fleet requires Observer credentials and affected endpoints; Fabric.js requires an application that accepts attacker-controlled canvas/gradient data and renders exported SVG into the DOM; Cordova requires a target app that opens attacker-controlled or interceptable content in InAppBrowser on iOS.
  2. Prove the boundary with canaries, not secrets. Use inert command markers, disposable files, synthetic share links, lab deny rules, harmless archive marker filenames, symlinks to non-sensitive marker files, test Fleet hosts with canary enrollment values, harmless SVG markers, and inert Cordova callback payloads. Do not run production commands, read real user files, recover production enrollment secrets, delete live share links, overwrite out-of-scope files, or target real mobile users.
  3. Capture positive and negative controls. The strongest reports show the vulnerable behavior on an affected version and the same canary blocked by path existence checks, segment-aware prefix handling, sort-column allowlists, SVG escaping, or callback-ID validation.

Replayable validation boundaries

File Browser command allowlist shell validation

  • Use only a lab File Browser instance or a tenant where the owner explicitly approved command-execution testing. The preconditions are narrow: command execution is enabled, a shell interpreter is configured, and the test user has Execute permission plus at least one allowed command.
  • Choose one harmless allowed command such as printf, echo, or true and pair it with an inert marker command that writes only to a disposable temp path or prints a canary string. Do not use commands that read system files, enumerate users, open sockets, or persist changes.
  • Open the approved command API/WebSocket path as the disposable user and submit two controls: the marker command alone should be rejected by the allowlist, while the allowed command plus a shell metacharacter and the same marker should execute if the boundary is vulnerable.
  • Positive proof is the mismatch between allowlist rejection for the raw marker and execution when the marker is chained after an allowed first token through the configured shell.
  • Evidence: File Browser version, command-execution setting, configured shell value, disposable user permissions, allowed command list, redacted request transcript, canary-only output, and patched/default-disabled behavior in v2.33.8 or later.

File Browser future public-share validation

  • Use a lab File Browser instance or explicitly approved tenant with a disposable low-privileged user that has share and download permission.
  • Create a public share for a logical path that does not exist yet, such as /canary/future.txt, and record only the returned public hash/URL.
  • Later create a harmless marker file at that exact path as the same user or via an approved setup step.
  • Positive proof is that the previously issued public URL now returns the marker file without creating a new share after the file exists.
  • Evidence: File Browser version, user role/scope, requested logical path, share hash, marker filename/content, public route status, and patched-version rejection for non-existent paths.

File Browser cross-user share-prefix cleanup validation

  • Build two disposable users and paths where the attacker's deleted path is a byte prefix of a victim share path, for example /a and /admin-report only in a lab namespace.
  • Have the victim user create a synthetic public share to a harmless marker file.
  • Have the attacker delete only their own disposable file/directory at the prefix path.
  • Positive proof is removal of the victim's share record or public link availability changing solely because of prefix cleanup.
  • Stop at canary share deletion. Do not target production shares, shared roots, or files that carry business impact.
  • Evidence: user scopes, logical paths, share hashes before/after, delete request, and patched segment-aware cleanup behavior.

File Browser public directory rule-rebase validation

  • Use a lab File Browser instance with one owner account, a public directory share, and a deliberately configured deny rule for a descendant path under that shared directory.
  • Create a harmless marker file below the denied descendant path, for example /projects/private/canary.txt, then share the parent directory such as /projects.
  • Request the marker through the public share or public download route using the path relative to the shared directory.
  • Positive proof is that the public request returns or lists the denied marker even though the same rule blocks access when evaluated relative to the owner's original scope.
  • Stop at a synthetic marker. Do not enumerate real shared trees or retrieve production files from known public links.
  • Evidence: File Browser version, owner scope, deny rule, shared directory, public URL/hash redacted as needed, marker path/content, public route status, and fixed-version denial with original-scope rule evaluation.

File Browser archive separator traversal validation

  • Use a Linux-hosted lab File Browser instance and a disposable user with create/download permission.
  • Create a benign file whose stored filename contains URL-encoded backslashes and Windows-style traversal text, for example a marker named like ..\\..\\fb-canary.txt inside a test folder.
  • Download the folder as zip/tar and inspect the archive entry names without extracting onto a real workstation. Prefer zipinfo, bsdtar -tf, or an isolated disposable Windows VM with a throwaway extraction directory.
  • Positive proof is that the archive contains the backslash traversal entry verbatim and a Windows extractor would place the marker outside the chosen extraction root.
  • Do not publish payloads that overwrite startup folders, shell profiles, configuration files, or user documents.
  • Evidence: File Browser version, create request path, archive entry listing, extractor behavior in a disposable VM if used, and fixed-version normalization/rejection.
  • Use only a lab instance where you control the filesystem and can create a symlink inside the user's scoped tree to a non-sensitive marker file outside that scope.
  • Test both shapes separately when relevant: a symlink as the final path component and a regular file reached through a symlinked ancestor directory.
  • As the scoped user, request metadata/raw download for the in-scope symlink path and, only in a disposable setup, test overwrite/share behavior against an inert marker.
  • Positive proof is access to the marker outside the configured scope through a path that is lexically inside the scope.
  • Stop at synthetic marker reads/writes. Do not follow links toward /etc, home directories, database files, cloud credentials, or other live secrets.
  • Evidence: File Browser version, configured user scope, symlink path and target, API route, marker content before/after, public-share behavior if tested, and fixed-version symlink containment failure.

GHSA-8wc8-hf36-mjh9 / CVE-2026-55668 and GHSA-fmm7-x4gx-8jhr / CVE-2026-55667 show two incomplete-fix variants after the original scoped-root symlink work. Treat them as different primitives:

  • Dangling final symlink on create: a user with Create and Modify can write through an in-scope symlink whose out-of-scope target does not exist. The fallback validates the symlink's lexical parent, then O_CREATE follows the link and creates the external file. Existing targets remain blocked.
  • Symlinked ancestor on upload cleanup: a Create-only user can submit a path below an escaping directory symlink. The guarded write fails, but failure cleanup calls an unguarded recursive remove, potentially deleting the external canary despite the HTTP denial and without Delete permission.

Use a disposable filesystem with /scope and /outside, and have the lab administrator plant the symlinks; File Browser does not provide the attacker with a symlink-creation API. For the create case, point a final link at one nonexistent marker under /outside and upload a short canary with override enabled. For the cleanup case, point an in-scope directory link at /outside, seed one expendable marker, and submit an upload to that marker path as a Create-only user. Capture the HTTP status and before/after inode/hash state. The cleanup variant is especially important because a denied response can coincide with deletion.

Positive proof is limited to pre-existing in-scope symlink -> create/cleanup sink follows outside scope -> one disposable external marker created or removed. Include controls for an existing final target, no Modify permission, no Create permission, Delete permission disabled, and File Browser 2.63.16 or later. Never point at another tenant, application database, startup file, service configuration, home directory, or recursively populated directory.

July 20 follow-up: archive builder manufactures traversal separators

GHSA-83xp-526h-j3ww is an incomplete-fix variant affecting File Browser >=2.63.6,<=2.63.16. The archive builder explicitly rewrites backslashes to /. On POSIX, a backslash is valid filename data, so a single stored filename containing backslash-delimited .. text becomes a slash-delimited traversal entry in zip, tar, and related output. This differs from the original Windows-extractor-only case: the builder itself manufactures the portable separator sequence.

Reuse the archive lab, but compare three artifacts without extracting onto a real workstation: the stored POSIX filename, the archive entry emitted by the affected version, and the entry emitted by File Browser 2.63.17+. Use only a marker name inside a disposable folder and inspect with zipinfo/tar -tf plus a containment-checking extractor in an isolated temp root if extraction evidence is required. Positive proof is one in-scope POSIX filename -> archive-time backslash rewrite -> normalized entry contains parent segments. Do not target startup folders, executable paths, user documents, or a victim workstation.

July 20 follow-up: username-to-home collisions and share-secret serialization

GHSA-7rc3-g7h6-22m7 adds a many-to-one identity/filesystem boundary when self-registration and automatic user-directory creation are both enabled. Raw usernames can be unique while cleanUsername() maps them to the same home path. In a two-user lab, register one ordinary name and one syntactically different colliding name, seed one marker in the first home, and compare the resulting scopes/inodes. Positive proof is distinct account identities -> identical normalized home directory -> second user reads or rewrites the first user's marker. Stop at synthetic files and include a non-colliding username plus fixed release as controls.

GHSA-833g-cqhp-h72j shows that password-protected share API responses serialized both the password hash and direct bypass token. Use only a disposable user's own share and a fake password; record field presence and whether the returned canary token bypasses the lab password prompt. If administrative scope is explicitly approved, compare an admin list response using only shares created by the tester. Do not crack hashes, enumerate other users' shares, or preserve reusable tokens. Report share record -> unfiltered API serialization -> password verifier/bypass capability returned to a caller whose role should not receive it.

Fleet cursor-sort oracle validation

  • Validate only in a test Fleet deployment with disposable hosts and deliberately seeded canary enrollment values.
  • Use an Observer or Team Observer account and target the affected host-listing or Apple MDM commands route.
  • Supply a sensitive joined-table column as order_key only against canary columns/rows, then vary the cursor parameter to demonstrate that response ordering or presence leaks one character of the known canary.
  • A minimal proof can stop after recovering a short synthetic prefix such as CANARY-, paired with a negative control where unsupported order_key values are rejected.
  • Do not extract real node_key, orbit_node_key, APNS tokens, host identifiers beyond the canary scope, or production MDM data.
  • Evidence: Fleet version, endpoint, Observer role, redacted request parameters, canary value, binary-search transcript for the canary prefix, and allowlist rejection on a fixed build.

Fabric.js SVG export-to-DOM validation

  • Confirm the application stores or accepts user-controlled Fabric.js object data with gradient colorStops and later calls toSVG() or otherwise serializes the canvas to SVG.
  • Use a harmless attribute-breakout marker such as a non-networking event that writes to a local test DOM marker; avoid credential theft, external callbacks, or user-targeted payloads.
  • Render the generated SVG only in a lab page or authorized app path that mirrors the production rendering sink, especially innerHTML/HTML insertion paths.
  • Positive proof is controlled markup/script execution from the serialized gradient color field, plus a patched/escaped output where quotes and angle brackets remain inert.
  • Evidence: Fabric.js version, sanitized object JSON, emitted SVG fragment, DOM insertion sink, marker execution, and patched serialization output.

Cordova InAppBrowser callback-ID dispatch validation

  • Use a test build of the target iOS Cordova app with non-sensitive plugins and a lab InAppBrowser URL controlled by the tester.
  • Create or wait for a pending native plugin callback with a predictable test callback ID, then have the InAppBrowser content post a message to the Cordova InAppBrowser bridge with that canary ID and inert data.
  • Positive proof is that the host app dispatches the plugin result for the canary callback without the message originating from the legitimate plugin flow.
  • Keep payloads inert: update a test label, write a disposable local marker, or log a canary string. Do not access camera, contacts, geolocation, files, OAuth tokens, or user accounts.
  • Evidence: plugin versions, iOS app build, controlled URL, callback ID format, posted message body, canary callback effect, and fixed-version validation failure.

Reporting heuristics

  • Name the exact crossed boundary in the report title: command allowlist first-token shell bypass, future share grant to later file exposure, logical path prefix to cross-user share deletion, public directory share rule rebasing, archive entry separator traversal on Windows extraction, scoped-root symlink follow, ORDER BY cursor oracle to enrollment-key leakage, canvas data to trusted SVG DOM, or InAppBrowser web content to native callback dispatch.
  • Keep impact tied to demonstrated permissions and sinks. These are high-signal when the report preserves the role, route, platform, filesystem setup, extractor, symlink precondition, shell configuration, and rendering requirements instead of claiming blanket file disclosure, database exfiltration, arbitrary host write, or mobile RCE.
  • Redact share hashes, enrollment secrets, mobile device identifiers, APNS material, plugin callback data, user files, command output beyond the canary, and filesystem paths that reveal tenant names or sensitive directory layouts. Synthetic canaries are sufficient.

Notes on skipped adjacent items

The same scan rechecked Disclosed, PortSwigger, Trail of Bits, ProjectDiscovery, GitHub advisory published/updated feeds, and CISA KEV. ConnectBot DER/SSH memory-allocation items, PyO3 memory-safety issues, File Browser public-login resource exhaustion, Jenkins stored XSS, Apostrophe updated rich-text/password/XSS/CLI items, Docker cp race updates, and generic resource-consumption updates were tracked but not promoted because they did not add a clearer replayable offensive workflow than the command, share, oracle, serialization, filesystem-boundary, and mobile bridge boundaries above. No new PortSwigger, Trail of Bits, ProjectDiscovery, Disclosed, or CISA KEV item in this hour added additional durable guidance.

October 5 follow-up: Capacitor internal HTTP proxy loads remote content at the app origin (CVE-2026-103922)

Capacitor's WebView navigation guard validated only host and scheme, never the path, and the internal proxy endpoint /_capacitor_http_interceptor_ is served at the app's own origin. A frame navigation to that path was always classified as in-app; loading it as a document makes the native layer fetch an arbitrary caller-specified URL and hand the response body to the WebView at the app origin — script in that response runs with full same-origin trust: localStorage, cookies, and every registered native Capacitor plugin capability. The handler was served regardless of whether the CapacitorHttp plugin was enabled, so "we never use that plugin" apps were still exposed. Delivery: any user-controlled link rendered inside the app WebView (chat, comments, rich text) that the victim taps. Both Android and iOS.

Operator axes:

  • In-app proxy paths are origin-escalation surfaces on mobile. Any hybrid-app framework that serves an internal HTTP-intercept or file-proxy route at the app scheme (capacitor://, https://localhost conventions, content:// bridges) must be tested as a document URL, not just a subresource. Host+scheme-only navigation guards protect the scheme, not the internal endpoints sharing it. Same shape as the WebView-bridge callback dispatch on this page and the desktop localhost-listener class: the privileged origin's own routes are part of the untrusted surface.
  • Served-regardless-of-plugin-config means fingerprint the route, not the feature. Probe /_capacitor_http_interceptor_/?url=<owned listener> as a top-level navigation in a lab build; proof is your owned listener's response body rendering with app-origin localStorage read capability (use a synthetic key you seeded).
  • Audit rule for hybrid apps: enumerate internal paths from framework source (_capacitor_, /_next/, ionic:// internals), and for each, check whether the navigation guard's allow/deny decision considers the path at all. Report as path-blind navigation guard → internal proxy → remote script at app origin → native plugin reach, and keep plugin proofs to inert capability calls (test label, disposable marker) per this page's existing boundaries.