diff --git a/dissect/database/ese/ese.py b/dissect/database/ese/ese.py index 88ab392..9ad5ecb 100755 --- a/dissect/database/ese/ese.py +++ b/dissect/database/ese/ese.py @@ -10,11 +10,13 @@ from dissect.database.ese.c_ese import c_ese, pgnoFDPMSO, ulDAEMagic from dissect.database.ese.exception import InvalidDatabase from dissect.database.ese.page import Page -from dissect.database.ese.table import Catalog, Table +from dissect.database.ese.table import Catalog if TYPE_CHECKING: from collections.abc import Iterator + from dissect.database.ese.table import Table + class ESE: """ESE database class. diff --git a/dissect/database/ese/lcmapstring.py b/dissect/database/ese/lcmapstring.py index 97024c8..71dccba 100644 --- a/dissect/database/ese/lcmapstring.py +++ b/dissect/database/ese/lcmapstring.py @@ -1,6 +1,7 @@ # Based on Wine source # https://github.com/wine-mirror/wine/blob/master/dlls/kernelbase/locale.c # http://www.flounder.com/localeexplorer.htm +from __future__ import annotations from enum import IntEnum, IntFlag diff --git a/dissect/database/ese/ntds/objects/object.py b/dissect/database/ese/ntds/objects/object.py index 9b7ac5d..6803918 100644 --- a/dissect/database/ese/ntds/objects/object.py +++ b/dissect/database/ese/ntds/objects/object.py @@ -3,7 +3,7 @@ from functools import cached_property from typing import TYPE_CHECKING, Any, ClassVar -from dissect.database.ese.ntds.util import DN, InstanceType, SystemFlags, decode_value +from dissect.database.ese.ntds.util import InstanceType, decode_value if TYPE_CHECKING: from collections.abc import Iterator @@ -11,6 +11,7 @@ from dissect.database.ese.ntds.database import Database from dissect.database.ese.ntds.sd import SecurityDescriptor + from dissect.database.ese.ntds.util import DN, SystemFlags from dissect.database.ese.record import Record diff --git a/dissect/database/ese/ntds/objects/user.py b/dissect/database/ese/ntds/objects/user.py index 4900262..107e84e 100644 --- a/dissect/database/ese/ntds/objects/user.py +++ b/dissect/database/ese/ntds/objects/user.py @@ -3,13 +3,14 @@ from typing import TYPE_CHECKING from dissect.database.ese.ntds.objects.organizationalperson import OrganizationalPerson -from dissect.database.ese.ntds.util import SAMAccountType, UserAccountControl +from dissect.database.ese.ntds.util import UserAccountControl if TYPE_CHECKING: from collections.abc import Iterator from dissect.database.ese.ntds.objects.group import Group from dissect.database.ese.ntds.objects.object import Object + from dissect.database.ese.ntds.util import SAMAccountType class User(OrganizationalPerson): diff --git a/dissect/database/ese/ntds/schema.py b/dissect/database/ese/ntds/schema.py index 0d752de..8a1cd72 100644 --- a/dissect/database/ese/ntds/schema.py +++ b/dissect/database/ese/ntds/schema.py @@ -198,7 +198,6 @@ def load(self, db: Database) -> None: Args: db: The database instance to load the schema from. """ - # Load the schema entries from the DMD object # This _should_ have all the attribute and class schema entries # We used to perform an index search on objectClass (ATTc0, INDEX_00000000), but apparently diff --git a/dissect/database/ese/table.py b/dissect/database/ese/table.py index 03ba67f..bc8a7b1 100644 --- a/dissect/database/ese/table.py +++ b/dissect/database/ese/table.py @@ -14,7 +14,7 @@ from dissect.database.ese.cursor import RawCursor from dissect.database.ese.index import Index from dissect.database.ese.record import Record -from dissect.database.ese.util import COLUMN_TYPE_MAP, ColumnType, RecordValue +from dissect.database.ese.util import COLUMN_TYPE_MAP if TYPE_CHECKING: from collections.abc import Iterator @@ -22,6 +22,7 @@ from dissect.database.ese.cursor import Cursor from dissect.database.ese.ese import ESE from dissect.database.ese.page import Page + from dissect.database.ese.util import ColumnType, RecordValue class Table: diff --git a/dissect/database/ese/tools/sru.py b/dissect/database/ese/tools/sru.py index 54a3cbd..6c716ca 100644 --- a/dissect/database/ese/tools/sru.py +++ b/dissect/database/ese/tools/sru.py @@ -9,11 +9,12 @@ from dissect.util.ts import oatimestamp, wintimestamp from dissect.database.ese.ese import ESE -from dissect.database.ese.record import Record, serialise_record_column_values +from dissect.database.ese.record import serialise_record_column_values if TYPE_CHECKING: from collections.abc import Iterator + from dissect.database.ese.record import Record from dissect.database.ese.table import Table from dissect.database.ese.util import RecordValue diff --git a/dissect/database/ese/tools/ual.py b/dissect/database/ese/tools/ual.py index 9f95fc1..37f7e3a 100644 --- a/dissect/database/ese/tools/ual.py +++ b/dissect/database/ese/tools/ual.py @@ -1,17 +1,22 @@ +from __future__ import annotations + import argparse import datetime import ipaddress import json -from collections.abc import Iterator from pathlib import Path -from typing import BinaryIO +from typing import TYPE_CHECKING, BinaryIO from dissect.util.ts import wintimestamp from dissect.database.ese.ese import ESE -from dissect.database.ese.table import Table from dissect.database.ese.util import RecordValue +if TYPE_CHECKING: + from collections.abc import Iterator + + from dissect.database.ese.table import Table + UalValue = RecordValue | ipaddress.IPv4Address | ipaddress.IPv6Interface | tuple[datetime.datetime] SKIP_TABLES = [ diff --git a/dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py b/dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py index 63e1a30..9393461 100644 --- a/dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py +++ b/dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py @@ -178,7 +178,6 @@ def __init__(self, sqlcipher: SQLCipher): def _read(self, offset: int, length: int) -> bytes: """Calculates which pages to read from based on the given offset and length. Returns decrypted bytes.""" - start_page = offset // self.align num_pages = length // self.align return b"".join( @@ -191,7 +190,6 @@ def _read_page(self, page_num: int, verify_hmac: bool = False) -> bytes: References: - https://github.com/sqlcipher/sqlcipher-tools/blob/master/decrypt.c """ - if page_num < 1: raise ValueError("The first page number is 1") @@ -294,7 +292,6 @@ class SQLCipher1(SQLCipher): def derive_key(passphrase: bytes, salt: bytes, kdf_iter: int, kdf_algo: str | None) -> bytes: """Derive the database key as SQLCipher would using PBKDF2.""" - if not kdf_iter or not kdf_algo: return passphrase diff --git a/dissect/database/sqlite3/sqlite3.py b/dissect/database/sqlite3/sqlite3.py index 23bfad3..06014fd 100644 --- a/dissect/database/sqlite3/sqlite3.py +++ b/dissect/database/sqlite3/sqlite3.py @@ -15,7 +15,7 @@ NoCellData, ) from dissect.database.sqlite3.util import parse_table_columns_constraints -from dissect.database.sqlite3.wal import WAL, Checkpoint +from dissect.database.sqlite3.wal import WAL if TYPE_CHECKING: from collections.abc import Iterator @@ -23,6 +23,8 @@ from typing_extensions import Self + from dissect.database.sqlite3.wal import Checkpoint + ENCODING = { 1: "utf-8", 2: "utf-16-le", @@ -259,7 +261,7 @@ def __init__(self, name: str, description: str): self.default_value = self._parse_default_value_from_description(description) def _parse_default_value_from_description(self, description: str) -> bool | str | int | float | None: - """Find the default from the description string""" + """Find the default from the description string.""" if "DEFAULT" not in description.upper(): return None @@ -275,14 +277,13 @@ def _tokenize(self, description: str) -> list[str]: return [x for x in tokens if x and x != " "] def _get_default_value(self, tokens: list[str]) -> str: - """Retrieve the default from the tokens""" - + """Retrieve the default from the tokens.""" # The +1 is to account for the space after the default value_index = [x.upper() for x in tokens].index("DEFAULT") + 1 return tokens[value_index] def _parse_default_value(self, value: str) -> bool | str | int | float | None: - """Parses the default value + """Parses the default value. The value can hold an expression surrounded by (). This can be a literal, so these values get stripped. @@ -293,7 +294,7 @@ def _parse_default_value(self, value: str) -> bool | str | int | float | None: return None def _parse_literal(self, value: str) -> bool | str | int | float: - """Tries to convert a literal from a string to any type + """Tries to convert a literal from a string to any type. CURRENT_(TIME|DATE|TIMESTAMP) isn't being taken into account. """ @@ -385,7 +386,6 @@ def _match_columns_to_values(self, columns: list[Column], values: list[Any]) -> If there are any cell values with unknown column names they get added to the unknown list. """ - row_values = {} unknowns = [] diff --git a/dissect/database/sqlite3/util.py b/dissect/database/sqlite3/util.py index 1e20ff8..905d8d3 100644 --- a/dissect/database/sqlite3/util.py +++ b/dissect/database/sqlite3/util.py @@ -109,7 +109,6 @@ def parse_table_columns_constraints(sql: str) -> tuple[str | None, list[str], li def split_column_def(sql: str, column_def: str) -> tuple[str, str]: """Splits the column definition to name and constraint.""" - column_parts = column_def.split(maxsplit=1) if not column_parts: raise InvalidSQL(f"Not a valid CREATE TABLE definition: empty column definition in {sql!r}") diff --git a/pyproject.toml b/pyproject.toml index 0730738..bd75c47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,7 +107,7 @@ select = [ "SLOT", "SIM", "TID", - "TCH", + "TC", "PTH", "PLC", "TRY", @@ -115,8 +115,25 @@ select = [ "PERF", "FURB", "RUF", + "D" ] -ignore = ["E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM105", "TRY003"] +ignore = [ + "E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM105", "TRY003", "PLC0415", + # Ignore some pydocstyle rules for now as they require a larger cleanup + "D1", + "D205", + "D301", + "D417", + # Seems bugged: https://github.com/astral-sh/ruff/issues/16824 + "D402", +] +future-annotations = true + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.flake8-type-checking] +strict = true [tool.ruff.lint.per-file-ignores] "tests/_docs/**" = ["INP001"] @@ -125,6 +142,7 @@ ignore = ["E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM1 [tool.ruff.lint.isort] known-first-party = ["dissect.database"] known-third-party = ["dissect"] +required-imports = ["from __future__ import annotations"] [tool.setuptools.packages.find] include = ["dissect.*"] diff --git a/tests/_docs/conf.py b/tests/_docs/conf.py index 601c783..b6ea857 100644 --- a/tests/_docs/conf.py +++ b/tests/_docs/conf.py @@ -1,3 +1,5 @@ +from __future__ import annotations + project = "dissect.database" extensions = [ diff --git a/tests/ese/test_cursor.py b/tests/ese/test_cursor.py index 9d19f8c..83a8f08 100644 --- a/tests/ese/test_cursor.py +++ b/tests/ese/test_cursor.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import BinaryIO from dissect.database.ese.ese import ESE diff --git a/tests/ese/test_page.py b/tests/ese/test_page.py index b99ab4e..37c7773 100644 --- a/tests/ese/test_page.py +++ b/tests/ese/test_page.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from unittest.mock import Mock from dissect.database.ese.c_ese import c_ese diff --git a/tests/ese/test_record.py b/tests/ese/test_record.py index 79dc077..12d34c6 100644 --- a/tests/ese/test_record.py +++ b/tests/ese/test_record.py @@ -71,7 +71,6 @@ def test_parse_value_encoding(windows_search_db: BinaryIO) -> None: Resources: - https://github.com/fox-it/dissect.esedb/pull/48 """ - db = ESE(windows_search_db) table = db.table("SystemIndex_PropertyStore") diff --git a/tests/ese/test_table.py b/tests/ese/test_table.py index 062d920..621e5f8 100644 --- a/tests/ese/test_table.py +++ b/tests/ese/test_table.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from unittest.mock import MagicMock from dissect.database.ese.table import Table diff --git a/tests/sqlite3/test_sqlcipher.py b/tests/sqlite3/test_sqlcipher.py index 707763d..010eb37 100644 --- a/tests/sqlite3/test_sqlcipher.py +++ b/tests/sqlite3/test_sqlcipher.py @@ -61,7 +61,6 @@ def _assert_sqlite_db(sqlite: SQLite3) -> None: ) def test_decrypt_community(cipher: Callable, path_str: str, kwargs: dict) -> None: """Test if we can parse a SQLCipher (4.5.6 community) encrypted database.""" - path = absolute_path("_data/sqlite3/encryption/sqlcipher/" + path_str) with pytest.raises(SQLCipherError, match="Decryption of SQLCipher database failed"): @@ -79,7 +78,6 @@ def test_decrypt_community(cipher: Callable, path_str: str, kwargs: dict) -> Non def test_decrypt_community_plaintext_header() -> None: """Test if we can parse and decrypt a SQLCipher 4.5.6 database with a 32-byte plaintext header.""" - path = absolute_path("_data/sqlite3/encryption/sqlcipher/aes256_hmac_sha512_kdf_256000_plain_header.sqlite") salt = bytes.fromhex("01010101010101010101010101010101") diff --git a/tests/sqlite3/test_sqlite3.py b/tests/sqlite3/test_sqlite3.py index c2afe8f..f5caaf9 100644 --- a/tests/sqlite3/test_sqlite3.py +++ b/tests/sqlite3/test_sqlite3.py @@ -121,7 +121,6 @@ def test_cell_overflow_reserved_page_size_regression() -> None: >>> con.commit() ... con.close() """ - db = sqlite3.SQLite3(absolute_path("_data/sqlite3/overflow.db")) assert db.header.reserved_size == 32 assert db.header.page_size == 4096 diff --git a/tests/sqlite3/test_wal.py b/tests/sqlite3/test_wal.py index ee27e6b..e669561 100644 --- a/tests/sqlite3/test_wal.py +++ b/tests/sqlite3/test_wal.py @@ -186,7 +186,6 @@ def test_wal_page_count() -> None: >>> con.commit() # Copy page_count.db* files before closing """ - db = sqlite3.SQLite3(absolute_path("_data/sqlite3/page_count.db")) table = db.table("t1") assert table.sql == "CREATE TABLE t1 (a, b)"