VCF Automation Blog

from Stefan Schnell

Calculate SHA256 Hashes for Actions


This Python source code shows how to built hash values from the script code of actions, in conjunction with the name, version, description, output-type and input-parameters. This hash value can be used to compare an action in different stages, like development, integration and production. On this way it can be ensured that a change of a script can be made visible, if the hash values in the stages differ. The proof that a script has been transferred unchanged to another stage is a very important factor in security audits.

"""
Python action to calculate Secure Hash Algorithm SHA256 values for
all actions.

@name getHashesForActions
@returns {Properties}

@author Stefan Schnell <mail@stefan-schnell.de>
@license MIT
@version 0.1.0

Checked with Aria Automation 8.12.0 and 8.17.0
"""

import hashlib
import json
import ssl
import urllib.request


def getAllActions(vcoUrl, bearerToken):
    """ Get all actions.

    @param {string} vcoUrl - URL of Aria orchestrator
    @param {string} bearerToken
    @returns {dictionary}
    """

    returnValue = {}

    try:

        requestActions = urllib.request.Request(
            url = vcoUrl + "/api/actions"
        )
        requestActions.add_header(
          "Authorization", "Bearer " + bearerToken
        )
        requestActions.add_header(
          "Content-Type", "application/json"
        )

        responseActions = urllib.request.urlopen(
            requestActions,
            context = ssl._create_unverified_context()
        )

        if responseActions.getcode() == 200:
            returnValue = json.loads(responseActions.read())

    except Exception as err:
        raise Exception("An error occurred at detecting all actions") \
            from err

    return returnValue


def getActionDetails(actionHref, bearerToken):
    """ Gets the details of the given action.

    @param {string} actionHref - Horizontal reference of action
    @param {string} bearerToken
    @returns {dictionary}
    """

    returnValue = {}

    try:

        requestAction = urllib.request.Request(
            url = actionHref
        )
        requestAction.add_header(
            "Authorization", "Bearer " + bearerToken
        )
        requestAction.add_header(
            "Content-Type", "application/json"
        )

        responseAction = urllib.request.urlopen(
            requestAction,
            context = ssl._create_unverified_context()
        )

        if responseAction.getcode() == 200:
            returnValue = json.loads(responseAction.read())

    except Exception as err:
        raise Exception("An error occurred at detecting action details") \
            from err

    return returnValue


def handler(context, inputs):
    """ Aria Automation standard handler, the main function.
    """

    results = {}

    try:

        vcoUrl = context["vcoUrl"]
        bearerToken = context["getToken"]()

        actions = getAllActions(
            vcoUrl,
            bearerToken
        )
        if not actions:
            raise ValueError("No actions were detected")

        for action in actions["link"]:

            actionDetails = getActionDetails(
                action["href"],
                bearerToken
            )
            if not actionDetails:
                raise Exception("No action details were detected")

            actionName = actionDetails["name"]
            actionVersion = actionDetails["version"]
            if "description" in actionDetails:
                actionDescription = actionDetails["description"]
            else:
                actionDescription = ""
            if "output-type" in actionDetails:
                actionOutputType = actionDetails["output-type"]
            else:
                actionOutputType = ""
            if "input-parameters" in actionDetails:
                actionInputParameters = actionDetails["input-parameters"]
            else:
                actionInputParameters = ""
            if "script" in actionDetails:
                actionScript = actionDetails["script"]
            else:
                actionScript = ""

            hashData = actionName + " ~ " + \
                actionVersion + " ~ " + \
                actionDescription + " ~ " + \
                str(actionOutputType) + " ~ " + \
                str(actionInputParameters) + " ~ " + \
                actionScript

            key = actionDetails["module"] + "." + actionDetails["name"]
            value = hashlib.sha256(
                hashData.encode("utf-8")
            ).hexdigest()

            results[key] = value

        results = dict(sorted(results.items()))

        for key in results:
            print(key, results[key])

        outputs = {
            "status": "done",
            "results": results
        }

    except Exception as err:

        outputs = {
            "status": "incomplete",
            "error": repr(err),
            "results": results
        }

    return outputs