Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
52508b4
fix(security): remove _WORKLET_RUNTIME pointer leak from RN runtime g…
raxodus Apr 23, 2026
50decef
fix(security): use safeAssign in runAnimations to prevent prototype p…
raxodus Apr 23, 2026
c95d4fe
fix(security): guard dangerous keys in setPath to prevent prototype p…
raxodus Apr 23, 2026
2ae5492
fix(security): iterative flattenArray and Object.hasOwn in has() (007…
raxodus Apr 23, 2026
5f9d8c8
fix(security): use Object.create(null) for accumulator objects (0074+…
raxodus Apr 23, 2026
8036a01
fix(security): disable Keyframe export to prevent prototype pollution…
raxodus Apr 23, 2026
094e2e3
fix(security): add array bounds check in SensorSetter to prevent buff…
raxodus Apr 23, 2026
491a0e0
fix(security): replace RegExp-based color parsing with manual string …
raxodus Apr 23, 2026
11511f7
feat(worklets): wrap worklet code under Symbol.for in Babel plugin
raxodus Apr 23, 2026
5498992
fix(security): disable RegExp serialization in worklet runtime (0074)
raxodus Apr 23, 2026
64967cf
feat(worklets): handle Symbol-wrapped worklet code in serialization
raxodus Apr 23, 2026
972e6dc
fix(worklets): remove eval from value unpacker
raxodus Apr 23, 2026
a6fed00
fix(worklets): pass evaluateWorkletFunction from C++ to value unpacker
raxodus Apr 23, 2026
502ea8e
fix(worklets): remove evalWithSourceUrl and evalWithSourceMap from ru…
raxodus Apr 23, 2026
c2c126b
chore(worklets): regenerate ValueUnpacker.cpp from modified TS source
raxodus Apr 23, 2026
1b31671
fix(worklets): update WorkletInitData type and getWorkletCode for Sym…
raxodus Apr 23, 2026
d84a5ce
chore(worklets): rebuild plugin bundle with Symbol wrapping changes
raxodus Apr 23, 2026
bad7cc0
chore: scope under @exodus and set versions for RN 0.85 upgrade
raxodus Apr 23, 2026
7c3bf1a
chore: fix workspace references for @exodus/ scoped package names
raxodus Apr 24, 2026
9a2b92c
chore: regenerate lockfile after workspace renames
raxodus Apr 24, 2026
0f1ec8e
fix(android): align worklets references with @exodus/react-native-wor…
raxodus Apr 27, 2026
4cbcc1e
fix(reanimated): align jsVersion constant with published exodus version
raxodus Apr 29, 2026
6e0689d
fix(reanimated): inline captured RegExp literals into worklets to avo…
raxodus Apr 29, 2026
d86d862
fix(worklets): self-reference uses scoped @exodus/react-native-workle…
raxodus Apr 29, 2026
dc14b69
fix(worklets): align jsVersion constant with published exodus version
raxodus Apr 29, 2026
9be32ca
chore: bump versions — reanimated 4.3.0-exodus.2 + worklets 0.9.0-exo…
raxodus Apr 29, 2026
b03042e
feat(featureFlags): make FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS ru…
raxodus May 13, 2026
584ff4c
chore: reanimated 4.3.0-exodus.3
raxodus May 13, 2026
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
2 changes: 1 addition & 1 deletion apps/common-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"react-native": "*"
},
"dependencies": {
"@exodus/react-native-reanimated": "workspace:*",
"@fortawesome/fontawesome-svg-core": "6.5.2",
"@fortawesome/free-solid-svg-icons": "6.5.2",
"@fortawesome/react-native-fontawesome": "0.3.2",
Expand All @@ -38,7 +39,6 @@
"react-native-gesture-handler": "2.28.0",
"react-native-mmkv": "4.0.0",
"react-native-nitro-modules": "0.31.9",
"react-native-reanimated": "workspace:*",
"react-native-safe-area-context": "5.6.1",
"react-native-screens": "4.19.0-nightly-20251125-46052f31e",
"react-native-svg": "patch:react-native-svg@npm%3A15.15.3#~/.yarn/patches/react-native-svg-npm-15.15.3-0699a4dc13.patch",
Expand Down
2 changes: 1 addition & 1 deletion apps/macos-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
"type:check": "tsc --noEmit"
},
"dependencies": {
"@exodus/react-native-reanimated": "workspace:*",
"common-app": "workspace:*",
"react": "19.1.4",
"react-native": "0.81.4",
"react-native-macos": "patch:react-native-macos@npm%3A0.81.4#~/.yarn/patches/react-native-macos-npm-0.81.4.patch",
"react-native-reanimated": "workspace:*",
"react-native-worklets": "0.8.1"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion apps/next-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
"format": "prettier --write --list-different ."
},
"dependencies": {
"@exodus/react-native-reanimated": "workspace:*",
"expo": "54.0.13",
"next": "15.5.4",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.85.0-rc.1",
"react-native-reanimated": "workspace:*",
"react-native-web": "0.21.1",
"react-native-worklets": "0.8.1",
"webpack": "5.102.1"
Expand Down
2 changes: 1 addition & 1 deletion apps/tvos-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"type:check": "tsc --noEmit"
},
"dependencies": {
"@exodus/react-native-reanimated": "workspace:*",
"react": "19.2.3",
"react-native": "npm:react-native-tvos@0.84.1-0",
"react-native-reanimated": "workspace:*",
"react-native-worklets": "0.8.1"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"private": true,
"scripts": {
"build": "husky && yarn build-packages",
"build-packages": "yarn workspace react-native-reanimated build",
"build-packages": "yarn workspace @exodus/react-native-reanimated build",
"build-all": "husky && yarn workspaces foreach --all --parallel --topological-dev run build",
"format": "yarn format:root && yarn format:workspaces",
"format:root": "prettier --cache --write --list-different --ignore-path .gitignore --ignore-path .prettierignore --ignore-path .prettiereslintignore .",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void AnimatedPropsRegistry::update(jsi::Runtime &rt, const jsi::Value &operation
const jsi::Value &updates = item.getProperty(rt, "updates");
addUpdatesToBatch(shadowNode, jsi::dynamicFromValue(rt, updates));

if constexpr (StaticFeatureFlags::getFlag("FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS")) {
if (RuntimeFeatureFlags::forceReactRenderForSettledAnimations()) {
timestampMap_[shadowNode->getTag()] = timestamp;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,12 @@ jsi::Value ReanimatedModuleProxy::getStaticFeatureFlag(jsi::Runtime &rt, const j

jsi::Value
ReanimatedModuleProxy::setDynamicFeatureFlag(jsi::Runtime &rt, const jsi::Value &name, const jsi::Value &value) {
reanimated::DynamicFeatureFlags::setFlag(name.asString(rt).utf8(rt), value.asBool());
const auto flagName = name.asString(rt).utf8(rt);
const auto flagValue = value.asBool();
if (flagName == "FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS") {
reanimated::RuntimeFeatureFlags::setForceReactRenderForSettledAnimations(flagValue);
}
reanimated::DynamicFeatureFlags::setFlag(flagName, flagValue);
return jsi::Value::undefined();
}

Expand Down Expand Up @@ -528,9 +533,9 @@ void ReanimatedModuleProxy::unregisterCSSTransition(jsi::Runtime &rt, const jsi:
}

jsi::Value ReanimatedModuleProxy::getSettledUpdates(jsi::Runtime &rt) {
react_native_assert(
StaticFeatureFlags::getFlag("FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS") &&
"getSettledUpdates requires FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS static feature flag to be enabled");
if (!RuntimeFeatureFlags::forceReactRenderForSettledAnimations()) {
return jsi::Array(rt, 0);
}

// TODO(future): use unified timestamp
const auto currentTimestamp = getAnimationTimestamp_();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@ void RNRuntimeDecorator::decorate(
jsi::Runtime &rnRuntime,
jsi::Runtime &uiRuntime,
const std::shared_ptr<ReanimatedModuleProxy> &reanimatedModuleProxy) {
auto workletRuntimeValue = rnRuntime.global()
.getPropertyAsObject(rnRuntime, "ArrayBuffer")
.asFunction(rnRuntime)
.callAsConstructor(rnRuntime, {static_cast<double>(sizeof(void *))});
uintptr_t *workletRuntimeData =
reinterpret_cast<uintptr_t *>(workletRuntimeValue.getObject(rnRuntime).getArrayBuffer(rnRuntime).data(rnRuntime));
workletRuntimeData[0] = reinterpret_cast<uintptr_t>(&uiRuntime);
rnRuntime.global().setProperty(rnRuntime, "_WORKLET_RUNTIME", workletRuntimeValue);
// Security: _WORKLET_RUNTIME pointer leak removed (0056)

#ifndef NDEBUG
checkJSVersion(rnRuntime);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <reanimated/Tools/FeatureFlags.h>

#include <atomic>
#include <string>
#include <unordered_map>

Expand All @@ -15,4 +16,15 @@ void DynamicFeatureFlags::setFlag(const std::string &name, bool value) {
flags_[name] = value;
}

std::atomic<bool> RuntimeFeatureFlags::forceReactRenderForSettledAnimations_{
StaticFeatureFlags::getFlag("FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS")};

bool RuntimeFeatureFlags::forceReactRenderForSettledAnimations() {
return forceReactRenderForSettledAnimations_.load(std::memory_order_relaxed);
}

void RuntimeFeatureFlags::setForceReactRenderForSettledAnimations(bool value) {
forceReactRenderForSettledAnimations_.store(value, std::memory_order_relaxed);
}

} // namespace reanimated
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include <atomic>
#include <string>
#include <unordered_map>

Expand Down Expand Up @@ -41,4 +42,15 @@ class DynamicFeatureFlags {
static std::unordered_map<std::string, bool> flags_;
};

// Per-flag runtime override for select flags that we need to toggle at runtime.
// Defaults to the compile-time StaticFeatureFlags value. Thread-safe via atomic.
class RuntimeFeatureFlags {
public:
static bool forceReactRenderForSettledAnimations();
static void setForceReactRenderForSettledAnimations(bool value);

private:
static std::atomic<bool> forceReactRenderForSettledAnimations_;
};

} // namespace reanimated
4 changes: 2 additions & 2 deletions packages/react-native-reanimated/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ file(GLOB_RECURSE REANIMATED_ANDROID_CPP_SOURCES CONFIGURE_DEPENDS

find_package(fbjni REQUIRED CONFIG)
find_package(ReactAndroid REQUIRED CONFIG)
find_package(react-native-worklets REQUIRED CONFIG)
find_package(exodus_react-native-worklets REQUIRED CONFIG)

add_library(reanimated SHARED ${REANIMATED_COMMON_CPP_SOURCES}
${REANIMATED_ANDROID_CPP_SOURCES})
Expand Down Expand Up @@ -99,4 +99,4 @@ target_link_libraries(
ReactAndroid::jsi
fbjni::fbjni
android
react-native-worklets::worklets)
exodus_react-native-worklets::worklets)
10 changes: 5 additions & 5 deletions packages/react-native-reanimated/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ dependencies {
if (project == rootProject) {
// This is needed for linting in Reanimated's repo.
} else {
if (rootProject.subprojects.find { it.name == "react-native-worklets" }) {
implementation project(":react-native-worklets")
if (rootProject.subprojects.find { it.name == "exodus_react-native-worklets" }) {
implementation project(":exodus_react-native-worklets")
} else {
throw new GradleException("[Reanimated] `react-native-worklets` library not found. Please install it as a dependency in your project. Install `react-native-worklets` with your package manager, i.e. `yarn add react-native-worklets` or `npm i react-native-worklets`. Read the documentation for more details: https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#unable-to-find-a-specification-for-rnworklets-depended-upon-by-rnreanimated")
}
Expand All @@ -328,14 +328,14 @@ dependencies {
preBuild.dependsOn(prepareReanimatedHeadersForPrefabs)

if (project != rootProject) {
evaluationDependsOn(":react-native-worklets")
evaluationDependsOn(":exodus_react-native-worklets")

afterEvaluate {
tasks.named("externalNativeBuildDebug").configure {
dependsOn(findProject(":react-native-worklets").tasks.named("externalNativeBuildDebug"))
dependsOn(findProject(":exodus_react-native-worklets").tasks.named("externalNativeBuildDebug"))
}
tasks.named("externalNativeBuildRelease").configure {
dependsOn(findProject(":react-native-worklets").tasks.named("externalNativeBuildRelease"))
dependsOn(findProject(":exodus_react-native-worklets").tasks.named("externalNativeBuildRelease"))
}
tasks.named("clean") {
it.finalizedBy(cleanCMakeCache)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ class SensorSetter : public HybridClass<SensorSetter> {
size_t size = value->size();
auto elements = value->getRegion(0, size);
double array[7];
for (size_t i = 0; i < size; i++) {
// Security: bounds check to prevent buffer overflow (0080)
size_t max = sizeof(array) / sizeof(array[0]);
for (size_t i = 0; i < size && i < max; i++) {
array[i] = elements[i];
}
callback_(array, orientationDegrees);
Expand Down
10 changes: 5 additions & 5 deletions packages/react-native-reanimated/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-reanimated",
"version": "4.3.0",
"name": "@exodus/react-native-reanimated",
"version": "4.3.0-exodus.3",
"description": "More powerful alternative to Animated library for React Native.",
"keywords": [
"react-native",
Expand Down Expand Up @@ -34,7 +34,7 @@
"type:check:strict": "yarn type:check:strict:src && yarn type:check:strict:app",
"type:check:strict:src": "yarn tsc --noEmit --customConditions react-native-strict-api",
"type:check:strict:app": "yarn workspace common-app type:check:strict",
"build": "yarn workspace react-native-worklets build && bob build",
"build": "yarn workspace @exodus/react-native-worklets build && bob build",
"circular-dependency-check": "yarn madge --extensions js,jsx --circular lib",
"tree-shake:check:web": "yarn is-tree-shakable --resolution web",
"validate-peers": "node ../../scripts/validate-compatibility-peer-dependencies.js"
Expand Down Expand Up @@ -96,9 +96,9 @@
"semver": "^7.7.3"
},
"peerDependencies": {
"@exodus/react-native-worklets": "0.9.x",
"react": "*",
"react-native": "0.81 - 0.85",
"react-native-worklets": "0.8.x"
"react-native": "0.81 - 0.85"
},
"devDependencies": {
"@babel/core": "7.28.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-reanimated/plugin/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @ts-ignore plugin type isn't exposed
const plugin = require('react-native-worklets/plugin');
const plugin = require('@exodus/react-native-worklets/plugin');

module.exports = plugin;
Loading
Loading