diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4270cb3b6..3cc51f3f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,12 +17,6 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_DATEST_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: us-east-1 - APP_VERIFIER_EXE: D:\a\work\aws-iot-device-sdk-cpp-v2\build\samples\pub_sub\cycle_pub_sub\RelWithDebInfo\cycle-pub-sub.exe - APP_VERIFIER_OUTPUT_XML: D:\a\work\aws-iot-device-sdk-cpp-v2\app_verifier_output.xml - APP_VERIFIER_OUTPUT_XML_HELPER: D:\a\work\aws-iot-device-sdk-cpp-v2\utils\appverifier_xml_util.py - # All of the tests that will be run by AppVerifier - APP_VERIFIER_TESTS: Exceptions Handles Heaps Leak Locks Memory SRWLock Threadpool TLS - jobs: linux-compat: runs-on: ubuntu-latest @@ -90,20 +84,6 @@ jobs: cd D:\a\work python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz', 'builder.pyz')" python builder.pyz build -p ${{ env.PACKAGE_NAME }} - - name: Run and check AppVerifier - run: | - echo "Enabling AppVerifier:" - appverif -enable ${{ env.APP_VERIFIER_TESTS }} -for ${{ env.APP_VERIFIER_EXE }} - echo "Starting AppVerifier utility sample run:" - python -m pip install termcolor - python -m pip install boto3 - python ${{ env.APP_VERIFIER_OUTPUT_XML_HELPER }} --launch_sample true --sample_file ${{ env.APP_VERIFIER_EXE }} - echo "Exporting XML log:" - appverif -export log -for ${{ env.APP_VERIFIER_EXE }} -with to=${{ env.APP_VERIFIER_OUTPUT_XML }} - echo "Disabling AppVerifier:" - appverif -delete settings -for ${{ env.APP_VERIFIER_EXE }} - echo "Starting AppVerifier utility parsing XML:" - python ${{ env.APP_VERIFIER_OUTPUT_XML_HELPER }} --parse_xml true --xml_file ${{ env.APP_VERIFIER_OUTPUT_XML }} windows-vs14: runs-on: windows-2019 # windows-2019 is last env with Visual Studio 2015 (v14.0) @@ -117,20 +97,6 @@ jobs: cd D:\a\work python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-Tv140 --cmake-extra=-A${{ matrix.arch }} - - name: Run and check AppVerifier - run: | - echo "Enabling AppVerifier:" - appverif -enable ${{ env.APP_VERIFIER_TESTS }} -for ${{ env.APP_VERIFIER_EXE }} - echo "Starting AppVerifier utility sample run:" - python -m pip install termcolor - python -m pip install boto3 - python ${{ env.APP_VERIFIER_OUTPUT_XML_HELPER }} --launch_sample true --sample_file ${{ env.APP_VERIFIER_EXE }} - echo "Exporting XML log:" - appverif -export log -for ${{ env.APP_VERIFIER_EXE }} -with to=${{ env.APP_VERIFIER_OUTPUT_XML }} - echo "Disabling AppVerifier:" - appverif -delete settings -for ${{ env.APP_VERIFIER_EXE }} - echo "Starting AppVerifier utility parsing XML:" - python ${{ env.APP_VERIFIER_OUTPUT_XML_HELPER }} --parse_xml true --xml_file ${{ env.APP_VERIFIER_OUTPUT_XML }} windows-no-cpu-extensions: runs-on: windows-latest @@ -141,20 +107,22 @@ jobs: cd D:\a\work python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz', 'builder.pyz')" python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DUSE_CPU_EXTENSIONS=OFF - - name: Run and check AppVerifier - run: | - echo "Enabling AppVerifier:" - appverif -enable ${{ env.APP_VERIFIER_TESTS }} -for ${{ env.APP_VERIFIER_EXE }} - echo "Starting AppVerifier utility sample run:" - python -m pip install termcolor - python -m pip install boto3 - python ${{ env.APP_VERIFIER_OUTPUT_XML_HELPER }} --launch_sample true --sample_file ${{ env.APP_VERIFIER_EXE }} - echo "Exporting XML log:" - appverif -export log -for ${{ env.APP_VERIFIER_EXE }} -with to=${{ env.APP_VERIFIER_OUTPUT_XML }} - echo "Disabling AppVerifier:" - appverif -delete settings -for ${{ env.APP_VERIFIER_EXE }} - echo "Starting AppVerifier utility parsing XML:" - python ${{ env.APP_VERIFIER_OUTPUT_XML_HELPER }} --parse_xml true --xml_file ${{ env.APP_VERIFIER_OUTPUT_XML }} + + windows-app-verifier: + runs-on: windows-2022 # latest + steps: + - name: Build ${{ env.PACKAGE_NAME }} + consumers + run: | + md D:\a\work + cd D:\a\work + python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz', 'builder.pyz')" + python builder.pyz build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DUSE_CPU_EXTENSIONS=OFF + - name: Run and check AppVerifier + run: | + cd D:\a\work + echo "Starting to run AppVerifier with cycle pub-sub sample" + python -m pip install boto3 + python .\aws-iot-device-sdk-cpp-v2\utils\appverifier_launch_sample.py --sample_file .\aws-iot-device-sdk-cpp-v2\build\samples\pub_sub\cycle_pub_sub\RelWithDebInfo\cycle-pub-sub.exe osx: runs-on: macos-latest diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index 74daf7728..814492ca7 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -43,4 +43,5 @@ jobs: run: for filename in $(< fileList.txt); do ./scancode-toolkit/scancode -l -n 30 --json-pp - ./new-ref/$filename | grep short_name | sort | uniq >> new-licenses.txt; done # compare - name: License test - run: if ! cmp old-licenses.txt new-licenses.txt; then echo "Licenses differ! Failing."; exit -1; else echo "Licenses are the same. Success."; exit 0; fi \ No newline at end of file + run: | + if ! cmp old-licenses.txt new-licenses.txt; then echo "Licenses differ! Failing."; exit -1; else echo "Licenses are the same. Success."; exit 0; fi diff --git a/utils/appverifier_launch_sample.py b/utils/appverifier_launch_sample.py new file mode 100644 index 000000000..f6febe65e --- /dev/null +++ b/utils/appverifier_launch_sample.py @@ -0,0 +1,149 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +# Built-in +import argparse +import os +import subprocess +import pathlib +import sys +import tempfile +import appverifier_xml +# Needs to be installed via pip +import boto3 # - for launching sample + + +def launchSample(sample_file, sample_region, sample_secret_endpoint, sample_secret_certificate, sample_secret_private_key, sample_arguments): + print("Attempting to get credentials from secrets using Boto3...") + try: + secrets_client = boto3.client( + "secretsmanager", region_name=sample_region) + sample_endpoint = secrets_client.get_secret_value( + SecretId=sample_secret_endpoint)["SecretString"] + sample_certificate = secrets_client.get_secret_value( + SecretId=sample_secret_certificate) + sample_private_key = secrets_client.get_secret_value( + SecretId=sample_secret_private_key) + except Exception: # lgtm [py/catch-base-exception] + sys.exit("ERROR: Could not get secrets to launch sample!") + + current_folder = pathlib.Path(__file__).resolve() + # Remove the name of the python file + current_folder = str(current_folder).replace("appverifier_xml_util.py", "") + + print("Saving credentials to file...") + tmp_certificate_file_path = str(current_folder) + "tmp_certificate.pem" + tmp_private_key_path = str(current_folder) + "tmp_privatekey.pem.key" + with open(tmp_certificate_file_path, "w") as file: + file.write(sample_certificate["SecretString"]) # lgtm [py/clear-text-storage-sensitive-data] + with open(tmp_private_key_path, "w") as file: + file.write(sample_private_key["SecretString"]) # lgtm [py/clear-text-storage-sensitive-data] + print("Saved credentials to file...") + + print("Processing arguments...") + launch_arguments = [] + launch_arguments.append("--endpoint") + launch_arguments.append(sample_endpoint) + launch_arguments.append("--cert") + launch_arguments.append(tmp_certificate_file_path) + launch_arguments.append("--key") + launch_arguments.append(tmp_private_key_path) + sample_arguments_split = sample_arguments.split(" ") + for arg in sample_arguments_split: + launch_arguments.append(arg) + + print("Running sample...") + exit_code = 0 + sample_return = subprocess.run( + args=launch_arguments, executable=sample_file) + exit_code = sample_return.returncode + + print("Deleting credentials files...") + os.remove(tmp_certificate_file_path) + os.remove(tmp_private_key_path) + + if (exit_code == 0): + print("SUCCESS: Finished running sample! Exiting with success") + else: + print("ERROR: Sample did not return success! Exit code " + str(exit_code)) + return exit_code + + +def registerAppVerifier(test_executable, app_verifier_tests_list): + arguments = ["appverif", "-enable"] + app_verifier_tests_list + ["-for", test_executable] + print (f'Calling AppVerifier with: {subprocess.list2cmdline(arguments)}') + # NOTE: Needs elevated permissions. + subprocess.run(args=arguments) + + +def unregisterAppVerifier(test_executable): + arguments = ["appverif", "-delete", "settings", "-for", test_executable] + print (f'Calling AppVerifier with: {subprocess.list2cmdline(arguments)}') + # NOTE: Needs elevated permissions. + subprocess.run(args=arguments) + + +def checkAppVerifierXML(test_executable, tmp_xml_file_path): + appverif_xml_dump_args = ["appverif", "-export", "log", "-for", test_executable, "-with", "to="+ tmp_xml_file_path] + print (f'Calling AppVerifier with: {subprocess.list2cmdline(appverif_xml_dump_args)}') + # NOTE: Needs elevated permissions + subprocess.run(args=appverif_xml_dump_args) + + xml_result = appverifier_xml.parseXML(tmp_xml_file_path, True) + if (xml_result != 0): + print (f"ERROR: XML parse returned failure!") + return xml_result + + +def booleanString(string): + string = string.lower() + if string not in {"false", "true"}: + raise ValueError("Boolean is not true or false!") + return string == "true" + + +def main(): + argument_parser = argparse.ArgumentParser( + description="AppVerifier XML output util") + argument_parser.add_argument("--sample_file", metavar="", + required=True, default="", help="Sample to launch that AppVerifier is following") + argument_parser.add_argument("--sample_region", metavar="", + required=False, default="us-east-1", help="The name of the region to use for accessing secrets") + argument_parser.add_argument("--sample_secret_endpoint", metavar="", + required=False, default="unit-test/endpoint", help="The name of the secret containing the endpoint") + argument_parser.add_argument("--sample_secret_certificate", metavar="", required=False, + default="unit-test/certificate", help="The name of the secret containing the certificate PEM file") + argument_parser.add_argument("--sample_secret_private_key", metavar="", required=False, + default="unit-test/privatekey", help="The name of the secret containing the private key PEM file") + argument_parser.add_argument("--sample_arguments", metavar="", + required=False, default="", help="Arguments to pass to sample") + + parsed_commands = argument_parser.parse_args() + + print ("Registering with AppVerifier") + app_verifier_tests = ["Exceptions", "Handles", "Heaps", "Leak", "Locks", "Memory", "SRWLock", "Threadpool", "TLS"] + registerAppVerifier(parsed_commands.sample_file, app_verifier_tests) + + print("Starting to launch sample...") + sample_result = launchSample( + parsed_commands.sample_file, + parsed_commands.sample_region, + parsed_commands.sample_secret_endpoint, + parsed_commands.sample_secret_certificate, + parsed_commands.sample_secret_private_key, + parsed_commands.sample_arguments) + + print ("Parsing XML...") + tmp_xml_file_path = os.path.join(tempfile.gettempdir(), "tmp.xml") + xml_result = checkAppVerifierXML(parsed_commands.sample_file, tmp_xml_file_path) + + print ("Unregistering with AppVerifier") + unregisterAppVerifier(parsed_commands.sample_file) + + if (xml_result != 0): + sys.exit(xml_result) + sys.exit(sample_result) + + +if __name__ == "__main__": + main() diff --git a/utils/appverifier_xml_util.py b/utils/appverifier_xml.py similarity index 69% rename from utils/appverifier_xml_util.py rename to utils/appverifier_xml.py index 7077cb236..ca8adba10 100644 --- a/utils/appverifier_xml_util.py +++ b/utils/appverifier_xml.py @@ -2,15 +2,8 @@ # SPDX-License-Identifier: Apache-2.0. # Built-in -from asyncio import subprocess import xml.etree.ElementTree as ElementTree import argparse -import os -import subprocess -import pathlib -# Needs to be installed via pip -import boto3 # - for launching sample -from termcolor import colored # - for terminal colored output s_AppVerifier_LogText = "{Application Verifier}logSession" s_AppVerifier_EntryText = "{Application Verifier}logEntry" @@ -18,6 +11,12 @@ # A dictionary to take the error codes and convert them to basic information # on what went wrong. +# +# How to adjust/learn more: +# To add/remove from this list, run "appverif" in a Windows terminal with +# administrator privileges and then press F1 to get the help page. Then search +# for the error code you got (minus the "0x" part at the beginning) and use the +# information there to add/adjust the entry in the dictionary below. s_AppVerifier_ErrorCodeHelp = { "Exceptions": { "0x650": "The application is trying to run code from an address that is non-executable or free" @@ -163,14 +162,8 @@ def parseXML(filepath, dump_xml_on_error): xml_is_app_verifier = False app_verifier_entries = [] - print("Looking for AppVerifier XML file...", flush=True) - - try: - xml_tree = ElementTree.parse(filepath) - except: - print( - colored("Exception occured while trying to open XML file!", "red"), flush=True) - return -1 + print("Looking for AppVerifier XML file...") + xml_tree = ElementTree.parse(filepath) # Go through every element in the XML tree for elem in xml_tree.iter(): @@ -181,14 +174,13 @@ def parseXML(filepath, dump_xml_on_error): # If the XML does not have any AppVerifier data, then something went wrong! if (xml_is_app_verifier == False): - print(colored( - "XML File from AppVerifier does not include a AppVerifier session!", "red"), flush=True) + print("ERROR: XML File from AppVerifier does not include a AppVerifier session!") return -1 # If we have AppVerifier entries, then a test or tests failed, so process the data, # print it, and then return with an error to stop the GitHub action from passing if (len(app_verifier_entries) > 0): - print(colored("AppVerifier entries found:", "yellow"), flush=True) + print("WARNING: AppVerifier entries found:") severity_error_found = False for entry in app_verifier_entries: @@ -203,37 +195,34 @@ def parseXML(filepath, dump_xml_on_error): print_red = True if (print_red): - print(colored( - f"[{element_time}] {element_severity.upper()} - Test: {element_layer_name} - Stop Code: {element_code}", "red"), flush=True) + print( + f"ERROR: [{element_time}] {element_severity.upper()} - Test: {element_layer_name} - Stop Code: {element_code}") else: print( - f"[{element_time}] {element_severity.upper()} - Test: {element_layer_name} - Stop Code: {element_code}", flush=True) - print("\t" + getErrorCodeMeaning(element_layer_name, - element_code), flush=True) + f"[{element_time}] {element_severity.upper()} - Test: {element_layer_name} - Stop Code: {element_code}") + print(f"\t{getErrorCodeMeaning(element_layer_name, element_code)}") print( "\nNOTE: The error codes and information provided are just guesses based on the error code.\n" "\tRun AppVerifier locally and use WinDBG combined with the AppVerifier help to discover more " - "about the error from its error code and how to debug it.", - flush=True) + "about the error from its error code and how to debug it.") if (severity_error_found == True and dump_xml_on_error != None): if (dump_xml_on_error == True): - print(colored("\nRaw XML output for errors found:\n", "red"), flush=True) + print("\nERROR: Raw XML output for errors found:\n") for entry in app_verifier_entries: print(ElementTree.tostring( - entry, encoding="unicode"), flush=True) + entry, encoding="unicode")) if (severity_error_found == True): - print(colored( - "\nFailed due to AppVerifier finding entries marked as severe", "red"), flush=True) + print( + "\nERROR: Failed due to AppVerifier finding entries marked as severe") return -1 else: - print( - colored("AppVerifier entries were not marked as severe", "green"), flush=True) + print("SUCCESS: AppVerifier entries were not marked as severe") return 0 else: - print(colored("No AppVerifier entries found! AppVerifier ran successfully and did not generate any entries", "green"), flush=True) + print("SUCCESS: No AppVerifier entries found! AppVerifier ran successfully and did not generate any entries") return 0 @@ -247,65 +236,6 @@ def getErrorCodeMeaning(element_layer_name, element_code): return "Util-script unknown layer: " + element_layer_name + " and error code: " + element_code -def launchSample(sample_file, sample_secret_endpoint, sample_secret_certificate, sample_secret_private_key, sample_arguments): - - print("Getting credentials from secrets...", flush=True) - try: - secrets_client = boto3.client( - "secretsmanager", region_name="us-east-1") - sample_endpoint = secrets_client.get_secret_value( - SecretId=sample_secret_endpoint)["SecretString"] - sample_certificate = secrets_client.get_secret_value( - SecretId=sample_secret_certificate) - sample_private_key = secrets_client.get_secret_value( - SecretId=sample_secret_private_key) - except: - print(colored("Could not get secrets to launch sample!", "red")) - exit(-1) - - current_folder = pathlib.Path(__file__).resolve() - # Remove the name of the python file - current_folder = str(current_folder).replace("appverifier_xml_util.py", "") - - print("Saving credentials to file...", flush=True) - tmp_certificate_file_path = str(current_folder) + "tmp_certificate.pem" - tmp_private_key_path = str(current_folder) + "tmp_privatekey.pem.key" - with open(tmp_certificate_file_path, "w") as file: - file.write(sample_certificate["SecretString"]) - with open(tmp_private_key_path, "w") as file: - file.write(sample_private_key["SecretString"]) - print("Saved credentials to file...", flush=True) - - print("Processing arguments...", flush=True) - launch_arguments = [] - launch_arguments.append("--endpoint") - launch_arguments.append(sample_endpoint) - launch_arguments.append("--cert") - launch_arguments.append(tmp_certificate_file_path) - launch_arguments.append("--key") - launch_arguments.append(tmp_private_key_path) - sample_arguments_split = sample_arguments.split(" ") - for arg in sample_arguments_split: - launch_arguments.append(arg) - - print("Running sample...", flush=True) - exit_code = 0 - sample_return = subprocess.run( - args=launch_arguments, executable=sample_file) - exit_code = sample_return.returncode - - print("Deleting credentials files...", flush=True) - os.remove(tmp_certificate_file_path) - os.remove(tmp_private_key_path) - - if (exit_code == 0): - print(colored("Finished running sample! Exiting with success", - "green"), flush=True) - else: - print(colored("Sample did not return success! Exit code " + str(exit_code), "red")) - return exit_code - - def booleanString(string): string = string.lower() if string not in {"false", "true"}: @@ -316,21 +246,7 @@ def booleanString(string): def main(): argument_parser = argparse.ArgumentParser( description="AppVerifier XML output util") - argument_parser.add_argument("--launch_sample", metavar="", required=False, default=False, type=booleanString, - help="If true, will launch the sample with the given arguments. Note that endpoint, certificate, and private key are all gotten via Boto3 secrets") - argument_parser.add_argument("--sample_file", metavar="", - required=False, default="", help="Sample to launch that AppVerifier is following") - argument_parser.add_argument("--sample_secret_endpoint", metavar="", - required=False, default="unit-test/endpoint", help="The name of the secret containing the endpoint") - argument_parser.add_argument("--sample_secret_certificate", metavar="", required=False, - default="unit-test/certificate", help="The name of the secret containing the certificate PEM file") - argument_parser.add_argument("--sample_secret_private_key", metavar="", required=False, - default="unit-test/privatekey", help="The name of the secret containing the private key PEM file") - argument_parser.add_argument("--sample_arguments", metavar="", - required=False, default="", help="Arguments to pass to sample") - argument_parser.add_argument("--parse_xml", metavar="", required=False, default=False, - type=booleanString, help="If true, the utility script will parse the AppVerifier XML passed") argument_parser.add_argument("--xml_file", metavar="", required=False, help="Path to XML file from AppVerifier") argument_parser.add_argument("--dump_xml_on_error", metavar="", default=True, required=False, @@ -338,27 +254,12 @@ def main(): parsed_commands = argument_parser.parse_args() - if (parsed_commands.launch_sample == True): - print("\n" + colored("Starting to launch sample...", "green"), flush=True) - sample_result = launchSample( - parsed_commands.sample_file, - parsed_commands.sample_secret_endpoint, - parsed_commands.sample_secret_certificate, - parsed_commands.sample_secret_private_key, - parsed_commands.sample_arguments) - print("\n") - exit(sample_result) - elif (parsed_commands.parse_xml == True): - print("\n" + colored("Starting AppVerifier XML check...", "green"), flush=True) - print(parsed_commands.dump_xml_on_error) - xml_result = parseXML(parsed_commands.xml_file, - parsed_commands.dump_xml_on_error) - print("\n") - exit(xml_result) - else: - print("\n" + colored("Error! Was not told to start sample or parse XML!", - "red") + "\n", flush=True) - exit(-1) + print("\nStarting AppVerifier XML check...", flush=True) + print(parsed_commands.dump_xml_on_error) + xml_result = parseXML(parsed_commands.xml_file, + parsed_commands.dump_xml_on_error) + print("\n") + exit(xml_result) if __name__ == "__main__":