# Using importlib to run any function on the gateway?

**URL:** <https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915>\
**Category:** Ignition\
**Tags:** ignition80\
**Created:** [April 14, 2020, 1:36pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915 "2020-04-14T13:36:46Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [April 14, 2020, 1:36pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/1 "2020-04-14T13:36:46Z")

</div>

Our app has a few database/calculation heavy functions that we would rather run on our very beefy gateway server than on our clients.

I recently learned about `importlib` and was playing around with using a Gateway message handler to try to run any arbitrary function via the gateway by giving the payload the function name and arguments for the function. I had two questions about this -

1. Security wise is this opening any vulnerabilities to me? My form submittal buttons now do a `system.util.sendMessage` to my abstract gateway message handler feeding the function name and arguments. I think it should be safe but I also never seen anyone do this and I get the feeling it might be for good reason.

2. How can I actually feed the arguments to the function? Right now I am hardcoding them as such in my gateway handler, which is only set up to handle a few functions

```auto
	import importlib
	import system.util
	logger = system.util.getLogger("Logger")
	logger.info("Payload")
	logger.info(str(payload))
	form = payload['form']
	data = payload['data']
	data['user'] = payload['user']
	library = 'forms.'+form
	module = importlib.import_module(library)
	action = payload['action']
	
	if action == 'create':
		module.create(data)

```

But if I wanted to be able to do it for any arbitrary function with both positional and keyword arguments, how could I feed them to a function?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 14, 2020, 2:32pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/2 "2020-04-14T14:32:42Z")

</div>

Not safe. Not terribly easy to attack, but no, not safe.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 14, 2020, 2:34pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/3 "2020-04-14T14:34:34Z")

</div>

The overall idea is fine, though. Just preload all your possible code in the gateway. Pass function name, positional parameters, and keyword parameters through your sendRequest() call.

Say you have script module 'beefyFunctions` set up with all the named functions (which can call other script modules as needed). Create a message handler ‘beefy’ like so:

```auto
functionName, args, kwargs = [payload[x] for x in ['functionName', 'args', 'kwargs']]
return getattr(beefyFunctions, functionName)(*args, **kwargs)

```

Then in a client, you’d do this:

```auto
payload = {'functionName': 'somePreloadedFunction', 'args': someArgsTuple, 'kwargs': someArgsDictionary}
result = system.util.sendRequest('myProject', 'beefy', payload)

```

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [April 14, 2020, 2:38pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/4 "2020-04-14T14:38:44Z")

</div>

Yes, correct me if I’m wrong but it really seems only attackable if someone could do a `system.util.sendMessage` and they could control all the parameters, which I think in our application would only be possible if they had designer access.

When you say preload all possible code in the gateway, are you saying that my current method where I check for the function name and then directly call it with `module.create` would be fine? I would just have to add `elif`'s for other functions to call to manually call.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 14, 2020, 2:48pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/5 "2020-04-14T14:48:03Z")

</div>

> [@bkarabinchak.psi](#):
>
> would only be possible if they had designer access

Normally, yes. But a faked or modified client can use the http/https connection to do anything a client is allowed. Faking a client is the "not terribly easy" part.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 14, 2020, 3:26pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/6 "2020-04-14T15:26:18Z")

</div>

Just dropping in to concur with Phil, a whitelist is the only secure way to do this. That’s why named queries exist, and the whole ‘client permissions’ aspect was added mid-stream to 7.9; gateways had far too much ‘trust’ of running clients.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [April 14, 2020, 3:27pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/7 "2020-04-14T15:27:57Z")

</div>

By whitelist, is that this part of @pturmel’s script `getattr(beefyFunctions,` where no matter what the client says, we are only grabbing functions from a specific, hardcoded module?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 14, 2020, 3:32pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/8 "2020-04-14T15:32:19Z")

</div>

Yes. You could be even more explicit about it - create a dictionary of function names to functions in the message handler, something like this:

```python
functions = {'history': beefy.history, 'query': beefy.query}

try:
    result = functions[payload['functionName']](*payload['arguments'])
    return result
except KeyError:
    log.errorf("Unauthorized function call to %s attempted", payload['functionName'])
    return "Unauthorized function attempted!"

```

But `getattr` should be just as safe - I’m not aware of any way to traverse a hierarchy using `getattr`. It’s just a choice of maintenance effort vs fully explicit behavior.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [April 14, 2020, 3:39pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/9 "2020-04-14T15:39:32Z")

</div>

I get most of this except how `functions[payload['functionName']](*payload['arguments'])` actually runs the function. I guess prior I would have a `import beefy` and so it would end up being beefy.history(\*payload[‘arguments’]) and thats how it would run?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 14, 2020, 3:51pm UTC](https://forum.inductiveautomation.com/t/using-importlib-to-run-any-function-on-the-gateway/33915/10 "2020-04-14T15:51:10Z")

</div>

Yes, exactly, although I don’t think you even need to `import beefy`, I believe we implicitly load all your project library stuff; don’t quote me on that, though.
