"""Run the narrowly scoped, read-only Mission 00 Firebird probe.""" from __future__ import annotations import argparse import ctypes import importlib.metadata import json import os import platform import re import struct import sys from pathlib import Path from typing import Any PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT / "src")) from gti_discovery.firebird_connection import ( AUTHORIZED_DATABASE_PATH, _FirebirdAdapter, _ADAPTER_CREATION_TOKEN, _create_firebird_adapter, connect_authorized_read_only, get_database_identity, validate_authorized_database_path, validate_authorized_database_identity, ) from gti_discovery.collector import _safe_sample_rows, collect_metadata from gti_discovery.fingerprint import ( SCHEMA_CHANGED, SCHEMA_SUPPORTED, SCHEMA_UNKNOWN, build_fingerprint, classify_schema, ) from gti_discovery.mapping import build_schema_mapping, identify_product_candidates from gti_discovery.provenance import ( ProvenanceError, validate_fdb_provenance, validate_imported_fdb_provenance, ) DEFAULT_TARGET = AUTHORIZED_DATABASE_PATH CLIENT_LIBRARY = Path(r"C:\Program Files\Firebird\Firebird_2_5\bin\fbclient.dll") _BLOCKER_CATEGORIES = { ValueError: "target_rejected", ImportError: "driver_unavailable", FileNotFoundError: "client_library_unavailable", PermissionError: "connection_failed", RuntimeError: "connection_failed", } class _FdbDriver: version = "unknown" def __init__(self, fdb_module: Any): self._fdb = fdb_module self.version = str(getattr(fdb_module, "__version__", "unknown")) self._last_connection = None def connect(self, **options): if set(options) - {"database", "read_only", "client_library"}: raise RuntimeError("unexpected connection settings") if options.pop("read_only", None) is not True: raise RuntimeError("read-only mode is required") client_library = options.pop("client_library", None) user = os.environ.get("FB_USER") password = os.environ.get("FB_PASSWORD") if not user or not password: raise RuntimeError("Firebird credentials are unavailable in FB_USER/FB_PASSWORD") isolation_level = _read_only_isolation_level(self._fdb) raw = self._fdb.connect( database=options["database"], user=user, password=password, fb_library_name=client_library, isolation_level=isolation_level, ) self._last_connection = raw observed = getattr(raw, "default_tpb", None) if observed != isolation_level: close = getattr(raw, "close", None) if callable(close): close() raise RuntimeError("driver did not establish the fdb read-only TPB") evidence = { "accepted": True, "driver_api": "fdb.Connection.default_tpb", "configured_tpb": bytes(isolation_level), "observed_default_tpb": bytes(observed), } return _create_firebird_adapter(raw, evidence, _ADAPTER_CREATION_TOKEN) def facts(self) -> dict[str, Any]: return {name: _fact(self._last_connection, name) for name in ( "engine_version", "database_sql_dialect", "ods_version", "ods_minor_version", "charset", "page_size", )} def compatibility_evidence(self, client_version: str) -> dict[str, Any]: isolation_level = _read_only_isolation_level(self._fdb) return { "fdb_version": self.version, "client_version": client_version, "firebird_version": ".".join(str(client_version).split(".")[:3]), "read_only_isolation": _read_only_isolation_level(self._fdb) is not None, "read_only_tpb": bytes(isolation_level), "read_only_api": "fdb.Connection.default_tpb", } def _read_only_isolation_level(fdb: Any) -> Any: """Use fdb 2.0.4's actual read-only TPB constant (including its spelling).""" value = getattr(fdb, "ISOLATION_LEVEL_READ_COMMITED_RO", None) if isinstance(value, bytes) and value == b"\x03\x08\x06\x0f\x11": return value raise RuntimeError("fdb read-only isolation API is unavailable") def _fact(connection: Any, name: str) -> Any: try: value = getattr(connection, name) except Exception: return None if isinstance(value, (str, int, float, bool)) or value is None: return value return str(value) def _read_file_version(path: Path) -> str | None: """Read Windows file-version metadata without loading the client DLL.""" if sys.platform != "win32": return None try: version = ctypes.windll.version size = version.GetFileVersionInfoSizeW(str(path), None) if not size: return None buffer = ctypes.create_string_buffer(size) if not version.GetFileVersionInfoW(str(path), 0, size, buffer): return None pointer = ctypes.c_void_p() length = ctypes.c_uint() if not version.VerQueryValueW(buffer, "\\", ctypes.byref(pointer), ctypes.byref(length)): return None class FixedFileInfo(ctypes.Structure): _fields_ = [("signature", ctypes.c_uint32), ("struct_version", ctypes.c_uint32), ("file_version_ms", ctypes.c_uint32), ("file_version_ls", ctypes.c_uint32)] info = ctypes.cast(pointer, ctypes.POINTER(FixedFileInfo)).contents return ".".join((str(info.file_version_ms >> 16), str(info.file_version_ms & 0xffff), str(info.file_version_ls >> 16), str(info.file_version_ls & 0xffff))) except Exception: return None def _client_metadata(path: Path) -> dict[str, Any]: exists = path.is_file() return {"path": str(path), "exists": exists, "version": _read_file_version(path) if exists else None} def _read_client_architecture(path: Path) -> str: """Read PE architecture metadata without loading or executing the DLL.""" try: with path.open("rb") as handle: if handle.read(2) != b"MZ": return "UNKNOWN" handle.seek(0x3C) offset = struct.unpack(" str: return "x64" if struct.calcsize("P") == 8 else "x86" if struct.calcsize("P") == 4 else "UNKNOWN" def validate_firebird_runtime(*, fdb_module: Any = None) -> dict[str, Any]: """Return deterministic dependency evidence; never infer unknown facts.""" try: provenance = validate_fdb_provenance() except ProvenanceError as error: return {"status": "UNKNOWN", "blocker": "compatibility_unproven", "provenance": error.evidence} client = _client_metadata(CLIENT_LIBRARY) client = dict(client) client["architecture"] = client.get("architecture", _read_client_architecture(CLIENT_LIBRARY)) distribution_version = None try: distribution_version = importlib.metadata.version("fdb") except (importlib.metadata.PackageNotFoundError, ImportError): pass fdb_version = getattr(fdb_module, "__version__", distribution_version) python_architecture = _python_architecture() read_only_api = ( fdb_module is not None and isinstance(getattr(fdb_module, "ISOLATION_LEVEL_READ_COMMITED_RO", None), bytes) and getattr(fdb_module, "ISOLATION_LEVEL_READ_COMMITED_RO") == b"\x03\x08\x06\x0f\x11" ) version_match = fdb_version == "2.0.4" and distribution_version == "2.0.4" architecture_match = client["architecture"] in {"x86", "x64", "arm64"} and ( (python_architecture == "x64" and client["architecture"] == "x64") or (python_architecture == "x86" and client["architecture"] == "x86") ) valid = ( provenance.get("status") == "VALID" and client["exists"] is True and client["version"] == "2.5.9.27139" and client["architecture"] != "UNKNOWN" and architecture_match and version_match and fdb_module is not None and read_only_api ) compatibility = { "status": "VALID" if valid else "UNKNOWN", "python_client_architecture_match": architecture_match, "fdb_client_version_match": version_match, "read_only_api": read_only_api, } return { "status": "VALID" if valid else "UNKNOWN", "python_runtime": {"version": platform.python_version(), "architecture": python_architecture}, "fdb": {"version": fdb_version, "distribution_version": distribution_version}, "client": client, "provenance": provenance, "compatibility": compatibility, **({} if valid else {"blocker": "compatibility_unproven"}), } def _blocker_category(error: Exception) -> str: if type(error).__name__ == "DatabaseError" and ( "-902" in str(error) or "user name and password are not defined" in str(error).lower() ): return "credentials_rejected" if isinstance(error, ValueError) and "read-only" in str(error).lower(): return "read_only_unproven" if isinstance(error, RuntimeError) and "credentials" in str(error).lower(): return "credentials_unavailable" if isinstance(error, RuntimeError) and "compatib" in str(error).lower(): return "compatibility_unproven" if isinstance(error, RuntimeError) and "provenance" in str(error).lower(): return "compatibility_unproven" return next((category for error_type, category in _BLOCKER_CATEGORIES.items() if isinstance(error, error_type)), "unexpected_failure") def _firebird_259(version: Any) -> bool: match = re.match(r"^(\d+)\.(\d+)\.(\d+)", str(version or "")) return bool(match and tuple(int(item) for item in match.groups()) == (2, 5, 9)) def _validate_runtime_compatibility(driver: Any) -> dict[str, Any]: """Prove the installed 2.5.9 client and usable driver before connect.""" client = _client_metadata(CLIENT_LIBRARY) if not client["exists"] or not _firebird_259(client["version"]): raise RuntimeError("Firebird 2.5.9 client compatibility is unproven") if type(driver) is not _FdbDriver: raise RuntimeError("Firebird driver/client compatibility is unproven") try: import fdb as installed_fdb installed_version = importlib.metadata.version("fdb") except (ImportError, importlib.metadata.PackageNotFoundError) as error: raise RuntimeError("Firebird driver/client compatibility is unproven") from error if ( driver._fdb is not installed_fdb or installed_version != "2.0.4" or getattr(installed_fdb, "__version__", None) != "2.0.4" or _read_only_isolation_level(installed_fdb) != b"\x03\x08\x06\x0f\x11" ): raise RuntimeError("Firebird driver/client compatibility is unproven") return client def _validate_preimport_compatibility() -> dict[str, Any]: provenance = validate_fdb_provenance() client = _client_metadata(CLIENT_LIBRARY) client = dict(client) client["architecture"] = client.get("architecture", _read_client_architecture(CLIENT_LIBRARY)) try: fdb_version = importlib.metadata.version("fdb") except importlib.metadata.PackageNotFoundError as error: raise RuntimeError("fdb 2.0.4 compatibility is unproven") from error if ( not client["exists"] or not _firebird_259(client["version"]) or client["architecture"] == "UNKNOWN" or client["architecture"] != _python_architecture() or fdb_version != "2.0.4" ): raise RuntimeError("Firebird 2.5.9/fdb 2.0.4 compatibility is unproven") if not _preimport_read_only_api_proven(): raise RuntimeError("Firebird read-only isolation API compatibility is unproven") return {"client": client, "provenance": provenance} def _preimport_read_only_api_proven() -> bool: """Prove the pinned driver's read-only API from installed source, without import.""" try: spec = __import__("importlib.util", fromlist=["find_spec"]).find_spec("fdb") locations = list(getattr(spec, "submodule_search_locations", ()) or ()) if not locations: return False package = Path(locations[0]) init_source = (package / "__init__.py").read_text(encoding="utf-8") core_source = (package / "fbcore.py").read_text(encoding="utf-8") except (OSError, UnicodeError, ImportError, AttributeError, TypeError): return False return ( "ISOLATION_LEVEL_READ_COMMITED_RO" in init_source and "ISOLATION_LEVEL_READ_COMMITED_RO = bs([" in core_source and all(token in core_source for token in ( "isc_tpb_version3", "isc_tpb_read", "isc_tpb_wait", "isc_tpb_read_committed", "isc_tpb_rec_version", )) ) def _security_gate(path: os.PathLike[str] | str, driver: Any) -> dict[str, Any]: validate_authorized_database_path(path) if not os.environ.get("FB_USER") or not os.environ.get("FB_PASSWORD"): raise RuntimeError("Firebird credentials are unavailable in FB_USER/FB_PASSWORD") evidence = validate_firebird_runtime(fdb_module=getattr(driver, "_fdb", None)) if evidence["status"] != "VALID": raise RuntimeError("Firebird driver/client compatibility is unproven") return {"client": evidence["client"], "compatibility_evidence": evidence} _SAMPLE_DENYLIST = ( "CUSTOMER", "CLIENT", "EMAIL", "PHONE", "MOBILE", "CONTACT", "ADDRESS", "PRICE", "COST", "AMOUNT", "TOTAL", "TAX", "CRED", "PASSWORD", "SECRET", "TOKEN", "BLOB", "SOURCE", "DEFAULT", "SALARY", "MARGIN", "DISCOUNT", "PROFIT", "IMAGE", "PHOTO", "FILE", "PATH", "URL", ) _SAMPLE_ALLOWLIST = ( "ID", "KEY", "CODE", "CODIGO", "SKU", "ITEM", "PRODUCT", "DESCRIPTION", "DESCRICAO", "NAME", "NOME", "BARCODE", "EAN", "GTIN", "REFERENCE", "REFERENCIA", "REF", "UNIT", "UNIDADE", "CATEGORY", "GROUP", "GRUPO", "BRAND", "MARCA", "PRODUTO", "ESTOQUE", "STATUS", "ACTIVE", "ATIVO", ) def _permitted_sample_plan(metadata: dict[str, Any]) -> list[tuple[str, list[str]]]: fields_by_relation: dict[str, list[dict[str, Any]]] = {} for field in metadata.get("fields", []): if not isinstance(field, dict): continue relation = str(field.get("RELATION_NAME", "")).strip() if relation: fields_by_relation.setdefault(relation.upper(), []).append(field) candidates = identify_product_candidates(metadata, {}) plan = [] for candidate in candidates: relation = str(candidate.get("relation", "")).strip() if not relation or candidate.get("classification") == "UNKNOWN": continue columns = [] for field in fields_by_relation.get(relation.upper(), []): name = str(field.get("FIELD_NAME", "")).strip() type_name = str(field.get("TYPE_NAME", field.get("FIELD_TYPE", ""))).upper() upper_name = name.upper() if not name or any(word in upper_name for word in _SAMPLE_DENYLIST): continue if "BLOB" in type_name or not any(word in upper_name for word in _SAMPLE_ALLOWLIST): continue columns.append(name) if columns: plan.append((relation, columns[:8])) return plan def _collect_samples(reader: Any, metadata: dict[str, Any]) -> dict[str, list[dict[str, Any]]]: samples = {} plan = _permitted_sample_plan(metadata) if not isinstance(plan, list) or any( not isinstance(item, tuple) or len(item) != 2 or not isinstance(item[0], str) or not isinstance(item[1], list) or not item[1] or any( not isinstance(column, str) or any(word in column.upper() for word in _SAMPLE_DENYLIST) or not any(word in column.upper() for word in _SAMPLE_ALLOWLIST) for column in item[1] ) for item in plan ): raise RuntimeError("sample plan is not permitted") for relation, columns in plan: rows = reader.sample_relation_rows(relation, columns, 5) if not isinstance(rows, list) or any(not isinstance(row, dict) for row in rows): raise RuntimeError("sample evidence is malformed") samples[relation] = _safe_sample_rows(rows) return samples def _blocked( blocker: str, *, connection_attempts: int = 0, other_databases_accessed: int | str = 0, ) -> dict[str, Any]: return { "status": "MISSION_00_BLOCKED", "blocker": blocker, "application_writes": 0, "other_databases_accessed": other_databases_accessed, "connection_attempts": connection_attempts, } def _valid_metadata(metadata: Any) -> bool: if not isinstance(metadata, dict): return False if not all(isinstance(metadata.get(name), list) for name in ("relations", "fields", "errors", "unavailable")): return False return ( all(isinstance(row, dict) for name in ("relations", "fields", "errors") for row in metadata[name]) and all(isinstance(name, str) and bool(name) for name in metadata["unavailable"]) ) def _valid_mapping(mapping: Any) -> bool: required = {"product", "fields", "fiscal", "stock", "media", "incremental", "triggers"} if type(mapping) is not dict or set(mapping) != required: return False allowed = {"CONFIRMED", "INFERRED", "UNKNOWN"} def strings(value: Any) -> bool: return type(value) is list and all(type(item) is str and bool(item) for item in value) def classified(value: Any, keys: set[str]) -> bool: return type(value) is dict and set(value) == keys and value["classification"] in allowed def field_record(value: Any) -> bool: if type(value) is not dict or not {"classification", "sample_present", "type"} <= set(value): return False if set(value) - {"classification", "sample_present", "type", "subtype", "value"}: return False return value["classification"] in allowed and type(value["sample_present"]) is bool and type(value["type"]) is str and bool(value["type"]) and all( type(value[key]) in (str, int, float, bool) for key in ("subtype", "value") if key in value ) def field_map(value: Any) -> bool: return type(value) is dict and all(type(key) is str and bool(key) and field_record(item) for key, item in value.items()) def relation_record(value: Any) -> bool: if type(value) is not dict or not {"classification", "fields", "relationships"} <= set(value): return False if set(value) - {"classification", "fields", "relationships", "evidence"}: return False relationships = value["relationships"] return value["classification"] in allowed and field_map(value["fields"]) and type(relationships) is list and all( type(item) is dict and set(item) == {"child_relation", "child_field", "referenced_relation", "referenced_field"} and all(type(item[key]) is str and bool(item[key]) for key in item) for item in relationships ) and ("evidence" not in value or type(value["evidence"]) is str) def relation_map(value: Any) -> bool: return type(value) is dict and all(type(key) is str and bool(key) and relation_record(item) for key, item in value.items()) product = mapping["product"] product_keys = {"classification", "relation", "evidence", "candidates"} candidate_keys = {"classification", "relation", "evidence"} evidence_keys = {"primary_key", "field_count", "semantic_fields", "sample_fields", "semantic_sample_fields", "approximate_count"} if not classified(product, product_keys) or (product["relation"] is not None and (type(product["relation"]) is not str or not product["relation"])) or type(product["evidence"]) is not dict or set(product["evidence"]) - evidence_keys: return False candidates = product.get("candidates") if not isinstance(candidates, list) or not all( classified(item, candidate_keys) and type(item["relation"]) is str and bool(item["relation"]) and type(item["evidence"]) is dict and set(item["evidence"]) - evidence_keys == set() and strings(item["evidence"].get("primary_key")) and type(item["evidence"].get("field_count")) is int and strings(item["evidence"].get("semantic_fields")) for item in candidates ): return False for evidence in (product["evidence"], *(item["evidence"] for item in candidates)): if not evidence: continue if not strings(evidence.get("primary_key")) or type(evidence.get("field_count")) is not int or evidence["field_count"] < 0 or not strings(evidence.get("semantic_fields")): return False if "sample_fields" in evidence and not strings(evidence["sample_fields"]): return False if "semantic_sample_fields" in evidence and not strings(evidence["semantic_sample_fields"]): return False if "approximate_count" in evidence and type(evidence["approximate_count"]) not in (str, int, float, bool, type(None)): return False if not field_map(mapping["fields"]): return False for section in ("fiscal", "media"): value = mapping[section] if not classified(value, {"classification", "fields", "relations"}) or not field_map(value["fields"]) or not relation_map(value["relations"]): return False if not classified(mapping["stock"], {"classification", "relations"}) or not relation_map(mapping["stock"]["relations"]): return False incremental = mapping["incremental"] evidence = incremental.get("evidence") if not classified(incremental, {"classification", "evidence"}) or type(evidence) is not dict or set(evidence) != {"timestamps", "triggers", "generators"}: return False if not strings(evidence["timestamps"]) or type(evidence["triggers"]) is not int or evidence["triggers"] < 0 or type(evidence["generators"]) is not int or evidence["generators"] < 0: return False triggers = mapping["triggers"] return type(triggers) is list and all( classified(trigger, {"classification", "relation", "trigger", "timing", "event", "trigger_type", "dependencies", "source"}) and all(type(trigger[key]) is str and bool(trigger[key]) for key in ("relation", "trigger", "timing", "event", "source")) and type(trigger["trigger_type"]) in (str, int) and bool(str(trigger["trigger_type"])) and strings(trigger.get("dependencies")) for trigger in triggers ) def _valid_fingerprint(value: Any) -> bool: return isinstance(value, str) and bool(re.fullmatch(r"[0-9a-f]{64}", value)) def _public_compatibility(evidence: Any) -> dict[str, str]: """Expose compatibility categories only; never serialize internal evidence.""" if not isinstance(evidence, dict): return {"status": "UNKNOWN"} return { "status": evidence.get("status", "UNKNOWN") if evidence.get("status") in {"VALID", "UNKNOWN"} else "UNKNOWN", "architecture": "MATCH" if evidence.get("compatibility", {}).get("python_client_architecture_match") is True else "UNKNOWN", "fdb_client": "MATCH" if evidence.get("compatibility", {}).get("fdb_client_version_match") is True else "UNKNOWN", "read_only_api": "PROVEN" if evidence.get("compatibility", {}).get("read_only_api") is True else "UNKNOWN", } def _public_metadata(metadata: dict[str, Any]) -> dict[str, Any]: """Remove database identity rows, which may contain an absolute FDB path.""" return {key: value for key, value in metadata.items() if key != "database"} def run_discovery(path: os.PathLike[str] | str, *, driver: Any) -> dict[str, Any]: """Run the gated, structured, sanitized discovery workflow.""" try: security = _security_gate(path, driver) client = security.get("client", security) except RuntimeError as error: return _blocked(_blocker_category(error)) try: connection = connect_authorized_read_only( path, driver, client_library=str(CLIENT_LIBRARY) ) except Exception as error: return _blocked(_blocker_category(error), connection_attempts=1) try: try: database_name = get_database_identity(connection) validate_authorized_database_identity(database_name, path) except Exception: return _blocked( "identity_mismatch", connection_attempts=1, other_databases_accessed="UNKNOWN", ) try: facts = driver.facts() if callable(getattr(driver, "facts", None)) else {} metadata = collect_metadata(connection) if not _valid_metadata(metadata): return _blocked("discovery_invalid", connection_attempts=1) samples = _collect_samples(connection, metadata) mapping = build_schema_mapping(metadata, samples) if not _valid_mapping(mapping): return _blocked("discovery_invalid", connection_attempts=1) unavailable = set(metadata.get("unavailable", [])) optional_catalog_gaps = {"events", "packages", "procedure_parameters"} blocking_errors = [ error for error in metadata.get("errors", []) if not ( error.get("kind") == "optional_unsupported" or error.get("query") == "monitoring" ) ] complete = ( not blocking_errors and unavailable <= optional_catalog_gaps and (not metadata.get("incomplete") or bool(unavailable) and unavailable <= optional_catalog_gaps) and bool(metadata.get("relations")) and bool(samples) ) fingerprint = None classification = SCHEMA_UNKNOWN if complete: evidence = {"metadata": metadata, "samples": samples, "mapping": mapping} fingerprint = build_fingerprint(evidence) if not _valid_fingerprint(fingerprint): return _blocked("discovery_invalid", connection_attempts=1) expected = getattr(driver, "expected_fingerprints", ()) classification = classify_schema( metadata, expected, mapping_ambiguous=mapping.get("product", {}).get("classification") == "UNKNOWN", ) if classification not in {SCHEMA_SUPPORTED, SCHEMA_CHANGED, SCHEMA_UNKNOWN}: return _blocked("discovery_invalid", connection_attempts=1) return { "status": "CONNECTED" if complete else SCHEMA_UNKNOWN, "driver_version": str(getattr(driver, "version", "unknown")), "architecture": platform.architecture()[0], "engine_version": facts.get("engine_version"), "dialect": facts.get("database_sql_dialect"), "ods": { "major": facts.get("ods_version"), "minor": facts.get("ods_minor_version"), }, "charset": facts.get("charset"), "page_size": facts.get("page_size"), "metadata": _public_metadata(metadata), "samples": samples, "schema_mapping": mapping, "fingerprint": fingerprint, "schema_classification": classification, "compatibility": _public_compatibility(security.get("compatibility_evidence")), "application_writes": 0, "other_databases_accessed": 0, "connection_attempts": 1, } except Exception: return _blocked("discovery_failed", connection_attempts=1) finally: connection.close() def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.parse_args(argv) try: if not os.environ.get("FB_USER") or not os.environ.get("FB_PASSWORD"): raise RuntimeError("credentials unavailable") compatibility = _validate_preimport_compatibility() import fdb provenance = validate_imported_fdb_provenance(fdb) runtime_compatibility = validate_firebird_runtime(fdb_module=fdb) if runtime_compatibility["status"] != "VALID": raise RuntimeError("Firebird driver/client compatibility is unproven") result = run_discovery(DEFAULT_TARGET, driver=_FdbDriver(fdb)) result["compatibility"] = _public_compatibility(runtime_compatibility) except Exception as error: result = { "status": "MISSION_00_BLOCKED", "blocker": _blocker_category(error), "application_writes": 0, "other_databases_accessed": 0, "connection_attempts": 0, } print(json.dumps(result, ensure_ascii=True, sort_keys=True)) return 0 if result["status"] == "CONNECTED" else 2 if __name__ == "__main__": sys.exit(main())