1515# You should have received a copy of the GNU General Public License
1616# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
18+ """
19+ This package contains all linter plugins.
20+ Plugins are discovered dynamically at runtime. To add a new plugin:
21+ 1. Create a new .py file in this directory.
22+ 2. Define a class that inherits from FilePlugin or FilesPlugin.
23+
24+ The discovery logic only searches the top-level of this package.
25+ Nested sub-packages are currently not supported for plugin discovery,
26+ which excludes plugins in subfolders as a byproduct. However, the
27+ intended way to disable a plugin is to add `is_disabled = True` to its class.
28+ """
29+
1830import difflib
19- from typing import Iterable , List
31+ import importlib
32+ import pkgutil
33+ from typing import Iterable , Type
2034
2135from troubadix .plugin import FilePlugin , FilesPlugin , Plugin
22- from troubadix .plugins .spaces_before_dots import CheckSpacesBeforeDots
23-
24- from .badwords import CheckBadwords
25- from .copyright_text import CheckCopyrightText
26- from .copyright_year import CheckCopyrightYear
27- from .creation_date import CheckCreationDate
28- from .cve_format import CheckCVEFormat
29- from .cvss_format import CheckCVSSFormat
30- from .dependencies import CheckDependencies
31- from .dependency_category_order import CheckDependencyCategoryOrder
32- from .deprecated_dependency import CheckDeprecatedDependency
33- from .deprecated_functions import CheckDeprecatedFunctions
34- from .double_end_points import CheckDoubleEndPoints
35- from .duplicate_oid import CheckDuplicateOID
36- from .duplicated_script_tags import CheckDuplicatedScriptTags
37- from .encoding import CheckEncoding
38- from .forking_nasl_functions import CheckForkingNaslFunctions
39- from .get_kb_on_services import CheckGetKBOnServices
40- from .grammar import CheckGrammar
41- from .http_links_in_tags import CheckHttpLinksInTags
42- from .if_statement_syntax import CheckIfStatementSyntax
43- from .illegal_characters import CheckIllegalCharacters
44- from .infos_array_keys import CheckInfosArrayKeys
45- from .log_messages import CheckLogMessages
46- from .malformed_dependencies import CheckMalformedDependencies
47- from .misplaced_compare_in_if import CheckMisplacedCompareInIf
48- from .missing_desc_exit import CheckMissingDescExit
49- from .missing_tag_solution import CheckMissingTagSolution
50- from .multiple_re_parameters import CheckMultipleReParameters
51- from .newlines import CheckNewlines
52- from .overlong_description_lines import CheckOverlongDescriptionLines
53- from .overlong_script_tags import CheckOverlongScriptTags
54- from .prod_svc_detect_in_vulnvt import CheckProdSvcDetectInVulnvt
55- from .qod import CheckQod
56- from .reporting_consistency import CheckReportingConsistency
57- from .script_add_preference_id import CheckScriptAddPreferenceId
58- from .script_add_preference_type import CheckScriptAddPreferenceType
59- from .script_calls_empty_values import CheckScriptCallsEmptyValues
60- from .script_calls_recommended import CheckScriptCallsRecommended
61- from .script_category import CheckScriptCategory
62- from .script_copyright import CheckScriptCopyright
63- from .script_family import CheckScriptFamily
64- from .script_tag_form import CheckScriptTagForm
65- from .script_tag_whitespaces import CheckScriptTagWhitespaces
66- from .script_tags_mandatory import CheckScriptTagsMandatory
67- from .script_version_and_last_modification_tags import (
68- CheckScriptVersionAndLastModificationTags ,
69- )
70- from .script_xref_form import CheckScriptXrefForm
71- from .script_xref_url import CheckScriptXrefUrl
72- from .security_messages import CheckSecurityMessages
73- from .set_get_kb_calls import CheckWrongSetGetKBCalls
74- from .severity_date import CheckSeverityDate
75- from .severity_format import CheckSeverityFormat
76- from .severity_origin import CheckSeverityOrigin
77- from .solution_text import CheckSolutionText
78- from .solution_type import CheckSolutionType
79- from .spaces_in_filename import CheckSpacesInFilename
80- from .spelling import CheckSpelling
81- from .tabs import CheckTabs
82- from .todo_tbd import CheckTodoTbd
83- from .trailing_spaces_tabs import CheckTrailingSpacesTabs
84- from .using_display import CheckUsingDisplay
85- from .valid_oid import CheckValidOID
86- from .valid_script_tag_names import CheckValidScriptTagNames
87- from .variable_assigned_in_if import CheckVariableAssignedInIf
88- from .variable_redefinition_in_foreach import CheckVariableRedefinitionInForeach
89- from .vt_file_permissions import CheckVTFilePermissions
90- from .vt_placement import CheckVTPlacement
91-
92- # plugins checking single files
93- _FILE_PLUGINS = [
94- CheckBadwords ,
95- CheckCopyrightText ,
96- CheckCopyrightYear ,
97- CheckCreationDate ,
98- CheckCVEFormat ,
99- CheckCVSSFormat ,
100- CheckDependencies ,
101- CheckDependencyCategoryOrder ,
102- CheckDeprecatedDependency ,
103- CheckDeprecatedFunctions ,
104- CheckDoubleEndPoints ,
105- CheckDuplicatedScriptTags ,
106- CheckEncoding ,
107- CheckForkingNaslFunctions ,
108- CheckGetKBOnServices ,
109- CheckGrammar ,
110- CheckHttpLinksInTags ,
111- CheckIllegalCharacters ,
112- CheckLogMessages ,
113- CheckMalformedDependencies ,
114- CheckMisplacedCompareInIf ,
115- CheckMissingDescExit ,
116- CheckMissingTagSolution ,
117- CheckMultipleReParameters ,
118- CheckNewlines ,
119- CheckOverlongDescriptionLines ,
120- CheckOverlongScriptTags ,
121- CheckProdSvcDetectInVulnvt ,
122- CheckQod ,
123- CheckReportingConsistency ,
124- CheckScriptAddPreferenceType ,
125- CheckScriptCallsEmptyValues ,
126- CheckScriptCallsRecommended ,
127- CheckScriptCategory ,
128- CheckScriptCopyright ,
129- CheckScriptFamily ,
130- CheckScriptTagForm ,
131- CheckScriptTagsMandatory ,
132- CheckScriptTagWhitespaces ,
133- CheckScriptVersionAndLastModificationTags ,
134- CheckScriptXrefForm ,
135- CheckScriptXrefUrl ,
136- CheckSecurityMessages ,
137- CheckSeverityDate ,
138- CheckSeverityFormat ,
139- CheckSeverityOrigin ,
140- CheckSolutionText ,
141- CheckSolutionType ,
142- CheckSpacesInFilename ,
143- CheckTabs ,
144- CheckTodoTbd ,
145- CheckTrailingSpacesTabs ,
146- CheckUsingDisplay ,
147- CheckValidOID ,
148- CheckValidScriptTagNames ,
149- CheckVariableAssignedInIf ,
150- CheckVariableRedefinitionInForeach ,
151- CheckVTFilePermissions ,
152- CheckVTPlacement ,
153- CheckWrongSetGetKBCalls ,
154- CheckSpacesBeforeDots ,
155- CheckIfStatementSyntax ,
156- CheckScriptAddPreferenceId ,
157- CheckInfosArrayKeys ,
158- ]
159-
160- # plugins checking all files
161- _FILES_PLUGINS = [
162- CheckDuplicateOID ,
163- CheckSpelling ,
164- ]
36+
37+
38+ def _get_all_subclasses (cls : Type ) -> Iterable [Type ]:
39+ """Recursively find all subclasses of a given class."""
40+ for subclass in cls .__subclasses__ ():
41+ yield subclass
42+ yield from _get_all_subclasses (subclass )
43+
44+
45+ def _discover_plugins () -> tuple [list [Type [FilePlugin ]], list [Type [FilesPlugin ]]]:
46+ """
47+ Dynamically discover all concrete plugin classes.
48+
49+ A tuple containing (list of file plugins, list of files plugins).
50+ """
51+ for _loader , module_name , _is_pkg in pkgutil .iter_modules (__path__ ):
52+ importlib .import_module (f"{ __name__ } .{ module_name } " )
53+
54+ def _is_valid_plugin (cls : Type ) -> bool :
55+ return cls .__module__ .startswith (__name__ ) and not getattr (cls , "is_disabled" , False )
56+
57+ # Only include plugins defined in this package and that are not disabled.
58+ # excludes the plugins baseclasses and external plugins.
59+ file_plugins = [cls for cls in _get_all_subclasses (FilePlugin ) if _is_valid_plugin (cls )]
60+ files_plugins = [cls for cls in _get_all_subclasses (FilesPlugin ) if _is_valid_plugin (cls )]
61+
62+ return (
63+ sorted (file_plugins , key = lambda x : x .__name__ ),
64+ sorted (files_plugins , key = lambda x : x .__name__ ),
65+ )
66+
67+
68+ _FILE_PLUGINS , _FILES_PLUGINS = _discover_plugins ()
16569
16670
16771class Plugins :
@@ -183,8 +87,8 @@ def __iter__(self) -> Iterable[Plugin]:
18387class StandardPlugins (Plugins ):
18488 def __init__ (
18589 self ,
186- excluded_plugins : List [str ] = None ,
187- included_plugins : List [str ] = None ,
90+ excluded_plugins : list [str ] = None ,
91+ included_plugins : list [str ] = None ,
18892 ) -> None :
18993 file_plugins = _FILE_PLUGINS
19094 files_plugins = _FILES_PLUGINS
@@ -204,15 +108,15 @@ def __init__(
204108 super ().__init__ (file_plugins = file_plugins , files_plugins = files_plugins )
205109
206110 @staticmethod
207- def _exclude_plugins (excluded : Iterable [str ], plugins : Iterable [Plugin ]) -> List [Plugin ]:
111+ def _exclude_plugins (excluded : Iterable [str ], plugins : Iterable [Plugin ]) -> list [Plugin ]:
208112 return [
209113 plugin
210114 for plugin in plugins
211115 if plugin .__name__ not in excluded and plugin .name not in excluded
212116 ]
213117
214118 @staticmethod
215- def _include_plugins (included : Iterable [str ], plugins : Iterable [Plugin ]) -> List [Plugin ]:
119+ def _include_plugins (included : Iterable [str ], plugins : Iterable [Plugin ]) -> list [Plugin ]:
216120 return [
217121 plugin for plugin in plugins if plugin .__name__ in included or plugin .name in included
218122 ]
0 commit comments