Skip to content

zipSync throws Permission denied to access property "constructor" in Firefox WebExtensions (Xray wrapper context) #294

Description

@ITSC-GmbH

Description

When calling zipSync(data) inside a Firefox WebExtension (e.g. in a Content Script or sandbox environment), the execution fails with:

Error: Permission denied to access property "constructor"

The exact same code executes without issues in Google Chrome and Node.js.

Steps to Reproduce

Option 1 – do everything yourself:

  1. Create a Firefox WebExtension with a content script.

  2. In the content script, prepare a file mapping object with Uint8Array data:

    import * as fflate from 'fflate';
    
    const data = {
      "document.txt": fflate.strToU8("Hello World"),
      "image.png": new Uint8Array([/* ... */])
    };
    
    const zipped = fflate.zipSync(data);
  3. Run the extension in Firefox.

  4. Observe the exception thrown during zipSync.

Option 2 – use the attached example extension:

fflate-zipsync-permission-issue.zip

  1. Unzip fflate-zipsync-permission-issue.zip

  2. Open about:debugging in Firefox

  3. Click on "This Firefox"

  4. "Load Temporary Add-on..."

  5. Select manifest.json from fflate-zipsync-permission-issue

  6. Click on Add-on Icon

Root Cause Analysis

  1. Cross-Realm / Xray Wrapper Behavior:
    In Firefox WebExtensions, objects passed between DOM/extension scopes are wrapped in [Xray wrappers](https://firefox-source-docs.mozilla.org/dom/xrayWrappers.html). In this security context, native ArrayBuffer.isView(val) returns false for wrapped Uint8Array instances across realms.

  2. False Directory Branching in fltn:
    Because ArrayBuffer.isView(val) returns false, the flattening function fltn(d, p, t, o) assumes val is a nested directory dictionary object instead of binary data:

    // Inside fltn:
    if (ArrayBuffer.isView(val))
        t[n] = [val, op];
    else {
        t[n += '/'] = [new u8(0), op];
        fltn(val, n, t, o); // <-- val is Uint8Array, treated as directory
    }
  3. for..in Prototype Traversal Blocked by Firefox Security:
    When fltn subsequently iterates over the Uint8Array using for (var k in d), it traverses prototype properties (such as .constructor). Firefox's security layer prohibits access to restricted prototype properties through Xray wrappers and throws:
    Permission denied to access property "constructor".

Suggested Fix

  1. Robust TypedArray check:
    Enhance the isView check in fltn (and related functions) to support cross-realm / wrapped TypedArrays, for example:

    const isView = (v) => ArrayBuffer.isView(v) || (
      v != null &&
      typeof v === 'object' &&
      typeof v.byteLength === 'number' &&
      typeof v.BYTES_PER_ELEMENT === 'number'
    );
  2. Safer iteration:
    Use Object.keys(d) or Object.prototype.hasOwnProperty.call(d, k) instead of plain for..in loops in fltn and spread/merge helper functions (mrg, zipSync, etc.) to prevent walking the prototype chain.

Environment

  • Browser: Mozilla Firefox 153.0.1
  • Context: WebExtension (Content Script / Add-on Sandbox)
  • Library Version: fflate 0.8.3 (latest)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions