Dynamically loading assets with require() that might not exist in webpack + VueJS - javascript

I'm trying to load images dynamically which may or may not exist.
In this case, crypto-currency icons using their 3 letter symbol. I have a few hundred in .svg format in my statics library and when I pull data from a price server I try to match up the icons I have with the symbols coming from the server and to serve a fallback image if I don't have the asset.
In my index.vue I can get away with this code and everything works fine:
<img :src="'statics/icons/svg/' + coin.symbol + '.svg'" v-img-fallback="'statics/icons/svg/fallback.svg'"/>
However in a subcomponent that opens if a user clicks a coin the same code will fail to load both the primary and fallback images. I've tried numerous ways but the only way to get an image to load from my subcomponent is to either hard code it like this:
<img src="statics/icons/svg/btc.svg"/>
Which is impossible for me as I need the modal to be dynamically generated for any possible coin...
Or using require() like this:
<img :src="imageSrc" v-img-fallback="require('../statics/icons/svg/fallback.svg')"/>
// Computed:
imageSrc () {
if (this.coinData.symbol) {
return require('../statics/icons/svg/' + this.coinData.symbol + '.svg')
}
}
However this crashes my app if require() looks for an asset that doesn't exist. I need a method that fails gracefully so that the v-img-fallback can detect it and supply the fallback.
I've tried doing something like return require(image1) || require(fallback) but it doesn't work.

This is a common request and latest WebPack, AFAIK (and I just searched for it again), does not expose an API for especifically testing the existence of a module.
In other words, you'd have to handle the uncertainty of the loading yourself. Example:
computed: {
imageSrc () {
if (this.coinData.symbol) {
try {
return require('../statics/icons/svg/' + this.coinData.symbol + '.svg')
} catch (e) {
if (e.name !== "ModuleNotFoundError") throw e; // handle false-positives
// in cordova, use the line below instead of the above
// if (!e.message.startsWith('Cannot find module')) throw e;
return require('../statics/icons/svg/fallback.svg');
}
}
return require('../statics/icons/svg/fallback.svg');
}
}
This way I'd argue you wouldn't even need a fallback src in the template. You could return it in the computed property itself.

Related

how to fix Error: java.lang.ClassNotFoundException on frida

I'm trying to bypass a root detection mechanism on an android app using Frida, I've tried so many different scripts (frida code share) and different approaches (like hiding root) with no luck!
So I tried to locate the class and method responsible for checking if the device is rooted or not and changing it's return value.
This is my script :
setTimeout(function() { // avoid java.lang.ClassNotFoundException
Java.perform(function() {
var hook = Java.use("app.name.RootUtils");
console.log("info: hooking target class");
hook.isRooted.overload().implementation = function() {
console.log("info: entered target method");
return Java.use("java.lang.Boolean").$new(false);
}
});
},0);
If I inject this code normally it won't work because it looks like the isRooted method will get called before it
If I use spawn to run the app and change this method return value it fails with error :
frida.core.RPCException: Error: java.lang.ClassNotFoundException: Didn't find class ...
I've also tried spawning the app and then using objection to run "android root disable" but it will return this error :
frida.core.RPCException: TypeError: cannot read property 'getApplicationContext' of null
at getApplicationContext (src/android/lib/libjava.ts:21)
I'm not sure if this is a problem with Frida or my system or ...
I think if I was able to make my main code runs at exactly after the class gets loaded (like using a loop to check or using a hook) the problem would be fixed but I don't know how to write that kind of code in js for frida.
I'm on macOS 11.5.1 using python 3.9 and installed latest version of frida and objection
I've tested on one rooted phone with android 10 and an emulator with android 6
I was able to solve this issue with a simple yet not very technical solution.
I used a setInteval to run my hooking over and over until it gets to work, (as #Robert mentioned, I also needed to wrap hooking inside a try catch to prevent the code from stoping after first try)
This may not work for everyone but since it worked for me I will post the final code, may it helps someone else in the future :)
Java.perform(function() {
var it = setInterval(function(){
try{
var hook = Java.use("app.name.RootUtils");
console.log("info: hooking target class");
hook.isRooted.overload().implementation = function() {
console.log("info: entered target method");
clearInterval(it);
return Java.use("java.lang.Boolean").$new(false);
}
} catch(e) {
console.log("failed!");
}
},200); // runs every 200milisecods
});
PS : you may need to change interval time to match your app needs, it worked for me with 200 miliseconds.
Sometimes app would do encryptions in its class loader, you may need to replace Java.classFactory.loader with the app's customized class loader in order to make Java.use function properly.
Here's how it's done:
Java.perform(function() {
//get real classloader
//from http://www.lixiaopeng.top/article/63.html
var application = Java.use("android.app.Application");
var classloader;
application.attach.overload('android.content.Context')
.implementation = function(context) {
var result = this.attach(context); // run attach as it is
classloader = context.getClassLoader(); // get real classloader
Java.classFactory.loader = classloader;
return result;
}
//interesting classes
const interestingClassPath = "com.myApp.SometingInteresting";
interestingClass = Java.use(interestingClassPath);
//do whatever you like here
})
Class not found
How do you know the class is app.name.RootUtils have you decompiled to app using Jadx or apktool? How about the method where RootUtils.isRooted() is called? Is there any special code that loads the RootUtils class e.g. from a non-standard dex file included in the app? If the class is loaded from a special dex file you could hook this dex loading mechanism and first execute it and then install your hook for RootUtils.isRooted().
Alternatively assuming RootUtils.isRooted() is called only from one other method and does not use special code for loading the RootUtils class you could hook that method and use the this hook to install install your RootUtils.isRooted() hook.
Error handling
The correct way to handle errors in JavaScript is using try catch block, not the setTimeout function:
Java.perform(() => {
try {
var hook = Java.use("app.name.RootUtils");
...
} catch (e) {
console.log("Failed to hook root detection" + e);
}
}
Regarding your problems hooking the class

Importing script and calling a method on a variable

I'm new to typescript and I'm trying to use a bit of code I found online to change links to look nice for my site,
<script src="https://wow.zamimg.com/widgets/power.js"></script>
<script>var wowhead_tooltips = {
"colorlinks": true,
"iconizelinks": true,
"renamelinks": true
};
</script>
I included that into my index.html, and it works great, until I load a component, I spent a lot of time looking around and found that I need to call $WowheadPower.refreshLinks();
To get the links to change when a new element is added, I wasn't sure how to declare that variable in typescript so I could tie it to various angular commands I wanted to do, unless I add it in a try catch:
loadScript(){
try{
// update tooltips
if(typeof $WowheadPower == 'undefined'){
$.getScript('//wow.zamimg.com/widgets/power.js');
} else {
$WowheadPower.refreshLinks();
console.log($WowheadPower)
}
} finally {}
}
I get an error that says
Cannot find name '$WowheadPower'
but I saved it anyway, and somehow on my page it works as I want it too.
It works perfect, but I still got the error so I declared it
try{
// update tooltips
var $WowheadPower
if(typeof $WowheadPower == 'undefined'){
$.getScript('//wow.zamimg.com/widgets/power.js');
} else {
$ WowheadPower.refreshLinks();
console.log($WowheadPower)
}
} finally {}
and it broke, I assume because I overwrote the correctly variable that has the right method.
Now I have to leave the error in to get functionality, but the error stops me from compiling when I ng serve. Until I hit save on VScode then it works fine again.
Any thoughts on how to resolve this?
since $WowheadPower is an external variable imported from another file, you can tell typescript that it exists without explicitly declaring it:
declare var $WowheadPower: any
write this at the beginning of your code so you tell TS that this variable exists. Ideally, you would write an interface that correctly defines $WowheadPower's type instead of any there.

Vue.js/Nuxt.js Load Fallback Image on 404

I am trying to set a default image (placeholder image) in case the image resource is not found (404). I have a dict article which has a value for the key author_image. So that string is not empty but it just can't load that image.
In my template:
<img
:src="article.author_image"
alt="Author Image"
#error="setFallbackImageUrl"
>
In my methods:
methods: {
setFallbackImageUrl(event) {
console.log('Image failed to load, setting fallback.')
event.target.src = '~/assets/icons/avatar.svg'
}
}
I can see in my console log that setFallbackImageUrl is called but the image src is not updated. I made sure that the avatar.svg is actually correct, if I just hard code that over article.author_image that works.
Any suggestions on what I might be doing wrong?
The issue comes while loading your image via vue loader and webpack as the file extensions (.png, .svg, etc) are not module requests (read more about handling assets with vue loader).
You'll need to wrap it in the require to access it. the example by #Toni Michel Caubet works because he is using a link.
This Works for me.
methods: {
setFallbackImageUrl(event) {
console.log('Image failed to load, setting fallback.')
event.target.src = require(`~/assets/icons/${'avatar' + '.svg'}`)
}
}
Note: it's not needed to wrap the image in "${'avatar' + '.svg'}" i just removed my dynamic values and added "avatar".
Just in case someone is hitting a brick wall with webpack (files from assets/static)
The accepted answer from here should resolve it:
Dynamic img src URL with "OR" statement not working properly in NUXT component

Javascript Runtime Error: 'Application is undefined'

I need to know if this is correct. I'm just beginning in app development using WinJS. I've identified the source of the problem and got rid of it but I don't know if that's the correct method.Please help!
// Optimize the load of the application and while the splash screen is
// shown, execute high priority scheduled work.
ui.disableAnimations();
var p = ui.processAll().then(function () {
//return nav.navigate(nav.location || Application.navigator.home, nav.state);
return nav.navigate(nav.location || app.local, nav.state)
}).then(function () {
return sched.requestDrain(sched.Priority.aboveNormal + 1);
}).then(function () {
ui.enableAnimations();
});
The problem is in the first .then(). The commented line was the default line, I've changed it for the app to work.I've absolutely no idea what it is.Please tell me what it means and what is changed. By the way, 'app' is WinJS.Application and Application is a WinJS namespace in navigator.js where the home property is located.
This error would suggest that navigator.js isn't being loaded by the time this code is executed. The Application namespace, which is entirely arbitrary and unrelated to WinJS.Application, is defined only in navigator.js, so if that file isn't loaded that namespace won't exist.
A WinJS namespace, by the way, is just a formalization of a module pattern in JavaScript that helps you keep the global namespace from getting cluttered. Declaring a namespace like navigator.js does it:
WinJS.Namespace.define("Application", {
PageControlNavigator: WinJS.Class.define(
just creates a single object in the global namespace called "Application" and then defines members for it. (You can change "Application" to anything you want, by the way. Nothing else in navigator.js relies on it, and navigator.js is something that comes from the app templates in Visual Studio and isn't part of WinJS itself.)
So again, my suspicion is that you don't have (or whatever the proper path is) in your default.html, the path to it isn't correct, or that perhaps it's being loaded after the other code is trying to execute. Try setting breakpoints on WinJS.Namespace.define and see if that file is loaded and the breakpoint gets hit.

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Categories

Resources