diff --git a/slither/core/source_mapping/source_mapping.py b/slither/core/source_mapping/source_mapping.py index fbb7279a2..7a6cdae41 100644 --- a/slither/core/source_mapping/source_mapping.py +++ b/slither/core/source_mapping/source_mapping.py @@ -84,8 +84,8 @@ def content(self) -> str: assert self.compilation_unit return ( self.compilation_unit.core.source_code[self.filename.absolute] - .encode("utf8")[self.start : self.end] - .decode("utf8") + .encode("utf8")[self.start : self.end] + .decode("utf8") ) @property diff --git a/slither/tools/documentation/__main__.py b/slither/tools/documentation/__main__.py index 417e4f09a..dccdabba4 100644 --- a/slither/tools/documentation/__main__.py +++ b/slither/tools/documentation/__main__.py @@ -156,7 +156,9 @@ def _handle_function( prompt = "Create a natpsec documentation for this solidity code with only notice and dev.\n" srcmap = function.source_mapping src = function.compilation_unit.core.source_code[srcmap.filename.absolute] - first_char_index = len(src.encode("utf8")[:srcmap.start].decode("utf8")) # convert byte offset to char offset + first_char_index = len( + src.encode("utf8")[: srcmap.start].decode("utf8") + ) # convert byte offset to char offset prev_char = src[first_char_index - 1] prompt += srcmap.content @@ -201,7 +203,9 @@ def _handle_function( if not answer_processed: return overwrite - create_patch(all_patches, srcmap.filename.absolute, srcmap.start, srcmap.start, "", answer_processed) + create_patch( + all_patches, srcmap.filename.absolute, srcmap.start, srcmap.start, "", answer_processed + ) return overwrite diff --git a/slither/tools/flattening/flattening.py b/slither/tools/flattening/flattening.py index 858a83c1f..518e36717 100644 --- a/slither/tools/flattening/flattening.py +++ b/slither/tools/flattening/flattening.py @@ -107,8 +107,9 @@ def _get_source_code( :return: """ src_mapping = contract.source_mapping - # TODO: this needs to be encoded before it gets indexed! - src_bytes = self._compilation_unit.core.source_code[src_mapping.filename.absolute] + src_bytes = self._compilation_unit.core.source_code[src_mapping.filename.absolute].encode( + "utf8" + ) to_patch = [] # interface must use external @@ -123,8 +124,8 @@ def _get_source_code( + f.parameters_src().source_mapping.length ) attributes_end = f.returns_src().source_mapping.start - attributes = src_bytes[attributes_start:attributes_end] - regex = re.search(r"((\sexternal)\s+)|(\sexternal)$|(\)external)$", attributes.decode("utf8")) + attributes = src_bytes[attributes_start:attributes_end].decode("utf8") + regex = re.search(r"((\sexternal)\s+)|(\sexternal)$|(\)external)$", attributes) if regex: to_patch.append( Patch( @@ -133,7 +134,7 @@ def _get_source_code( ) ) else: - raise SlitherException(f"External keyword not found {f.name} {attributes.decode("utf8")}") + raise SlitherException(f"External keyword not found {f.name} {attributes}") for var in f.parameters: if var.location == "calldata": @@ -157,11 +158,11 @@ def _get_source_code( + f.parameters_src().source_mapping["length"] ) attributes_end = f.returns_src().source_mapping["start"] - attributes = src_bytes[attributes_start:attributes_end] + attributes = src_bytes[attributes_start:attributes_end].decode("utf8") regex = ( - re.search(r"((\sexternal)\s+)|(\sexternal)$|(\)external)$", attributes.decode("utf8")) + re.search(r"((\sexternal)\s+)|(\sexternal)$|(\)external)$", attributes) if visibility == "external" - else re.search(r"((\spublic)\s+)|(\spublic)$|(\)public)$", attributes.decode("utf8")) + else re.search(r"((\spublic)\s+)|(\spublic)$|(\)public)$", attributes) ) if regex: to_patch.append( @@ -174,7 +175,7 @@ def _get_source_code( ) else: raise SlitherException( - f"{visibility} keyword not found {f.name} {attributes.decode("utf8")}" + f"{visibility} keyword not found {f.name} {attributes}" ) if self._private_to_internal: @@ -182,8 +183,8 @@ def _get_source_code( if variable.visibility == "private": attributes_start = variable.source_mapping.start attributes_end = attributes_start + variable.source_mapping.length - attributes = src_bytes[attributes_start:attributes_end] - regex = re.search(r" private ", attributes.decode("utf8")) + attributes = src_bytes[attributes_start:attributes_end].decode("utf8") + regex = re.search(r" private ", attributes) if regex: to_patch.append( Patch( @@ -193,7 +194,7 @@ def _get_source_code( ) else: raise SlitherException( - f"private keyword not found {variable.name} {attributes.decode("utf8")}" + f"private keyword not found {variable.name} {attributes}" ) if self._remove_assert: @@ -210,12 +211,6 @@ def _get_source_code( to_patch.sort(key=lambda x: x.index, reverse=True) - # Note: foundry and solc and everything else return srcmap offsets per-byte - # and it seems the rest of slither operates on bytes also - # it might just be the mutator and flattener that are incorrectly applying offsets directly to strings - # I think I just need to do the following (and similar for mutations) - # content = content.encode("utf8")[start:end].decode("utf8") - content = src_mapping.content.encode("utf8") start = src_mapping.start for patch in to_patch: @@ -223,15 +218,35 @@ def _get_source_code( index = patch.index index = index - start if patch_type == "public_to_external": - content = content[:index].decode("utf8") + "public" + content[index + len("external") :].decode("utf8") + content = ( + content[:index].decode("utf8") + + "public" + + content[index + len("external") :].decode("utf8") + ) elif patch_type == "external_to_internal": - content = content[:index].decode("utf8") + "internal" + content[index + len("external") :].decode("utf8") + content = ( + content[:index].decode("utf8") + + "internal" + + content[index + len("external") :].decode("utf8") + ) elif patch_type == "public_to_internal": - content = content[:index].decode("utf8") + "internal" + content[index + len("public") :].decode("utf8") + content = ( + content[:index].decode("utf8") + + "internal" + + content[index + len("public") :].decode("utf8") + ) elif patch_type == "private_to_internal": - content = content[:index].decode("utf8") + "internal" + content[index + len("private") :].decode("utf8") + content = ( + content[:index].decode("utf8") + + "internal" + + content[index + len("private") :].decode("utf8") + ) elif patch_type == "calldata_to_memory": - content = content[:index].decode("utf8") + "memory" + content[index + len("calldata") :].decode("utf8") + content = ( + content[:index].decode("utf8") + + "memory" + + content[index + len("calldata") :].decode("utf8") + ) else: assert patch_type == "line_removal" content = content[:index].decode("utf8") + " // " + content[index:].decode("utf8")