Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,21 @@ Don't open a public issue for a security problem.
## Supported Versions

Fixes ship in a new release off the latest published version. Stay on it; there are no backport branches.

## Tooling

Development installs use basic-ftp 6, Zod 4, minimatch 11 and glob 14 for their security fixes. These overrides stay in pnpm's workspace configuration; they do not change the plugin's Expo peer requirement or public configuration.

- basic-ftp keeps the Client methods used by release-it's get-uri dependency. The Unix listing parser is checked in a timed child process.
- Expo CLI's plugin and output schemas run against Zod 4. Oversized constrained arrays must fail before parsing their elements, including asynchronous parsing.
- The minimatch patch adds a callable CommonJS export for ESLint and glob 7 while retaining the modern named exports. The pattern-amplification regression runs with a timeout.
- Modern glob callers use glob 14. The checks cover normal file matching and rejection of an already-cancelled walk.
- deepmerge 4.3.2 is named by the registry advisory but is unpublished. Our 4.3.1 patch skips `__proto__` keys when copying either merge operand, including null-prototype inputs, in the source and both bundled builds. The security checks exercise these cases and ordinary nested/array merges. Remove the patch and its advisory exception together once a fixed upstream release is available and passes these checks.

`pnpm run security:check` runs the installed-package regressions before the high-severity audit gate. The deepmerge exception is limited to GHSA-gcqx-w48m-w2h4, alongside the existing patched image-size exceptions. Keep these checks in the test command and release workflow.

## Development

Use Node 24 and the pinned pnpm version. We use Jest 30 with a TypeScript transformer for these Node-based config-plugin tests. Type checking runs separately. The old expo-module-scripts package and its `expo-module` command are removed; use `pnpm run build`, `pnpm run clean` and `pnpm run test` directly. Jest mocks that require Babel hoisting need explicit setup before importing the module under test.

These are contributor tooling changes. Consumers keep the same plugin options. Security commits under `build(deps)` deliberately recommend a patch release under our changelog policy; ordinary chore, build and docs commits alone recommend no release. Public breaking changes still require a conventional breaking-change note with migration instructions, which the shared preset carries into the changelog, release body and annotated tag.
6 changes: 5 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
module.exports = require("expo-module-scripts/jest-preset-plugin");
module.exports = {
testEnvironment: "node",
testRegex: "/__tests__/.*(test|spec)\\.[jt]sx?$",
transform: { "^.+\\.[jt]sx?$": "<rootDir>/tools/jest-transform.cjs" },
};
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"scripts": {
"build": "tsc --project tsconfig.build.json",
"changelog:check": "node tools/changelog-check.mjs",
"clean": "expo-module clean",
"clean": "node -e \"require('node:fs').rmSync('build', { recursive: true, force: true })\"",
"lint": "oxlint --report-unused-disable-directives --deny-warnings",
"lint:fix": "oxlint --report-unused-disable-directives --deny-warnings --fix",
"format": "oxfmt --check .",
Expand All @@ -34,20 +34,18 @@
"test": "node tools/dependency-security-check.mjs && jest",
"prepare": "pnpm run build",
"release": "release-it",
"security:check": "node tools/dependency-security-check.mjs && pnpm audit --audit-level high",
"expo-module": "expo-module"
"security:check": "node tools/dependency-security-check.mjs && pnpm audit --audit-level high"
},
"devDependencies": {
"@babel/core": "^7.29.7",
"@release-it/conventional-changelog": "^12.0.0",
"@types/jest": "^29.5.14",
"@types/jest": "^30.0.0",
"@types/node": "^24.13.3",
"conventional-changelog": "^8.1.0",
"conventional-changelog-conventionalcommits": "^10.2.1",
"conventional-recommended-bump": "^12.1.0",
"expo": ">=49.0.0",
"expo-module-scripts": "^3.0.3",
"jest": "^29",
"jest": "^30.5.2",
"magic-codemods": "^1.1.0",
"magic-oxfmt-config": "^1.2.0",
"magic-oxlint-config": "^1.2.0",
Expand Down
54 changes: 54 additions & 0 deletions patches/deepmerge@4.3.1.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
diff --git a/dist/cjs.js b/dist/cjs.js
index 7c36cbdd06cc22c92d590be3f87a08a91ca06b40..ca9390887c0d69e87442b9b1bea240c960b4ecf7 100644
--- a/dist/cjs.js
+++ b/dist/cjs.js
@@ -80,11 +80,12 @@ function mergeObject(target, source, options) {
var destination = {};
if (options.isMergeableObject(target)) {
getKeys(target).forEach(function(key) {
+ if (key === "__proto__") return;
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
});
}
getKeys(source).forEach(function(key) {
- if (propertyIsUnsafe(target, key)) {
+ if (key === "__proto__" || propertyIsUnsafe(target, key)) {
return
}

diff --git a/dist/umd.js b/dist/umd.js
index 4071e7c56ec8e5273fe166f6e723ac460fab8b72..96d55af0e4e2dc221558faff08435cebb76097e8 100644
--- a/dist/umd.js
+++ b/dist/umd.js
@@ -84,11 +84,12 @@
var destination = {};
if (options.isMergeableObject(target)) {
getKeys(target).forEach(function(key) {
+ if (key === "__proto__") return;
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
});
}
getKeys(source).forEach(function(key) {
- if (propertyIsUnsafe(target, key)) {
+ if (key === "__proto__" || propertyIsUnsafe(target, key)) {
return
}

diff --git a/index.js b/index.js
index 77968aed5d456cccc294f60912ef381fa1e52a7b..7db1332228f8d832f23e67209ef33adbd2d5b55e 100644
--- a/index.js
+++ b/index.js
@@ -55,11 +55,12 @@ function mergeObject(target, source, options) {
var destination = {}
if (options.isMergeableObject(target)) {
getKeys(target).forEach(function(key) {
+ if (key === "__proto__") return;
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
})
}
getKeys(source).forEach(function(key) {
- if (propertyIsUnsafe(target, key)) {
+ if (key === "__proto__" || propertyIsUnsafe(target, key)) {
return
}

10 changes: 10 additions & 0 deletions patches/minimatch@11.0.4.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
diff --git a/dist/commonjs/index.js b/dist/commonjs/index.js
index eb1c915f43b2debf5faa7ac3a73c262a161c7a4f..ec0c1a8311e7d3ca6f0846ce624edbb4b3b855c3 100644
--- a/dist/commonjs/index.js
+++ b/dist/commonjs/index.js
@@ -1548,3 +1548,5 @@ exports.minimatch.Minimatch = Minimatch;
exports.minimatch.escape = escape_js_1.escape;
exports.minimatch.unescape = unescape_js_1.unescape;
//# sourceMappingURL=index.js.map
+// Keep legacy CommonJS callers callable while retaining all modern named exports.
+module.exports = Object.assign(exports.minimatch, exports);
Loading
Loading