I'm using Dropbox iOS API v2. When trying to sort out folders from files, files that are packages (i.e. a preferences file, or xcodeproject file, .framework, etc.) show up as folder types. Is there a way to distinguish between folders and file packages?
DBFILESMetadata * metaData = ...;
if ([metaData isKindOfClass:[DBFILESFileMetadata class]]) {
// is a file
} else if ([metaData isKindOfClass:[DBFILESFolderMetadata class]]) {
// is a folder or file package
} else if ([metaData isKindOfClass:[DBFILESDeletedMetadata class]]) {
// has been deleted
}
If the 'bundle' (B) bit is set, then it should be treated as a bundle, rather than a single file. However, not all 'packages' are 'bundles'.
See: How do I flag a folder as being a package?
If the package is used by an app that is installed on the device, then its extension should be registered in the system by that application to identify it as being a package folder rather than a plain folder. However, I'm not sure if you can identify these as packages from an app that does not have that particular type registered.
See: https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFBundles/DocumentPackages/DocumentPackages.html
If the package is not a bundle and has an extension that is not registered by an app installed on the current device, then I don't think there is any method that can be certain to be accurate, and your work around of has-a-filename-extension is probably the only thing you can do.
This is what I ended up with.
DBFILESMetadata * metaData = ...;
if ([metaData isKindOfClass:[DBFILESFileMetadata class]]) {
// is a file
} else if ([metaData isKindOfClass:[DBFILESFolderMetadata class]]) {
if ([[metaData.name pathExtension] isEqualToString:#""] == NO) {
// is a file package / bundle
} else {
// is a folder
}
} else if ([metaData isKindOfClass:[DBFILESDeletedMetadata class]]) {
// has been deleted
}
Related
Given the plugins that are available in the Nativescript community, your Nativescript app may or may not be sufficient to pass security penetration testing.
Below are two plugins to list a few.
https://www.npmjs.com/package/#nstudio/root-detection
https://www.npmjs.com/package/nativescript-jailbreak-detector
In some scenarios, you could achieve better results by manually writing your own checks against a jailbreak and dynamic instrumentation (e.g. Frida), since there are so many tools to bypass jailbreak detection (e.g. HideJB) nowadays.
What are some ways we can detect jailbreak and protect against dynamic instrumentation on iOS Nativescript?
Detection can be carried out on multi-levels:
Check if URLs are openable via illegal URL schemes
Check if files are openable on illegal directories
Check if illegal files exist (incl. Cydia, Sileo, HideJB, etc.)
Check if files are writable on restricted directories
Code
public amIJailbroken(): boolean {
let urlSchemes: Array<string> = ['undecimus://', 'cydia://', 'sileo://', 'zbra://', 'filza://', 'activator://'];
// List of suspicious files associated with jailbreak
let paths: Array<string> = [
'/.bootstrapped_electra',
'/.cydia_no_stash',
'/.installed_unc0ver',
'/Applications/blackra1n.app',
'/Applications/Cydia.app',
'/Applications/FakeCarrier.app',
'/Applications/HideJB.app',
'/Applications/Icy.app',
'/Applications/IntelliScreen.app',
'/Applications/MxTube.app',
'/Applications/RockApp.app',
'/Applications/SBSettings.app',
'/Applications/SBSetttings.app',
'/Applications/Sileo.app',
'/Applications/Snoop-itConfig.app',
'/Applications/WinterBoard.app',
'/bin.sh',
'/bin/bash',
'/bin/sh',
'/etc/apt',
'/etc/apt/sources.list.d/electra.list',
'/etc/apt/sources.list.d/sileo.sources',
'/etc/apt/undecimus/undecimus.list',
'/etc/ssh/sshd_config',
'/jb/amfid_payload.dylib',
'/jb/jailbreakd.plist',
'/jb/libjailbreak.dylib',
'/jb/lzma',
'/jb/offsets.plist',
'/Library/dpkg/info/re.frida.server.list',
'/Library/LaunchDaemons/re.frida.server.plist',
'/Library/MobileSubstrate/CydiaSubstrate.dylib',
'/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist',
'/Library/MobileSubstrate/DynamicLibraries/Veency.plist',
'/Library/MobileSubstrate/HideJB.dylib',
'/Library/MobileSubstrate/MobileSubstrate.dylib',
'/Library/PreferenceBundles/ABypassPrefs.bundle',
'/Library/PreferenceBundles/FlyJBPrefs.bundle',
'/Library/PreferenceBundles/HideJBPrefs.bundle',
'/Library/PreferenceBundles/LibertyPref.bundle',
'/Library/PreferenceBundles/ShadowPreferences.bundle',
'/private/etc/apt',
'/private/etc/dpkg/origins/debian',
'/private/etc/ssh/sshd_config',
'/private/var/cache/apt/',
'/private/var/lib/apt',
'/private/var/lib/apt/',
'/private/var/lib/cydia',
'/private/var/log/syslog',
'/private/var/mobile/Library/SBSettings/Themes',
'/private/var/mobileLibrary/SBSettingsThemes/',
'/private/var/stash',
'/private/var/tmp/cydia.log',
'/private/var/Users/',
'/System/Library/LaunchDaemons/com.ikey.bbot.plist',
'/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist',
'/usr/bin/cycript',
'/usr/bin/ssh',
'/usr/bin/sshd',
'/usr/lib/libcycript.dylib',
'/usr/lib/libhooker.dylib',
'/usr/lib/libjailbreak.dylib',
'/usr/lib/libsubstitute.dylib',
'/usr/lib/substrate',
'/usr/lib/TweakInject',
'/usr/libexec/cydia/',
'/usr/libexec/cydia/firmware.sh',
'/usr/libexec/sftp-server',
'/usr/libexec/ssh-keysign',
'/usr/local/bin/cycript',
'/usr/sbin/frida-server',
'/usr/sbin/sshd',
'/usr/share/jailbreak/injectme.plist',
'/var/binpack',
'/var/cache/apt',
'/var/checkra1n.dmg',
'/var/lib/apt',
'/var/lib/cydia',
'/var/lib/dpkg/info/mobilesubstrate.md5sums',
'/var/log/apt',
'/var/log/syslog',
'/var/tmp/cydia.log',
];
// Check if target is not an iOS simulator
if (!isIOS || !this.isTarget()) return false;
else {
// Check URL schemes
for (const url of urlSchemes) {
if (this.canOpenIllegalURL(url)) return true;
}
// Check files and directories associated with jailbreaks
for (const path of paths) {
if (this.canOpenIllegalFile(path)) return true;
}
// Check file permissions outside device sandbox, if writtable = jailbroken
if (this.canWriteToRestrictedDirectories()) return true;
return false;
}
}
/*
********** Helper Methods **********
*/
/* Check if environment is being run as a RELEASE build */
private isTarget() {
return process.env.RELEASE_ENV;
}
/* Check if we can open illegal URL schemes */
private canOpenIllegalURL(url): boolean {
return UIApplication.sharedApplication.canOpenURL(NSURL.URLWithString(url + 'package/com.example.app'));
}
/* Check if file is openable */
private canOpenIllegalFile(path): boolean {
const file = fopen(path, 'r');
if (!file) {
fclose(file);
return this.fileExists(path) || this.directoryExists(path);
}
fclose(file);
return true;
}
/* Check if file exists at path */
private fileExists(path): boolean {
return NSFileManager.defaultManager.fileExistsAtPath(path);
}
/* Check if directory exists at path */
private directoryExists(path): boolean {
return NSFileManager.defaultManager.fileExistsAtPathIsDirectory(path, new interop.Reference());
}
/* Check if file is writtable to illegal directory */
private canWriteToRestrictedDirectories(): boolean {
let error;
try {
const stringToBeWritten = NSString.stringWithString('I am evil.');
stringToBeWritten.writeToFileAtomicallyEncodingError('/private/jailbreak.txt', true, NSUTF8StringEncoding);
stringToBeWritten.writeToFileAtomicallyEncodingError('/root/jailbreak.txt', true, NSUTF8StringEncoding);
NSFileManager.defaultManager.removeItemAtPathError('/private/jailbreak.txt');
NSFileManager.defaultManager.removeItemAtPathError('/root/jailbreak.txt');
} catch (e) {
error = e;
}
return !error ? true : false;
}
References
The research comes from a consolidation of the following ideas:
https://stackoverflow.com/a/26712383/2192332
https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06j-testing-resiliency-against-reverse-engineering
https://github.com/securing/IOSSecuritySuite/blob/master/IOSSecuritySuite/JailbreakChecker.swift
https://github.com/avltree9798/isJailbroken/blob/master/isJailbroken/JB.m
Improvements
Please feel free to suggest!
E.g. Checking for illegal dynamic libraries in memory using _dyld_get_image_name
I'm trying to write a java program that reacts if a new entry occures in the file C:/xampp/apache/logs/access.log in order to recognize a new request to my Apache Server.
I used the following code:
public static void monitor() throws IOException {
WatchService watcher = FileSystems.getDefault().newWatchService();
File file = new File("C:/xampp/apache/logs/");
Path dir = file.toPath();
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY, OVERFLOW);
for (;;) {
// wait for key to be signaled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// get file name
#SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path fileName = ev.context();
System.out.println(kind.name() + ": " + fileName);
if (kind == OVERFLOW) {
continue;
} else if (kind == ENTRY_CREATE) {
System.out.println("entry created occured");
// process create event
} else if (kind == ENTRY_DELETE) {
// process delete event
} else if (kind == ENTRY_MODIFY && fileName.toString().equals("access.log")) {
System.out.println("entry modified occured");
// process modify event
}
}
// Reset the key -- this step is critical if you want to
// receive further watch events. If the key is no longer valid,
// the directory is inaccessible so exit the loop.
boolean valid = key.reset();
if (!valid) {
break;
}
}
}
But it does not recognize the change in access.log until I manually open the file. Is there something wrong with my code?
There are differents options.
There are two questions that can be kind of similiar, the only difference is that they want to check a whole direcotry instead of just a file, but you could adapt the code to detect if the modified file is the one that you want.
Watching a Directory for Changes in Java
Java detect changes in filesystem
For a specific solution I've found
http://www.rgagnon.com/javadetails/java-0490.html
This code launches a thread that checks the lastModified value of the file, if it's different from the previous one, it means that the file has been modified. I don't know if it's very efficient, check them out.
I'm trying to change the location of the AndroidManifest.xml file when using the experimental gradle plugin version 0.7.x. The reason for doing this is that I generate the file (as there is no manifest merger/property replacer in the experimental plugin) so I don't want an output file together with the sources.
My app build.gradle:
apply plugin: "com.android.model.application"
def buildDir = project.buildDir
model {
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.myapp.android"
minSdkVersion.apiLevel 9
targetSdkVersion.apiLevel 23
versionCode 1
versionName "1.0"
}
sources {
main {
manifest {
source {
srcDirs = ["$buildDir"]
}
}
}
}
}
}
task createManifest {
doLast {
buildDir.mkdirs()
new FileOutputStream(new File(buildDir, "AndroidManifest.xml"))
}
}
tasks.all { task ->
if (task.name.startsWith('check') && task.name.endsWith('Manifest')) {
task.dependsOn createManifest
}
}
The above configures fine but when I try to build I get:
A problem was found with the configuration of task ':app:checkDebugManifest'.
> File '/home/the_jk/source/test/app/src/main/AndroidManifest.xml' specified for property 'manifest' does not exist.`
I cannot seem to change the default manifest "property" at all, anyone had any luck?
Try
manifest { source { srcDirs = $buildDir } }
This seems to do ok with just 1 manifest, but the experimental plugin barfs if you give it 2 directories that you want to merge.
(I'm also guessing you have some other task to generate the manifest in the $buildDir since that gets blown away by clean tasks....)
Update:
Secondary issue, the check[Deubg|Release]Manifest task wants the file to exist when it runs. The above works ok for me for a static file. For something generated from a task in the build directory I had to add a dependency that looks like
task createManifest {
// code to create $buildDir if it did not exist
// code to generate AndrdroidManfest.xml in $buildDir
}
tasks.all {
task ->
if (task.name.startsWith('check') && task.name.endsWith('Manifest')) {
task.dependsOn createManifest
}
}
The tasks.all loop lets me only add it if checkDebugManifest and/or checkReleaseManifest tasks are going to happen (I had trouble with ./gradlew clean not finding the checkDebugManifest task without it.)
I had a similar issue with com.android.tools.build:gradle-experimental:0.7.0-alpha4. The way I solve it was with the following code:
sources {
main {
manifest {
source {
srcDirs = [ "$buildDir" ]
}
}
}
}
I have a project that uses CocoaPods. As a result, I have a workspace which contains two projects: mine and Pods.
Pods contains code which I'd like to localize, and I've created .strings files in Pod. However, NSLocalizedString fails to load these strings. I suspect this happens because the .strings file is not in the main bundle, but there's no Pod bundle, because it is compiled into a static library.
Is there a better way to localize code in a CocoaPods project than in my main project?
NSLocalizedString just invokes NSBundle's localizedStringForKey:value:table: so I wrote a NSBundle category to enable looking into several bundles (which in iOS are just folders):
NSString * const kLocalizedStringNotFound = #"kLocalizedStringNotFound";
+ (NSString *)localizedStringForKey:(NSString *)key
value:(NSString *)value
table:(NSString *)tableName
backupBundle:(NSBundle *)bundle
{
// First try main bundle
NSString * string = [[NSBundle mainBundle] localizedStringForKey:key
value:kLocalizedStringNotFound
table:tableName];
// Then try the backup bundle
if ([string isEqualToString:kLocalizedStringNotFound])
{
string = [bundle localizedStringForKey:key
value:kLocalizedStringNotFound
table:tableName];
}
// Still not found?
if ([string isEqualToString:kLocalizedStringNotFound])
{
NSLog(#"No localized string for '%#' in '%#'", key, tableName);
string = value.length > 0 ? value : key;
}
return string;
}
Then redefined NSLocalizedString macro in my prefix file:
#undef NSLocalizedString
#define NSLocalizedString(key, comment) \
[NSBundle localizedStringForKey:key value:nil table:#"MyStringsFile" backupBundle:AlternativeBundleInsideMain]
The same for other macros if needed (i.e. NSLocalizedStringWithDefaultValue)
#Rivera Swift 2.0 version:
static let kLocalizedStringNotFound = "kLocalizedStringNotFound"
static func localizedStringForKey(key:String,
value:String?,
table:String?,
bundle:NSBundle?) -> String {
// First try main bundle
var string:String = NSBundle.mainBundle().localizedStringForKey(key, value: kLocalizedStringNotFound, table: table)
// Then try the backup bundle
if string == kLocalizedStringNotFound {
string = bundle!.localizedStringForKey(key, value: kLocalizedStringNotFound, table: table)
}
// Still not found?
if string == kLocalizedStringNotFound {
print("No localized string for '\(key)' in '\(table)'")
if let value = value {
string = value.characters.count > 0 ? value : key
} else {
string = key
}
}
return string;
}
You should not put any file in the Pods project, because the pod command will recreate the project again and again.
So put the string files in your own project.
If you want to ship localized string files in your own Pod, you should include it in a bundle and make sure, the bundle will be installed in your Podspec file.
For example:
def s.post_install(target)
puts "\nGenerating YOURPOD resources bundle\n".yellow if config.verbose?
Dir.chdir File.join(config.project_pods_root, 'YOURPOD') do
command = "xcodebuild -project YOURPOD.xcodeproj -target YOURPODResources CONFIGURATION_BUILD_DIR=../Resources"
command << " 2>&1 > /dev/null" unless config.verbose?
unless system(command)
raise ::Pod::Informative, "Failed to generate YOURPOD resources bundle"
end
File.open(File.join(config.project_pods_root, target.target_definition.copy_resources_script_name), 'a') do |file|
file.puts "install_resource 'Resources/YOURPODResources.bundle'"
end
end
end
The best approach for localizing resources in a pod (public or private is indifferent) is to add the .strings localization files to the pod bundle.
The main app is very picky when comes to pick up the localization files from the pod frameworks, here the steps you must follow:
1 - Create the Localizable.strings and all the other *.strings files you need for your localization and put them in a folder, something like this:
Some constraints to respect:
The folders name must be XX.lproj, where XX is your EXACT language name, note: en != en-GB != en-US
All the lproj folder need to be at the same level
Every lproj folder need to have the same number of .strings files and with the same names
2 - Configure your .podspec file so the pod can pick up the localization correctly and copy them in the .framework.
Example:
s.resource = 'your_path/Localizations/**/*', '... possibly other resources'
after doing pod install the result in your Development Pods folder should be something like this:
One important thing to check is that created framework is that all the .lproj folder need to be on the root folder, otherwise are not picked up correctly by the main app.
3 - In your pod's code instead than the usual NSLocalizedString you must use NSLocalizedStringFromTableInBundle:
NSLocalizedStringFromTableInBundle(#"My string", nil, [NSBundle bundleForClass:[MCLoginContext class]], #"String context")
The entire process is very delicate and annoying, but that's should be all.
for swift you could use a Loc enum:
enum Loc : String{
case ok = "OK"
case cancel = "Cancel"
var localized: String {
let s = self.rawValue
let bundle = Bundle(for: <classname inside the bundle>.self)
let result = NSLocalizedString(s, tableName: nil, bundle: bundle, value: "", comment: "")
return result;
}
}
And use it so:
let s = Loc.ok.localized
print(s)
I am trying to save some files on micro SDCard. To check the availability of SDCard, I am using the following method;
private boolean isSdCardReady() {
Enumeration e = FileSystemRegistry.listRoots();
while (e.hasMoreElements()) {
if (e.nextElement().toString().equalsIgnoreCase("sdcard/")) {
return true;
}
}
return false;
}
Even if this method returns true, when I try to save files, it gives exception net.rim.device.api.io.file.FileIOException: File system is not ready.
What does this means? If SDCard is not available, then why its listed in FileSystemRegistry.listRoots()?
How can I make sure that SDCard is available for writing?
My development environment:
BlackBerry JDE Eclipse Plugin 1.5.0
BlackBerry OS 4.5
BlackBerry Bold with a 3G card
Usually I had this error when I tried to access SD card on device restart. You have to postpone all operations in app until startup finished:
while (ApplicationManager.getApplicationManager().inStartup()) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
}
I remember one more possible cause mentioned here. You have to close all streams after using.
Solved the problem. I was looking for "sdcard" while rootsEnum.nextElement().toString(); returns "SDCard". Yeah, its case sensitive. Now, instead of using hard-coded "SDCard", I've changed the above method to the following;
private static String getSdCardRootDir() {
Enumeration rootsEnum = FileSystemRegistry.listRoots();
while (rootsEnum.hasMoreElements()) {
String rootDir = rootsEnum.nextElement().toString();
if (rootDir.equalsIgnoreCase("sdcard/")) {
return "file:///" + rootDir;
}
}
return null;
}
Using this, I got the root directory in its system defined case.