Skip to content

Commit

Permalink
Skipping remote validation for newly created local plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
lcwiklinski-r7 committed Nov 21, 2024
1 parent b06558c commit 10f0f1e
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 20 deletions.
2 changes: 1 addition & 1 deletion icon_validator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "2.47.21"
VERSION = "2.47.22"
48 changes: 30 additions & 18 deletions icon_validator/rules/plugin_validators/version_bump_validator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import git
from typing import List

from icon_validator.rules.validator import KomandPluginValidator
from icon_validator.exceptions import ValidationException, NO_LOCAL_CON_VERSION, \
Expand Down Expand Up @@ -40,31 +41,41 @@ def __init__(self):
self.local_version = None
self.remote_version = None

@staticmethod
def is_initial_plugin_version(version_history: List[str]) -> bool:
initial_versions = ["0.1.0 - Initial plugin", "1.0.0 - Initial plugin"]
if len(version_history) == 1 and any(version in version_history for version in initial_versions):
return True
return False

@staticmethod
def get_remote_spec(spec):
"""
Get the existing remote spec for this plugin from the repo
"""
version_history = spec.spec_dictionary().get("version_history")
directory = spec.directory.split(f"/{RepoConstants.PLUGIN_DIRNAME}/")[0]

try:
repo = Repo(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
except InvalidGitRepositoryError:
raise ValidationException("Incorrect directory passed- must be an individual plugin directory")
if not VersionBumpValidator.is_initial_plugin_version(version_history):
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

@staticmethod
def get_plugin_spec_blob(remote_list: [git.RemoteReference], plugin_name: str):
Expand Down Expand Up @@ -383,14 +394,15 @@ def validate(self, spec):
remote_spec = VersionBumpValidator.get_remote_spec(spec)
local_spec = spec.spec_dictionary()

# case: new plugin with no remote spec
if remote_spec is None:
return

self.local_version, self.remote_version = self.get_versions(local_spec, remote_spec)

# we should validate connection versions regardless of the version changes to ensure these remain stable
self.validate_connection_version(local_spec, remote_spec)

# case: new plugin with no remote spec
if remote_spec is None:
return
# perform the different sections of validation
# Check if we already did a major version bump- if so, no need to do all this checking
if not self.check_major_version_increment_needed():
Expand Down
66 changes: 66 additions & 0 deletions unit_test/plugin_examples/initial_plugin/plugin.spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
plugin_spec_version: v2
extension: plugin
products: [insightconnect]
name: rss
title: RSS
description: The RSS plugin allows you to monitor an RSS feed
version: 0.1.0
connection_version: 1
vendor: rapid7
support: community
support_versions: ["2024-04-25"]
status: []
sdk:
type: slim
version: 5.4.7
user: nobody
key_features:
- Monitor an RSS feed
requirements:
- The URL for the feed you want to monitor
links:
- "[Feedparser](https://github.com/kurtmckee/feedparser)"
references:
- "[Feedparser](https://github.com/kurtmckee/feedparser)"
- "[Input Templating](https://docs.rapid7.com/insightconnect/format-strings-with-templates/)"
- "[Python Script plugin](https://docs.rapid7.com/insightconnect/python-2-or-3-script/)"
version_history:
- "0.1.0 - Initial plugin"
resources:
source_url: https://github.com/rapid7/insightconnect-plugins/tree/master/plugins/rss
license_url: https://github.com/rapid7/insightconnect-plugins/blob/master/LICENSE
tags:
- rss
- atom
- feed
hub_tags:
use_cases: [data_utility, threat_detection_and_response, alerting_and_notifications]
keywords: [rss, atom, feeds]
features: []
language: python
connection:
url:
title: URL
description: Feed URL
type: string
required: true
example: https://example.com/rss/current
triggers:
poll:
title: Poll Feed
description: Poll feed for latest event
input:
frequency:
title: Frequency
description: How frequently (in seconds) to poll for new entries
type: integer
required: true
default: 15
example: 15
output:
results:
title: Results
description: RSS data
type: object
required: true
example: '{"title": "First item title", "title_detail": {"type": "text/plain", "language": null, "base": "", "value": "First item title"}, "links": [{"rel": "alternate", "type": "text/html", "href": "http://example.org/item/1"}, {"url": "", "rel": "enclosure"}], "link": "http://example.org/item/1", "summary": "Watch out for\n<span>\nnasty tricks</span>", "summary_detail": {"type": "text/html", "language": null, "base": "", "value": "Watch out for\n<span>\nnasty tricks</span>"}, "authors": [{"email": "[email protected]"}], "author": "[email protected]", "author_detail": {"email": "[email protected]"}, "tags": [{"term": "Miscellaneous", "scheme": null, "label": null}], "comments": "http://example.org/comments/1", "id": "http://example.org/guid/1", "guidislink": false, "published": "Thu, 05 Sep 2002 0:00:01 GMT"}'
29 changes: 28 additions & 1 deletion unit_test/test_validate_plugin/test_validate_plugin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import unittest

from git import InvalidGitRepositoryError

from icon_validator.exceptions import NO_LOCAL_CON_VERSION, NO_CON_VERSION_CHANGE, \
INVALID_CON_VERSION_CHANGE, INCORRECT_CON_VERSION_CHANGE, FIRST_TIME_CON_VERSION_ISSUE
from icon_validator.validate import validate
Expand Down Expand Up @@ -51,6 +54,14 @@ def test_convert_valid_datetime(self, table_string: str, expected: str):
response = convert_to_valid_datetime(table_string)
self.assertEqual(expected, response)

def test_is_initial_plugin_version(self):
result = VersionBumpValidator.is_initial_plugin_version(["1.0.0 - Initial plugin"])
self.assertEqual(result, True)

def test_is_not_initial_plugin_version(self):
result = VersionBumpValidator.is_initial_plugin_version(["2.0.0 - Test"])
self.assertEqual(result, False)

def test_plugin_validate(self):
# example workflow in plugin_examples directory. Run tests with these files
directory_to_test = "plugin_examples/good_plugin"
Expand Down Expand Up @@ -516,6 +527,22 @@ def test_major_version_connection_input_removed_should_fail(self):
result = validate(directory_to_test, file_to_test, False, True, [VersionBumpValidator()])
self.assertEqual(result, 1)

def test_initial_plugin_not_on_the_remote_should_pass(self):
# example spec in plugin_examples directory. Run tests with these files
directory_to_test = "plugin_examples/initial_plugin"
file_to_test = "plugin.spec.yaml"
VersionBumpValidator.get_remote_spec = MagicMock(return_value=None)
result = validate(directory_to_test, file_to_test, False, True, [VersionBumpValidator()])
self.assertEqual(result, 0)

def test_not_initial_plugin_not_on_the_remote_should_fail(self):
# example spec in plugin_examples directory. Run tests with these files
directory_to_test = "plugin_examples/good_plugin_no_actions"
file_to_test = "plugin.spec.yaml"
VersionBumpValidator.get_remote_spec = MagicMock(None)
result = validate(directory_to_test, file_to_test, False, True, [VersionBumpValidator()])
self.assertEqual(result, 1)

def test_major_version_input_now_required_should_fail(self):
# example spec in plugin_examples directory. Run tests with these files
directory_to_test = "plugin_examples/plugin_major_version_bump_all"
Expand Down Expand Up @@ -689,7 +716,7 @@ def test_version_new_output_bumped_succeed(self):

@parameterized.expand([
# ('test_name', new_yaml, exception, mock_local_spec)
('initial_no_connection', "plugin.spec.good.new.nonrequired.input.yaml", NO_LOCAL_CON_VERSION, False),
('initial_no_connection', "plugin.spec.good.new.nonrequired.input.yaml", None, False),
('changed_con_schema_no_ver_bump', "plugin.spec.bad.new.connection.yaml", NO_CON_VERSION_CHANGE, True),
('changed_con_schema_with_ver_bump', "plugin.spec.good.new.connection.yaml", None, True),
('invalid_con_ver_bump', "plugin.spec.bad.connection.change.yaml", INVALID_CON_VERSION_CHANGE, True),
Expand Down

0 comments on commit 10f0f1e

Please sign in to comment.