react-native: App Rejected from App Store [Resolved: because of Amap and JSPatch, not React Native]

Our App got rejected by App Store on March 17 with the following message from Apple

Mar 17, 2017 at 8:00 PM
From Apple

2. 5 Performance: Software Requirements (iOS)

Thank you for submitting your app.

Upon further review, we found your app out of compliance with the following guideline(s):

Performance - 2.5.2

Your app, extension, and/or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval, which is not in compliance with App Store Review Guideline 2.5.2 and section 3.3.2 of the Apple Developer Program License Agreement.

This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes. This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior and/or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.

Next Steps

Perform an in-depth review of your app and remove any code, frameworks, or SDKs that fall in line with the functionality described above and resubmit your app’s binary for review.

Best regards,
App Store Review

List of iOS packages used by us

  • Reachability
  • GoogleSignin
  • Alamofire
  • Crashlytics
  • Fabric
  • NVActivityIndicator
  • React Native SQLite Storage
  • RNTableView
  • CodePush
  • RNDeviceInfo
  • react-native-mixpanel
  • OneSignal
  • react-native-camera
  • react-native-keep-awake

We were suspecting the CodePush. So, We re-submitted the App without CodePush. Still, Apple rejected by saying the same message.

They have mentioned that We shouldn’t use dlopen(), dlsym() etc., So, We did the search of whole Repo for those functions. We found those functions in React Native itself. We found dlsym in RCTUtils.m ( https://github.com/facebook/react-native/blob/master/React/Base/RCTUtils.m#L535 ) etc.,

Thanks,

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 17
  • Comments: 62 (34 by maintainers)

Most upvoted comments

Hi,

Finally, We found out the root cause of the problem. It is not rejected because of the React Native or CodePush. We had our Manufacturer’s SDK in our iOS App. They were using Amap ( Alibaba Map ) which was using the JSPatch. Since everything was in the compiled code, We were not able to figure out.

Thank you @brentvatne, @axemclion and the whole community for helping us to find the cause.

As @hilkeheremans posted earlier, please have speculative discussions somewhere else.

@sriraman I think you should do the following:

  1. Submit an appeal to get the app approved
  2. Ask Apple about exactly which library they are referring to
  3. Ask Apple their position about React Native
  4. Open the source code for a couple people that can help looking for problems, preferably including someone in the RN core team

The remote possibility of react native being the rejection cause is very prejudicial to React Native image and community, we must find the real root cause ASAP and end this.

@axemclion Could you send me the link that developer license because I couldn’t find it.

There is nothing about it here: https://developer.apple.com/app-store/review/guidelines/

Just “(vii) They must use the Mac App Store to distribute updates; other update mechanisms are not allowed.”

Could we refrain from this type of discussion in this thread? The purpose of this thread is first and foremost to help solve the issue @sriraman has reported.

At this time, there is still nothing that clearly points towards Apple rejecting, as a whole, mechanisms such as Code Push or RN, so let’s not speculate on that too much, at least not until we get a clear response from Apple. Most of us are aware of the Apple guidelines already, and as you probably know, both RN and Code Push fall under some very relevant exceptions in there.

I think Apple will eventually reject apps that use CodePush even if they are allowed now. This is because it’s just out of compliance with the guidelines to update apps or their parts without App Store. And it’s completely not related to system methods that are used or not.

And because RN allows running external js code itself, there is a non-zero probability that one day Apple will reject all apps that use RN.

I think RN should prevent loading & running external code to protect from that scenario. Once Apple rejects RN, it won’t be easy to convince them that after last changes it’s not possible anymore or it will take ages. RN should be proactive and protect from that now. Not wait for the disaster.

I suspect it’s due to react-native-one-signal.

The selector helpers here allow swizzling out any arbitrary methods. If these selector helpers are accessible from JavaScript in any way, then it’s very likely that this is causing the issue, since you would be able to swap out native functions pretty easily.

@geoc3 Hi, I work Expo – with Expo specifically we’ve heard of zero App Store rejections due to this reason. While Apple’s policies aren’t always clear-cut, we make sure that the Expo codebase follows the Apple guidelines (technically & w.r.t. intent) and since Expo developers generally write only JavaScript, that clears the likelihood of a developer’s accidentally breaking the Apple guidelines. The Expo standalone app builder creates the IPA with the right compiler flags so the developer is safe from accidentally submitting a build that contains debug code paths, in the event that those are setting off Apple’s analyzer.

All this is to say that between an audited Expo native code base, verified compiler settings, and my own deep knowledge of how RN works and how it relates to Apple’s message (n.b. RN does not convert JS to native code nor does it “pass arbitrary parameters to dynamic methods”), both Expo and other bare RN apps (without custom code in violation) comply with the Apple guidelines to the best of my knowledge.

Apple rejected our app again with the same message. They didn’t give any proper reason for the rejection after reviewing the app for past 7 days. Now, We are starting the appeal process to App Review Board. I’ll update the same thread once we get the response from the App Review Board

We asked for the specific reason. They sent us the following message

Specifically, this was rejected because it contains a library which takes javascript and converts it directly to native code. It would be appropriate to remove all libraries and frameworks which perform such functionality before resubmitting for review.

What should we do next?

@ptomasroos You are completely right, but you only think about it from a developer perspective. But look at it from a user perspective, security, and Apple guidelines. RN allows writing apps that have some features and then reloads it’s code and becomes completely different App. It’s out of compliance with the Apple guidelines. It’s not safe for users. Apple can’t control that kind of apps. That’s all.

All iOS browsers are based on built-in WebView. Firefox and others are just UIs for WebView from Apple. Apple want’s to have everything under control. Apple can kill RN on iOS with one decision and one entry in its guidelines. Because it’s Apple and they can.

IMHO, it’s better to make CodeShip incompatible with latest RN on iOS because of some security mechanisms in RN framework than waiting until Apple kill RN on iOS.

UPDATE: Of course it’s open source and developers, as well as CodeShip can make it possible again to run external js code on iOS but in core RN it should not be possible and be 100% compatible with Apple guidelines.

UPDATE (2): Apple is just killing https://rollout.io/ project after 3 years of its existence. I suggest to not be so confident about RN future.

@lyahdav I don’t think removing those calls would break React Native in production. Trying to come up with a minimal repro.

All of these methods are quite common in typical iOS programming, especially the latter three. Also historically Apple’s automatic scanners sometimes have had false positives (ex: thinking a private API was called when there definitely were no calls to it).

For what it’s worth, today the App Store accepted our RN app we submitted a couple days ago. We use RN and Crashlytics (not all of Fabric though).

@kapone89 If you had carefully read through this thread above, you would find that both your last question and the gist of your initial remarks were already replied to. So might I suggest you re-read this entire thread?

@brunolemos Thanks for the suggestion. We spoke with @brentvatne . We are opening our source to him. I think he can help us in finding the issue if we are doing something wrong.

We are starting the Appeal also. I’ll let everyone know once we find the root cause for the rejection.

@hilkeheremans We are using older version only (v0.30).

We were able to get an app using CodePush into the app store. I don’t believe it would be CodePush causing the problem.

which takes javascript and converts it directly to native code

hmm, which library does that?

@kapone89 We are working with @sriraman and rest of the folks to see how we can resolve this issue. At this point, we have not heard any reports of CodePush apps being banned. Note that Ios Developer License section 3.3.2 explicitly states that it is ok to update code running in JavaScriptCore or Cordova, and React Native apps use JSC.

Also note that if the purpose of the app is changed, that would be a violation. Our team at Microsoft is proactively monitoring the situation and will be updating this thread as we get updates.

@kapone89 That means web browsers should also be banned it runs external js code? Seems legit. Worst comment I’ve read in a while.

If you look at their latest review guidelines they removed the verbiage of allowing apps running on the javascriptcore to update remotely.

source? from what people have posted above it’s still there in the latest agreement.

Their response of ‘your app takes javascript and converts it to native code’ terrifies me a little bit since that is what React Native does at it’s core

React Native doesn’t convert JavaScript to native code. It’s not much different from what a browser does, provide access to few native APIs from JavaScript.

Just an aside – I really doubt this is a factor – but @sriraman, what version of RN are you using?

That linked ticket has good excerpts on the rules and why something like CodePush shouldn’t be the problem ^.

I agree with @brunolemos in that you should try to get Apple to answer specific questions through an appeal. Thanks for hanging in there for the rest of us 🙏

As far as we know, CodePush (for RN and Cordova, in this context the two are equivalent) itself is well within the Apple guidelines. CodePush and RN do not directly or indirectly pass arbitrary parameters to dynamic methods like dlopen and dlsym. As an app developer you shouldn’t drastically change the advertised or intended purpose of your app (don’t change your chat messenger into a catalog of obscene content), but from a technical perspective CodePush follows the guidelines. And as @sriraman posted, removing CodePush from a rejected app did not lead to the app’s getting accepted – there are no data points indicating that CodePush is a problem so far.

If you intentionally went out of your way, you could write a RN module or Cordova plugin or a plain Obj-C class for a plain UIKit app (e.g. Rollout, NativeScript) that does invoke those dynamic methods from JavaScript, but neither RN, Cordova, nor UIKit offer or encourage this functionality.

As mentioned above you could enable ATS, which enforces HTTPS for all connections of your app. Using HTTPS would greatly reduce the opportunity for MITM attacks – an attacker would need to steal your server’s private TLS keys or have physical access to the unlocked iOS device.

@sriraman Those calls you linked to also have static arguments. You could try commenting them out anyway to see if your app gets approved, but many people are reporting that their apps with RN and CodePush are getting approved.

The dlopen, dlsym, respondsToSelector:, performSelector:, and method_exchangeImplementations APIs by themselves are public and may be used, the issue is specifically with “code which passes arbitrary parameters to dynamic methods”. E.g. the dlsym case that @brunolemos linked to refers to functions with static strings, so should be fine.

My suspicion would be CodePush, which is specifically meant to deploy updates after the initial review process. Maybe try resubmitting without that and see if it goes through? The fact that others are able to deploy with it could just be explained by them randomly picking out apps for more granular checks.

I also want to clarify that React Native’s calls to those functions may not have raised that alert. Other libraries may be invoking those methods, too.