Android - Customise the order of ACRA report content? - android

Having read this documentation, I'm using this code...
import org.acra.ACRA;
import org.acra.annotation.ReportsCrashes;
import org.acra.ReportField;
#ReportsCrashes (
formUri = "http://example.com/crash-reports/emailer.php",
customReportContent = {ReportField.APP_VERSION_NAME, ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, ReportField.STACK_TRACE, ReportField.LOGCAT},
sharedPreferencesName = "ACRA_SHARED_PREFS",
sharedPreferencesMode = Context.MODE_PRIVATE
)
...to specify what content to include in my ACRA reports.
I would like to know if/how it's possible to also specify the order of this content, as the content in the emails that are sent from my emailer.php script seems to be a bit random.

The ReportBuilder class in ACRA constructs the report. It does so in a deterministic fashion and the order is determine by the code in that class.

Related

How can I implement SSL Certificate Pinning while using React Native

I need to implement SSL Certificate Pinning in my react native application.
I know very little about SSL/TLS let alone pinning.
I am also not a native mobile developer, though I know Java and learned Objective-C on this project enough to get around.
I started searching for how to execute this task.
Doesn't React Native already implement this?
No, My initial search lead me to this proposal which has received no activity since August 2nd 2016.
From it I learned that react-native uses OkHttp which does support Pinning, but I wouldn't be able to pull it off from Javascript, which is not really a requirement but a plus.
Implement it in Javascript.
While react seems like it uses the nodejs runtime, it is more like a browser than node, meaning it does not support all native modules, specifically the https module, for which I had implemented certificate pinning following this article. Thus could not carry it into react native.
I tried using rn-nodeify but the modules didn't work. This has been true since RN 0.33 to RN 0.35 which I'm currently on.
Implement using phonegap plugin
I thought of using a phongape-plugin however since I have a dependency on libraries that require react 0.32+ I can't use react-native-cordova-plugin
Just do it natively
While I'm not a native app developer I can always take a crack at it, only a matter of time.
Android has certificate pinning
I learned that android supports SSL Pinning however was unsuccessful as it seems that this approach does not work Prior to Android 7. As well as only working for android.
The bottom line
I have exhausted several directions and will continue to pursue more native implementation, maybe figure out how to configure OkHttp and RNNetworking then maybe bridging back to react-native.
But is there already any implementations or guide for IOS and android?
After exhausting the current spectrum of available options from Javascript I decided to simply implement certificate pinning natively it all seems so simple now that I'm done.
Skip to headers titled Android Solution and IOS Solution if you don't want to read through the process of reaching the solution.
Android
Following Kudo's recommendation I thought out to implement pinning using okhttp3.
client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build())
.build();
I first started by learning how to create a native android bridge with react nativecreating a toast module. I then extended it with a method for sending a simple request
#ReactMethod
public void showURL(String url, int duration) {
try {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
Toast.makeText(getReactApplicationContext(), response.body().string(), duration).show();
} catch (IOException e) {
Toast.makeText(getReactApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
Succeeding in sending a request I then turned to sending a request pinned.
I used these packages in my file
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
Kudo's approach wasn't clear on where I would get the public keys or how to generate them. luckily okhttp3 docs in addition to providing a clear demonstration of how to use the CertificatePinner stated that to get the public keys all I would need to do is send a request with an incorrect pin, and the correct pins will appear in the error message.
After taking a moment to realise that OkHttpClent.Builder() can be chained and I can include the CertificatePinner before the build, unlike the misleading example in Kudo's proposal (probably and older version) I came up with this method.
#ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
Callback successCallback) {
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
Response response =client.newCall(request).execute();
successCallback.invoke(response.body().string());
} catch (Exception e) {
errorCallbackContainingCorrectKeys.invoke(e.getMessage());
}
}
Then replacing the public keychains I got in the error yielded back the page's body, indicating I had made a successful request, I change one letter of the key to make sure it was working and I knew I was on track.
I finally had this method in my ToastModule.java file
#ReactMethod
public void getKeyChainForHost(String hostname, Callback errorCallbackContainingCorrectKeys,
Callback successCallback) {
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
.add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
.add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
.build();
OkHttpClient client = (new OkHttpClient.Builder()).certificatePinner(certificatePinner).build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
Response response =client.newCall(request).execute();
successCallback.invoke(response.body().string());
} catch (Exception e) {
errorCallbackContainingCorrectKeys.invoke(e.getMessage());
}
}
Android Solution Extending React Native's OkHttpClient
Having figured out how to send pinned http request was good, now I can use the method I created, but ideally I thought it would be best to extend the existing client, so as to immediately gain the benefit of implementing.
This solution is valid as of RN0.35 and I don't know how it will fair in the future.
While looking into ways of extending the OkHttpClient for RN I came across this article explaining how to add TLS 1.2 support through replacing the SSLSocketFactory.
reading it I learned react uses an OkHttpClientProvider for creating the OkHttpClient instance used by the XMLHttpRequest Object and therefore if we replace that instance we would apply pinning to all the app.
I added a file called OkHttpCertPin.java to my android/app/src/main/java/com/dreidev folder
package com.dreidev;
import android.util.Log;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.CertificatePinner;
public class OkHttpCertPin {
private static String hostname = "*.efghermes.com";
private static final String TAG = "OkHttpCertPin";
public static OkHttpClient extend(OkHttpClient currentClient){
try {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=")
.add(hostname, "sha256/aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=")
.add(hostname, "sha256/HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=")
.build();
Log.d(TAG, "extending client");
return currentClient.newBuilder().certificatePinner(certificatePinner).build();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return currentClient;
}
}
This package has a method extend which takes an existing OkHttpClient and rebuilds it adding the certificatePinner and returns the newly built instance.
I then modified my MainActivity.java file following this answer's advice by adding the following methods
.
.
.
import com.facebook.react.ReactActivity;
import android.os.Bundle;
import com.dreidev.OkHttpCertPin;
import com.facebook.react.modules.network.OkHttpClientProvider;
import okhttp3.OkHttpClient;
public class MainActivity extends ReactActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rebuildOkHtttp();
}
private void rebuildOkHtttp() {
OkHttpClient currentClient = OkHttpClientProvider.getOkHttpClient();
OkHttpClient replacementClient = OkHttpCertPin.extend(currentClient);
OkHttpClientProvider.replaceOkHttpClient(replacementClient);
}
.
.
.
This solution was carried out in favor of completely reimplementing the OkHttpClientProvider createClient method, as inspecting the provider I realized that the master version had implemented TLS 1.2 support but was not yet an available option for me to use, and so rebuilding was found to be the best means of extending the client. I'm wondering how this approach will fair as I upgrade but for now it works well.
Update It seems that starting 0.43 this trick no longer works. For timebound reasons I will freeze my project at 0.42 for now, until the reason for why rebuilding stopped working is clear.
Solution IOS
For IOS I had thought I would need to follow a similar method, again starting with Kudo's proposal as my lead.
Inspecting the RCTNetwork module I learned that NSURLConnection was used, so instead of trying to create a completely new module with AFNetworking as suggested in the proposal I discovered TrustKit
following its Getting Started Guide I simply added
pod 'TrustKit'
to my podfile and ran pod install
the GettingStartedGuide explained how I can configure this pod from my pList.file but preferring to use code than configuration files I added the following lines to my AppDelegate.m file
.
.
.
#import <TrustKit/TrustKit.h>
.
.
.
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Initialize TrustKit
NSDictionary *trustKitConfig =
#{
// Auto-swizzle NSURLSession delegates to add pinning validation
kTSKSwizzleNetworkDelegates: #YES,
kTSKPinnedDomains: #{
// Pin invalid SPKI hashes to *.yahoo.com to demonstrate pinning failures
#"efghermes.com" : #{
kTSKEnforcePinning:#YES,
kTSKIncludeSubdomains:#YES,
kTSKPublicKeyAlgorithms : #[kTSKAlgorithmRsa2048],
// Wrong SPKI hashes to demonstrate pinning failure
kTSKPublicKeyHashes : #[
#"+Jg+cke8HLJNzDJB4qc1Aus14rNb6o+N3IrsZgZKXNQ=",
#"aR6DUqN8qK4HQGhBpcDLVnkRAvOHH1behpQUU1Xl7fE=",
#"HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY="
],
// Send reports for pinning failures
// Email info#datatheorem.com if you need a free dashboard to see your App's reports
kTSKReportUris: #[#"https://overmind.datatheorem.com/trustkit/report"]
},
}
};
[TrustKit initializeWithConfiguration:trustKitConfig];
.
.
.
I got the public key hashes from my android implementation and it just worked (the version of TrustKit I received in my pods is 1.3.2)
I was glad IOS turned out to be a breath
As a side note TrustKit warned that it's Auto-swizzle won't work if the NSURLSession and Connection are already swizzled. that said it seems to be working well so far.
Conclusion
This answer presents the solution for both Android and IOS, given I was able to implement this in native code.
One possible improvement may be to implement a common platform module where setting public keys and configuring the Network providers of both android and IOS can be managed in javascript.
Kudo's proposal mentioned simply adding the public keys to the js bundle may however expose a vulnerability, where somehow the bundle file can be replaced.
I don't know how that attack vector can function, but certainly the extra step of signing the bundle.js as proposed may protect the js bundle.
Another approach may be to simply encode the js bundle into a 64 bit string and include it in the native code directly as mentioned in this issue's conversation. This approach has the benefit of obfuscating as well hardwiring the js bundle into the app, making it inaccessible for attackers or so I think.
If you read this far I hope I enlightened you on your quest for fixing your bug and wish you enjoy a sunny day.
You can use this lib https://github.com/nlt2390/react-native-pinning-ssl
It verifies SSL connection using SHA1 keys, not certificates.

Android ACRA - Application Crash Reporting for Android

I am trying to figure out how to add ACRA in my test project in Android Studio.
Following this article (which explains how to do it in Eclipse) https://github.com/ACRA/acra/wiki/BasicSetup, I have passed all so far up to the point where is explains to annotate your Application class with #ReportsCrashes. In this part, I dont know what to put as formUri.
I am just creating my own test app (API22 Lollipop) and trying to add ACRA support to it. I don't have any server, it is just a simple Android app.
import org.acra.*;
import org.acra.annotation.*;
#ReportsCrashes(
formKey = "", // This is required for backward compatibility but not used
formUri = "http://www.backendofyourchoice.com/reportpath"
)
public class MyApplication extends Application {
}
What do I put as the formUri?
Can you explain what formUri is for and how it works as I am new to dev?
Please explain
Thanks,
formUri points to your crash report server.
There are many to choose from, commercial and free.
The ACRA wiki lists several.
ACRAlyzer is one, but you will need to host it yourself.
Read about Acralyzer: https://github.com/ACRA/acralyzer/wiki/setup
It is simple backend for ACRA reports.
#ReportsCrashes(
formUri = "https://[your.couchdb.host]/acra-[yourappname]/_design/acra-storage/_update/report",
formUriBasicAuthLogin="[reporteruser]",
formUriBasicAuthPassword="[reporterpassword]",
reportType = org.acra.sender.HttpSender.Type.JSON,
httpMethod = org.acra.sender.HttpSender.Method.PUT,
....
)
public class [YourApplication] extends Application {
#Override
public final void onCreate() {
super.onCreate();
ACRA.init(this);
}
There you will find the usage of the Acralyzer user interface: https://github.com/ACRA/acralyzer/wiki/usermanual

Verify that ACRA is working

I am just about to release an app and I saw this stackoverflow question about things you should do before releasing an app. It tells me to use ACRA so I followed the instructions on this page.
So know I have a class called MyApplication like so :
import android.app.Application;
import org.acra.*;
import org.acra.annotation.*;
#ReportsCrashes(formKey = "", // will not be used
mailTo = "c#gmail.com",
mode = ReportingInteractionMode.TOAST,
customReportContent = { ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME, ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, ReportField.CUSTOM_DATA, ReportField.STACK_TRACE, ReportField.LOGCAT },
resToastText = R.string.crash_report_text)
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// The following line triggers the initialization of ACRA
ACRA.init(this);
}
}
Now apparently when my application crashes I think the user is supposed to be able to send the report somehow, so I put a null pointer in my app but it just crashes and nothing happens. Am I supposed to do anything else?
I guess you might not have registered your application on BugSense
Where you want to send the crash reporst that depands on you only. When you are using Google Docs (deprecated now), you have to use your formKey you got from your google docs document.
If you want to store the reports on your own server, you can leave the formKey field blank. The only thing you have to do is to enter a valid url to your server (formUri = ....).
The other strings are for the type of dialog, which should or shouldn't appear.
Instead of using your own server, you can use BugSense. See this thread on stackoverflow.
As the use of Google Docs is deprecated for ACRA. I recommend you to use **BugSense** as your Back-End service:
1. Go to their site and sign in: http://www.bugsense.com/
2. Create a new project to monitor in BugSense site, as a result you will receive an API Key for this application.
3. Finally add this line to you Application class in you project:
#ReportsCrashes(formUri = "http://www.bugsense.com/api/acra?api_key=YOUR_API_KEY", formKey="")
Check out BugSense Docmentation

How to send Android Crash report using ACRA

Am trying to send crash report from my applicatio to my domain or Mail but failed still.
To get the crash report in mail, I did
#ReportsCrashes(
formKey = "",
mailTo = "abc#gmail.com"
)
And the response is,
Sending file 1372758321000-approved.stacktrace
checkAndSendReports - finish
To get the crash report in my domain, I did
#ReportsCrashes(
formKey = "",
formUri = "http://www.abc.com/test1"
)
And the response is,
Sending file 1372856882000-approved.stacktrace
Failed to send crash report for 1372856882000-approved.stacktrace
org.acra.sender.ReportSenderException: Error while sending FORM report via Http POST
Any help will be handy for me and appreciated.
ACRA works for me sending reports by e-mail when I do exactly as they say in their docs:
#ReportsCrashes(mailTo = "reports#yourdomain.com", // my email here
mode = ReportingInteractionMode.TOAST,
resToastText = R.string.crash_toast_text)
https://github.com/ACRA/acra/wiki/Report-Destinations#sending-reports-by-email
You are probably forgetting the toast part. Or can it be you don't have an e-mail program (such as when you're running on the simulator).
I think sending reports by Google docs are not supported anymore.
Your application class should look like this.
import android.app.Application;
import org.acra.ACRA;
import org.acra.ReportField;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
#ReportsCrashes(mailTo = "user#domain.com", customReportContent = {
ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME,
ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL,
ReportField.CUSTOM_DATA, ReportField.STACK_TRACE, ReportField.LOGCAT},
mode = ReportingInteractionMode.TOAST, resToastText = R.string.crash_toast_text)
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
ACRA.init(this);
}
}
No,not like Alex say,the mode property has no releation to the reporting type,you can see it in the source code in github
using the mailTo type,you should make sure that:
your app has the permission to connect network;
have an e-mail program in your device like Alex say;
have you invoked the ACRA.init(this) method in your application's oncreate()?
if all of these have done,then run your app,it will note you to configure the email,such as username and password and so on.

as3 android saving downloaded files from server

The following code WORKS when run on the Desktop but does not on the android device. I am thinking it has something to do with the FileStream below.
Any thoughts on how I can save this to an Android device too?
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.net.FileReference;
import flash.net.URLRequest;
import flash.events.Event;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, complete_handler);
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(new URLRequest("http://massmediamail.com/mp3s/Why%20a%20Protestant%20Pastor%20Beacame%20Catholic.mp3"));
//any file type;
function complete_handler(event:Event):void
{
var data:ByteArray = event.target.data;
var fr:FileReference = new FileReference();
trace(File.applicationDirectory.nativePath);
fr.save(data, 'Catholic.mp3');
var fileStream:FileStream = new FileStream();
trace(File.applicationDirectory.nativePath);
fileStream.open(new File (File.applicationStorageDirectory.nativePath+"\\Catholic.mp3"),FileMode.WRITE);
fileStream.writeBytes(data, 0, data.length);
}
HERE IS THE ERROR:
Error #2044: Unhandled IOErrorEvent:. text=Error #2038: File I/O Error.
at Untitled_fla::MainTimeline/complete_handler()[Untitled_fla.MainTimeline::frame1:25]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
THIS IS THE LINE IT'S REFERRING TOO:
fileStream.open(new File (File.applicationStorageDirectory.nativePath+"\\Catholic.mp3"),FileMode.WRITE);
It works with Air for Android. Yes you can do more with it later but this is the basic start.
import flash.net.FileReference;
/// It can be an mp3,jpg, png, etc... just change the url
/// and the extension name. nice huh?
var yourFileLocation = "http://YourWeb.com/YourSong.mp3";
var yourFileName = "YourSong.mp3";
var daFile:FileReference = new FileReference();
daFile.download(new URLRequest(yourFileLocation), yourFileName);
I worked FOREVER... to find this. I hope it helps many. Why is this not more common knowledge? Somethings in action-script are impossible to find sometimes.
There is ONE question I have regarding this. How can I make the code download directly to a location on the device rather than the user having to choose the location. Thanks Much!
You'll definitely need to change to "/" over the "\", Android is a linux based system. Actually you should use File.separator property for this rather than a string, this will keep your code cross-platform.
Generally though you should be able to use this:
File.applicationStorageDirectory.resolvePath( "Catholic.mp3" );
Have you given the application permission to write to the filesystem?
To do this you need to add the following, in particular the "WRITE_EXTERNAL_STORAGE" line, to your application descriptor.
<android>
<manifestAdditions><![CDATA[
<manifest android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
]]></manifestAdditions>
</android>
Also you should listen for these errors to get more information, and handle them in your code:
fileStream.addEventListener( IOErrorEvent.IO_ERROR, fileStream_errorHandler,

Categories

Resources