canari.framework - Canari Framework Annotations & Extras¶
New in version 3.0.
The Canari framework provides a few easy to use transform annotations and extras. Annotations are used to set transform
class attributes. Although you can set these attributes without the use of annotations, a good reason for using them is
to “pin” the presence of an important setting to the top of the class definition. For example,
marks the transform as a privileged transform, meaning it requires
root privileges in order to execute. Whereas
@Deprecated would signify that the transform has been deprecated, and so on. Annotations are applied to classes in
the following manner:
@Deprecated class MyTransform(Transform): pass
Canari supports the following zero argument annotations:
||the transform requires superuser privileges in order to execute.|
||the transform is deprecated.|
||the transform can be used as a remote transform.|
||the transform should operate in debug mode in Maltego.|
Multiple annotations can be combined to achieve the desired transform profile. For example, if we want to enable the debug window and have our user enter their superuser credentials, we would define the transform like so:
@RequireSuperUser @EnableDebugWindow class MyTransform(Transform): pass
As stated above, the
@RequireSuperUser marks the transform as a privileged transform which requires
privileges in order to execute. The behavior of the different Canari transform runners differs when they come across a
transform that has this attribute set. When using canari run-transform, canari debug-transform,
dispatcher, a graphical password dialog box will appear prompting the user to enter their sudo credentials.
When using the canari shell or plume (transform application server), Canari will ask whether or not the user wishes to load superuser transforms. If the user chooses to load superuser transforms, Canari will rerun the user’s command using sudo, which will prompt the user for their credentials in the terminal. Here’s an example of what the sample output would look like:
$ canari shell foo Looking for transforms in foo... Package loaded. A transform requiring 'root' access was detected. Would you like to run this shell as 'root'? [y/N]: y Need to be root to run this transform... sudo'ing... Password: Looking for transforms in foo... Package loaded. Welcome to Canari 3.0. >>>
@EnableDebugWindow annotation instructs the canari create-profile to create a transform profile that
forces Maltego to display the “Debug” output pane each time the transform is executed. This is useful for debugging the
response XML or viewing debug or diagnostic messages being written to
Messages written to
stderr during transform execution will only be seen if transforms are executed locally.
Remote transforms do not transmit this information.
Request Filtering with
Canari also supports the concept of transform request filtering by applying the
@RequestFilter annotation to a
transform. This is especially useful for use-cases where a license or authorization check needs to be performed prior
to transform execution. For example, let’s assume that you want to check whether a user is submitting a valid license
key prior to executing a transform request:
def check_api_key(request, response, config): if not request.parameters['my_api.key'] == 'cool': raise MaltegoException('Invalid license key!') @RequestFilter(check_api_key) class MyTransform(Transform): def do_transform(request, response, config): # TODO: something cool return response
RequestFilter annotation accepts the following arguments in its constructor:
- filter (callable) – a callable that accepts three arguments:
- remote_only (bool) –
Trueif the filter should only be applied when the transform is operating in remote mode, otherwise
- filter (callable) – a callable that accepts three arguments:
Just like the
Transform.do_transform() method, request filters can also modify the contents of the
config objects and raise exceptions, if necessary, to interrupt transform execution. Request
filters are expected to return either
True, to cancel transform execution, or
False, to allow the transform to
continue executing. The following example illustrates how you can gracefully interrupt the execution of a transform
and communicate the reason via a Maltego UI message:
count = 0 def check_access_count(request, response, config): global count if count == 500: response += UIMessage('Access denied: you have reached your limit.') return True count += 1
In the example above, our request filter is keeping track of a global request counter. If that counter reaches its threshold, any subsequent transform requests will be cancelled and the user will be informed of the reason via a Maltego informational UI message. Otherwise, the counter is incremented and transform execution continues.
When a request filter returns
True the current transform request will be cancelled and the empty or modified
response object will be returned.
Request filtering can also be enabled for remote transforms only. This makes it easier to develop the transform locally,
void of any request filtering checks, by setting the remote_only keyword argument to
True in your
RequestFilter annotation, like so:
def check_api_key(request, response, config): if not request.parameters['my_api.key'] == 'cool': raise MaltegoException('Invalid license key!') @RequestFilter(check_api_key, remote_only=True) class MyTransform(Transform): def do_transform(request, response, config): # TODO: something cool return response
In the example above,
check_api_key() will only be called if
MyTransform is running in Plume.
Foreign Language Support¶
If you’ve already developed your cool Maltego transforms in another language, such as Perl, Ruby, or Java, you can still
take advantage of Canari’s powerful transform packaging and distribution features. By setting
Transform.do_transform to an instance of
ExternalCommand and placing your transform code in the
<project name>/src/<project name>/resources/external directory, you have the ability to run transform code written
in other languages. For example, let’s assume we’ve written a Perl transform,
do_nothing.pl, that we’d like to
package and distribute using the Canari framework:
#!/usr/bin/perl print "<MaltegoMessage><MaltegoTransformResponseMessage/></MaltegoMessage>\n";
First, you’ll have to create a transform:
class DoNothing(Transform): do_transform = ExternalCommand('perl', 'do_nothing.pl')
Finally, you’ll have to place the do_nothing.pl file in your
<project name>/src/<project name>/resources/external
Canari development quick-start guide for information on how to create a transform package and write transform code.
ExternalCommand constructor accepts the following arguments:
ExternalCommand(interpreter, program[, args=None])¶
- interpreter (str) – the name of the program interpreter (i.e. perl, java, ruby, etc.)
- program (str) – the name of the transform script or executable file.
- args (iterable) – an optional list of arguments to pass to the transform executable or script.
In the event that
interpreter is either perl, ruby, or java, the appropriate flags will be set to include the
<project name>/src/<project name>/resources/external directory as part of the default module or class search path.
This is done to support relative module or library imports without having to modify your pre-existing code.