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:
-
Create a Firefox WebExtension with a content script.
-
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);
-
Run the extension in Firefox.
-
Observe the exception thrown during zipSync.
Option 2 – use the attached example extension:
fflate-zipsync-permission-issue.zip
-
Unzip fflate-zipsync-permission-issue.zip
-
Open about:debugging in Firefox
-
Click on "This Firefox"
-
"Load Temporary Add-on..."
-
Select manifest.json from fflate-zipsync-permission-issue
-
Click on Add-on Icon
Root Cause Analysis
-
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.
-
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
}
-
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
-
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'
);
-
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)
Description
When calling
zipSync(data)inside a Firefox WebExtension (e.g. in a Content Script or sandbox environment), the execution fails with:The exact same code executes without issues in Google Chrome and Node.js.
Steps to Reproduce
Option 1 – do everything yourself:
Create a Firefox WebExtension with a content script.
In the content script, prepare a file mapping object with
Uint8Arraydata:Run the extension in Firefox.
Observe the exception thrown during
zipSync.Option 2 – use the attached example extension:
fflate-zipsync-permission-issue.zip
Unzip fflate-zipsync-permission-issue.zip
Open
about:debuggingin FirefoxClick on "This Firefox"
"Load Temporary Add-on..."
Select manifest.json from fflate-zipsync-permission-issue
Click on Add-on Icon
Root Cause Analysis
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)returnsfalsefor wrappedUint8Arrayinstances across realms.False Directory Branching in
fltn:Because
ArrayBuffer.isView(val)returnsfalse, the flattening functionfltn(d, p, t, o)assumesvalis a nested directory dictionary object instead of binary data:for..inPrototype Traversal Blocked by Firefox Security:When
fltnsubsequently iterates over theUint8Arrayusingfor (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
Robust TypedArray check:
Enhance the
isViewcheck infltn(and related functions) to support cross-realm / wrapped TypedArrays, for example:Safer iteration:
Use
Object.keys(d)orObject.prototype.hasOwnProperty.call(d, k)instead of plainfor..inloops infltnand spread/merge helper functions (mrg,zipSync, etc.) to prevent walking the prototype chain.Environment
fflate0.8.3 (latest)