diff --git a/.pylintrc b/.pylintrc index cd1ca3bd08..2cb0a35b26 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ -# Pylint 3.0.x configuration file +# Pylint 3.2.x configuration file # # This file is generated by l2tdevtools update-dependencies.py, any dependency # related changes should be made in dependencies.ini. @@ -29,6 +29,7 @@ clear-cache-post-run=no # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. +# extension-pkg-allow-list= extension-pkg-allow-list=pybde,pycaes,pycreg,pyesedb,pyevt,pyevtx,pyewf,pyfcrypto,pyfsapfs,pyfsext,pyfsfat,pyfshfs,pyfsntfs,pyfsxfs,pyfvde,pyfwnt,pyfwsi,pylnk,pyluksde,pymodi,pymsiecf,pyolecf,pyphdi,pyqcow,pyregf,pyscca,pysigscan,pysmdev,pysmraw,pytsk3,pyvhdi,pyvmdk,pyvsapm,pyvsgpt,pyvshadow,pyvslvm,xattr,yara,zstd # A comma-separated list of package or module names from where C extensions may @@ -63,10 +64,11 @@ ignore-paths= # Emacs file locks ignore-patterns=^\.# -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. +# List of module names for which member attributes should not be checked and +# will not be imported (useful for modules/projects where namespaces are +# manipulated during runtime and thus existing member attributes cannot be +# deduced by static analysis). It supports qualified module names, as well as +# Unix pattern matching. ignored-modules= # Python code to execute, usually for sys.path manipulation such as @@ -85,11 +87,16 @@ limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. +# load-plugins= load-plugins=pylint.extensions.docparams # Pickle collected data for later comparisons. persistent=yes +# Resolve imports to .pyi stubs if available. May reduce no-member messages and +# increase not-an-iterable messages. +prefer-stubs=no + # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. py-version=3.12 @@ -440,7 +447,6 @@ confidence=HIGH, # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". - disable=assignment-from-none, bad-inline-option, consider-using-f-string, @@ -478,6 +484,7 @@ disable=assignment-from-none, # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. +# enable= enable=c-extension-no-member @@ -510,6 +517,11 @@ max-nested-blocks=5 # printed. never-returning-functions=sys.exit,argparse.parse_error +# Let 'consider-using-join' be raised when the separator to join on would be +# non-empty (resulting in expected fixes of the type: ``"- " + " - +# ".join(items)``) +suggest-join-with-non-empty-separator=yes + [REPORTS] diff --git a/plaso/analysis/hash_tagging.py b/plaso/analysis/hash_tagging.py index a3205ea9d7..2f49519a84 100644 --- a/plaso/analysis/hash_tagging.py +++ b/plaso/analysis/hash_tagging.py @@ -106,6 +106,8 @@ def _MakeRequestAndDecodeJSON(self, url, method, **kwargs): if method_upper not in ('GET', 'POST'): raise ValueError('Method {0:s} is not supported') + response = None + try: if method_upper == 'GET': response = requests.get(url, timeout=self._REQUEST_TIMEOUT, **kwargs) diff --git a/plaso/cli/image_export_tool.py b/plaso/cli/image_export_tool.py index b1b58442f1..a7589d6541 100644 --- a/plaso/cli/image_export_tool.py +++ b/plaso/cli/image_export_tool.py @@ -218,6 +218,9 @@ def _ExtractDataStream( if not destination_path.endswith(os.path.sep): destination_path = destination_path + os.path.sep + # TODO: refactor + path = None + target_path = os.path.join(target_directory, target_filename) if target_path.startswith(destination_path): path = target_path[len(destination_path):] diff --git a/plaso/cli/tool_options.py b/plaso/cli/tool_options.py index be0eb96c1c..08c192f5da 100644 --- a/plaso/cli/tool_options.py +++ b/plaso/cli/tool_options.py @@ -53,8 +53,7 @@ def ListAnalysisPlugins(self): column_width = 10 for name, _, _ in analysis_plugin_info: - if len(name) > column_width: - column_width = len(name) + column_width = max(column_width, len(name)) table_view = views.ViewsFactory.GetTableView( self._views_format_type, column_names=['Name', 'Description'], diff --git a/plaso/cli/tools.py b/plaso/cli/tools.py index 0dd5c3a5ce..36e7be1b6e 100644 --- a/plaso/cli/tools.py +++ b/plaso/cli/tools.py @@ -334,8 +334,7 @@ def ListTimeZones(self): """Lists the time zones.""" max_length = 0 for time_zone_name in pytz.all_timezones: - if len(time_zone_name) > max_length: - max_length = len(time_zone_name) + max_length = max(max_length, len(time_zone_name)) utc_date_time = datetime.datetime.utcnow() diff --git a/plaso/cli/views.py b/plaso/cli/views.py index 93d15ad4a3..9645ab14ed 100644 --- a/plaso/cli/views.py +++ b/plaso/cli/views.py @@ -164,9 +164,7 @@ def AddRow(self, values): """ super(CLITableView, self).AddRow(values) - value_length = len(values[0]) - if value_length > self._column_width: - self._column_width = value_length + self._column_width = max(self._column_width, len(values[0])) def Write(self, output_writer): """Writes the table to the output writer. diff --git a/plaso/engine/extractors.py b/plaso/engine/extractors.py index e0e157b578..e49e1dc071 100644 --- a/plaso/engine/extractors.py +++ b/plaso/engine/extractors.py @@ -391,14 +391,12 @@ def _ExtractPathSpecsFromDirectory(self, file_entry, depth=0): if sub_file_entry.IsDirectory(): sub_directories.append(sub_file_entry) - for path_spec in self._ExtractPathSpecsFromFile(sub_file_entry): - yield path_spec + yield from self._ExtractPathSpecsFromFile(sub_file_entry) for sub_file_entry in sub_directories: try: - for path_spec in self._ExtractPathSpecsFromDirectory( - sub_file_entry, depth=depth + 1): - yield path_spec + yield from self._ExtractPathSpecsFromDirectory( + sub_file_entry, depth=depth + 1) except ( IOError, dfvfs_errors.AccessError, dfvfs_errors.BackEndError, @@ -463,15 +461,12 @@ def _ExtractPathSpecsFromFileSystem( if find_specs: searcher = file_system_searcher.FileSystemSearcher( file_system, path_spec) - for extracted_path_spec in searcher.Find(find_specs=find_specs): - yield extracted_path_spec + yield from searcher.Find(find_specs=find_specs) elif recurse_file_system: file_entry = file_system.GetFileEntryByPathSpec(path_spec) if file_entry: - for extracted_path_spec in self._ExtractPathSpecsFromDirectory( - file_entry): - yield extracted_path_spec + yield from self._ExtractPathSpecsFromDirectory(file_entry) else: yield path_spec @@ -535,8 +530,7 @@ def ExtractPathSpecs( yield path_spec else: - for extracted_path_spec in self._ExtractPathSpecsFromFileSystem( + yield from self._ExtractPathSpecsFromFileSystem( path_spec, find_specs=find_specs, recurse_file_system=recurse_file_system, - resolver_context=resolver_context): - yield extracted_path_spec + resolver_context=resolver_context) diff --git a/plaso/engine/processing_status.py b/plaso/engine/processing_status.py index 99c3558b39..2c16c7b4ed 100644 --- a/plaso/engine/processing_status.py +++ b/plaso/engine/processing_status.py @@ -340,6 +340,7 @@ def _UpdateProcessStatus( process_status.pid = pid process_status.status = status + # pylint: disable=consider-using-min-builtin if used_memory > 0: process_status.used_memory = used_memory diff --git a/plaso/engine/yaml_timeliner_file.py b/plaso/engine/yaml_timeliner_file.py index 863404e005..1a89d25bda 100644 --- a/plaso/engine/yaml_timeliner_file.py +++ b/plaso/engine/yaml_timeliner_file.py @@ -117,5 +117,4 @@ def ReadFromFile(self, path): TimelinerDefinition: a timeliner definition. """ with open(path, 'r', encoding='utf-8') as file_object: - for yaml_definition in self._ReadFromFileObject(file_object): - yield yaml_definition + yield from self._ReadFromFileObject(file_object) diff --git a/plaso/formatters/yaml_formatters_file.py b/plaso/formatters/yaml_formatters_file.py index f1093a79e3..32bc4aaf1b 100644 --- a/plaso/formatters/yaml_formatters_file.py +++ b/plaso/formatters/yaml_formatters_file.py @@ -232,6 +232,8 @@ def _ReadFormatterDefinition(self, formatter_definition_values): 'Invalid event formatter definition: {0:s} missing source.'.format( data_type)) + formatter = None + if formatter_type == 'basic': formatter = interface.BasicEventFormatter( data_type=data_type, format_string=message, @@ -286,5 +288,4 @@ def ReadFromFile(self, path): EventFormatter: an event formatter. """ with open(path, 'r', encoding='utf-8') as file_object: - for yaml_definition in self._ReadFromFileObject(file_object): - yield yaml_definition + yield from self._ReadFromFileObject(file_object) diff --git a/plaso/lib/bufferlib.py b/plaso/lib/bufferlib.py index 915424c4f1..c6e8ef6ee8 100644 --- a/plaso/lib/bufferlib.py +++ b/plaso/lib/bufferlib.py @@ -55,8 +55,7 @@ def Clear(self): def Flush(self): """Returns a generator for all items and clear the buffer.""" - for item in self: - yield item + yield from self self.Clear() def GetCurrent(self): diff --git a/plaso/multi_process/merge_helpers.py b/plaso/multi_process/merge_helpers.py index cfc43115fc..f9cf7ec522 100644 --- a/plaso/multi_process/merge_helpers.py +++ b/plaso/multi_process/merge_helpers.py @@ -43,9 +43,7 @@ def _GetAttributeContainers(self, task_storage_reader): AttributeContainer: attribute container. """ for container_type in self._CONTAINER_TYPES: - for container in task_storage_reader.GetAttributeContainers( - container_type): - yield container + yield from task_storage_reader.GetAttributeContainers(container_type) self.fully_merged = True diff --git a/plaso/parsers/bsm.py b/plaso/parsers/bsm.py index d96a5e5478..2ff9dfafd2 100644 --- a/plaso/parsers/bsm.py +++ b/plaso/parsers/bsm.py @@ -438,10 +438,14 @@ def _FormatInAddrExToken(self, token_data): dict[str, str]: token values. """ protocol = self._NETWORK_PROTOCOLS.get(token_data.net_type, 'UNKNOWN') + if token_data.net_type == 4: ip_address = self._FormatPackedIPv6Address(token_data.ip_address[:4]) elif token_data.net_type == 16: ip_address = self._FormatPackedIPv6Address(token_data.ip_address) + else: + ip_address = None + return { 'protocols': protocol, 'net_type': token_data.net_type, diff --git a/plaso/parsers/cookie_plugins/ganalytics.py b/plaso/parsers/cookie_plugins/ganalytics.py index 1506f6d41e..03867819e6 100644 --- a/plaso/parsers/cookie_plugins/ganalytics.py +++ b/plaso/parsers/cookie_plugins/ganalytics.py @@ -177,6 +177,11 @@ def _ParseCookieData( number_of_sessions = self._ParseIntegerValue(fields[5]) + else: + domain_hash = None + number_of_sessions = None + visitor_identifier = None + event_data = GoogleAnalyticsUtmaEventData() event_data.cookie_name = self.COOKIE_NAME event_data.domain_hash = domain_hash @@ -245,6 +250,11 @@ def _ParseCookieData( else: date_time = self._ParsePosixTime(fields[3]) + else: + date_time = None + domain_hash = None + number_of_pages_viewed = None + event_data = GoogleAnalyticsUtmbEventData() event_data.cookie_name = self.COOKIE_NAME event_data.domain_hash = domain_hash @@ -366,6 +376,12 @@ def _ParseCookieData( key, _, value = variable.partition('=') extra_attributes[key] = urlparse.unquote(value) + else: + date_time = None + domain_hash = None + number_of_sessions = None + number_of_sources = None + event_data = GoogleAnalyticsUtmzEventData() event_data.cookie_name = self.COOKIE_NAME event_data.domain_hash = domain_hash diff --git a/plaso/parsers/esedb_plugins/srum.py b/plaso/parsers/esedb_plugins/srum.py index 4a4f5780ce..1898dafe1f 100644 --- a/plaso/parsers/esedb_plugins/srum.py +++ b/plaso/parsers/esedb_plugins/srum.py @@ -238,6 +238,8 @@ def _ConvertValueBinaryDataToFloatingPointValue(self, value): floating_point_map = self._GetDataTypeMap('float32le') elif value_length == 8: floating_point_map = self._GetDataTypeMap('float64le') + else: + floating_point_map = None try: return self._ReadStructureFromByteStream(value, 0, floating_point_map) diff --git a/plaso/parsers/java_idx.py b/plaso/parsers/java_idx.py index 4d3620068d..417448836f 100644 --- a/plaso/parsers/java_idx.py +++ b/plaso/parsers/java_idx.py @@ -100,6 +100,8 @@ def ParseFileObject(self, parser_mediator, file_object): section1_map = self._GetDataTypeMap('java_idx_603_section1') elif file_header.format_version == 605: section1_map = self._GetDataTypeMap('java_idx_605_section1') + else: + section1_map = None try: section1, data_size = self._ReadStructureFromFileObject( @@ -116,6 +118,8 @@ def ParseFileObject(self, parser_mediator, file_object): elif file_header.format_version in (603, 604, 605): file_offset = 128 section2_map = self._GetDataTypeMap('java_idx_603_section2') + else: + section2_map = None try: section2, data_size = self._ReadStructureFromFileObject( diff --git a/plaso/parsers/plist_plugins/interface.py b/plaso/parsers/plist_plugins/interface.py index 52ecbdf871..50a330aafb 100644 --- a/plaso/parsers/plist_plugins/interface.py +++ b/plaso/parsers/plist_plugins/interface.py @@ -803,9 +803,8 @@ def _RecurseKey(self, plist_item, depth=15, key_path=''): elif isinstance(plist_item, (list, tuple)): for sub_plist_item in plist_item: - for subkey_values in self._RecurseKey( - sub_plist_item, depth=depth - 1, key_path=key_path): - yield subkey_values + yield from self._RecurseKey( + sub_plist_item, depth=depth - 1, key_path=key_path) elif hasattr(plist_item, 'items'): for subkey_name, value in plist_item.items(): @@ -818,10 +817,9 @@ def _RecurseKey(self, plist_item, depth=15, key_path=''): for sub_plist_item in value: if isinstance(sub_plist_item, dict): - subkey_path = '{0:s}/{1:s}'.format(key_path, subkey_name) - for subkey_values in self._RecurseKey( - sub_plist_item, depth=depth - 1, key_path=subkey_path): - yield subkey_values + subkey_path = '/'.join([key_path, subkey_name]) + yield from self._RecurseKey( + sub_plist_item, depth=depth - 1, key_path=subkey_path) # pylint: disable=arguments-differ @abc.abstractmethod diff --git a/plaso/parsers/spotlight_storedb.py b/plaso/parsers/spotlight_storedb.py index a71bf51d7d..5cd16b84fd 100644 --- a/plaso/parsers/spotlight_storedb.py +++ b/plaso/parsers/spotlight_storedb.py @@ -1097,6 +1097,9 @@ def _ReadMetadataAttributePageValues( data_type_map = self._GetDataTypeMap( 'spotlight_store_db_property_value21') + else: + data_type_map = None + page_data_offset = 12 page_data_size = page_header.used_page_size - 20 page_value_index = 0 @@ -1242,12 +1245,11 @@ def _ReadMetadataAttributeStreamsMap( stream_values = self._ReadStreamsMap(parent_file_entry, streams_map_number) if streams_map_number == 1: - data_type_map = self._GetDataTypeMap( - 'spotlight_metadata_attribute_type') - + data_type_map = self._GetDataTypeMap('spotlight_metadata_attribute_type') elif streams_map_number == 2: - data_type_map = self._GetDataTypeMap( - 'spotlight_metadata_attribute_value') + data_type_map = self._GetDataTypeMap('spotlight_metadata_attribute_value') + else: + data_type_map = None for index, stream_value in enumerate(stream_values): if index == 0: diff --git a/plaso/parsers/text_plugins/syslog.py b/plaso/parsers/text_plugins/syslog.py index 4e7724d9dc..9de086daa6 100644 --- a/plaso/parsers/text_plugins/syslog.py +++ b/plaso/parsers/text_plugins/syslog.py @@ -264,15 +264,14 @@ def _ParseSshdMessageBody(self, body): key = keys[0] structure = structure[0] - if key not in ('failed_connection', 'login', 'opened_connection'): - return None - if key == 'failed_connection': event_data = SyslogSSHFailedConnectionEventData() elif key == 'login': event_data = SyslogSSHLoginEventData() elif key == 'opened_connection': event_data = SyslogSSHOpenedConnectionEventData() + else: + return None event_data.authentication_method = structure.get( 'authentication_method', None) diff --git a/plaso/parsers/windefender_history.py b/plaso/parsers/windefender_history.py index 0a899c7112..a9fc4643a3 100644 --- a/plaso/parsers/windefender_history.py +++ b/plaso/parsers/windefender_history.py @@ -121,8 +121,8 @@ def _ReadThreatTrackingData(self, threat_tracking_data, file_offset): else: header = self._ReadThreatTrackingHeader(threat_tracking_data) - values_data_offset = header.header_size + 4 - values_data_end_offset = header.total_data_size + values_data_offset = header.header_size + 4 + values_data_end_offset = header.total_data_size while values_data_offset < values_data_end_offset: threat_value, data_size = self._ReadThreatTrackingValue( diff --git a/plaso/parsers/winreg_plugins/appcompatcache.py b/plaso/parsers/winreg_plugins/appcompatcache.py index 870fc25842..aaa3c1c6d6 100644 --- a/plaso/parsers/winreg_plugins/appcompatcache.py +++ b/plaso/parsers/winreg_plugins/appcompatcache.py @@ -333,6 +333,7 @@ def _ParseCachedEntry2003(self, value_data, cached_entry_offset): maximum_path_size = cached_entry.maximum_path_size path_offset = cached_entry.path_offset + path = None if path_offset > 0 and path_size > 0: path_size += path_offset maximum_path_size += path_offset @@ -379,6 +380,7 @@ def _ParseCachedEntryVista(self, value_data, cached_entry_offset): maximum_path_size = cached_entry.maximum_path_size path_offset = cached_entry.path_offset + path = None if path_offset > 0 and path_size > 0: path_size += path_offset maximum_path_size += path_offset @@ -426,6 +428,7 @@ def _ParseCachedEntry7(self, value_data, cached_entry_offset): maximum_path_size = cached_entry.maximum_path_size path_offset = cached_entry.path_offset + path = None if path_offset > 0 and path_size > 0: path_size += path_offset maximum_path_size += path_offset @@ -483,6 +486,8 @@ def _ParseCachedEntry8(self, value_data, cached_entry_offset): data_type_map_name = 'appcompatcache_cached_entry_body_8_0' elif cached_entry.signature == self._CACHED_ENTRY_SIGNATURE_8_1: data_type_map_name = 'appcompatcache_cached_entry_body_8_1' + else: + data_type_map_name = None data_type_map = self._GetDataTypeMap(data_type_map_name) context = dtfabric_data_maps.DataTypeMapContext() diff --git a/plaso/parsers/winreg_plugins/programscache.py b/plaso/parsers/winreg_plugins/programscache.py index d8557dcce1..0f05b4d745 100644 --- a/plaso/parsers/winreg_plugins/programscache.py +++ b/plaso/parsers/winreg_plugins/programscache.py @@ -87,11 +87,6 @@ def _ParseValueData(self, parser_mediator, registry_key, registry_value): f'unable to parse header value with error: {exception!s}') return - if header.format_version not in (1, 9, 12, 19): - parser_mediator.ProduceExtractionWarning( - f'unsupported format version: {header.format_version:d}') - return - known_folder_identifier = None if header.format_version == 1: value_data_offset = 8 @@ -103,6 +98,11 @@ def _ParseValueData(self, parser_mediator, registry_key, registry_value): known_folder_identifier = uuid.UUID(bytes_le=value_data[4:20]) value_data_offset = 20 + else: + parser_mediator.ProduceExtractionWarning( + f'unsupported format version: {header.format_version:d}') + return + entry_header_map = self._GetDataTypeMap('programscache_entry_header') entry_footer_map = self._GetDataTypeMap('programscache_entry_footer') diff --git a/tox.ini b/tox.ini index 2e0c2cdf8d..b94b82fab7 100644 --- a/tox.ini +++ b/tox.ini @@ -53,7 +53,7 @@ deps = -rrequirements.txt -rtest_requirements.txt docformatter - pylint >= 3.0.0, < 3.1.0 + pylint >= 3.2.0, < 3.3.0 setuptools yamllint >= 1.26.0 commands = diff --git a/utils/plot_cpu_usage.py b/utils/plot_cpu_usage.py index 6af53d9707..cb4f56a35d 100755 --- a/utils/plot_cpu_usage.py +++ b/utils/plot_cpu_usage.py @@ -45,11 +45,7 @@ def Main(): options = argument_parser.parse_args() if not os.path.isdir(options.profile_path): - print('No such directory: {0:s}'.format(options.profile_path)) - return False - - if options.profiler not in ('analyzers', 'parsers', 'processing'): - print('Unsupported profiler: {0:s}'.format(options.profiler)) + print(f'No such directory: {options.profile_path:s}') return False processes = [] @@ -68,16 +64,18 @@ def Main(): name_prefix = 'processing' name_suffix = 'processing' + else: + print(f'Unsupported profiler: {options.profiler:s}') + return False + names = ['time', 'name', 'cpu'] - glob_pattern = '{0:s}-*-{1:s}.csv.gz'.format(name_prefix, name_suffix) - glob_expression = os.path.join(options.profile_path, glob_pattern) + glob_expression = os.path.join( + options.profile_path, f'{name_prefix:s}-*-{name_suffix:s}.csv.gz') for csv_file_name in glob.glob(glob_expression): process_name = os.path.basename(csv_file_name) - process_name_prefix = '{0:s}-'.format(name_prefix) - process_name_suffix = '-{0:s}.csv.gz'.format(name_suffix) - process_name = process_name.replace(process_name_prefix, '').replace( - process_name_suffix, '') + process_name = process_name.replace(f'{name_prefix:s}-', '') + process_name = process_name.replace(f'-{name_suffix:s}.csv.gz', '') if processes and process_name not in processes: continue