truthound 1.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truthound/__init__.py +162 -0
- truthound/adapters.py +100 -0
- truthound/api.py +365 -0
- truthound/audit/__init__.py +248 -0
- truthound/audit/core.py +967 -0
- truthound/audit/filters.py +620 -0
- truthound/audit/formatters.py +707 -0
- truthound/audit/logger.py +902 -0
- truthound/audit/middleware.py +571 -0
- truthound/audit/storage.py +1083 -0
- truthound/benchmark/__init__.py +123 -0
- truthound/benchmark/base.py +757 -0
- truthound/benchmark/comparison.py +635 -0
- truthound/benchmark/generators.py +706 -0
- truthound/benchmark/reporters.py +718 -0
- truthound/benchmark/runner.py +635 -0
- truthound/benchmark/scenarios.py +712 -0
- truthound/cache.py +252 -0
- truthound/checkpoint/__init__.py +136 -0
- truthound/checkpoint/actions/__init__.py +164 -0
- truthound/checkpoint/actions/base.py +324 -0
- truthound/checkpoint/actions/custom.py +234 -0
- truthound/checkpoint/actions/discord_notify.py +290 -0
- truthound/checkpoint/actions/email_notify.py +405 -0
- truthound/checkpoint/actions/github_action.py +406 -0
- truthound/checkpoint/actions/opsgenie.py +1499 -0
- truthound/checkpoint/actions/pagerduty.py +226 -0
- truthound/checkpoint/actions/slack_notify.py +233 -0
- truthound/checkpoint/actions/store_result.py +249 -0
- truthound/checkpoint/actions/teams_notify.py +1570 -0
- truthound/checkpoint/actions/telegram_notify.py +419 -0
- truthound/checkpoint/actions/update_docs.py +552 -0
- truthound/checkpoint/actions/webhook.py +293 -0
- truthound/checkpoint/analytics/__init__.py +147 -0
- truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
- truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
- truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
- truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
- truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
- truthound/checkpoint/analytics/analyzers/base.py +270 -0
- truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
- truthound/checkpoint/analytics/analyzers/trend.py +314 -0
- truthound/checkpoint/analytics/models.py +292 -0
- truthound/checkpoint/analytics/protocols.py +549 -0
- truthound/checkpoint/analytics/service.py +718 -0
- truthound/checkpoint/analytics/stores/__init__.py +16 -0
- truthound/checkpoint/analytics/stores/base.py +306 -0
- truthound/checkpoint/analytics/stores/memory_store.py +353 -0
- truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
- truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
- truthound/checkpoint/async_actions.py +794 -0
- truthound/checkpoint/async_base.py +708 -0
- truthound/checkpoint/async_checkpoint.py +617 -0
- truthound/checkpoint/async_runner.py +639 -0
- truthound/checkpoint/checkpoint.py +527 -0
- truthound/checkpoint/ci/__init__.py +61 -0
- truthound/checkpoint/ci/detector.py +355 -0
- truthound/checkpoint/ci/reporter.py +436 -0
- truthound/checkpoint/ci/templates.py +454 -0
- truthound/checkpoint/circuitbreaker/__init__.py +133 -0
- truthound/checkpoint/circuitbreaker/breaker.py +542 -0
- truthound/checkpoint/circuitbreaker/core.py +252 -0
- truthound/checkpoint/circuitbreaker/detection.py +459 -0
- truthound/checkpoint/circuitbreaker/middleware.py +389 -0
- truthound/checkpoint/circuitbreaker/registry.py +357 -0
- truthound/checkpoint/distributed/__init__.py +139 -0
- truthound/checkpoint/distributed/backends/__init__.py +35 -0
- truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
- truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
- truthound/checkpoint/distributed/backends/local_backend.py +397 -0
- truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
- truthound/checkpoint/distributed/base.py +774 -0
- truthound/checkpoint/distributed/orchestrator.py +765 -0
- truthound/checkpoint/distributed/protocols.py +842 -0
- truthound/checkpoint/distributed/registry.py +449 -0
- truthound/checkpoint/idempotency/__init__.py +120 -0
- truthound/checkpoint/idempotency/core.py +295 -0
- truthound/checkpoint/idempotency/fingerprint.py +454 -0
- truthound/checkpoint/idempotency/locking.py +604 -0
- truthound/checkpoint/idempotency/service.py +592 -0
- truthound/checkpoint/idempotency/stores.py +653 -0
- truthound/checkpoint/monitoring/__init__.py +134 -0
- truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
- truthound/checkpoint/monitoring/aggregators/base.py +372 -0
- truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
- truthound/checkpoint/monitoring/aggregators/window.py +493 -0
- truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
- truthound/checkpoint/monitoring/collectors/base.py +257 -0
- truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
- truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
- truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
- truthound/checkpoint/monitoring/events.py +410 -0
- truthound/checkpoint/monitoring/protocols.py +636 -0
- truthound/checkpoint/monitoring/service.py +578 -0
- truthound/checkpoint/monitoring/views/__init__.py +17 -0
- truthound/checkpoint/monitoring/views/base.py +172 -0
- truthound/checkpoint/monitoring/views/queue_view.py +220 -0
- truthound/checkpoint/monitoring/views/task_view.py +240 -0
- truthound/checkpoint/monitoring/views/worker_view.py +263 -0
- truthound/checkpoint/registry.py +337 -0
- truthound/checkpoint/runner.py +356 -0
- truthound/checkpoint/transaction/__init__.py +133 -0
- truthound/checkpoint/transaction/base.py +389 -0
- truthound/checkpoint/transaction/compensatable.py +537 -0
- truthound/checkpoint/transaction/coordinator.py +576 -0
- truthound/checkpoint/transaction/executor.py +622 -0
- truthound/checkpoint/transaction/idempotency.py +534 -0
- truthound/checkpoint/transaction/saga/__init__.py +143 -0
- truthound/checkpoint/transaction/saga/builder.py +584 -0
- truthound/checkpoint/transaction/saga/definition.py +515 -0
- truthound/checkpoint/transaction/saga/event_store.py +542 -0
- truthound/checkpoint/transaction/saga/patterns.py +833 -0
- truthound/checkpoint/transaction/saga/runner.py +718 -0
- truthound/checkpoint/transaction/saga/state_machine.py +793 -0
- truthound/checkpoint/transaction/saga/strategies.py +780 -0
- truthound/checkpoint/transaction/saga/testing.py +886 -0
- truthound/checkpoint/triggers/__init__.py +58 -0
- truthound/checkpoint/triggers/base.py +237 -0
- truthound/checkpoint/triggers/event.py +385 -0
- truthound/checkpoint/triggers/schedule.py +355 -0
- truthound/cli.py +2358 -0
- truthound/cli_modules/__init__.py +124 -0
- truthound/cli_modules/advanced/__init__.py +45 -0
- truthound/cli_modules/advanced/benchmark.py +343 -0
- truthound/cli_modules/advanced/docs.py +225 -0
- truthound/cli_modules/advanced/lineage.py +209 -0
- truthound/cli_modules/advanced/ml.py +320 -0
- truthound/cli_modules/advanced/realtime.py +196 -0
- truthound/cli_modules/checkpoint/__init__.py +46 -0
- truthound/cli_modules/checkpoint/init.py +114 -0
- truthound/cli_modules/checkpoint/list.py +71 -0
- truthound/cli_modules/checkpoint/run.py +159 -0
- truthound/cli_modules/checkpoint/validate.py +67 -0
- truthound/cli_modules/common/__init__.py +71 -0
- truthound/cli_modules/common/errors.py +414 -0
- truthound/cli_modules/common/options.py +419 -0
- truthound/cli_modules/common/output.py +507 -0
- truthound/cli_modules/common/protocol.py +552 -0
- truthound/cli_modules/core/__init__.py +48 -0
- truthound/cli_modules/core/check.py +123 -0
- truthound/cli_modules/core/compare.py +104 -0
- truthound/cli_modules/core/learn.py +57 -0
- truthound/cli_modules/core/mask.py +77 -0
- truthound/cli_modules/core/profile.py +65 -0
- truthound/cli_modules/core/scan.py +61 -0
- truthound/cli_modules/profiler/__init__.py +51 -0
- truthound/cli_modules/profiler/auto_profile.py +175 -0
- truthound/cli_modules/profiler/metadata.py +107 -0
- truthound/cli_modules/profiler/suite.py +283 -0
- truthound/cli_modules/registry.py +431 -0
- truthound/cli_modules/scaffolding/__init__.py +89 -0
- truthound/cli_modules/scaffolding/base.py +631 -0
- truthound/cli_modules/scaffolding/commands.py +545 -0
- truthound/cli_modules/scaffolding/plugins.py +1072 -0
- truthound/cli_modules/scaffolding/reporters.py +594 -0
- truthound/cli_modules/scaffolding/validators.py +1127 -0
- truthound/common/__init__.py +18 -0
- truthound/common/resilience/__init__.py +130 -0
- truthound/common/resilience/bulkhead.py +266 -0
- truthound/common/resilience/circuit_breaker.py +516 -0
- truthound/common/resilience/composite.py +332 -0
- truthound/common/resilience/config.py +292 -0
- truthound/common/resilience/protocols.py +217 -0
- truthound/common/resilience/rate_limiter.py +404 -0
- truthound/common/resilience/retry.py +341 -0
- truthound/datadocs/__init__.py +260 -0
- truthound/datadocs/base.py +571 -0
- truthound/datadocs/builder.py +761 -0
- truthound/datadocs/charts.py +764 -0
- truthound/datadocs/dashboard/__init__.py +63 -0
- truthound/datadocs/dashboard/app.py +576 -0
- truthound/datadocs/dashboard/components.py +584 -0
- truthound/datadocs/dashboard/state.py +240 -0
- truthound/datadocs/engine/__init__.py +46 -0
- truthound/datadocs/engine/context.py +376 -0
- truthound/datadocs/engine/pipeline.py +618 -0
- truthound/datadocs/engine/registry.py +469 -0
- truthound/datadocs/exporters/__init__.py +49 -0
- truthound/datadocs/exporters/base.py +198 -0
- truthound/datadocs/exporters/html.py +178 -0
- truthound/datadocs/exporters/json_exporter.py +253 -0
- truthound/datadocs/exporters/markdown.py +284 -0
- truthound/datadocs/exporters/pdf.py +392 -0
- truthound/datadocs/i18n/__init__.py +86 -0
- truthound/datadocs/i18n/catalog.py +960 -0
- truthound/datadocs/i18n/formatting.py +505 -0
- truthound/datadocs/i18n/loader.py +256 -0
- truthound/datadocs/i18n/plurals.py +378 -0
- truthound/datadocs/renderers/__init__.py +42 -0
- truthound/datadocs/renderers/base.py +401 -0
- truthound/datadocs/renderers/custom.py +342 -0
- truthound/datadocs/renderers/jinja.py +697 -0
- truthound/datadocs/sections.py +736 -0
- truthound/datadocs/styles.py +931 -0
- truthound/datadocs/themes/__init__.py +101 -0
- truthound/datadocs/themes/base.py +336 -0
- truthound/datadocs/themes/default.py +417 -0
- truthound/datadocs/themes/enterprise.py +419 -0
- truthound/datadocs/themes/loader.py +336 -0
- truthound/datadocs/themes.py +301 -0
- truthound/datadocs/transformers/__init__.py +57 -0
- truthound/datadocs/transformers/base.py +268 -0
- truthound/datadocs/transformers/enrichers.py +544 -0
- truthound/datadocs/transformers/filters.py +447 -0
- truthound/datadocs/transformers/i18n.py +468 -0
- truthound/datadocs/versioning/__init__.py +62 -0
- truthound/datadocs/versioning/diff.py +639 -0
- truthound/datadocs/versioning/storage.py +497 -0
- truthound/datadocs/versioning/version.py +358 -0
- truthound/datasources/__init__.py +223 -0
- truthound/datasources/_async_protocols.py +222 -0
- truthound/datasources/_protocols.py +159 -0
- truthound/datasources/adapters.py +428 -0
- truthound/datasources/async_base.py +599 -0
- truthound/datasources/async_factory.py +511 -0
- truthound/datasources/base.py +516 -0
- truthound/datasources/factory.py +433 -0
- truthound/datasources/nosql/__init__.py +47 -0
- truthound/datasources/nosql/base.py +487 -0
- truthound/datasources/nosql/elasticsearch.py +801 -0
- truthound/datasources/nosql/mongodb.py +636 -0
- truthound/datasources/pandas_optimized.py +582 -0
- truthound/datasources/pandas_source.py +216 -0
- truthound/datasources/polars_source.py +395 -0
- truthound/datasources/spark_source.py +479 -0
- truthound/datasources/sql/__init__.py +154 -0
- truthound/datasources/sql/base.py +710 -0
- truthound/datasources/sql/bigquery.py +410 -0
- truthound/datasources/sql/cloud_base.py +199 -0
- truthound/datasources/sql/databricks.py +471 -0
- truthound/datasources/sql/mysql.py +316 -0
- truthound/datasources/sql/oracle.py +427 -0
- truthound/datasources/sql/postgresql.py +321 -0
- truthound/datasources/sql/redshift.py +479 -0
- truthound/datasources/sql/snowflake.py +439 -0
- truthound/datasources/sql/sqlite.py +286 -0
- truthound/datasources/sql/sqlserver.py +437 -0
- truthound/datasources/streaming/__init__.py +47 -0
- truthound/datasources/streaming/base.py +350 -0
- truthound/datasources/streaming/kafka.py +670 -0
- truthound/decorators.py +98 -0
- truthound/docs/__init__.py +69 -0
- truthound/docs/extractor.py +971 -0
- truthound/docs/generator.py +601 -0
- truthound/docs/parser.py +1037 -0
- truthound/docs/renderer.py +999 -0
- truthound/drift/__init__.py +22 -0
- truthound/drift/compare.py +189 -0
- truthound/drift/detectors.py +464 -0
- truthound/drift/report.py +160 -0
- truthound/execution/__init__.py +65 -0
- truthound/execution/_protocols.py +324 -0
- truthound/execution/base.py +576 -0
- truthound/execution/distributed/__init__.py +179 -0
- truthound/execution/distributed/aggregations.py +731 -0
- truthound/execution/distributed/arrow_bridge.py +817 -0
- truthound/execution/distributed/base.py +550 -0
- truthound/execution/distributed/dask_engine.py +976 -0
- truthound/execution/distributed/mixins.py +766 -0
- truthound/execution/distributed/protocols.py +756 -0
- truthound/execution/distributed/ray_engine.py +1127 -0
- truthound/execution/distributed/registry.py +446 -0
- truthound/execution/distributed/spark_engine.py +1011 -0
- truthound/execution/distributed/validator_adapter.py +682 -0
- truthound/execution/pandas_engine.py +401 -0
- truthound/execution/polars_engine.py +497 -0
- truthound/execution/pushdown/__init__.py +230 -0
- truthound/execution/pushdown/ast.py +1550 -0
- truthound/execution/pushdown/builder.py +1550 -0
- truthound/execution/pushdown/dialects.py +1072 -0
- truthound/execution/pushdown/executor.py +829 -0
- truthound/execution/pushdown/optimizer.py +1041 -0
- truthound/execution/sql_engine.py +518 -0
- truthound/infrastructure/__init__.py +189 -0
- truthound/infrastructure/audit.py +1515 -0
- truthound/infrastructure/config.py +1133 -0
- truthound/infrastructure/encryption.py +1132 -0
- truthound/infrastructure/logging.py +1503 -0
- truthound/infrastructure/metrics.py +1220 -0
- truthound/lineage/__init__.py +89 -0
- truthound/lineage/base.py +746 -0
- truthound/lineage/impact_analysis.py +474 -0
- truthound/lineage/integrations/__init__.py +22 -0
- truthound/lineage/integrations/openlineage.py +548 -0
- truthound/lineage/tracker.py +512 -0
- truthound/lineage/visualization/__init__.py +33 -0
- truthound/lineage/visualization/protocols.py +145 -0
- truthound/lineage/visualization/renderers/__init__.py +20 -0
- truthound/lineage/visualization/renderers/cytoscape.py +329 -0
- truthound/lineage/visualization/renderers/d3.py +331 -0
- truthound/lineage/visualization/renderers/graphviz.py +276 -0
- truthound/lineage/visualization/renderers/mermaid.py +308 -0
- truthound/maskers.py +113 -0
- truthound/ml/__init__.py +124 -0
- truthound/ml/anomaly_models/__init__.py +31 -0
- truthound/ml/anomaly_models/ensemble.py +362 -0
- truthound/ml/anomaly_models/isolation_forest.py +444 -0
- truthound/ml/anomaly_models/statistical.py +392 -0
- truthound/ml/base.py +1178 -0
- truthound/ml/drift_detection/__init__.py +26 -0
- truthound/ml/drift_detection/concept.py +381 -0
- truthound/ml/drift_detection/distribution.py +361 -0
- truthound/ml/drift_detection/feature.py +442 -0
- truthound/ml/drift_detection/multivariate.py +495 -0
- truthound/ml/monitoring/__init__.py +88 -0
- truthound/ml/monitoring/alerting/__init__.py +33 -0
- truthound/ml/monitoring/alerting/handlers.py +427 -0
- truthound/ml/monitoring/alerting/rules.py +508 -0
- truthound/ml/monitoring/collectors/__init__.py +19 -0
- truthound/ml/monitoring/collectors/composite.py +105 -0
- truthound/ml/monitoring/collectors/drift.py +324 -0
- truthound/ml/monitoring/collectors/performance.py +179 -0
- truthound/ml/monitoring/collectors/quality.py +369 -0
- truthound/ml/monitoring/monitor.py +536 -0
- truthound/ml/monitoring/protocols.py +451 -0
- truthound/ml/monitoring/stores/__init__.py +15 -0
- truthound/ml/monitoring/stores/memory.py +201 -0
- truthound/ml/monitoring/stores/prometheus.py +296 -0
- truthound/ml/rule_learning/__init__.py +25 -0
- truthound/ml/rule_learning/constraint_miner.py +443 -0
- truthound/ml/rule_learning/pattern_learner.py +499 -0
- truthound/ml/rule_learning/profile_learner.py +462 -0
- truthound/multitenancy/__init__.py +326 -0
- truthound/multitenancy/core.py +852 -0
- truthound/multitenancy/integration.py +597 -0
- truthound/multitenancy/isolation.py +630 -0
- truthound/multitenancy/manager.py +770 -0
- truthound/multitenancy/middleware.py +765 -0
- truthound/multitenancy/quota.py +537 -0
- truthound/multitenancy/resolvers.py +603 -0
- truthound/multitenancy/storage.py +703 -0
- truthound/observability/__init__.py +307 -0
- truthound/observability/context.py +531 -0
- truthound/observability/instrumentation.py +611 -0
- truthound/observability/logging.py +887 -0
- truthound/observability/metrics.py +1157 -0
- truthound/observability/tracing/__init__.py +178 -0
- truthound/observability/tracing/baggage.py +310 -0
- truthound/observability/tracing/config.py +426 -0
- truthound/observability/tracing/exporter.py +787 -0
- truthound/observability/tracing/integration.py +1018 -0
- truthound/observability/tracing/otel/__init__.py +146 -0
- truthound/observability/tracing/otel/adapter.py +982 -0
- truthound/observability/tracing/otel/bridge.py +1177 -0
- truthound/observability/tracing/otel/compat.py +681 -0
- truthound/observability/tracing/otel/config.py +691 -0
- truthound/observability/tracing/otel/detection.py +327 -0
- truthound/observability/tracing/otel/protocols.py +426 -0
- truthound/observability/tracing/processor.py +561 -0
- truthound/observability/tracing/propagator.py +757 -0
- truthound/observability/tracing/provider.py +569 -0
- truthound/observability/tracing/resource.py +515 -0
- truthound/observability/tracing/sampler.py +487 -0
- truthound/observability/tracing/span.py +676 -0
- truthound/plugins/__init__.py +198 -0
- truthound/plugins/base.py +599 -0
- truthound/plugins/cli.py +680 -0
- truthound/plugins/dependencies/__init__.py +42 -0
- truthound/plugins/dependencies/graph.py +422 -0
- truthound/plugins/dependencies/resolver.py +417 -0
- truthound/plugins/discovery.py +379 -0
- truthound/plugins/docs/__init__.py +46 -0
- truthound/plugins/docs/extractor.py +444 -0
- truthound/plugins/docs/renderer.py +499 -0
- truthound/plugins/enterprise_manager.py +877 -0
- truthound/plugins/examples/__init__.py +19 -0
- truthound/plugins/examples/custom_validators.py +317 -0
- truthound/plugins/examples/slack_notifier.py +312 -0
- truthound/plugins/examples/xml_reporter.py +254 -0
- truthound/plugins/hooks.py +558 -0
- truthound/plugins/lifecycle/__init__.py +43 -0
- truthound/plugins/lifecycle/hot_reload.py +402 -0
- truthound/plugins/lifecycle/manager.py +371 -0
- truthound/plugins/manager.py +736 -0
- truthound/plugins/registry.py +338 -0
- truthound/plugins/security/__init__.py +93 -0
- truthound/plugins/security/exceptions.py +332 -0
- truthound/plugins/security/policies.py +348 -0
- truthound/plugins/security/protocols.py +643 -0
- truthound/plugins/security/sandbox/__init__.py +45 -0
- truthound/plugins/security/sandbox/context.py +158 -0
- truthound/plugins/security/sandbox/engines/__init__.py +19 -0
- truthound/plugins/security/sandbox/engines/container.py +379 -0
- truthound/plugins/security/sandbox/engines/noop.py +144 -0
- truthound/plugins/security/sandbox/engines/process.py +336 -0
- truthound/plugins/security/sandbox/factory.py +211 -0
- truthound/plugins/security/signing/__init__.py +57 -0
- truthound/plugins/security/signing/service.py +330 -0
- truthound/plugins/security/signing/trust_store.py +368 -0
- truthound/plugins/security/signing/verifier.py +459 -0
- truthound/plugins/versioning/__init__.py +41 -0
- truthound/plugins/versioning/constraints.py +297 -0
- truthound/plugins/versioning/resolver.py +329 -0
- truthound/profiler/__init__.py +1729 -0
- truthound/profiler/_lazy.py +452 -0
- truthound/profiler/ab_testing/__init__.py +80 -0
- truthound/profiler/ab_testing/analysis.py +449 -0
- truthound/profiler/ab_testing/base.py +257 -0
- truthound/profiler/ab_testing/experiment.py +395 -0
- truthound/profiler/ab_testing/tracking.py +368 -0
- truthound/profiler/auto_threshold.py +1170 -0
- truthound/profiler/base.py +579 -0
- truthound/profiler/cache_patterns.py +911 -0
- truthound/profiler/caching.py +1303 -0
- truthound/profiler/column_profiler.py +712 -0
- truthound/profiler/comparison.py +1007 -0
- truthound/profiler/custom_patterns.py +1170 -0
- truthound/profiler/dashboard/__init__.py +50 -0
- truthound/profiler/dashboard/app.py +476 -0
- truthound/profiler/dashboard/components.py +457 -0
- truthound/profiler/dashboard/config.py +72 -0
- truthound/profiler/distributed/__init__.py +83 -0
- truthound/profiler/distributed/base.py +281 -0
- truthound/profiler/distributed/dask_backend.py +498 -0
- truthound/profiler/distributed/local_backend.py +293 -0
- truthound/profiler/distributed/profiler.py +304 -0
- truthound/profiler/distributed/ray_backend.py +374 -0
- truthound/profiler/distributed/spark_backend.py +375 -0
- truthound/profiler/distributed.py +1366 -0
- truthound/profiler/enterprise_sampling.py +1065 -0
- truthound/profiler/errors.py +488 -0
- truthound/profiler/evolution/__init__.py +91 -0
- truthound/profiler/evolution/alerts.py +426 -0
- truthound/profiler/evolution/changes.py +206 -0
- truthound/profiler/evolution/compatibility.py +365 -0
- truthound/profiler/evolution/detector.py +372 -0
- truthound/profiler/evolution/protocols.py +121 -0
- truthound/profiler/generators/__init__.py +48 -0
- truthound/profiler/generators/base.py +384 -0
- truthound/profiler/generators/ml_rules.py +375 -0
- truthound/profiler/generators/pattern_rules.py +384 -0
- truthound/profiler/generators/schema_rules.py +267 -0
- truthound/profiler/generators/stats_rules.py +324 -0
- truthound/profiler/generators/suite_generator.py +857 -0
- truthound/profiler/i18n.py +1542 -0
- truthound/profiler/incremental.py +554 -0
- truthound/profiler/incremental_validation.py +1710 -0
- truthound/profiler/integration/__init__.py +73 -0
- truthound/profiler/integration/adapters.py +345 -0
- truthound/profiler/integration/context.py +371 -0
- truthound/profiler/integration/executor.py +527 -0
- truthound/profiler/integration/naming.py +75 -0
- truthound/profiler/integration/protocols.py +243 -0
- truthound/profiler/memory.py +1185 -0
- truthound/profiler/migration/__init__.py +60 -0
- truthound/profiler/migration/base.py +345 -0
- truthound/profiler/migration/manager.py +444 -0
- truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
- truthound/profiler/ml/__init__.py +73 -0
- truthound/profiler/ml/base.py +244 -0
- truthound/profiler/ml/classifier.py +507 -0
- truthound/profiler/ml/feature_extraction.py +604 -0
- truthound/profiler/ml/pretrained.py +448 -0
- truthound/profiler/ml_inference.py +1276 -0
- truthound/profiler/native_patterns.py +815 -0
- truthound/profiler/observability.py +1184 -0
- truthound/profiler/process_timeout.py +1566 -0
- truthound/profiler/progress.py +568 -0
- truthound/profiler/progress_callbacks.py +1734 -0
- truthound/profiler/quality.py +1345 -0
- truthound/profiler/resilience.py +1180 -0
- truthound/profiler/sampled_matcher.py +794 -0
- truthound/profiler/sampling.py +1288 -0
- truthound/profiler/scheduling/__init__.py +82 -0
- truthound/profiler/scheduling/protocols.py +214 -0
- truthound/profiler/scheduling/scheduler.py +474 -0
- truthound/profiler/scheduling/storage.py +457 -0
- truthound/profiler/scheduling/triggers.py +449 -0
- truthound/profiler/schema.py +603 -0
- truthound/profiler/streaming.py +685 -0
- truthound/profiler/streaming_patterns.py +1354 -0
- truthound/profiler/suite_cli.py +625 -0
- truthound/profiler/suite_config.py +789 -0
- truthound/profiler/suite_export.py +1268 -0
- truthound/profiler/table_profiler.py +547 -0
- truthound/profiler/timeout.py +565 -0
- truthound/profiler/validation.py +1532 -0
- truthound/profiler/visualization/__init__.py +118 -0
- truthound/profiler/visualization/base.py +346 -0
- truthound/profiler/visualization/generator.py +1259 -0
- truthound/profiler/visualization/plotly_renderer.py +811 -0
- truthound/profiler/visualization/renderers.py +669 -0
- truthound/profiler/visualization/sections.py +540 -0
- truthound/profiler/visualization.py +2122 -0
- truthound/profiler/yaml_validation.py +1151 -0
- truthound/py.typed +0 -0
- truthound/ratelimit/__init__.py +248 -0
- truthound/ratelimit/algorithms.py +1108 -0
- truthound/ratelimit/core.py +573 -0
- truthound/ratelimit/integration.py +532 -0
- truthound/ratelimit/limiter.py +663 -0
- truthound/ratelimit/middleware.py +700 -0
- truthound/ratelimit/policy.py +792 -0
- truthound/ratelimit/storage.py +763 -0
- truthound/rbac/__init__.py +340 -0
- truthound/rbac/core.py +976 -0
- truthound/rbac/integration.py +760 -0
- truthound/rbac/manager.py +1052 -0
- truthound/rbac/middleware.py +842 -0
- truthound/rbac/policy.py +954 -0
- truthound/rbac/storage.py +878 -0
- truthound/realtime/__init__.py +141 -0
- truthound/realtime/adapters/__init__.py +43 -0
- truthound/realtime/adapters/base.py +533 -0
- truthound/realtime/adapters/kafka.py +487 -0
- truthound/realtime/adapters/kinesis.py +479 -0
- truthound/realtime/adapters/mock.py +243 -0
- truthound/realtime/base.py +553 -0
- truthound/realtime/factory.py +382 -0
- truthound/realtime/incremental.py +660 -0
- truthound/realtime/processing/__init__.py +67 -0
- truthound/realtime/processing/exactly_once.py +575 -0
- truthound/realtime/processing/state.py +547 -0
- truthound/realtime/processing/windows.py +647 -0
- truthound/realtime/protocols.py +569 -0
- truthound/realtime/streaming.py +605 -0
- truthound/realtime/testing/__init__.py +32 -0
- truthound/realtime/testing/containers.py +615 -0
- truthound/realtime/testing/fixtures.py +484 -0
- truthound/report.py +280 -0
- truthound/reporters/__init__.py +46 -0
- truthound/reporters/_protocols.py +30 -0
- truthound/reporters/base.py +324 -0
- truthound/reporters/ci/__init__.py +66 -0
- truthound/reporters/ci/azure.py +436 -0
- truthound/reporters/ci/base.py +509 -0
- truthound/reporters/ci/bitbucket.py +567 -0
- truthound/reporters/ci/circleci.py +547 -0
- truthound/reporters/ci/detection.py +364 -0
- truthound/reporters/ci/factory.py +182 -0
- truthound/reporters/ci/github.py +388 -0
- truthound/reporters/ci/gitlab.py +471 -0
- truthound/reporters/ci/jenkins.py +525 -0
- truthound/reporters/console_reporter.py +299 -0
- truthound/reporters/factory.py +211 -0
- truthound/reporters/html_reporter.py +524 -0
- truthound/reporters/json_reporter.py +256 -0
- truthound/reporters/markdown_reporter.py +280 -0
- truthound/reporters/sdk/__init__.py +174 -0
- truthound/reporters/sdk/builder.py +558 -0
- truthound/reporters/sdk/mixins.py +1150 -0
- truthound/reporters/sdk/schema.py +1493 -0
- truthound/reporters/sdk/templates.py +666 -0
- truthound/reporters/sdk/testing.py +968 -0
- truthound/scanners.py +170 -0
- truthound/scheduling/__init__.py +122 -0
- truthound/scheduling/cron.py +1136 -0
- truthound/scheduling/presets.py +212 -0
- truthound/schema.py +275 -0
- truthound/secrets/__init__.py +173 -0
- truthound/secrets/base.py +618 -0
- truthound/secrets/cloud.py +682 -0
- truthound/secrets/integration.py +507 -0
- truthound/secrets/manager.py +633 -0
- truthound/secrets/oidc/__init__.py +172 -0
- truthound/secrets/oidc/base.py +902 -0
- truthound/secrets/oidc/credential_provider.py +623 -0
- truthound/secrets/oidc/exchangers.py +1001 -0
- truthound/secrets/oidc/github/__init__.py +110 -0
- truthound/secrets/oidc/github/claims.py +718 -0
- truthound/secrets/oidc/github/enhanced_provider.py +693 -0
- truthound/secrets/oidc/github/trust_policy.py +742 -0
- truthound/secrets/oidc/github/verification.py +723 -0
- truthound/secrets/oidc/github/workflow.py +691 -0
- truthound/secrets/oidc/providers.py +825 -0
- truthound/secrets/providers.py +506 -0
- truthound/secrets/resolver.py +495 -0
- truthound/stores/__init__.py +177 -0
- truthound/stores/backends/__init__.py +18 -0
- truthound/stores/backends/_protocols.py +340 -0
- truthound/stores/backends/azure_blob.py +530 -0
- truthound/stores/backends/concurrent_filesystem.py +915 -0
- truthound/stores/backends/connection_pool.py +1365 -0
- truthound/stores/backends/database.py +743 -0
- truthound/stores/backends/filesystem.py +538 -0
- truthound/stores/backends/gcs.py +399 -0
- truthound/stores/backends/memory.py +354 -0
- truthound/stores/backends/s3.py +434 -0
- truthound/stores/backpressure/__init__.py +84 -0
- truthound/stores/backpressure/base.py +375 -0
- truthound/stores/backpressure/circuit_breaker.py +434 -0
- truthound/stores/backpressure/monitor.py +376 -0
- truthound/stores/backpressure/strategies.py +677 -0
- truthound/stores/base.py +551 -0
- truthound/stores/batching/__init__.py +65 -0
- truthound/stores/batching/base.py +305 -0
- truthound/stores/batching/buffer.py +370 -0
- truthound/stores/batching/store.py +248 -0
- truthound/stores/batching/writer.py +521 -0
- truthound/stores/caching/__init__.py +60 -0
- truthound/stores/caching/backends.py +684 -0
- truthound/stores/caching/base.py +356 -0
- truthound/stores/caching/store.py +305 -0
- truthound/stores/compression/__init__.py +193 -0
- truthound/stores/compression/adaptive.py +694 -0
- truthound/stores/compression/base.py +514 -0
- truthound/stores/compression/pipeline.py +868 -0
- truthound/stores/compression/providers.py +672 -0
- truthound/stores/compression/streaming.py +832 -0
- truthound/stores/concurrency/__init__.py +81 -0
- truthound/stores/concurrency/atomic.py +556 -0
- truthound/stores/concurrency/index.py +775 -0
- truthound/stores/concurrency/locks.py +576 -0
- truthound/stores/concurrency/manager.py +482 -0
- truthound/stores/encryption/__init__.py +297 -0
- truthound/stores/encryption/base.py +952 -0
- truthound/stores/encryption/keys.py +1191 -0
- truthound/stores/encryption/pipeline.py +903 -0
- truthound/stores/encryption/providers.py +953 -0
- truthound/stores/encryption/streaming.py +950 -0
- truthound/stores/expectations.py +227 -0
- truthound/stores/factory.py +246 -0
- truthound/stores/migration/__init__.py +75 -0
- truthound/stores/migration/base.py +480 -0
- truthound/stores/migration/manager.py +347 -0
- truthound/stores/migration/registry.py +382 -0
- truthound/stores/migration/store.py +559 -0
- truthound/stores/observability/__init__.py +106 -0
- truthound/stores/observability/audit.py +718 -0
- truthound/stores/observability/config.py +270 -0
- truthound/stores/observability/factory.py +208 -0
- truthound/stores/observability/metrics.py +636 -0
- truthound/stores/observability/protocols.py +410 -0
- truthound/stores/observability/store.py +570 -0
- truthound/stores/observability/tracing.py +784 -0
- truthound/stores/replication/__init__.py +76 -0
- truthound/stores/replication/base.py +260 -0
- truthound/stores/replication/monitor.py +269 -0
- truthound/stores/replication/store.py +439 -0
- truthound/stores/replication/syncer.py +391 -0
- truthound/stores/results.py +359 -0
- truthound/stores/retention/__init__.py +77 -0
- truthound/stores/retention/base.py +378 -0
- truthound/stores/retention/policies.py +621 -0
- truthound/stores/retention/scheduler.py +279 -0
- truthound/stores/retention/store.py +526 -0
- truthound/stores/streaming/__init__.py +138 -0
- truthound/stores/streaming/base.py +801 -0
- truthound/stores/streaming/database.py +984 -0
- truthound/stores/streaming/filesystem.py +719 -0
- truthound/stores/streaming/reader.py +629 -0
- truthound/stores/streaming/s3.py +843 -0
- truthound/stores/streaming/writer.py +790 -0
- truthound/stores/tiering/__init__.py +108 -0
- truthound/stores/tiering/base.py +462 -0
- truthound/stores/tiering/manager.py +249 -0
- truthound/stores/tiering/policies.py +692 -0
- truthound/stores/tiering/store.py +526 -0
- truthound/stores/versioning/__init__.py +56 -0
- truthound/stores/versioning/base.py +376 -0
- truthound/stores/versioning/store.py +660 -0
- truthound/stores/versioning/strategies.py +353 -0
- truthound/types.py +56 -0
- truthound/validators/__init__.py +774 -0
- truthound/validators/aggregate/__init__.py +27 -0
- truthound/validators/aggregate/central.py +116 -0
- truthound/validators/aggregate/extremes.py +116 -0
- truthound/validators/aggregate/spread.py +118 -0
- truthound/validators/aggregate/sum.py +64 -0
- truthound/validators/aggregate/type.py +78 -0
- truthound/validators/anomaly/__init__.py +93 -0
- truthound/validators/anomaly/base.py +431 -0
- truthound/validators/anomaly/ml_based.py +1190 -0
- truthound/validators/anomaly/multivariate.py +647 -0
- truthound/validators/anomaly/statistical.py +599 -0
- truthound/validators/base.py +1089 -0
- truthound/validators/business_rule/__init__.py +46 -0
- truthound/validators/business_rule/base.py +147 -0
- truthound/validators/business_rule/checksum.py +509 -0
- truthound/validators/business_rule/financial.py +526 -0
- truthound/validators/cache.py +733 -0
- truthound/validators/completeness/__init__.py +39 -0
- truthound/validators/completeness/conditional.py +73 -0
- truthound/validators/completeness/default.py +98 -0
- truthound/validators/completeness/empty.py +103 -0
- truthound/validators/completeness/nan.py +337 -0
- truthound/validators/completeness/null.py +152 -0
- truthound/validators/cross_table/__init__.py +17 -0
- truthound/validators/cross_table/aggregate.py +333 -0
- truthound/validators/cross_table/row_count.py +122 -0
- truthound/validators/datetime/__init__.py +29 -0
- truthound/validators/datetime/format.py +78 -0
- truthound/validators/datetime/freshness.py +269 -0
- truthound/validators/datetime/order.py +73 -0
- truthound/validators/datetime/parseable.py +185 -0
- truthound/validators/datetime/range.py +202 -0
- truthound/validators/datetime/timezone.py +69 -0
- truthound/validators/distribution/__init__.py +49 -0
- truthound/validators/distribution/distribution.py +128 -0
- truthound/validators/distribution/monotonic.py +119 -0
- truthound/validators/distribution/outlier.py +178 -0
- truthound/validators/distribution/quantile.py +80 -0
- truthound/validators/distribution/range.py +254 -0
- truthound/validators/distribution/set.py +125 -0
- truthound/validators/distribution/statistical.py +459 -0
- truthound/validators/drift/__init__.py +79 -0
- truthound/validators/drift/base.py +427 -0
- truthound/validators/drift/multi_feature.py +401 -0
- truthound/validators/drift/numeric.py +395 -0
- truthound/validators/drift/psi.py +446 -0
- truthound/validators/drift/statistical.py +510 -0
- truthound/validators/enterprise.py +1658 -0
- truthound/validators/geospatial/__init__.py +80 -0
- truthound/validators/geospatial/base.py +97 -0
- truthound/validators/geospatial/boundary.py +238 -0
- truthound/validators/geospatial/coordinate.py +351 -0
- truthound/validators/geospatial/distance.py +399 -0
- truthound/validators/geospatial/polygon.py +665 -0
- truthound/validators/i18n/__init__.py +308 -0
- truthound/validators/i18n/bidi.py +571 -0
- truthound/validators/i18n/catalogs.py +570 -0
- truthound/validators/i18n/dialects.py +763 -0
- truthound/validators/i18n/extended_catalogs.py +549 -0
- truthound/validators/i18n/formatting.py +1434 -0
- truthound/validators/i18n/loader.py +1020 -0
- truthound/validators/i18n/messages.py +521 -0
- truthound/validators/i18n/plural.py +683 -0
- truthound/validators/i18n/protocols.py +855 -0
- truthound/validators/i18n/tms.py +1162 -0
- truthound/validators/localization/__init__.py +53 -0
- truthound/validators/localization/base.py +122 -0
- truthound/validators/localization/chinese.py +362 -0
- truthound/validators/localization/japanese.py +275 -0
- truthound/validators/localization/korean.py +524 -0
- truthound/validators/memory/__init__.py +94 -0
- truthound/validators/memory/approximate_knn.py +506 -0
- truthound/validators/memory/base.py +547 -0
- truthound/validators/memory/sgd_online.py +719 -0
- truthound/validators/memory/streaming_ecdf.py +753 -0
- truthound/validators/ml_feature/__init__.py +54 -0
- truthound/validators/ml_feature/base.py +249 -0
- truthound/validators/ml_feature/correlation.py +299 -0
- truthound/validators/ml_feature/leakage.py +344 -0
- truthound/validators/ml_feature/null_impact.py +270 -0
- truthound/validators/ml_feature/scale.py +264 -0
- truthound/validators/multi_column/__init__.py +89 -0
- truthound/validators/multi_column/arithmetic.py +284 -0
- truthound/validators/multi_column/base.py +231 -0
- truthound/validators/multi_column/comparison.py +273 -0
- truthound/validators/multi_column/consistency.py +312 -0
- truthound/validators/multi_column/statistical.py +299 -0
- truthound/validators/optimization/__init__.py +164 -0
- truthound/validators/optimization/aggregation.py +563 -0
- truthound/validators/optimization/covariance.py +556 -0
- truthound/validators/optimization/geo.py +626 -0
- truthound/validators/optimization/graph.py +587 -0
- truthound/validators/optimization/orchestrator.py +970 -0
- truthound/validators/optimization/profiling.py +1312 -0
- truthound/validators/privacy/__init__.py +223 -0
- truthound/validators/privacy/base.py +635 -0
- truthound/validators/privacy/ccpa.py +670 -0
- truthound/validators/privacy/gdpr.py +728 -0
- truthound/validators/privacy/global_patterns.py +604 -0
- truthound/validators/privacy/plugins.py +867 -0
- truthound/validators/profiling/__init__.py +52 -0
- truthound/validators/profiling/base.py +175 -0
- truthound/validators/profiling/cardinality.py +312 -0
- truthound/validators/profiling/entropy.py +391 -0
- truthound/validators/profiling/frequency.py +455 -0
- truthound/validators/pushdown_support.py +660 -0
- truthound/validators/query/__init__.py +91 -0
- truthound/validators/query/aggregate.py +346 -0
- truthound/validators/query/base.py +246 -0
- truthound/validators/query/column.py +249 -0
- truthound/validators/query/expression.py +274 -0
- truthound/validators/query/result.py +323 -0
- truthound/validators/query/row_count.py +264 -0
- truthound/validators/referential/__init__.py +80 -0
- truthound/validators/referential/base.py +395 -0
- truthound/validators/referential/cascade.py +391 -0
- truthound/validators/referential/circular.py +563 -0
- truthound/validators/referential/foreign_key.py +624 -0
- truthound/validators/referential/orphan.py +485 -0
- truthound/validators/registry.py +112 -0
- truthound/validators/schema/__init__.py +41 -0
- truthound/validators/schema/column_count.py +142 -0
- truthound/validators/schema/column_exists.py +80 -0
- truthound/validators/schema/column_order.py +82 -0
- truthound/validators/schema/column_pair.py +85 -0
- truthound/validators/schema/column_pair_set.py +195 -0
- truthound/validators/schema/column_type.py +94 -0
- truthound/validators/schema/multi_column.py +53 -0
- truthound/validators/schema/multi_column_aggregate.py +175 -0
- truthound/validators/schema/referential.py +274 -0
- truthound/validators/schema/table_schema.py +91 -0
- truthound/validators/schema_validator.py +219 -0
- truthound/validators/sdk/__init__.py +250 -0
- truthound/validators/sdk/builder.py +680 -0
- truthound/validators/sdk/decorators.py +474 -0
- truthound/validators/sdk/enterprise/__init__.py +211 -0
- truthound/validators/sdk/enterprise/docs.py +725 -0
- truthound/validators/sdk/enterprise/fuzzing.py +659 -0
- truthound/validators/sdk/enterprise/licensing.py +709 -0
- truthound/validators/sdk/enterprise/manager.py +543 -0
- truthound/validators/sdk/enterprise/resources.py +628 -0
- truthound/validators/sdk/enterprise/sandbox.py +766 -0
- truthound/validators/sdk/enterprise/signing.py +603 -0
- truthound/validators/sdk/enterprise/templates.py +865 -0
- truthound/validators/sdk/enterprise/versioning.py +659 -0
- truthound/validators/sdk/templates.py +757 -0
- truthound/validators/sdk/testing.py +807 -0
- truthound/validators/security/__init__.py +181 -0
- truthound/validators/security/redos/__init__.py +182 -0
- truthound/validators/security/redos/core.py +861 -0
- truthound/validators/security/redos/cpu_monitor.py +593 -0
- truthound/validators/security/redos/cve_database.py +791 -0
- truthound/validators/security/redos/ml/__init__.py +155 -0
- truthound/validators/security/redos/ml/base.py +785 -0
- truthound/validators/security/redos/ml/datasets.py +618 -0
- truthound/validators/security/redos/ml/features.py +359 -0
- truthound/validators/security/redos/ml/models.py +1000 -0
- truthound/validators/security/redos/ml/predictor.py +507 -0
- truthound/validators/security/redos/ml/storage.py +632 -0
- truthound/validators/security/redos/ml/training.py +571 -0
- truthound/validators/security/redos/ml_analyzer.py +937 -0
- truthound/validators/security/redos/optimizer.py +674 -0
- truthound/validators/security/redos/profiler.py +682 -0
- truthound/validators/security/redos/re2_engine.py +709 -0
- truthound/validators/security/redos.py +886 -0
- truthound/validators/security/sql_security.py +1247 -0
- truthound/validators/streaming/__init__.py +126 -0
- truthound/validators/streaming/base.py +292 -0
- truthound/validators/streaming/completeness.py +210 -0
- truthound/validators/streaming/mixin.py +575 -0
- truthound/validators/streaming/range.py +308 -0
- truthound/validators/streaming/sources.py +846 -0
- truthound/validators/string/__init__.py +57 -0
- truthound/validators/string/casing.py +158 -0
- truthound/validators/string/charset.py +96 -0
- truthound/validators/string/format.py +501 -0
- truthound/validators/string/json.py +77 -0
- truthound/validators/string/json_schema.py +184 -0
- truthound/validators/string/length.py +104 -0
- truthound/validators/string/like_pattern.py +237 -0
- truthound/validators/string/regex.py +202 -0
- truthound/validators/string/regex_extended.py +435 -0
- truthound/validators/table/__init__.py +88 -0
- truthound/validators/table/base.py +78 -0
- truthound/validators/table/column_count.py +198 -0
- truthound/validators/table/freshness.py +362 -0
- truthound/validators/table/row_count.py +251 -0
- truthound/validators/table/schema.py +333 -0
- truthound/validators/table/size.py +285 -0
- truthound/validators/timeout/__init__.py +102 -0
- truthound/validators/timeout/advanced/__init__.py +247 -0
- truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
- truthound/validators/timeout/advanced/prediction.py +773 -0
- truthound/validators/timeout/advanced/priority.py +618 -0
- truthound/validators/timeout/advanced/redis_backend.py +770 -0
- truthound/validators/timeout/advanced/retry.py +721 -0
- truthound/validators/timeout/advanced/sampling.py +788 -0
- truthound/validators/timeout/advanced/sla.py +661 -0
- truthound/validators/timeout/advanced/telemetry.py +804 -0
- truthound/validators/timeout/cascade.py +477 -0
- truthound/validators/timeout/deadline.py +657 -0
- truthound/validators/timeout/degradation.py +525 -0
- truthound/validators/timeout/distributed.py +597 -0
- truthound/validators/timeseries/__init__.py +89 -0
- truthound/validators/timeseries/base.py +326 -0
- truthound/validators/timeseries/completeness.py +617 -0
- truthound/validators/timeseries/gap.py +485 -0
- truthound/validators/timeseries/monotonic.py +310 -0
- truthound/validators/timeseries/seasonality.py +422 -0
- truthound/validators/timeseries/trend.py +510 -0
- truthound/validators/uniqueness/__init__.py +59 -0
- truthound/validators/uniqueness/approximate.py +475 -0
- truthound/validators/uniqueness/distinct_values.py +253 -0
- truthound/validators/uniqueness/duplicate.py +118 -0
- truthound/validators/uniqueness/primary_key.py +140 -0
- truthound/validators/uniqueness/unique.py +191 -0
- truthound/validators/uniqueness/within_record.py +599 -0
- truthound/validators/utils.py +756 -0
- truthound-1.0.8.dist-info/METADATA +474 -0
- truthound-1.0.8.dist-info/RECORD +877 -0
- truthound-1.0.8.dist-info/WHEEL +4 -0
- truthound-1.0.8.dist-info/entry_points.txt +2 -0
- truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Sandbox context implementation.
|
|
2
|
+
|
|
3
|
+
This module provides the concrete implementation of SandboxContext
|
|
4
|
+
that tracks sandbox state and resource usage.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from truthound.plugins.security.protocols import SecurityPolicy, SandboxContext
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class SandboxContextImpl:
|
|
20
|
+
"""Concrete implementation of SandboxContext.
|
|
21
|
+
|
|
22
|
+
Tracks the state of a sandbox instance including resource usage
|
|
23
|
+
and lifecycle information.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
plugin_id: ID of the plugin being sandboxed
|
|
27
|
+
policy: Security policy applied to this sandbox
|
|
28
|
+
sandbox_id: Unique ID for this sandbox instance
|
|
29
|
+
created_at: When the sandbox was created
|
|
30
|
+
started_at: When execution started (None if not started)
|
|
31
|
+
finished_at: When execution finished (None if not finished)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
plugin_id: str
|
|
35
|
+
policy: SecurityPolicy
|
|
36
|
+
sandbox_id: str = ""
|
|
37
|
+
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
38
|
+
started_at: datetime | None = None
|
|
39
|
+
finished_at: datetime | None = None
|
|
40
|
+
|
|
41
|
+
# Internal state
|
|
42
|
+
_process_id: int | None = field(default=None, repr=False)
|
|
43
|
+
_container_id: str | None = field(default=None, repr=False)
|
|
44
|
+
_alive: bool = field(default=True, repr=False)
|
|
45
|
+
_memory_used_mb: float = field(default=0.0, repr=False)
|
|
46
|
+
_cpu_percent: float = field(default=0.0, repr=False)
|
|
47
|
+
_start_time: float = field(default=0.0, repr=False)
|
|
48
|
+
|
|
49
|
+
def __post_init__(self) -> None:
|
|
50
|
+
"""Generate sandbox ID if not provided."""
|
|
51
|
+
if not self.sandbox_id:
|
|
52
|
+
self.sandbox_id = self._generate_id()
|
|
53
|
+
|
|
54
|
+
def _generate_id(self) -> str:
|
|
55
|
+
"""Generate unique sandbox ID."""
|
|
56
|
+
content = f"{self.plugin_id}-{self.created_at.isoformat()}-{id(self)}"
|
|
57
|
+
return hashlib.sha256(content.encode()).hexdigest()[:16]
|
|
58
|
+
|
|
59
|
+
def is_alive(self) -> bool:
|
|
60
|
+
"""Check if sandbox is still running."""
|
|
61
|
+
return self._alive
|
|
62
|
+
|
|
63
|
+
def get_resource_usage(self) -> dict[str, float]:
|
|
64
|
+
"""Get current resource usage.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Dict with memory_mb, cpu_percent, and execution_time_sec
|
|
68
|
+
"""
|
|
69
|
+
execution_time = 0.0
|
|
70
|
+
if self._start_time > 0:
|
|
71
|
+
if self.finished_at:
|
|
72
|
+
execution_time = (self.finished_at - self.started_at).total_seconds() if self.started_at else 0.0
|
|
73
|
+
else:
|
|
74
|
+
execution_time = time.perf_counter() - self._start_time
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
"memory_mb": self._memory_used_mb,
|
|
78
|
+
"cpu_percent": self._cpu_percent,
|
|
79
|
+
"execution_time_sec": execution_time,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def mark_started(self) -> None:
|
|
83
|
+
"""Mark sandbox as started."""
|
|
84
|
+
self.started_at = datetime.now(timezone.utc)
|
|
85
|
+
self._start_time = time.perf_counter()
|
|
86
|
+
|
|
87
|
+
def mark_finished(self) -> None:
|
|
88
|
+
"""Mark sandbox as finished."""
|
|
89
|
+
self.finished_at = datetime.now(timezone.utc)
|
|
90
|
+
self._alive = False
|
|
91
|
+
|
|
92
|
+
def mark_terminated(self) -> None:
|
|
93
|
+
"""Mark sandbox as terminated."""
|
|
94
|
+
self._alive = False
|
|
95
|
+
if not self.finished_at:
|
|
96
|
+
self.finished_at = datetime.now(timezone.utc)
|
|
97
|
+
|
|
98
|
+
def update_resource_usage(
|
|
99
|
+
self,
|
|
100
|
+
memory_mb: float | None = None,
|
|
101
|
+
cpu_percent: float | None = None,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Update resource usage metrics."""
|
|
104
|
+
if memory_mb is not None:
|
|
105
|
+
self._memory_used_mb = memory_mb
|
|
106
|
+
if cpu_percent is not None:
|
|
107
|
+
self._cpu_percent = cpu_percent
|
|
108
|
+
|
|
109
|
+
def set_process_id(self, pid: int) -> None:
|
|
110
|
+
"""Set the process ID for process-based sandboxes."""
|
|
111
|
+
self._process_id = pid
|
|
112
|
+
|
|
113
|
+
def set_container_id(self, container_id: str) -> None:
|
|
114
|
+
"""Set the container ID for container-based sandboxes."""
|
|
115
|
+
self._container_id = container_id
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def process_id(self) -> int | None:
|
|
119
|
+
"""Get process ID if applicable."""
|
|
120
|
+
return self._process_id
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def container_id(self) -> str | None:
|
|
124
|
+
"""Get container ID if applicable."""
|
|
125
|
+
return self._container_id
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def execution_time_sec(self) -> float:
|
|
129
|
+
"""Get current execution time in seconds."""
|
|
130
|
+
return self.get_resource_usage()["execution_time_sec"]
|
|
131
|
+
|
|
132
|
+
def to_dict(self) -> dict[str, Any]:
|
|
133
|
+
"""Convert to dictionary for serialization."""
|
|
134
|
+
return {
|
|
135
|
+
"plugin_id": self.plugin_id,
|
|
136
|
+
"sandbox_id": self.sandbox_id,
|
|
137
|
+
"created_at": self.created_at.isoformat(),
|
|
138
|
+
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
139
|
+
"finished_at": self.finished_at.isoformat() if self.finished_at else None,
|
|
140
|
+
"is_alive": self._alive,
|
|
141
|
+
"resource_usage": self.get_resource_usage(),
|
|
142
|
+
"process_id": self._process_id,
|
|
143
|
+
"container_id": self._container_id,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# Verify implementation satisfies protocol
|
|
148
|
+
def _verify_protocol() -> None:
|
|
149
|
+
"""Verify SandboxContextImpl implements SandboxContext protocol."""
|
|
150
|
+
context: SandboxContext = SandboxContextImpl(
|
|
151
|
+
plugin_id="test",
|
|
152
|
+
policy=SecurityPolicy.standard(),
|
|
153
|
+
)
|
|
154
|
+
assert hasattr(context, "plugin_id")
|
|
155
|
+
assert hasattr(context, "policy")
|
|
156
|
+
assert hasattr(context, "sandbox_id")
|
|
157
|
+
assert callable(context.is_alive)
|
|
158
|
+
assert callable(context.get_resource_usage)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Sandbox engine implementations.
|
|
2
|
+
|
|
3
|
+
This module contains concrete implementations of the SandboxEngine protocol:
|
|
4
|
+
- NoopSandboxEngine: No isolation (for trusted plugins)
|
|
5
|
+
- ProcessSandboxEngine: Subprocess isolation with resource limits
|
|
6
|
+
- ContainerSandboxEngine: Docker/Podman container isolation
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from truthound.plugins.security.sandbox.engines.noop import NoopSandboxEngine
|
|
12
|
+
from truthound.plugins.security.sandbox.engines.process import ProcessSandboxEngine
|
|
13
|
+
from truthound.plugins.security.sandbox.engines.container import ContainerSandboxEngine
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"NoopSandboxEngine",
|
|
17
|
+
"ProcessSandboxEngine",
|
|
18
|
+
"ContainerSandboxEngine",
|
|
19
|
+
]
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""Container-based sandbox engine using Docker/Podman.
|
|
2
|
+
|
|
3
|
+
This engine provides maximum isolation by running code in containers:
|
|
4
|
+
- Complete filesystem isolation
|
|
5
|
+
- Network isolation
|
|
6
|
+
- Resource limits via cgroups
|
|
7
|
+
- Seccomp profiles for syscall filtering
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import pickle
|
|
17
|
+
import shutil
|
|
18
|
+
import tempfile
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Callable
|
|
21
|
+
|
|
22
|
+
from truthound.plugins.security.protocols import (
|
|
23
|
+
IsolationLevel,
|
|
24
|
+
SecurityPolicy,
|
|
25
|
+
SandboxContext,
|
|
26
|
+
)
|
|
27
|
+
from truthound.plugins.security.sandbox.context import SandboxContextImpl
|
|
28
|
+
from truthound.plugins.security.exceptions import (
|
|
29
|
+
SandboxTimeoutError,
|
|
30
|
+
SandboxResourceError,
|
|
31
|
+
SandboxError,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ContainerSandboxEngine:
|
|
38
|
+
"""Container-based sandbox engine using Docker or Podman.
|
|
39
|
+
|
|
40
|
+
Provides the strongest isolation by running code in containers.
|
|
41
|
+
Automatically detects and uses Docker or Podman.
|
|
42
|
+
|
|
43
|
+
Features:
|
|
44
|
+
- Complete filesystem isolation
|
|
45
|
+
- Network isolation (configurable)
|
|
46
|
+
- Memory and CPU limits via cgroups
|
|
47
|
+
- Read-only filesystem option
|
|
48
|
+
- Configurable image
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
DEFAULT_IMAGE = "python:3.11-slim"
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def isolation_level(self) -> IsolationLevel:
|
|
55
|
+
"""Return the isolation level provided by this engine."""
|
|
56
|
+
return IsolationLevel.CONTAINER
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
image: str = DEFAULT_IMAGE,
|
|
61
|
+
runtime: str | None = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Initialize the container sandbox engine.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
image: Default Docker image to use
|
|
67
|
+
runtime: Container runtime ("docker" or "podman"), auto-detected if None
|
|
68
|
+
"""
|
|
69
|
+
self._image = image
|
|
70
|
+
self._runtime = runtime
|
|
71
|
+
self._contexts: dict[str, SandboxContextImpl] = {}
|
|
72
|
+
self._container_ids: list[str] = []
|
|
73
|
+
|
|
74
|
+
async def _detect_runtime(self) -> str:
|
|
75
|
+
"""Detect available container runtime."""
|
|
76
|
+
if self._runtime:
|
|
77
|
+
return self._runtime
|
|
78
|
+
|
|
79
|
+
# Try Docker first, then Podman
|
|
80
|
+
for runtime in ["docker", "podman"]:
|
|
81
|
+
try:
|
|
82
|
+
process = await asyncio.create_subprocess_exec(
|
|
83
|
+
runtime, "version",
|
|
84
|
+
stdout=asyncio.subprocess.DEVNULL,
|
|
85
|
+
stderr=asyncio.subprocess.DEVNULL,
|
|
86
|
+
)
|
|
87
|
+
await process.communicate()
|
|
88
|
+
if process.returncode == 0:
|
|
89
|
+
self._runtime = runtime
|
|
90
|
+
return runtime
|
|
91
|
+
except FileNotFoundError:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
raise SandboxError(
|
|
95
|
+
"No container runtime available. Install Docker or Podman."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def create_sandbox(
|
|
99
|
+
self,
|
|
100
|
+
plugin_id: str,
|
|
101
|
+
policy: SecurityPolicy,
|
|
102
|
+
) -> SandboxContext:
|
|
103
|
+
"""Create a sandbox context.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
plugin_id: Plugin identifier
|
|
107
|
+
policy: Security policy to apply
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
SandboxContext for execution
|
|
111
|
+
"""
|
|
112
|
+
context = SandboxContextImpl(
|
|
113
|
+
plugin_id=plugin_id,
|
|
114
|
+
policy=policy,
|
|
115
|
+
)
|
|
116
|
+
self._contexts[context.sandbox_id] = context
|
|
117
|
+
logger.debug(f"Created container sandbox context for {plugin_id}")
|
|
118
|
+
return context
|
|
119
|
+
|
|
120
|
+
async def execute(
|
|
121
|
+
self,
|
|
122
|
+
context: SandboxContext,
|
|
123
|
+
func: Callable[..., Any],
|
|
124
|
+
*args: Any,
|
|
125
|
+
**kwargs: Any,
|
|
126
|
+
) -> Any:
|
|
127
|
+
"""Execute function in container.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
context: Sandbox context
|
|
131
|
+
func: Function to execute
|
|
132
|
+
*args: Positional arguments
|
|
133
|
+
**kwargs: Keyword arguments
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Function result
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
SandboxTimeoutError: If execution times out
|
|
140
|
+
SandboxResourceError: If resource limits exceeded
|
|
141
|
+
SandboxError: If container execution fails
|
|
142
|
+
"""
|
|
143
|
+
impl = self._get_impl(context)
|
|
144
|
+
impl.mark_started()
|
|
145
|
+
policy = context.policy
|
|
146
|
+
limits = policy.resource_limits
|
|
147
|
+
|
|
148
|
+
# Detect runtime
|
|
149
|
+
runtime = await self._detect_runtime()
|
|
150
|
+
|
|
151
|
+
# Create temporary directory
|
|
152
|
+
temp_dir = Path(tempfile.mkdtemp(prefix="truthound_container_"))
|
|
153
|
+
data_path = temp_dir / "data.pkl"
|
|
154
|
+
result_path = temp_dir / "result.pkl"
|
|
155
|
+
script_path = temp_dir / "executor.py"
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
# Serialize function and data
|
|
159
|
+
with open(data_path, "wb") as f:
|
|
160
|
+
pickle.dump({
|
|
161
|
+
"func": func,
|
|
162
|
+
"args": args,
|
|
163
|
+
"kwargs": kwargs,
|
|
164
|
+
}, f)
|
|
165
|
+
|
|
166
|
+
# Create executor script
|
|
167
|
+
script = self._create_executor_script()
|
|
168
|
+
with open(script_path, "w") as f:
|
|
169
|
+
f.write(script)
|
|
170
|
+
|
|
171
|
+
# Build container command
|
|
172
|
+
container_cmd = self._build_container_command(
|
|
173
|
+
runtime=runtime,
|
|
174
|
+
policy=policy,
|
|
175
|
+
temp_dir=temp_dir,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Execute container
|
|
179
|
+
process = await asyncio.create_subprocess_exec(
|
|
180
|
+
*container_cmd,
|
|
181
|
+
stdout=asyncio.subprocess.PIPE,
|
|
182
|
+
stderr=asyncio.subprocess.PIPE,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
# Wait with timeout
|
|
187
|
+
stdout, stderr = await asyncio.wait_for(
|
|
188
|
+
process.communicate(),
|
|
189
|
+
timeout=limits.max_execution_time_sec + 10,
|
|
190
|
+
)
|
|
191
|
+
except asyncio.TimeoutError:
|
|
192
|
+
process.kill()
|
|
193
|
+
await process.wait()
|
|
194
|
+
impl.mark_terminated()
|
|
195
|
+
raise SandboxTimeoutError(
|
|
196
|
+
f"Container timed out after {limits.max_execution_time_sec}s",
|
|
197
|
+
plugin_id=context.plugin_id,
|
|
198
|
+
sandbox_id=context.sandbox_id,
|
|
199
|
+
timeout_seconds=limits.max_execution_time_sec,
|
|
200
|
+
execution_time=impl.execution_time_sec,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
impl.mark_finished()
|
|
204
|
+
|
|
205
|
+
# Check for result
|
|
206
|
+
if not result_path.exists():
|
|
207
|
+
stderr_text = stderr.decode() if stderr else "No output"
|
|
208
|
+
raise SandboxError(
|
|
209
|
+
f"Container produced no result. stderr: {stderr_text}",
|
|
210
|
+
plugin_id=context.plugin_id,
|
|
211
|
+
sandbox_id=context.sandbox_id,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Load result
|
|
215
|
+
with open(result_path, "rb") as f:
|
|
216
|
+
result_data = pickle.load(f)
|
|
217
|
+
|
|
218
|
+
if result_data.get("success"):
|
|
219
|
+
return result_data.get("result")
|
|
220
|
+
|
|
221
|
+
# Handle errors
|
|
222
|
+
error_type = result_data.get("error", "UnknownError")
|
|
223
|
+
error_msg = result_data.get("message", "Unknown error")
|
|
224
|
+
|
|
225
|
+
if error_type == "MemoryError":
|
|
226
|
+
raise SandboxResourceError(
|
|
227
|
+
f"Container memory limit exceeded: {error_msg}",
|
|
228
|
+
plugin_id=context.plugin_id,
|
|
229
|
+
sandbox_id=context.sandbox_id,
|
|
230
|
+
resource_type="memory",
|
|
231
|
+
limit=limits.max_memory_mb,
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
raise SandboxError(
|
|
235
|
+
f"Container error ({error_type}): {error_msg}",
|
|
236
|
+
plugin_id=context.plugin_id,
|
|
237
|
+
sandbox_id=context.sandbox_id,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
finally:
|
|
241
|
+
# Cleanup temp directory
|
|
242
|
+
if temp_dir.exists():
|
|
243
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
244
|
+
|
|
245
|
+
def _build_container_command(
|
|
246
|
+
self,
|
|
247
|
+
runtime: str,
|
|
248
|
+
policy: SecurityPolicy,
|
|
249
|
+
temp_dir: Path,
|
|
250
|
+
) -> list[str]:
|
|
251
|
+
"""Build container run command."""
|
|
252
|
+
limits = policy.resource_limits
|
|
253
|
+
|
|
254
|
+
cmd = [
|
|
255
|
+
runtime, "run",
|
|
256
|
+
"--rm", # Auto-remove container
|
|
257
|
+
f"--memory={limits.max_memory_mb}m",
|
|
258
|
+
f"--cpus={limits.max_cpu_percent / 100}",
|
|
259
|
+
f"--network={'bridge' if policy.allow_network else 'none'}",
|
|
260
|
+
"-v", f"{temp_dir}:/workspace:rw",
|
|
261
|
+
"-w", "/workspace",
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
# Add read-only root filesystem if not allowing file writes
|
|
265
|
+
if not policy.allow_file_write:
|
|
266
|
+
cmd.extend([
|
|
267
|
+
"--read-only",
|
|
268
|
+
"--tmpfs", "/tmp:rw,noexec,nosuid",
|
|
269
|
+
])
|
|
270
|
+
|
|
271
|
+
# Add security options
|
|
272
|
+
cmd.extend([
|
|
273
|
+
"--security-opt", "no-new-privileges",
|
|
274
|
+
"--cap-drop", "ALL",
|
|
275
|
+
])
|
|
276
|
+
|
|
277
|
+
# Add image and command
|
|
278
|
+
cmd.extend([
|
|
279
|
+
self._image,
|
|
280
|
+
"python", "/workspace/executor.py",
|
|
281
|
+
])
|
|
282
|
+
|
|
283
|
+
return cmd
|
|
284
|
+
|
|
285
|
+
def _create_executor_script(self) -> str:
|
|
286
|
+
"""Create Python script for container execution."""
|
|
287
|
+
return '''
|
|
288
|
+
import sys
|
|
289
|
+
import pickle
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
# Load data
|
|
293
|
+
with open("/workspace/data.pkl", "rb") as f:
|
|
294
|
+
data = pickle.load(f)
|
|
295
|
+
|
|
296
|
+
func = data["func"]
|
|
297
|
+
args = data["args"]
|
|
298
|
+
kwargs = data["kwargs"]
|
|
299
|
+
|
|
300
|
+
# Execute
|
|
301
|
+
result = func(*args, **kwargs)
|
|
302
|
+
|
|
303
|
+
# Save result
|
|
304
|
+
with open("/workspace/result.pkl", "wb") as f:
|
|
305
|
+
pickle.dump({"success": True, "result": result}, f)
|
|
306
|
+
|
|
307
|
+
except MemoryError as e:
|
|
308
|
+
with open("/workspace/result.pkl", "wb") as f:
|
|
309
|
+
pickle.dump({"success": False, "error": "MemoryError", "message": str(e)}, f)
|
|
310
|
+
except Exception as e:
|
|
311
|
+
with open("/workspace/result.pkl", "wb") as f:
|
|
312
|
+
pickle.dump({"success": False, "error": type(e).__name__, "message": str(e)}, f)
|
|
313
|
+
'''
|
|
314
|
+
|
|
315
|
+
def terminate(self, context: SandboxContext) -> None:
|
|
316
|
+
"""Terminate sandbox container.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
context: Sandbox to terminate
|
|
320
|
+
"""
|
|
321
|
+
impl = self._get_impl(context)
|
|
322
|
+
impl.mark_terminated()
|
|
323
|
+
|
|
324
|
+
# Kill container if running
|
|
325
|
+
container_id = impl.container_id
|
|
326
|
+
if container_id and self._runtime:
|
|
327
|
+
try:
|
|
328
|
+
asyncio.create_task(self._kill_container(container_id))
|
|
329
|
+
except Exception:
|
|
330
|
+
pass
|
|
331
|
+
|
|
332
|
+
self._contexts.pop(context.sandbox_id, None)
|
|
333
|
+
|
|
334
|
+
async def _kill_container(self, container_id: str) -> None:
|
|
335
|
+
"""Kill a container by ID."""
|
|
336
|
+
if not self._runtime:
|
|
337
|
+
return
|
|
338
|
+
try:
|
|
339
|
+
process = await asyncio.create_subprocess_exec(
|
|
340
|
+
self._runtime, "rm", "-f", container_id,
|
|
341
|
+
stdout=asyncio.subprocess.DEVNULL,
|
|
342
|
+
stderr=asyncio.subprocess.DEVNULL,
|
|
343
|
+
)
|
|
344
|
+
await process.wait()
|
|
345
|
+
except Exception as e:
|
|
346
|
+
logger.warning(f"Failed to kill container {container_id}: {e}")
|
|
347
|
+
|
|
348
|
+
async def cleanup(self) -> None:
|
|
349
|
+
"""Clean up all sandbox resources."""
|
|
350
|
+
# Terminate all contexts
|
|
351
|
+
for context in list(self._contexts.values()):
|
|
352
|
+
self.terminate(context)
|
|
353
|
+
self._contexts.clear()
|
|
354
|
+
|
|
355
|
+
# Kill any remaining containers
|
|
356
|
+
for container_id in self._container_ids:
|
|
357
|
+
await self._kill_container(container_id)
|
|
358
|
+
self._container_ids.clear()
|
|
359
|
+
|
|
360
|
+
def _get_impl(self, context: SandboxContext) -> SandboxContextImpl:
|
|
361
|
+
"""Get implementation from context."""
|
|
362
|
+
if isinstance(context, SandboxContextImpl):
|
|
363
|
+
return context
|
|
364
|
+
impl = self._contexts.get(context.sandbox_id)
|
|
365
|
+
if impl is None:
|
|
366
|
+
raise ValueError(f"Unknown sandbox context: {context.sandbox_id}")
|
|
367
|
+
return impl
|
|
368
|
+
|
|
369
|
+
async def check_available(self) -> bool:
|
|
370
|
+
"""Check if container runtime is available.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
True if Docker or Podman is available
|
|
374
|
+
"""
|
|
375
|
+
try:
|
|
376
|
+
await self._detect_runtime()
|
|
377
|
+
return True
|
|
378
|
+
except SandboxError:
|
|
379
|
+
return False
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""No-op sandbox engine for trusted plugins.
|
|
2
|
+
|
|
3
|
+
This engine provides no isolation but still enforces timeout limits.
|
|
4
|
+
Use only for trusted plugins that don't require sandboxing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any, Callable
|
|
13
|
+
|
|
14
|
+
from truthound.plugins.security.protocols import (
|
|
15
|
+
IsolationLevel,
|
|
16
|
+
SecurityPolicy,
|
|
17
|
+
SandboxContext,
|
|
18
|
+
)
|
|
19
|
+
from truthound.plugins.security.sandbox.context import SandboxContextImpl
|
|
20
|
+
from truthound.plugins.security.exceptions import SandboxTimeoutError
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NoopSandboxEngine:
|
|
26
|
+
"""No-isolation sandbox engine.
|
|
27
|
+
|
|
28
|
+
Executes code directly in the current process with only timeout
|
|
29
|
+
enforcement. Suitable for trusted plugins that don't need isolation.
|
|
30
|
+
|
|
31
|
+
Warning:
|
|
32
|
+
This engine provides NO security isolation. Only use for
|
|
33
|
+
fully trusted plugins in development or controlled environments.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def isolation_level(self) -> IsolationLevel:
|
|
38
|
+
"""Return the isolation level provided by this engine."""
|
|
39
|
+
return IsolationLevel.NONE
|
|
40
|
+
|
|
41
|
+
def __init__(self) -> None:
|
|
42
|
+
"""Initialize the no-op sandbox engine."""
|
|
43
|
+
self._contexts: dict[str, SandboxContextImpl] = {}
|
|
44
|
+
|
|
45
|
+
def create_sandbox(
|
|
46
|
+
self,
|
|
47
|
+
plugin_id: str,
|
|
48
|
+
policy: SecurityPolicy,
|
|
49
|
+
) -> SandboxContext:
|
|
50
|
+
"""Create a sandbox context (no actual sandbox created).
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
plugin_id: Plugin identifier
|
|
54
|
+
policy: Security policy (only timeout is enforced)
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
SandboxContext for tracking
|
|
58
|
+
"""
|
|
59
|
+
context = SandboxContextImpl(
|
|
60
|
+
plugin_id=plugin_id,
|
|
61
|
+
policy=policy,
|
|
62
|
+
)
|
|
63
|
+
self._contexts[context.sandbox_id] = context
|
|
64
|
+
logger.debug(f"Created no-op sandbox context for {plugin_id}")
|
|
65
|
+
return context
|
|
66
|
+
|
|
67
|
+
async def execute(
|
|
68
|
+
self,
|
|
69
|
+
context: SandboxContext,
|
|
70
|
+
func: Callable[..., Any],
|
|
71
|
+
*args: Any,
|
|
72
|
+
**kwargs: Any,
|
|
73
|
+
) -> Any:
|
|
74
|
+
"""Execute function with timeout enforcement only.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
context: Sandbox context
|
|
78
|
+
func: Function to execute
|
|
79
|
+
*args: Positional arguments
|
|
80
|
+
**kwargs: Keyword arguments
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Function result
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
SandboxTimeoutError: If execution times out
|
|
87
|
+
"""
|
|
88
|
+
impl = self._get_impl(context)
|
|
89
|
+
impl.mark_started()
|
|
90
|
+
timeout = context.policy.resource_limits.max_execution_time_sec
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Check if function is a coroutine
|
|
94
|
+
if asyncio.iscoroutinefunction(func):
|
|
95
|
+
result = await asyncio.wait_for(
|
|
96
|
+
func(*args, **kwargs),
|
|
97
|
+
timeout=timeout,
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
# Run sync function in executor with timeout
|
|
101
|
+
loop = asyncio.get_event_loop()
|
|
102
|
+
result = await asyncio.wait_for(
|
|
103
|
+
loop.run_in_executor(None, lambda: func(*args, **kwargs)),
|
|
104
|
+
timeout=timeout,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
impl.mark_finished()
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
except asyncio.TimeoutError:
|
|
111
|
+
impl.mark_terminated()
|
|
112
|
+
raise SandboxTimeoutError(
|
|
113
|
+
f"Execution timed out after {timeout}s",
|
|
114
|
+
plugin_id=context.plugin_id,
|
|
115
|
+
sandbox_id=context.sandbox_id,
|
|
116
|
+
timeout_seconds=timeout,
|
|
117
|
+
execution_time=impl.execution_time_sec,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def terminate(self, context: SandboxContext) -> None:
|
|
121
|
+
"""Terminate sandbox (no-op for this engine).
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
context: Sandbox to terminate
|
|
125
|
+
"""
|
|
126
|
+
impl = self._get_impl(context)
|
|
127
|
+
impl.mark_terminated()
|
|
128
|
+
self._contexts.pop(context.sandbox_id, None)
|
|
129
|
+
|
|
130
|
+
async def cleanup(self) -> None:
|
|
131
|
+
"""Clean up all contexts."""
|
|
132
|
+
for context in self._contexts.values():
|
|
133
|
+
context.mark_terminated()
|
|
134
|
+
self._contexts.clear()
|
|
135
|
+
|
|
136
|
+
def _get_impl(self, context: SandboxContext) -> SandboxContextImpl:
|
|
137
|
+
"""Get implementation from context."""
|
|
138
|
+
if isinstance(context, SandboxContextImpl):
|
|
139
|
+
return context
|
|
140
|
+
# Fallback: look up by sandbox_id
|
|
141
|
+
impl = self._contexts.get(context.sandbox_id)
|
|
142
|
+
if impl is None:
|
|
143
|
+
raise ValueError(f"Unknown sandbox context: {context.sandbox_id}")
|
|
144
|
+
return impl
|