Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ be on your way to contributing!

## Changelog

* 2.47.34 - `EnableCacheValidator` - Require `enable_cache: true` for any plugin that defines triggers (orchestrator-only plugins included) so triggers do not silently fail on SDK 6.6.0+ | `CloudReadyValidator` - now only enforces `enable_cache: false` for Cloud Ready plugins without triggers
* 2.47.33 - Updated dependencies to the latest versions
* 2.47.32 - `CloudReadyValidator` - Allow `enable_cache: true` for Cloud Ready plugins that define triggers
* 2.47.31 - `RuntimeValidator` - Detect caching via AST parsing to avoid false positives on the `cache` keyword | `DescriptionValidator` - Enforce 500 character description limit | `DockerValidator` - Skip build failures caused by dependency network issues
Expand Down
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.33"
VERSION = "2.47.34"
3 changes: 3 additions & 0 deletions icon_validator/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from icon_validator.rules.plugin_validators.version_pin_validator import *
from icon_validator.rules.plugin_validators.example_input_validator import *
from icon_validator.rules.plugin_validators.cloud_ready_validator import *
from icon_validator.rules.plugin_validators.enable_cache_validator import *
from icon_validator.rules.plugin_validators.supported_version_validator import *
from icon_validator.rules.plugin_validators.unapproved_keywords_validator import *
from icon_validator.rules.plugin_validators.help_example_validator import *
Expand Down Expand Up @@ -104,6 +105,7 @@
EncodingValidator(),
ExampleInputValidator(),
CloudReadyValidator(),
EnableCacheValidator(),
SupportedVersionValidator(),
UnapprovedKeywordsValidator(),
HelpExampleValidator(),
Expand Down Expand Up @@ -144,6 +146,7 @@
EncodingValidator(),
ExampleInputValidator(),
CloudReadyValidator(),
EnableCacheValidator(),
SupportedVersionValidator(),
UnapprovedKeywordsValidator(),
HelpExampleValidator(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@ class CloudReadyValidator(KomandPluginValidator):

@staticmethod
def validate_enable_cache_in_plugin_spec(plugin_spec: dict):
# Plugin with triggers must have caching enabled and vice versa
# A Cloud Ready plugin without triggers must not enable caching.
# (The with-triggers case is enforced for all plugins by EnableCacheValidator.)
has_triggers = bool(plugin_spec.get("triggers"))
enable_cache = plugin_spec.get("enable_cache", False)

if has_triggers and not enable_cache:
raise ValidationException(
"'enable_cache' must be set to 'true' for a Cloud Ready plugin with triggers. "
"Please check this field in plugin.spec and try again."
)

if not has_triggers and enable_cache:
raise ValidationException(
"'enable_cache' must be set to 'false' for a Cloud Ready plugin without triggers. "
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from icon_validator.exceptions import ValidationException
from icon_validator.rules.validator import KomandPluginValidator


class EnableCacheValidator(KomandPluginValidator):
# Triggers rely on caching on SDK 6.6.0+, so any plugin defining triggers must set
# enable_cache: true. This applies to all plugins, hence it is separate from the
# Cloud Ready, Dockerfile-dependent checks in CloudReadyValidator.

@staticmethod
def validate_enable_cache_with_triggers(plugin_spec: dict):
has_triggers = bool(plugin_spec.get("triggers"))
# strict: reject truthy strings like "false"
enable_cache = plugin_spec.get("enable_cache") is True

if has_triggers and not enable_cache:
raise ValidationException(
"'enable_cache' must be set to 'true' for a plugin with triggers. "
"Please check this field in plugin.spec and try again."
)

def validate(self, spec):
EnableCacheValidator.validate_enable_cache_with_triggers(spec.spec_dictionary())
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
plugin_spec_version: v2
extension: plugin
products: [insightconnect]
name: abuseipdb
title: AbuseIPDB
description: Enables the look up of IP reports, provides list and details of blacklisted IPs, and submissions of abusive IPs
version: 5.0.6
vendor: rapid7
support: community
status: []
resources:
source_url: https://github.com/rapid7/insightconnect-plugins/tree/master/abuseipdb
license_url: https://github.com/rapid7/insightconnect-plugins/blob/master/LICENSE
vendor_url: https://www.abuseipdb.com
tags:
- ip
- intelligence
- abuse
hub_tags:
use_cases: [data_enrichment, threat_detection_and_response]
keywords: [ip, intelligence, abuse]
features: []
types:

blacklisted:
ipAddress:
title: IP Address
description: IP Address of abusive IP
type: string
required: true
abuseConfidenceScore:
title: Abuse Confidence Score
description: Confidence that IP is abusive
type: string
required: true

report:
reportedAt:
title: Reported At
description: Date and time of report
type: string
required: false
comment:
title: Comment
description: Comment by reporter
type: string
required: false
categories:
title: Categories
description: List of categories
type: '[]integer'
required: false
reporterId:
title: Reporter ID
description: ID number of reporter
type: integer
required: false
reporterCountryCode:
title: Reporter Country Code
description: Country code of the reporter
type: string
required: false
reporterCountryName:
title: Reporter Country Name
description: Name of country reporter is from
type: string
required: false

reportedIPs:
ipAddress:
title: IP
type: string
description: IP Address of reported resource
numReports:
title: Number of Reports
type: integer
description: Number of reports of this IP
mostRecentReport:
title: Most Recent Report
type: string
description: Most recent report for this IP
abuseConfidenceScore:
title: Abuse Confidence Score
type: integer
description: Confidence that this IP is abusive
countryCode:
title: Country Code
type: string
description: Country code of IP

connection:
credentials:
title: API Key
description: API key from account
type: credential_secret_key
required: true
example: e73h82c63847f3ff1h5216b556edh153h30430d73bchhe680f70h1d8885fb8bb130b46c7767d6886

actions:
check_ip:
title: Check IP
description: Look up an IP address in the database
input:
address:
title: IP Address
description: IPv4 or IPv6 address e.g. 198.51.100.100, ::1, must be subscribed to accept bitmask wider than 255.255.255.0 (/24)
type: string
required: true
example: 198.51.100.100
days:
title: Days
description: Check for IP reports in the last x days
type: string
default: '30'
required: true
example: 30
verbose:
title: Verbose
description: When set, reports will include the comment (if any) and the reporter's
user ID number (0 if reported anonymously)
type: boolean
default: true
required: true
example: true
output:
ipAddress:
title: IP Address
description: Queried IP Address
type: string
required: false
isPublic:
title: Is Public
description: Whether or not the IP Address is public
type: boolean
required: false
ipVersion:
title: IP Version
description: Version of IP Address
type: integer
required: false
isWhitelisted:
title: Is Whitelisted
description: Whether or not IP Address is whitelisted
type: boolean
required: false
abuseConfidenceScore:
title: Abuse Confidence Score
description: Confidence of Abuse
type: integer
required: false
countryCode:
title: Country Code
description: Code of country IP is registered in
type: string
required: false
usageType:
title: Usage Type
description: How IP is used
type: string
required: false
isp:
title: ISP
description: Internet Service Provider for IP
type: string
required: false
domain:
title: Domain
description: Domain Name of IP
type: string
required: false
countryName:
title: Country Name
description: Name of Country IP is registered in
type: string
required: false
totalReports:
title: Total Reports
description: Total number of reports of abuse
type: integer
required: false
numDistinctUsers:
title: Number of Distinct Users
description: Number of distinct users who reported IP
type: integer
required: false
lastReportedAt:
title: Last Reported At
description: Date of last report
type: string
required: false
reports:
title: Reports
description: List of reports
type: '[]report'
required: false
found:
title: Found
description: Whether an IP address was found in the database
type: boolean
required: false

triggers:
new_message_received:
title: New Message Received
description: Trigger when a new message is received
input:
address:
title: IP Address
description: IPv4 or IPv6 address
type: string
required: true
example: 198.51.100.100
output:
ipAddress:
title: IP Address
description: The IP address
type: string
required: false
49 changes: 44 additions & 5 deletions unit_test/test_validate_plugin/test_validate_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from icon_validator.rules.plugin_validators.cloud_ready_validator import CloudReadyValidator
from icon_validator.rules.plugin_validators.confidential_validator import ConfidentialValidator
from icon_validator.rules.plugin_validators.enable_cache_validator import EnableCacheValidator
from icon_validator.rules.plugin_validators.description_validator import DescriptionValidator
from icon_validator.rules.plugin_validators.encoding_validator import EncodingValidator
from icon_validator.rules.plugin_validators.example_input_validator import ExampleInputValidator
Expand Down Expand Up @@ -475,20 +476,58 @@ def test_cloud_ready_validator_should_success_latest_version_string(self):
except ValidationException:
raise Exception("We do not expect the supplied docker string to fail. We should support ':latest'")

def test_cloud_ready_validator_triggers_enable_cache_false_should_fail(self):
def test_cloud_ready_validator_with_triggers_should_succeed(self):
# Cloud ready plugin with triggers and enable_cache: true should pass
directory_to_test = "plugin_examples/good_plugin_cloud_ready_with_triggers"
file_to_test = "plugin.spec.yaml"
result = validate(directory_to_test, file_to_test, False, True, [CloudReadyValidator()])
self.assertEqual(result, 0)

def test_enable_cache_validator_cloud_triggers_no_cache_should_fail(self):
# Cloud ready plugin with triggers must have enable_cache: true
directory_to_test = "plugin_examples/bad_plugin_cloud_ready_triggers_enable_cache_false"
file_to_test = "plugin.spec.yaml"
result = validate(directory_to_test, file_to_test, False, True, [CloudReadyValidator()])
result = validate(directory_to_test, file_to_test, False, True, [EnableCacheValidator()])
self.assertEqual(result, 1)

def test_cloud_ready_validator_with_triggers_should_succeed(self):
# Cloud ready plugin with triggers and enable_cache: true should pass
def test_enable_cache_validator_orchestrator_triggers_no_cache_should_fail(self):
# Orchestrator-only plugin (not cloud ready) with triggers must also have enable_cache: true
directory_to_test = "plugin_examples/bad_plugin_triggers_no_enable_cache"
file_to_test = "plugin.spec.yaml"
result = validate(directory_to_test, file_to_test, False, True, [EnableCacheValidator()])
self.assertEqual(result, 1)

def test_enable_cache_validator_triggers_with_cache_should_succeed(self):
# A plugin with triggers and enable_cache: true should pass (cloud_ready is ignored here)
directory_to_test = "plugin_examples/good_plugin_cloud_ready_with_triggers"
file_to_test = "plugin.spec.yaml"
result = validate(directory_to_test, file_to_test, False, True, [CloudReadyValidator()])
result = validate(directory_to_test, file_to_test, False, True, [EnableCacheValidator()])
self.assertEqual(result, 0)

def test_enable_cache_validator_no_triggers_should_succeed(self):
# A plugin without triggers is not required to enable caching
directory_to_test = "plugin_examples/good_plugin"
file_to_test = "plugin.spec.yaml"
result = validate(directory_to_test, file_to_test, False, True, [EnableCacheValidator()])
self.assertEqual(result, 0)

@parameterized.expand([
("triggers_and_cache_true", {"triggers": {"a_trigger": {}}, "enable_cache": True}, False),
("triggers_and_cache_absent", {"triggers": {"a_trigger": {}}}, True),
("triggers_and_cache_false", {"triggers": {"a_trigger": {}}, "enable_cache": False}, True),
("triggers_and_cache_string_false", {"triggers": {"a_trigger": {}}, "enable_cache": "false"}, True),
("triggers_and_cache_string_true", {"triggers": {"a_trigger": {}}, "enable_cache": "true"}, True),
("empty_triggers", {"triggers": {}, "enable_cache": False}, False),
("no_triggers_no_cache", {"enable_cache": False}, False),
("no_triggers_with_cache", {"enable_cache": True}, False),
])
def test_enable_cache_validator_logic(self, _name: str, plugin_spec: dict, should_raise: bool):
if should_raise:
with self.assertRaises(ValidationException):
EnableCacheValidator.validate_enable_cache_with_triggers(plugin_spec)
else:
EnableCacheValidator.validate_enable_cache_with_triggers(plugin_spec)

def test_acronym_validator_should_success(self):
# example workflow in plugin_examples directory. Run tests with these files
directory_to_test = "plugin_examples/good_plugin"
Expand Down
Loading