Example #1
0
 def validate_workflow_description_punctuation(description):
     if not description.endswith("."):
         raise ValidationException("Description does not end with a period when it should.")
     if description[0].islower():
         raise ValidationException("Description should not start with a lower case letter.")
     if description[0].isspace():
         raise ValidationException("Description should not start with a whitespace character.")
Example #2
0
    def validate_workflow_description_exists(spec):
        if "description" not in spec.spec_dictionary():
            raise ValidationException("Workflow description in yaml is missing.")

        description = spec.spec_dictionary()["description"]
        if description == "":
            raise ValidationException("Workflow description in yaml can not be blank")
    def validate_no_types_changed(self, remote: dict, local: dict):
        # verifies that types have not been changed between spec versions
        if SpecConstants.TYPES in remote and SpecConstants.TYPES in local:
            self.validate_all_types_exist(remote, local)
            for type_key, type_value in remote[SpecConstants.TYPES].items():
                for type_inner_key, type_inner_val in type_value.items():
                    local_type_in = local[SpecConstants.TYPES][type_key]
                    if type_inner_key not in local_type_in:
                        raise ValidationException(
                            f"Type {type_inner_key} removed from {type_key} without a major"
                            f" version increment."
                            f"{self.MAJOR_INSTRUCTIONS_STRING}")
                    if type_inner_val.get(
                            SpecConstants.TYPE
                    ) != local_type_in[type_inner_key].get(SpecConstants.TYPE):
                        raise ValidationException(
                            f"Type {type_inner_key} changed in type {type_key} without a major"
                            f" version increment."
                            f"{self.MAJOR_INSTRUCTIONS_STRING}")

                    if type_inner_val.get(
                            SpecConstants.REQUIRED
                    ) != local_type_in[type_inner_key].get(
                            SpecConstants.REQUIRED):
                        raise ValidationException(
                            f"Type {type_inner_key} changed in type {type_key} without a major"
                            f"version increment."
                            f"{self.MAJOR_INSTRUCTIONS_STRING}")
Example #4
0
    def validate(self, spec):
        # Using subprocess so we don't have to deal with connecting to different Docker environments
        # e.g docker-machine vs Docker for Mac vs native Docker
        # Directory of current plugin
        d = spec.directory
        build_image = [
            "docker", "build", "-q", "--pull", "-t", "docker_validator", d
        ]
        run_image = ["docker", "run", "--rm", "-t", "docker_validator", "info"]

        with open(os.devnull, "w") as fd:
            try:
                which = subprocess.check_call(["which", "docker"],
                                              stdout=fd,
                                              stderr=fd)
            except subprocess.CalledProcessError:
                sys.stdout.write(
                    "DockerValidator: docker binary missing in PATH, skipping..."
                )
            else:
                try:
                    subprocess.check_call(build_image, stdout=fd, stderr=fd)
                except subprocess.CalledProcessError as e:
                    raise ValidationException(
                        "The plugin is either broken or the image might not be built."
                        "Please try 'icon-plugin build image' to rebuild the image."
                        "'icon-plugin run -c bash' will open a bash shell on the build container."
                    ) from e

                try:
                    subprocess.check_call(run_image, stdout=fd, stderr=fd)
                except subprocess.CalledProcessError as e:
                    raise ValidationException(
                        "Docker failed at running info command. "
                        "Check your plugin code for run-time errors.") from e
 def check_minor_version_increment_needed(self, remote: dict, local: dict):
     # input: complete spec dictionary
     # Checks to see if version is valid sem-ver, and if we already bumped minor version
     local_version = local["version"].split('.')
     remote_version = remote["version"].split('.')
     if len(local_version) == 3 and len(remote_version) == 3:
         local_version = VersionBumpValidator.modify_version_array(
             local_version)
         remote_version = VersionBumpValidator.modify_version_array(
             remote_version)
         if int(local_version[1]) > int(remote_version[1]):
             if int(local_version[2]) > 0:
                 raise ValidationException(
                     "Minor version increment should set patch version to 0 "
                     "The resulting format should be X.Y.0")
             return False
         else:
             self.MINOR_INSTRUCTIONS_STRING = f" Please change the plugin version to " \
                                                              f"{int(remote_version[0])}." \
                                                              f"{int(remote_version[1]) + 1}.0"
             return True
     else:
         raise ValidationException(
             "Version does not match required semver format. "
             "Version should be in form X.Y.Z with X, Y, and Z "
             "being numbers. No special characters or spaces allowed. "
             "Versions start at 1.0.0, see https://semver.org/ for more information."
         )
 def validate_keywords_exists(spec: KomandPluginSpec):
     try:
         keywords = spec.spec_dictionary()["hub_tags"]["keywords"]
         if not keywords or not len(keywords):
             raise ValidationException("Empty required field 'keywords' in key 'hub_tags'.")
     except KeyError:
         raise ValidationException("Missing required field 'keywords' in key 'hub_tags'.")
    def get_remote_spec(spec):
        """
        Get the existing remote spec for this plugin from the repo
        """
        directory = spec.directory.split(
            f"/{RepoConstants.PLUGIN_DIRNAME}/")[0]
        try:
            repo = Repo(directory)
        except InvalidGitRepositoryError:
            raise ValidationException(
                "Incorrect directory passed- must be an individual plugin directory"
            )

        remote_list = repo.remote().refs
        blob = VersionBumpValidator.get_plugin_spec_blob(
            remote_list,
            spec.spec_dictionary()["name"])
        # case: remote spec not found
        if blob is None:
            return None

        # if all went well and no exceptions, we now have the blob of plugin spec
        # using a temp file because stream_data requires a data object
        try:
            remote_spec = yaml.safe_load(blob.data_stream.read())
        except yaml.YAMLError:
            raise ValidationException(
                "Remote plugin.spec.yaml contains incorrect yaml and must be fixed. "
                "If this change fixes remote spec, disregard this error message"
            )
        return remote_spec
Example #8
0
    def validate_workflow_versions(workflow_versions):
        try:
            if workflow_versions["name"] is None or workflow_versions[
                    "name"] == "":
                raise ValidationException(
                    "The name key in workflowVersions is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                "ICON file is missing the name key in workflowVersions. Try exporting the .icon file again"
            )

        try:
            if workflow_versions["type"] is None or workflow_versions[
                    "type"] == "":
                raise ValidationException(
                    "The type key in workflowVersions is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                "ICON file is missing the type key in workflowVersions. Try exporting the .icon file again"
            )

        # The version key may be a blank string
        try:
            if workflow_versions["version"] is None:
                raise ValidationException(
                    "The version key in workflowVersions is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                "ICON file is missing the version key in workflowVersions. Try exporting the .icon file again"
            )

        # The description key may be a blank string
        try:
            if workflow_versions["description"] is None:
                raise ValidationException(
                    "The description key in workflowVersions is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                "ICON file is missing the description key in workflowVersions. Try exporting the .icon file again"
            )

        # The tags key may be None or a list
        try:
            if workflow_versions["tags"] is not None and not isinstance(
                    workflow_versions["tags"], list):
                raise ValidationException(
                    "The description tags in workflowVersions is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                "ICON file is missing the tags key in workflowVersions. Try exporting the .icon file again"
            )

        for step, value in workflow_versions["steps"].items():
            WorkflowICONFileValidator.validate_workflow_versions_steps(
                step, value)
 def get_plugin_spec_blob(remote_list: [git.RemoteReference],
                          plugin_name: str):
     """
     Get the plugin spec blob from the remote repo
     """
     blob = None
     remote = None
     for _remote in remote_list:
         if _remote.name == "origin/master":
             remote = _remote
             # now get the blob representing the plugin folder and loop over until we find plugin spec
             try:
                 for _blob in _remote.object.tree[
                         RepoConstants.PLUGIN_DIRNAME][plugin_name]:
                     if _blob.name == RepoConstants.PLUGIN_SPEC:
                         blob = _blob
                         break
             except KeyError:
                 # plugin name not found, so version increment is not really relevant
                 return None
             if blob is None:
                 # throw error: no plugin spec found in remote
                 raise ValidationException(
                     f"{RepoConstants.PLUGIN_SPEC} not found in remote repo"
                 )
             break
     if remote is None:
         # throw exception : origin/master not found
         raise ValidationException(
             "Remote origin/master not found.'master' branch name changed, update validator"
         )
     return blob
 def validate_help_headers(help_str):
     """
     Checks that all the expected headers are included in help.md
     """
     if "# Description" not in help_str:
         raise ValidationException(
             "Help section is missing header: # Description.")
     if "# Key Features" not in help_str:
         raise ValidationException(
             "Help section is missing header: # Key Features.")
     if "# Requirements" not in help_str:
         raise ValidationException(
             "Help section is missing header: # Requirements.")
     if "# Documentation" not in help_str:
         raise ValidationException(
             "Help section is missing header: # Documentation.")
     if "## Setup" not in help_str:
         raise ValidationException(
             "Help section is missing header: ## Setup.")
     if "## Technical Details" not in help_str:
         raise ValidationException(
             "Help section is missing header: ## Technical Details.")
     if "## Troubleshooting" not in help_str:
         raise ValidationException(
             "Help section is missing header: ## Troubleshooting.")
     if "# Version History" not in help_str:
         raise ValidationException(
             "Help section is missing header: # Version History.")
     if "# Links" not in help_str:
         raise ValidationException(
             "Help section is missing header: # Links.")
     if "## References" not in help_str:
         raise ValidationException(
             "Help section is missing header: ## References.")
    def validate_screenshots_keys_exist(self, spec):
        """
        Checks that screenshots key exists.
        Checks that screenshots key has at lest one entry.
        """
        try:
            screenshots = spec.spec_dictionary()["resources"]["screenshots"]
        except KeyError:
            raise ValidationException(
                "The screenshots key under the resources key does not exist in the yaml."
                " please add this key.")
        if not isinstance(screenshots, list):
            raise ValidationException(
                "There are no screenshots listed in the yaml."
                " At lest one screenshot must be listed.")
        if len(screenshots) == 0:
            raise ValidationException(
                "There are no screenshots listed in the yaml."
                " At lest one screenshot must be listed.")

        for screenshot in screenshots:
            try:
                self._names_list.append(screenshot["name"])
            except KeyError:
                raise ValidationException(
                    "Each screenshot must have a 'name' key that coresposnds to the file name of the screenshot."
                    f" {screenshot} is missing this key.")
Example #12
0
 def validate(self, spec: KomandPluginSpec):
     try:
         result = UseCaseValidator.validate_use_cases(spec.spec_dictionary()["hub_tags"]["use_cases"])
         if len(result):
             err = ", ".join(result)
             raise ValidationException(f"Invalid use cases: {err}.")
     except KeyError:
         raise ValidationException("Missing required field 'use_cases' in key 'hub_tags'.")
Example #13
0
 def validate_workflow_vendor(spec):
     """
     Check that vendor key exists.
     Check that vendor is a string.
     """
     if "vendor" not in spec.spec_dictionary():
         raise ValidationException("Plugin vendor is missing.")
     if not isinstance(spec.spec_dictionary()["vendor"], str):
         raise ValidationException("Plugin vendor does not contain a string.")
 def validate_use_case_exists(spec: KomandPluginSpec):
     try:
         use_cases = spec.spec_dictionary()["hub_tags"]["use_cases"]
         if not use_cases:
             raise ValidationException(
                 "Empty required field 'use_cases' in key 'hub_tags'.")
     except KeyError:
         raise ValidationException(
             "Missing required field 'use_cases' in key 'hub_tags'.")
    def validate_plugin_description(spec):
        if "description" not in spec.spec_dictionary():
            raise ValidationException("Plugin description is missing.")

        try:
            DescriptionValidator.validate_description(
                spec.spec_dictionary()["description"])
        except ValidationException as e:
            raise ValidationException("Plugin description is not valid.", e)
Example #16
0
    def validate_plugin_title(spec):
        if "title" not in spec.spec_dictionary():
            raise ValidationException("Plugin title is missing.")

        try:
            TitleValidator.validate_title(spec.spec_dictionary()["title"],
                                          plugin_title=True)
        except Exception as e:
            raise ValidationException("Plugin title not valid.", e)
    def validate_spec(spec):
        sup_vers = "supported_versions"

        if sup_vers not in spec.spec_dictionary():
            raise ValidationException(f"{sup_vers} is missing from plugin.spec.yaml.")
        if not isinstance(spec.spec_dictionary()[sup_vers], list):
            raise ValidationException(f"{sup_vers} does not contain a list of values.")
        if len(spec.spec_dictionary()[sup_vers]) == 0:
            raise ValidationException(f"{sup_vers} list does not contain values.")
 def validate_workflow_version(spec):
     """
     Check that version key exists.
     Check that version is a string.
     """
     if "version" not in spec.spec_dictionary():
         raise ValidationException("Workflow version is missing.")
     if not isinstance(spec.spec_dictionary()["version"], str):
         raise ValidationException(
             "Workflow version does not contain a string.")
Example #19
0
 def validate_workflow_support(spec):
     """
     Check that support key exists.
     Check that support is a string.
     """
     if "support" not in spec.spec_dictionary():
         raise ValidationException("Plugin supporter is missing.")
     if not isinstance(spec.spec_dictionary()["support"], str):
         raise ValidationException(
             "Plugin supporter does not contain a string.")
 def validate_description(description):
     if description.endswith("."):
         raise ValidationException(
             "Description ends with a period when it should not.")
     if description[0].islower():
         raise ValidationException(
             "Description should not start with a lower case letter.")
     if description[0].isspace():
         raise ValidationException(
             "Description should not start with a whitespace character.")
 def validate_names_not_null(self):
     """
     Check that name keys in yaml are not null or blank strings.
     """
     for name in self._names_list:
         if not isinstance(name, str):
             raise ValidationException(
                 "The name key for a screenshot must not be null")
     if any(names == "" for names in self._names_list):
         raise ValidationException(
             "The name key for a screenshot may not be a blank string")
    def validate(self, spec):
        """
        Checks that title is not blank.
        Checks that title does not end with a period.
        Checks that title does not start with a lower case letter.
        Checks that title does not start with a space.
        Checks that title is 6 words or less.
        Checks that title is properly capitalized.
        """
        if "title" not in spec.spec_dictionary():
            raise ValidationException("Plugin title is missing.")

        title = spec.spec_dictionary()["title"]

        if not isinstance(title, str):
            raise ValidationException("Title must not be blank")
        if title == "":
            raise ValidationException("Title must not be blank")
        if title.endswith("."):
            raise ValidationException(
                "Title ends with period when it should not.")
        if title[0].islower():
            # This plugin title is OK: minFraud
            # This plugin title is OK: ifconfig.co
            raise ValidationException(
                "Title should not start with a lower case letter.")
        if title[0].isspace():
            raise ValidationException(
                "Title should not start with a whitespace character.")
        for word in title.split():
            if not title.startswith(word):
                if word in title_validation_list:
                    raise ValidationException(
                        f"Title contains a capitalized '{word}' when it should not."
                    )
                elif "By" == word and not title.endswith("By"):
                    # This is OK: Order By
                    # This is NOT OK: Search By String
                    raise ValidationException(
                        "Title contains a capitalized 'By' when it should not."
                    )
                elif "Of" == word and not title.endswith("Of"):
                    # This is OK: Member Of
                    # This is NOT OK: Type Of String
                    raise ValidationException(
                        "Title contains a capitalized 'Of' when it should not."
                    )
                elif not word[0].isupper() and not word.capitalize(
                ) in title_validation_list:
                    if not word.isnumeric():
                        if not word.lower() == "by" or word.lower() == "of":
                            raise ValidationException(
                                f"Title contains a lowercase '{word}' when it should not."
                            )
 def validate_no_sections_removed(self, remote: dict, local: dict):
     # checks if either "input" or "output" was removed or added to a trigger or action
     # remote and local are dictionaries of an action or trigger
     if (SpecConstants.INPUT in remote) != (SpecConstants.INPUT in local):
         raise ValidationException(
             f"Input section was added or removed without a major version increment."
             f"{self.MAJOR_INSTRUCTIONS_STRING}")
     if (SpecConstants.OUTPUT in remote) != (SpecConstants.OUTPUT in local):
         raise ValidationException(
             f"Output section was added or removed without a major version increment."
             f"{self.MAJOR_INSTRUCTIONS_STRING}")
 def check_for_new(self, remote: dict, local: dict, spec_type: str):
     # remote and local are the complete spec
     if spec_type in local:
         if spec_type not in remote:
             raise ValidationException(
                 f"Plugin spec section {spec_type} added to {RepoConstants.PLUGIN_SPEC}."
                 f"{self.MINOR_INSTRUCTIONS_STRING}")
         for key in local[spec_type]:
             if key not in remote[spec_type]:
                 raise ValidationException(
                     f"Added {spec_type}: {key}."
                     f"{self.MINOR_INSTRUCTIONS_STRING}")
    def validate_version_history(help_str):
        if "- Initial plugin" not in help_str:
            raise ValidationException("Initial plugin version line is missing: 1.0.0 - Initial plugin.")

        if "Support web server mode" not in help_str and "1.0.0 - Initial plugin" not in help_str:
            # Match legacy versioning which indicates this plugin came before web server mode existed
            if "* 0." in help_str:
                # Takes advantage of the fact that versioning used to start from 0.1.0 instead of 1.0.0
                raise ValidationException(
                    "Initial plugin was released prior to schema V2 but versioning history."
                    "does not document the upgrade to web server mode: Support web server mode."
                )
Example #26
0
    def validate_workflow_versions_steps(step, value):
        try:
            if value["nodeId"] != step:
                raise ValidationException(
                    f"The nodeId key in {step} is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                f"ICON file is missing the nodeId key in {step}. Try exporting the .icon file again"
            )

        try:
            if value["name"] is None or value["name"] == "":
                raise ValidationException(
                    f"The name key in {step} is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                f"ICON file is missing the name key in {step}. Try exporting the .icon file again"
            )

        try:
            if value["type"] is None or value["type"] == "":
                raise ValidationException(
                    f"The type key in {step} is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                f"ICON file is missing the type key in {step}. Try exporting the .icon file again"
            )

        # Must be a bool value
        try:
            if not isinstance(value["continueOnFailure"], bool):
                raise ValidationException(
                    f"The continueOnFailure key in {step} is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                f"ICON file is missing the continueOnFailure key in {step}. Try exporting the .icon file again"
            )

        # Must be a bool value
        try:
            if not isinstance(value["continueOnFailure"], bool):
                raise ValidationException(
                    f"The isDisabled key in {step} is not defined. Try exporting the .icon file again"
                )
        except KeyError:
            raise ValidationException(
                f"ICON file is missing the isDisabled key in {step}. Try exporting the .icon file again"
            )
    def validate_help_headers(help_str):
        # if plugin without tasks needs not to be regenerated, help.md won't be having Tasks section
        # Only raise exception if plugin.spec.yaml contains task and help.md does not
        if HelpValidator.taskExist and "### Tasks" not in help_str:
            raise ValidationException("Help section is missing header: ### Tasks")

        help_headers_errors = []
        for header in HelpValidator.HELP_HEADERS_LIST:
            if header not in help_str:
                help_headers_errors.append(f"Help section is missing header: {header}")

        if help_headers_errors:
            raise ValidationException("\n".join(help_headers_errors))
Example #28
0
 def validate_vendor(vendor):
     lvendor = vendor.lower()
     if lvendor == "komand":
         raise ValidationException(
             "Vendor 'komand' not allowed. It's likely you meant 'rapid7'.")
     if vendor.endswith("."):
         raise ValidationException(
             "Vendor ends with period when it should not.")
     if not vendor[0].islower():
         raise ValidationException(
             "Vendor starts with a capital letter when it should not.")
     if " " in vendor:
         raise ValidationException(
             "Vendor should be separated by underscores, not spaces.")
Example #29
0
    def validate(self, spec):
        d = spec.directory

        # Go plugins
        if os.path.isdir(f"{d}/connection"):
            if not os.path.isfile(f"{d}/Makefile"):
                raise ValidationException("File Makefile does not exist in: ",
                                          d)
            if not os.path.isfile(f"{d}/plugin.spec.yaml"):
                raise ValidationException(
                    "File plugin.spec.yaml does not exist in: ", d)
            if not os.path.isfile(f"{d}/Dockerfile"):
                raise ValidationException(
                    "File Dockerfile does not exist in: ", d)
        else:
            # Python plugins
            if os.path.isdir(f"{d}/bin"):
                if not os.path.isfile(f"{d}/Dockerfile"):
                    raise ValidationException(
                        "File Dockerfile does not exist in: ", d)
                if not os.path.isfile(f"{d}/Makefile"):
                    raise ValidationException(
                        "File Makefile does not exist in: ", d)
                if not os.path.isfile(f"{d}/plugin.spec.yaml"):
                    raise ValidationException(
                        "File plugin.spec.yaml does not exist in: ", d)
                if not os.path.isfile(f"{d}/setup.py"):
                    raise ValidationException(
                        "File setup.py does not exist in: ", d)
                if not os.path.isfile(f"{d}/requirements.txt"):
                    raise ValidationException(
                        "File requirements.txt does not exist in: ", d)
Example #30
0
    def validate_profanity(spec):
        raw_spec = spec.raw_spec()
        spec_words = raw_spec.split()

        for word in spec_words:
            if word in profanity_list:
                raise ValidationException(
                    f"{spec.spec_file_name} contains banned word: {word}.")

        help_lst = spec.raw_help().split()
        for word in help_lst:
            if word in profanity_list:
                raise ValidationException(
                    f"help.md contains banned word: {word}.")