git-subtree-dir: fiscal git-subtree-mainline:d95165fc8fgit-subtree-split:102ba84500
200 lines
6.6 KiB
Python
200 lines
6.6 KiB
Python
import pytest
|
|
|
|
import gti_discovery
|
|
from gti_discovery.catalog import FirebirdCatalog
|
|
from gti_discovery.collector import _approved_fixture_reader
|
|
from gti_discovery.firebird_queries import (
|
|
_ADAPTER_REGISTRATION_TOKEN,
|
|
_APPROVED_ADAPTER_TYPES,
|
|
_CATALOG_QUERIES,
|
|
_INTERNAL_QUERIES,
|
|
_execute_catalog,
|
|
_register_approved_adapter,
|
|
)
|
|
from gti_discovery.collector import _APPROVED_FIXTURE_TYPES
|
|
|
|
|
|
class RecordingAdapter:
|
|
read_only = True
|
|
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def execute(self, sql, params=()):
|
|
self.calls.append((sql, params))
|
|
return []
|
|
|
|
|
|
_register_approved_adapter(RecordingAdapter, _ADAPTER_REGISTRATION_TOKEN)
|
|
|
|
|
|
def make_structured_connection():
|
|
return FirebirdCatalog(RecordingAdapter())
|
|
|
|
|
|
def test_public_connection_has_no_sql_execute_method():
|
|
connection = make_structured_connection()
|
|
for name in ("execute", "query", "raw_execute", "execute_statement", "run_sql", "_run"):
|
|
assert not hasattr(connection, name)
|
|
assert name not in gti_discovery.__all__
|
|
|
|
|
|
def test_package_exports_no_raw_sql_or_parser_authority():
|
|
forbidden = {
|
|
"execute", "query", "raw_execute", "execute_statement", "run_sql",
|
|
"validate_read_only",
|
|
}
|
|
assert forbidden.isdisjoint(vars(gti_discovery))
|
|
assert forbidden.isdisjoint(gti_discovery.__all__)
|
|
|
|
|
|
def test_private_sql_runner_cannot_bypass_operation_boundary():
|
|
connection = make_structured_connection()
|
|
assert not hasattr(connection, "_run")
|
|
assert not hasattr(connection, "_run_sql")
|
|
|
|
|
|
def test_unknown_operation_key_never_reaches_driver():
|
|
adapter = RecordingAdapter()
|
|
with pytest.raises(ValueError):
|
|
_execute_catalog(adapter, "SELECT * FROM X")
|
|
assert adapter.calls == []
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"forbidden_operation",
|
|
["execute_procedure", "select_procedure", "execute_selectable_procedure", "invoke_udf"],
|
|
)
|
|
def test_procedure_and_udf_operations_never_reach_driver(forbidden_operation):
|
|
adapter = RecordingAdapter()
|
|
|
|
with pytest.raises(ValueError, match="unknown catalog operation"):
|
|
_execute_catalog(adapter, forbidden_operation)
|
|
|
|
assert adapter.calls == []
|
|
|
|
|
|
def test_procedure_api_is_metadata_only():
|
|
adapter = RecordingAdapter()
|
|
catalog = FirebirdCatalog(adapter)
|
|
|
|
assert catalog.get_procedures() == []
|
|
assert catalog.get_procedure_parameters() == []
|
|
assert not any("EXECUTE PROCEDURE" in sql.upper() for sql, _ in adapter.calls)
|
|
assert not any("FROM " + name in sql.upper() for name in ("SP_RECALC", "SOME_UDF") for sql, _ in adapter.calls)
|
|
|
|
|
|
@pytest.mark.parametrize("operation_key", [None, 1, [], {}, bytearray(b"database"), str("database") + ""])
|
|
def test_operation_key_must_be_an_exact_approved_string(operation_key):
|
|
adapter = RecordingAdapter()
|
|
if type(operation_key) is str:
|
|
pytest.skip("control case is tested by the static operation tests")
|
|
with pytest.raises(ValueError, match="operation"):
|
|
_execute_catalog(adapter, operation_key)
|
|
assert adapter.calls == []
|
|
|
|
|
|
def test_str_subclass_is_not_an_operation_key():
|
|
class Lookalike(str):
|
|
pass
|
|
|
|
adapter = RecordingAdapter()
|
|
with pytest.raises(ValueError, match="operation"):
|
|
_execute_catalog(adapter, Lookalike("database"))
|
|
assert adapter.calls == []
|
|
|
|
|
|
def test_arbitrary_adapter_is_rejected_before_driver_invocation():
|
|
class ArbitraryAdapter:
|
|
def execute(self, sql, params=()):
|
|
raise AssertionError("driver must not be invoked")
|
|
|
|
with pytest.raises(TypeError, match="approved implementation"):
|
|
_execute_catalog(ArbitraryAdapter(), "database")
|
|
|
|
|
|
def test_catalog_rejects_unregistered_read_only_adapter_before_driver_invocation():
|
|
class LookalikeAdapter:
|
|
read_only = True
|
|
|
|
def execute(self, sql, params=()):
|
|
raise AssertionError("driver must not be invoked")
|
|
|
|
with pytest.raises(TypeError, match="approved implementation"):
|
|
FirebirdCatalog(LookalikeAdapter())
|
|
|
|
|
|
def test_catalog_close_cannot_act_on_unregistered_adapter():
|
|
class LookalikeAdapter:
|
|
read_only = True
|
|
closed = False
|
|
|
|
def execute(self, sql, params=()):
|
|
raise AssertionError("driver must not be invoked")
|
|
|
|
def close(self):
|
|
self.closed = True
|
|
|
|
adapter = LookalikeAdapter()
|
|
with pytest.raises(TypeError, match="approved implementation"):
|
|
FirebirdCatalog(adapter)
|
|
assert adapter.closed is False
|
|
|
|
|
|
def test_fixture_reader_registration_requires_private_capability():
|
|
class UntrustedReader:
|
|
pass
|
|
|
|
with pytest.raises(TypeError):
|
|
_approved_fixture_reader(UntrustedReader)
|
|
|
|
|
|
def test_catalog_queries_are_immutable_and_operation_keyed():
|
|
with pytest.raises(TypeError):
|
|
_CATALOG_QUERIES["new"] = "SELECT 1"
|
|
assert set(_CATALOG_QUERIES) == {
|
|
"database", "relations", "fields", "keys", "indexes", "foreign_keys",
|
|
"triggers", "procedures", "procedure_parameters", "packages", "generators",
|
|
"dependencies", "monitoring",
|
|
}
|
|
|
|
|
|
def test_private_approved_type_registries_are_immutable_snapshots():
|
|
assert isinstance(_APPROVED_ADAPTER_TYPES, type(_CATALOG_QUERIES))
|
|
assert isinstance(_APPROVED_FIXTURE_TYPES, type(_CATALOG_QUERIES))
|
|
with pytest.raises(TypeError):
|
|
_APPROVED_ADAPTER_TYPES[RecordingAdapter] = True
|
|
with pytest.raises(TypeError):
|
|
_APPROVED_FIXTURE_TYPES[RecordingAdapter] = True
|
|
|
|
|
|
def test_registration_mutators_are_not_publicly_exported():
|
|
for name in ("_register_approved_adapter", "_approved_fixture_reader"):
|
|
assert name not in gti_discovery.__all__
|
|
assert not hasattr(gti_discovery, name)
|
|
|
|
|
|
def test_threat_model_documents_accidental_boundary_scope():
|
|
from pathlib import Path
|
|
|
|
state = Path("docs/AI_PROJECT_STATE.md").read_text(encoding="utf-8")
|
|
report = Path("docs/MISSION_00_REPORT.md").read_text(encoding="utf-8")
|
|
required = (
|
|
"implementation mistakes", "accidental unauthorized operations",
|
|
"arbitrary SQL", "wrong database", "writes", "procedures/UDFs",
|
|
"injection", "dependency shadowing",
|
|
)
|
|
for phrase in required:
|
|
assert phrase in state
|
|
assert phrase in report
|
|
assert "arbitrary Python" in state
|
|
assert "same process" in report
|
|
|
|
|
|
def test_legitimate_firebird_syntax_reaches_driver_without_old_parser(monkeypatch):
|
|
adapter = RecordingAdapter()
|
|
query = _INTERNAL_QUERIES["expression_probe"]
|
|
monkeypatch.setattr("gti_discovery.read_only.validate_read_only", lambda _: (_ for _ in ()).throw(AssertionError()))
|
|
_execute_catalog(adapter, "expression_probe")
|
|
assert adapter.calls == [(query, ())]
|