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.
Files changed (877) hide show
  1. truthound/__init__.py +162 -0
  2. truthound/adapters.py +100 -0
  3. truthound/api.py +365 -0
  4. truthound/audit/__init__.py +248 -0
  5. truthound/audit/core.py +967 -0
  6. truthound/audit/filters.py +620 -0
  7. truthound/audit/formatters.py +707 -0
  8. truthound/audit/logger.py +902 -0
  9. truthound/audit/middleware.py +571 -0
  10. truthound/audit/storage.py +1083 -0
  11. truthound/benchmark/__init__.py +123 -0
  12. truthound/benchmark/base.py +757 -0
  13. truthound/benchmark/comparison.py +635 -0
  14. truthound/benchmark/generators.py +706 -0
  15. truthound/benchmark/reporters.py +718 -0
  16. truthound/benchmark/runner.py +635 -0
  17. truthound/benchmark/scenarios.py +712 -0
  18. truthound/cache.py +252 -0
  19. truthound/checkpoint/__init__.py +136 -0
  20. truthound/checkpoint/actions/__init__.py +164 -0
  21. truthound/checkpoint/actions/base.py +324 -0
  22. truthound/checkpoint/actions/custom.py +234 -0
  23. truthound/checkpoint/actions/discord_notify.py +290 -0
  24. truthound/checkpoint/actions/email_notify.py +405 -0
  25. truthound/checkpoint/actions/github_action.py +406 -0
  26. truthound/checkpoint/actions/opsgenie.py +1499 -0
  27. truthound/checkpoint/actions/pagerduty.py +226 -0
  28. truthound/checkpoint/actions/slack_notify.py +233 -0
  29. truthound/checkpoint/actions/store_result.py +249 -0
  30. truthound/checkpoint/actions/teams_notify.py +1570 -0
  31. truthound/checkpoint/actions/telegram_notify.py +419 -0
  32. truthound/checkpoint/actions/update_docs.py +552 -0
  33. truthound/checkpoint/actions/webhook.py +293 -0
  34. truthound/checkpoint/analytics/__init__.py +147 -0
  35. truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
  36. truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
  37. truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
  38. truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
  39. truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
  40. truthound/checkpoint/analytics/analyzers/base.py +270 -0
  41. truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
  42. truthound/checkpoint/analytics/analyzers/trend.py +314 -0
  43. truthound/checkpoint/analytics/models.py +292 -0
  44. truthound/checkpoint/analytics/protocols.py +549 -0
  45. truthound/checkpoint/analytics/service.py +718 -0
  46. truthound/checkpoint/analytics/stores/__init__.py +16 -0
  47. truthound/checkpoint/analytics/stores/base.py +306 -0
  48. truthound/checkpoint/analytics/stores/memory_store.py +353 -0
  49. truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
  50. truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
  51. truthound/checkpoint/async_actions.py +794 -0
  52. truthound/checkpoint/async_base.py +708 -0
  53. truthound/checkpoint/async_checkpoint.py +617 -0
  54. truthound/checkpoint/async_runner.py +639 -0
  55. truthound/checkpoint/checkpoint.py +527 -0
  56. truthound/checkpoint/ci/__init__.py +61 -0
  57. truthound/checkpoint/ci/detector.py +355 -0
  58. truthound/checkpoint/ci/reporter.py +436 -0
  59. truthound/checkpoint/ci/templates.py +454 -0
  60. truthound/checkpoint/circuitbreaker/__init__.py +133 -0
  61. truthound/checkpoint/circuitbreaker/breaker.py +542 -0
  62. truthound/checkpoint/circuitbreaker/core.py +252 -0
  63. truthound/checkpoint/circuitbreaker/detection.py +459 -0
  64. truthound/checkpoint/circuitbreaker/middleware.py +389 -0
  65. truthound/checkpoint/circuitbreaker/registry.py +357 -0
  66. truthound/checkpoint/distributed/__init__.py +139 -0
  67. truthound/checkpoint/distributed/backends/__init__.py +35 -0
  68. truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
  69. truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
  70. truthound/checkpoint/distributed/backends/local_backend.py +397 -0
  71. truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
  72. truthound/checkpoint/distributed/base.py +774 -0
  73. truthound/checkpoint/distributed/orchestrator.py +765 -0
  74. truthound/checkpoint/distributed/protocols.py +842 -0
  75. truthound/checkpoint/distributed/registry.py +449 -0
  76. truthound/checkpoint/idempotency/__init__.py +120 -0
  77. truthound/checkpoint/idempotency/core.py +295 -0
  78. truthound/checkpoint/idempotency/fingerprint.py +454 -0
  79. truthound/checkpoint/idempotency/locking.py +604 -0
  80. truthound/checkpoint/idempotency/service.py +592 -0
  81. truthound/checkpoint/idempotency/stores.py +653 -0
  82. truthound/checkpoint/monitoring/__init__.py +134 -0
  83. truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
  84. truthound/checkpoint/monitoring/aggregators/base.py +372 -0
  85. truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
  86. truthound/checkpoint/monitoring/aggregators/window.py +493 -0
  87. truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
  88. truthound/checkpoint/monitoring/collectors/base.py +257 -0
  89. truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
  90. truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
  91. truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
  92. truthound/checkpoint/monitoring/events.py +410 -0
  93. truthound/checkpoint/monitoring/protocols.py +636 -0
  94. truthound/checkpoint/monitoring/service.py +578 -0
  95. truthound/checkpoint/monitoring/views/__init__.py +17 -0
  96. truthound/checkpoint/monitoring/views/base.py +172 -0
  97. truthound/checkpoint/monitoring/views/queue_view.py +220 -0
  98. truthound/checkpoint/monitoring/views/task_view.py +240 -0
  99. truthound/checkpoint/monitoring/views/worker_view.py +263 -0
  100. truthound/checkpoint/registry.py +337 -0
  101. truthound/checkpoint/runner.py +356 -0
  102. truthound/checkpoint/transaction/__init__.py +133 -0
  103. truthound/checkpoint/transaction/base.py +389 -0
  104. truthound/checkpoint/transaction/compensatable.py +537 -0
  105. truthound/checkpoint/transaction/coordinator.py +576 -0
  106. truthound/checkpoint/transaction/executor.py +622 -0
  107. truthound/checkpoint/transaction/idempotency.py +534 -0
  108. truthound/checkpoint/transaction/saga/__init__.py +143 -0
  109. truthound/checkpoint/transaction/saga/builder.py +584 -0
  110. truthound/checkpoint/transaction/saga/definition.py +515 -0
  111. truthound/checkpoint/transaction/saga/event_store.py +542 -0
  112. truthound/checkpoint/transaction/saga/patterns.py +833 -0
  113. truthound/checkpoint/transaction/saga/runner.py +718 -0
  114. truthound/checkpoint/transaction/saga/state_machine.py +793 -0
  115. truthound/checkpoint/transaction/saga/strategies.py +780 -0
  116. truthound/checkpoint/transaction/saga/testing.py +886 -0
  117. truthound/checkpoint/triggers/__init__.py +58 -0
  118. truthound/checkpoint/triggers/base.py +237 -0
  119. truthound/checkpoint/triggers/event.py +385 -0
  120. truthound/checkpoint/triggers/schedule.py +355 -0
  121. truthound/cli.py +2358 -0
  122. truthound/cli_modules/__init__.py +124 -0
  123. truthound/cli_modules/advanced/__init__.py +45 -0
  124. truthound/cli_modules/advanced/benchmark.py +343 -0
  125. truthound/cli_modules/advanced/docs.py +225 -0
  126. truthound/cli_modules/advanced/lineage.py +209 -0
  127. truthound/cli_modules/advanced/ml.py +320 -0
  128. truthound/cli_modules/advanced/realtime.py +196 -0
  129. truthound/cli_modules/checkpoint/__init__.py +46 -0
  130. truthound/cli_modules/checkpoint/init.py +114 -0
  131. truthound/cli_modules/checkpoint/list.py +71 -0
  132. truthound/cli_modules/checkpoint/run.py +159 -0
  133. truthound/cli_modules/checkpoint/validate.py +67 -0
  134. truthound/cli_modules/common/__init__.py +71 -0
  135. truthound/cli_modules/common/errors.py +414 -0
  136. truthound/cli_modules/common/options.py +419 -0
  137. truthound/cli_modules/common/output.py +507 -0
  138. truthound/cli_modules/common/protocol.py +552 -0
  139. truthound/cli_modules/core/__init__.py +48 -0
  140. truthound/cli_modules/core/check.py +123 -0
  141. truthound/cli_modules/core/compare.py +104 -0
  142. truthound/cli_modules/core/learn.py +57 -0
  143. truthound/cli_modules/core/mask.py +77 -0
  144. truthound/cli_modules/core/profile.py +65 -0
  145. truthound/cli_modules/core/scan.py +61 -0
  146. truthound/cli_modules/profiler/__init__.py +51 -0
  147. truthound/cli_modules/profiler/auto_profile.py +175 -0
  148. truthound/cli_modules/profiler/metadata.py +107 -0
  149. truthound/cli_modules/profiler/suite.py +283 -0
  150. truthound/cli_modules/registry.py +431 -0
  151. truthound/cli_modules/scaffolding/__init__.py +89 -0
  152. truthound/cli_modules/scaffolding/base.py +631 -0
  153. truthound/cli_modules/scaffolding/commands.py +545 -0
  154. truthound/cli_modules/scaffolding/plugins.py +1072 -0
  155. truthound/cli_modules/scaffolding/reporters.py +594 -0
  156. truthound/cli_modules/scaffolding/validators.py +1127 -0
  157. truthound/common/__init__.py +18 -0
  158. truthound/common/resilience/__init__.py +130 -0
  159. truthound/common/resilience/bulkhead.py +266 -0
  160. truthound/common/resilience/circuit_breaker.py +516 -0
  161. truthound/common/resilience/composite.py +332 -0
  162. truthound/common/resilience/config.py +292 -0
  163. truthound/common/resilience/protocols.py +217 -0
  164. truthound/common/resilience/rate_limiter.py +404 -0
  165. truthound/common/resilience/retry.py +341 -0
  166. truthound/datadocs/__init__.py +260 -0
  167. truthound/datadocs/base.py +571 -0
  168. truthound/datadocs/builder.py +761 -0
  169. truthound/datadocs/charts.py +764 -0
  170. truthound/datadocs/dashboard/__init__.py +63 -0
  171. truthound/datadocs/dashboard/app.py +576 -0
  172. truthound/datadocs/dashboard/components.py +584 -0
  173. truthound/datadocs/dashboard/state.py +240 -0
  174. truthound/datadocs/engine/__init__.py +46 -0
  175. truthound/datadocs/engine/context.py +376 -0
  176. truthound/datadocs/engine/pipeline.py +618 -0
  177. truthound/datadocs/engine/registry.py +469 -0
  178. truthound/datadocs/exporters/__init__.py +49 -0
  179. truthound/datadocs/exporters/base.py +198 -0
  180. truthound/datadocs/exporters/html.py +178 -0
  181. truthound/datadocs/exporters/json_exporter.py +253 -0
  182. truthound/datadocs/exporters/markdown.py +284 -0
  183. truthound/datadocs/exporters/pdf.py +392 -0
  184. truthound/datadocs/i18n/__init__.py +86 -0
  185. truthound/datadocs/i18n/catalog.py +960 -0
  186. truthound/datadocs/i18n/formatting.py +505 -0
  187. truthound/datadocs/i18n/loader.py +256 -0
  188. truthound/datadocs/i18n/plurals.py +378 -0
  189. truthound/datadocs/renderers/__init__.py +42 -0
  190. truthound/datadocs/renderers/base.py +401 -0
  191. truthound/datadocs/renderers/custom.py +342 -0
  192. truthound/datadocs/renderers/jinja.py +697 -0
  193. truthound/datadocs/sections.py +736 -0
  194. truthound/datadocs/styles.py +931 -0
  195. truthound/datadocs/themes/__init__.py +101 -0
  196. truthound/datadocs/themes/base.py +336 -0
  197. truthound/datadocs/themes/default.py +417 -0
  198. truthound/datadocs/themes/enterprise.py +419 -0
  199. truthound/datadocs/themes/loader.py +336 -0
  200. truthound/datadocs/themes.py +301 -0
  201. truthound/datadocs/transformers/__init__.py +57 -0
  202. truthound/datadocs/transformers/base.py +268 -0
  203. truthound/datadocs/transformers/enrichers.py +544 -0
  204. truthound/datadocs/transformers/filters.py +447 -0
  205. truthound/datadocs/transformers/i18n.py +468 -0
  206. truthound/datadocs/versioning/__init__.py +62 -0
  207. truthound/datadocs/versioning/diff.py +639 -0
  208. truthound/datadocs/versioning/storage.py +497 -0
  209. truthound/datadocs/versioning/version.py +358 -0
  210. truthound/datasources/__init__.py +223 -0
  211. truthound/datasources/_async_protocols.py +222 -0
  212. truthound/datasources/_protocols.py +159 -0
  213. truthound/datasources/adapters.py +428 -0
  214. truthound/datasources/async_base.py +599 -0
  215. truthound/datasources/async_factory.py +511 -0
  216. truthound/datasources/base.py +516 -0
  217. truthound/datasources/factory.py +433 -0
  218. truthound/datasources/nosql/__init__.py +47 -0
  219. truthound/datasources/nosql/base.py +487 -0
  220. truthound/datasources/nosql/elasticsearch.py +801 -0
  221. truthound/datasources/nosql/mongodb.py +636 -0
  222. truthound/datasources/pandas_optimized.py +582 -0
  223. truthound/datasources/pandas_source.py +216 -0
  224. truthound/datasources/polars_source.py +395 -0
  225. truthound/datasources/spark_source.py +479 -0
  226. truthound/datasources/sql/__init__.py +154 -0
  227. truthound/datasources/sql/base.py +710 -0
  228. truthound/datasources/sql/bigquery.py +410 -0
  229. truthound/datasources/sql/cloud_base.py +199 -0
  230. truthound/datasources/sql/databricks.py +471 -0
  231. truthound/datasources/sql/mysql.py +316 -0
  232. truthound/datasources/sql/oracle.py +427 -0
  233. truthound/datasources/sql/postgresql.py +321 -0
  234. truthound/datasources/sql/redshift.py +479 -0
  235. truthound/datasources/sql/snowflake.py +439 -0
  236. truthound/datasources/sql/sqlite.py +286 -0
  237. truthound/datasources/sql/sqlserver.py +437 -0
  238. truthound/datasources/streaming/__init__.py +47 -0
  239. truthound/datasources/streaming/base.py +350 -0
  240. truthound/datasources/streaming/kafka.py +670 -0
  241. truthound/decorators.py +98 -0
  242. truthound/docs/__init__.py +69 -0
  243. truthound/docs/extractor.py +971 -0
  244. truthound/docs/generator.py +601 -0
  245. truthound/docs/parser.py +1037 -0
  246. truthound/docs/renderer.py +999 -0
  247. truthound/drift/__init__.py +22 -0
  248. truthound/drift/compare.py +189 -0
  249. truthound/drift/detectors.py +464 -0
  250. truthound/drift/report.py +160 -0
  251. truthound/execution/__init__.py +65 -0
  252. truthound/execution/_protocols.py +324 -0
  253. truthound/execution/base.py +576 -0
  254. truthound/execution/distributed/__init__.py +179 -0
  255. truthound/execution/distributed/aggregations.py +731 -0
  256. truthound/execution/distributed/arrow_bridge.py +817 -0
  257. truthound/execution/distributed/base.py +550 -0
  258. truthound/execution/distributed/dask_engine.py +976 -0
  259. truthound/execution/distributed/mixins.py +766 -0
  260. truthound/execution/distributed/protocols.py +756 -0
  261. truthound/execution/distributed/ray_engine.py +1127 -0
  262. truthound/execution/distributed/registry.py +446 -0
  263. truthound/execution/distributed/spark_engine.py +1011 -0
  264. truthound/execution/distributed/validator_adapter.py +682 -0
  265. truthound/execution/pandas_engine.py +401 -0
  266. truthound/execution/polars_engine.py +497 -0
  267. truthound/execution/pushdown/__init__.py +230 -0
  268. truthound/execution/pushdown/ast.py +1550 -0
  269. truthound/execution/pushdown/builder.py +1550 -0
  270. truthound/execution/pushdown/dialects.py +1072 -0
  271. truthound/execution/pushdown/executor.py +829 -0
  272. truthound/execution/pushdown/optimizer.py +1041 -0
  273. truthound/execution/sql_engine.py +518 -0
  274. truthound/infrastructure/__init__.py +189 -0
  275. truthound/infrastructure/audit.py +1515 -0
  276. truthound/infrastructure/config.py +1133 -0
  277. truthound/infrastructure/encryption.py +1132 -0
  278. truthound/infrastructure/logging.py +1503 -0
  279. truthound/infrastructure/metrics.py +1220 -0
  280. truthound/lineage/__init__.py +89 -0
  281. truthound/lineage/base.py +746 -0
  282. truthound/lineage/impact_analysis.py +474 -0
  283. truthound/lineage/integrations/__init__.py +22 -0
  284. truthound/lineage/integrations/openlineage.py +548 -0
  285. truthound/lineage/tracker.py +512 -0
  286. truthound/lineage/visualization/__init__.py +33 -0
  287. truthound/lineage/visualization/protocols.py +145 -0
  288. truthound/lineage/visualization/renderers/__init__.py +20 -0
  289. truthound/lineage/visualization/renderers/cytoscape.py +329 -0
  290. truthound/lineage/visualization/renderers/d3.py +331 -0
  291. truthound/lineage/visualization/renderers/graphviz.py +276 -0
  292. truthound/lineage/visualization/renderers/mermaid.py +308 -0
  293. truthound/maskers.py +113 -0
  294. truthound/ml/__init__.py +124 -0
  295. truthound/ml/anomaly_models/__init__.py +31 -0
  296. truthound/ml/anomaly_models/ensemble.py +362 -0
  297. truthound/ml/anomaly_models/isolation_forest.py +444 -0
  298. truthound/ml/anomaly_models/statistical.py +392 -0
  299. truthound/ml/base.py +1178 -0
  300. truthound/ml/drift_detection/__init__.py +26 -0
  301. truthound/ml/drift_detection/concept.py +381 -0
  302. truthound/ml/drift_detection/distribution.py +361 -0
  303. truthound/ml/drift_detection/feature.py +442 -0
  304. truthound/ml/drift_detection/multivariate.py +495 -0
  305. truthound/ml/monitoring/__init__.py +88 -0
  306. truthound/ml/monitoring/alerting/__init__.py +33 -0
  307. truthound/ml/monitoring/alerting/handlers.py +427 -0
  308. truthound/ml/monitoring/alerting/rules.py +508 -0
  309. truthound/ml/monitoring/collectors/__init__.py +19 -0
  310. truthound/ml/monitoring/collectors/composite.py +105 -0
  311. truthound/ml/monitoring/collectors/drift.py +324 -0
  312. truthound/ml/monitoring/collectors/performance.py +179 -0
  313. truthound/ml/monitoring/collectors/quality.py +369 -0
  314. truthound/ml/monitoring/monitor.py +536 -0
  315. truthound/ml/monitoring/protocols.py +451 -0
  316. truthound/ml/monitoring/stores/__init__.py +15 -0
  317. truthound/ml/monitoring/stores/memory.py +201 -0
  318. truthound/ml/monitoring/stores/prometheus.py +296 -0
  319. truthound/ml/rule_learning/__init__.py +25 -0
  320. truthound/ml/rule_learning/constraint_miner.py +443 -0
  321. truthound/ml/rule_learning/pattern_learner.py +499 -0
  322. truthound/ml/rule_learning/profile_learner.py +462 -0
  323. truthound/multitenancy/__init__.py +326 -0
  324. truthound/multitenancy/core.py +852 -0
  325. truthound/multitenancy/integration.py +597 -0
  326. truthound/multitenancy/isolation.py +630 -0
  327. truthound/multitenancy/manager.py +770 -0
  328. truthound/multitenancy/middleware.py +765 -0
  329. truthound/multitenancy/quota.py +537 -0
  330. truthound/multitenancy/resolvers.py +603 -0
  331. truthound/multitenancy/storage.py +703 -0
  332. truthound/observability/__init__.py +307 -0
  333. truthound/observability/context.py +531 -0
  334. truthound/observability/instrumentation.py +611 -0
  335. truthound/observability/logging.py +887 -0
  336. truthound/observability/metrics.py +1157 -0
  337. truthound/observability/tracing/__init__.py +178 -0
  338. truthound/observability/tracing/baggage.py +310 -0
  339. truthound/observability/tracing/config.py +426 -0
  340. truthound/observability/tracing/exporter.py +787 -0
  341. truthound/observability/tracing/integration.py +1018 -0
  342. truthound/observability/tracing/otel/__init__.py +146 -0
  343. truthound/observability/tracing/otel/adapter.py +982 -0
  344. truthound/observability/tracing/otel/bridge.py +1177 -0
  345. truthound/observability/tracing/otel/compat.py +681 -0
  346. truthound/observability/tracing/otel/config.py +691 -0
  347. truthound/observability/tracing/otel/detection.py +327 -0
  348. truthound/observability/tracing/otel/protocols.py +426 -0
  349. truthound/observability/tracing/processor.py +561 -0
  350. truthound/observability/tracing/propagator.py +757 -0
  351. truthound/observability/tracing/provider.py +569 -0
  352. truthound/observability/tracing/resource.py +515 -0
  353. truthound/observability/tracing/sampler.py +487 -0
  354. truthound/observability/tracing/span.py +676 -0
  355. truthound/plugins/__init__.py +198 -0
  356. truthound/plugins/base.py +599 -0
  357. truthound/plugins/cli.py +680 -0
  358. truthound/plugins/dependencies/__init__.py +42 -0
  359. truthound/plugins/dependencies/graph.py +422 -0
  360. truthound/plugins/dependencies/resolver.py +417 -0
  361. truthound/plugins/discovery.py +379 -0
  362. truthound/plugins/docs/__init__.py +46 -0
  363. truthound/plugins/docs/extractor.py +444 -0
  364. truthound/plugins/docs/renderer.py +499 -0
  365. truthound/plugins/enterprise_manager.py +877 -0
  366. truthound/plugins/examples/__init__.py +19 -0
  367. truthound/plugins/examples/custom_validators.py +317 -0
  368. truthound/plugins/examples/slack_notifier.py +312 -0
  369. truthound/plugins/examples/xml_reporter.py +254 -0
  370. truthound/plugins/hooks.py +558 -0
  371. truthound/plugins/lifecycle/__init__.py +43 -0
  372. truthound/plugins/lifecycle/hot_reload.py +402 -0
  373. truthound/plugins/lifecycle/manager.py +371 -0
  374. truthound/plugins/manager.py +736 -0
  375. truthound/plugins/registry.py +338 -0
  376. truthound/plugins/security/__init__.py +93 -0
  377. truthound/plugins/security/exceptions.py +332 -0
  378. truthound/plugins/security/policies.py +348 -0
  379. truthound/plugins/security/protocols.py +643 -0
  380. truthound/plugins/security/sandbox/__init__.py +45 -0
  381. truthound/plugins/security/sandbox/context.py +158 -0
  382. truthound/plugins/security/sandbox/engines/__init__.py +19 -0
  383. truthound/plugins/security/sandbox/engines/container.py +379 -0
  384. truthound/plugins/security/sandbox/engines/noop.py +144 -0
  385. truthound/plugins/security/sandbox/engines/process.py +336 -0
  386. truthound/plugins/security/sandbox/factory.py +211 -0
  387. truthound/plugins/security/signing/__init__.py +57 -0
  388. truthound/plugins/security/signing/service.py +330 -0
  389. truthound/plugins/security/signing/trust_store.py +368 -0
  390. truthound/plugins/security/signing/verifier.py +459 -0
  391. truthound/plugins/versioning/__init__.py +41 -0
  392. truthound/plugins/versioning/constraints.py +297 -0
  393. truthound/plugins/versioning/resolver.py +329 -0
  394. truthound/profiler/__init__.py +1729 -0
  395. truthound/profiler/_lazy.py +452 -0
  396. truthound/profiler/ab_testing/__init__.py +80 -0
  397. truthound/profiler/ab_testing/analysis.py +449 -0
  398. truthound/profiler/ab_testing/base.py +257 -0
  399. truthound/profiler/ab_testing/experiment.py +395 -0
  400. truthound/profiler/ab_testing/tracking.py +368 -0
  401. truthound/profiler/auto_threshold.py +1170 -0
  402. truthound/profiler/base.py +579 -0
  403. truthound/profiler/cache_patterns.py +911 -0
  404. truthound/profiler/caching.py +1303 -0
  405. truthound/profiler/column_profiler.py +712 -0
  406. truthound/profiler/comparison.py +1007 -0
  407. truthound/profiler/custom_patterns.py +1170 -0
  408. truthound/profiler/dashboard/__init__.py +50 -0
  409. truthound/profiler/dashboard/app.py +476 -0
  410. truthound/profiler/dashboard/components.py +457 -0
  411. truthound/profiler/dashboard/config.py +72 -0
  412. truthound/profiler/distributed/__init__.py +83 -0
  413. truthound/profiler/distributed/base.py +281 -0
  414. truthound/profiler/distributed/dask_backend.py +498 -0
  415. truthound/profiler/distributed/local_backend.py +293 -0
  416. truthound/profiler/distributed/profiler.py +304 -0
  417. truthound/profiler/distributed/ray_backend.py +374 -0
  418. truthound/profiler/distributed/spark_backend.py +375 -0
  419. truthound/profiler/distributed.py +1366 -0
  420. truthound/profiler/enterprise_sampling.py +1065 -0
  421. truthound/profiler/errors.py +488 -0
  422. truthound/profiler/evolution/__init__.py +91 -0
  423. truthound/profiler/evolution/alerts.py +426 -0
  424. truthound/profiler/evolution/changes.py +206 -0
  425. truthound/profiler/evolution/compatibility.py +365 -0
  426. truthound/profiler/evolution/detector.py +372 -0
  427. truthound/profiler/evolution/protocols.py +121 -0
  428. truthound/profiler/generators/__init__.py +48 -0
  429. truthound/profiler/generators/base.py +384 -0
  430. truthound/profiler/generators/ml_rules.py +375 -0
  431. truthound/profiler/generators/pattern_rules.py +384 -0
  432. truthound/profiler/generators/schema_rules.py +267 -0
  433. truthound/profiler/generators/stats_rules.py +324 -0
  434. truthound/profiler/generators/suite_generator.py +857 -0
  435. truthound/profiler/i18n.py +1542 -0
  436. truthound/profiler/incremental.py +554 -0
  437. truthound/profiler/incremental_validation.py +1710 -0
  438. truthound/profiler/integration/__init__.py +73 -0
  439. truthound/profiler/integration/adapters.py +345 -0
  440. truthound/profiler/integration/context.py +371 -0
  441. truthound/profiler/integration/executor.py +527 -0
  442. truthound/profiler/integration/naming.py +75 -0
  443. truthound/profiler/integration/protocols.py +243 -0
  444. truthound/profiler/memory.py +1185 -0
  445. truthound/profiler/migration/__init__.py +60 -0
  446. truthound/profiler/migration/base.py +345 -0
  447. truthound/profiler/migration/manager.py +444 -0
  448. truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
  449. truthound/profiler/ml/__init__.py +73 -0
  450. truthound/profiler/ml/base.py +244 -0
  451. truthound/profiler/ml/classifier.py +507 -0
  452. truthound/profiler/ml/feature_extraction.py +604 -0
  453. truthound/profiler/ml/pretrained.py +448 -0
  454. truthound/profiler/ml_inference.py +1276 -0
  455. truthound/profiler/native_patterns.py +815 -0
  456. truthound/profiler/observability.py +1184 -0
  457. truthound/profiler/process_timeout.py +1566 -0
  458. truthound/profiler/progress.py +568 -0
  459. truthound/profiler/progress_callbacks.py +1734 -0
  460. truthound/profiler/quality.py +1345 -0
  461. truthound/profiler/resilience.py +1180 -0
  462. truthound/profiler/sampled_matcher.py +794 -0
  463. truthound/profiler/sampling.py +1288 -0
  464. truthound/profiler/scheduling/__init__.py +82 -0
  465. truthound/profiler/scheduling/protocols.py +214 -0
  466. truthound/profiler/scheduling/scheduler.py +474 -0
  467. truthound/profiler/scheduling/storage.py +457 -0
  468. truthound/profiler/scheduling/triggers.py +449 -0
  469. truthound/profiler/schema.py +603 -0
  470. truthound/profiler/streaming.py +685 -0
  471. truthound/profiler/streaming_patterns.py +1354 -0
  472. truthound/profiler/suite_cli.py +625 -0
  473. truthound/profiler/suite_config.py +789 -0
  474. truthound/profiler/suite_export.py +1268 -0
  475. truthound/profiler/table_profiler.py +547 -0
  476. truthound/profiler/timeout.py +565 -0
  477. truthound/profiler/validation.py +1532 -0
  478. truthound/profiler/visualization/__init__.py +118 -0
  479. truthound/profiler/visualization/base.py +346 -0
  480. truthound/profiler/visualization/generator.py +1259 -0
  481. truthound/profiler/visualization/plotly_renderer.py +811 -0
  482. truthound/profiler/visualization/renderers.py +669 -0
  483. truthound/profiler/visualization/sections.py +540 -0
  484. truthound/profiler/visualization.py +2122 -0
  485. truthound/profiler/yaml_validation.py +1151 -0
  486. truthound/py.typed +0 -0
  487. truthound/ratelimit/__init__.py +248 -0
  488. truthound/ratelimit/algorithms.py +1108 -0
  489. truthound/ratelimit/core.py +573 -0
  490. truthound/ratelimit/integration.py +532 -0
  491. truthound/ratelimit/limiter.py +663 -0
  492. truthound/ratelimit/middleware.py +700 -0
  493. truthound/ratelimit/policy.py +792 -0
  494. truthound/ratelimit/storage.py +763 -0
  495. truthound/rbac/__init__.py +340 -0
  496. truthound/rbac/core.py +976 -0
  497. truthound/rbac/integration.py +760 -0
  498. truthound/rbac/manager.py +1052 -0
  499. truthound/rbac/middleware.py +842 -0
  500. truthound/rbac/policy.py +954 -0
  501. truthound/rbac/storage.py +878 -0
  502. truthound/realtime/__init__.py +141 -0
  503. truthound/realtime/adapters/__init__.py +43 -0
  504. truthound/realtime/adapters/base.py +533 -0
  505. truthound/realtime/adapters/kafka.py +487 -0
  506. truthound/realtime/adapters/kinesis.py +479 -0
  507. truthound/realtime/adapters/mock.py +243 -0
  508. truthound/realtime/base.py +553 -0
  509. truthound/realtime/factory.py +382 -0
  510. truthound/realtime/incremental.py +660 -0
  511. truthound/realtime/processing/__init__.py +67 -0
  512. truthound/realtime/processing/exactly_once.py +575 -0
  513. truthound/realtime/processing/state.py +547 -0
  514. truthound/realtime/processing/windows.py +647 -0
  515. truthound/realtime/protocols.py +569 -0
  516. truthound/realtime/streaming.py +605 -0
  517. truthound/realtime/testing/__init__.py +32 -0
  518. truthound/realtime/testing/containers.py +615 -0
  519. truthound/realtime/testing/fixtures.py +484 -0
  520. truthound/report.py +280 -0
  521. truthound/reporters/__init__.py +46 -0
  522. truthound/reporters/_protocols.py +30 -0
  523. truthound/reporters/base.py +324 -0
  524. truthound/reporters/ci/__init__.py +66 -0
  525. truthound/reporters/ci/azure.py +436 -0
  526. truthound/reporters/ci/base.py +509 -0
  527. truthound/reporters/ci/bitbucket.py +567 -0
  528. truthound/reporters/ci/circleci.py +547 -0
  529. truthound/reporters/ci/detection.py +364 -0
  530. truthound/reporters/ci/factory.py +182 -0
  531. truthound/reporters/ci/github.py +388 -0
  532. truthound/reporters/ci/gitlab.py +471 -0
  533. truthound/reporters/ci/jenkins.py +525 -0
  534. truthound/reporters/console_reporter.py +299 -0
  535. truthound/reporters/factory.py +211 -0
  536. truthound/reporters/html_reporter.py +524 -0
  537. truthound/reporters/json_reporter.py +256 -0
  538. truthound/reporters/markdown_reporter.py +280 -0
  539. truthound/reporters/sdk/__init__.py +174 -0
  540. truthound/reporters/sdk/builder.py +558 -0
  541. truthound/reporters/sdk/mixins.py +1150 -0
  542. truthound/reporters/sdk/schema.py +1493 -0
  543. truthound/reporters/sdk/templates.py +666 -0
  544. truthound/reporters/sdk/testing.py +968 -0
  545. truthound/scanners.py +170 -0
  546. truthound/scheduling/__init__.py +122 -0
  547. truthound/scheduling/cron.py +1136 -0
  548. truthound/scheduling/presets.py +212 -0
  549. truthound/schema.py +275 -0
  550. truthound/secrets/__init__.py +173 -0
  551. truthound/secrets/base.py +618 -0
  552. truthound/secrets/cloud.py +682 -0
  553. truthound/secrets/integration.py +507 -0
  554. truthound/secrets/manager.py +633 -0
  555. truthound/secrets/oidc/__init__.py +172 -0
  556. truthound/secrets/oidc/base.py +902 -0
  557. truthound/secrets/oidc/credential_provider.py +623 -0
  558. truthound/secrets/oidc/exchangers.py +1001 -0
  559. truthound/secrets/oidc/github/__init__.py +110 -0
  560. truthound/secrets/oidc/github/claims.py +718 -0
  561. truthound/secrets/oidc/github/enhanced_provider.py +693 -0
  562. truthound/secrets/oidc/github/trust_policy.py +742 -0
  563. truthound/secrets/oidc/github/verification.py +723 -0
  564. truthound/secrets/oidc/github/workflow.py +691 -0
  565. truthound/secrets/oidc/providers.py +825 -0
  566. truthound/secrets/providers.py +506 -0
  567. truthound/secrets/resolver.py +495 -0
  568. truthound/stores/__init__.py +177 -0
  569. truthound/stores/backends/__init__.py +18 -0
  570. truthound/stores/backends/_protocols.py +340 -0
  571. truthound/stores/backends/azure_blob.py +530 -0
  572. truthound/stores/backends/concurrent_filesystem.py +915 -0
  573. truthound/stores/backends/connection_pool.py +1365 -0
  574. truthound/stores/backends/database.py +743 -0
  575. truthound/stores/backends/filesystem.py +538 -0
  576. truthound/stores/backends/gcs.py +399 -0
  577. truthound/stores/backends/memory.py +354 -0
  578. truthound/stores/backends/s3.py +434 -0
  579. truthound/stores/backpressure/__init__.py +84 -0
  580. truthound/stores/backpressure/base.py +375 -0
  581. truthound/stores/backpressure/circuit_breaker.py +434 -0
  582. truthound/stores/backpressure/monitor.py +376 -0
  583. truthound/stores/backpressure/strategies.py +677 -0
  584. truthound/stores/base.py +551 -0
  585. truthound/stores/batching/__init__.py +65 -0
  586. truthound/stores/batching/base.py +305 -0
  587. truthound/stores/batching/buffer.py +370 -0
  588. truthound/stores/batching/store.py +248 -0
  589. truthound/stores/batching/writer.py +521 -0
  590. truthound/stores/caching/__init__.py +60 -0
  591. truthound/stores/caching/backends.py +684 -0
  592. truthound/stores/caching/base.py +356 -0
  593. truthound/stores/caching/store.py +305 -0
  594. truthound/stores/compression/__init__.py +193 -0
  595. truthound/stores/compression/adaptive.py +694 -0
  596. truthound/stores/compression/base.py +514 -0
  597. truthound/stores/compression/pipeline.py +868 -0
  598. truthound/stores/compression/providers.py +672 -0
  599. truthound/stores/compression/streaming.py +832 -0
  600. truthound/stores/concurrency/__init__.py +81 -0
  601. truthound/stores/concurrency/atomic.py +556 -0
  602. truthound/stores/concurrency/index.py +775 -0
  603. truthound/stores/concurrency/locks.py +576 -0
  604. truthound/stores/concurrency/manager.py +482 -0
  605. truthound/stores/encryption/__init__.py +297 -0
  606. truthound/stores/encryption/base.py +952 -0
  607. truthound/stores/encryption/keys.py +1191 -0
  608. truthound/stores/encryption/pipeline.py +903 -0
  609. truthound/stores/encryption/providers.py +953 -0
  610. truthound/stores/encryption/streaming.py +950 -0
  611. truthound/stores/expectations.py +227 -0
  612. truthound/stores/factory.py +246 -0
  613. truthound/stores/migration/__init__.py +75 -0
  614. truthound/stores/migration/base.py +480 -0
  615. truthound/stores/migration/manager.py +347 -0
  616. truthound/stores/migration/registry.py +382 -0
  617. truthound/stores/migration/store.py +559 -0
  618. truthound/stores/observability/__init__.py +106 -0
  619. truthound/stores/observability/audit.py +718 -0
  620. truthound/stores/observability/config.py +270 -0
  621. truthound/stores/observability/factory.py +208 -0
  622. truthound/stores/observability/metrics.py +636 -0
  623. truthound/stores/observability/protocols.py +410 -0
  624. truthound/stores/observability/store.py +570 -0
  625. truthound/stores/observability/tracing.py +784 -0
  626. truthound/stores/replication/__init__.py +76 -0
  627. truthound/stores/replication/base.py +260 -0
  628. truthound/stores/replication/monitor.py +269 -0
  629. truthound/stores/replication/store.py +439 -0
  630. truthound/stores/replication/syncer.py +391 -0
  631. truthound/stores/results.py +359 -0
  632. truthound/stores/retention/__init__.py +77 -0
  633. truthound/stores/retention/base.py +378 -0
  634. truthound/stores/retention/policies.py +621 -0
  635. truthound/stores/retention/scheduler.py +279 -0
  636. truthound/stores/retention/store.py +526 -0
  637. truthound/stores/streaming/__init__.py +138 -0
  638. truthound/stores/streaming/base.py +801 -0
  639. truthound/stores/streaming/database.py +984 -0
  640. truthound/stores/streaming/filesystem.py +719 -0
  641. truthound/stores/streaming/reader.py +629 -0
  642. truthound/stores/streaming/s3.py +843 -0
  643. truthound/stores/streaming/writer.py +790 -0
  644. truthound/stores/tiering/__init__.py +108 -0
  645. truthound/stores/tiering/base.py +462 -0
  646. truthound/stores/tiering/manager.py +249 -0
  647. truthound/stores/tiering/policies.py +692 -0
  648. truthound/stores/tiering/store.py +526 -0
  649. truthound/stores/versioning/__init__.py +56 -0
  650. truthound/stores/versioning/base.py +376 -0
  651. truthound/stores/versioning/store.py +660 -0
  652. truthound/stores/versioning/strategies.py +353 -0
  653. truthound/types.py +56 -0
  654. truthound/validators/__init__.py +774 -0
  655. truthound/validators/aggregate/__init__.py +27 -0
  656. truthound/validators/aggregate/central.py +116 -0
  657. truthound/validators/aggregate/extremes.py +116 -0
  658. truthound/validators/aggregate/spread.py +118 -0
  659. truthound/validators/aggregate/sum.py +64 -0
  660. truthound/validators/aggregate/type.py +78 -0
  661. truthound/validators/anomaly/__init__.py +93 -0
  662. truthound/validators/anomaly/base.py +431 -0
  663. truthound/validators/anomaly/ml_based.py +1190 -0
  664. truthound/validators/anomaly/multivariate.py +647 -0
  665. truthound/validators/anomaly/statistical.py +599 -0
  666. truthound/validators/base.py +1089 -0
  667. truthound/validators/business_rule/__init__.py +46 -0
  668. truthound/validators/business_rule/base.py +147 -0
  669. truthound/validators/business_rule/checksum.py +509 -0
  670. truthound/validators/business_rule/financial.py +526 -0
  671. truthound/validators/cache.py +733 -0
  672. truthound/validators/completeness/__init__.py +39 -0
  673. truthound/validators/completeness/conditional.py +73 -0
  674. truthound/validators/completeness/default.py +98 -0
  675. truthound/validators/completeness/empty.py +103 -0
  676. truthound/validators/completeness/nan.py +337 -0
  677. truthound/validators/completeness/null.py +152 -0
  678. truthound/validators/cross_table/__init__.py +17 -0
  679. truthound/validators/cross_table/aggregate.py +333 -0
  680. truthound/validators/cross_table/row_count.py +122 -0
  681. truthound/validators/datetime/__init__.py +29 -0
  682. truthound/validators/datetime/format.py +78 -0
  683. truthound/validators/datetime/freshness.py +269 -0
  684. truthound/validators/datetime/order.py +73 -0
  685. truthound/validators/datetime/parseable.py +185 -0
  686. truthound/validators/datetime/range.py +202 -0
  687. truthound/validators/datetime/timezone.py +69 -0
  688. truthound/validators/distribution/__init__.py +49 -0
  689. truthound/validators/distribution/distribution.py +128 -0
  690. truthound/validators/distribution/monotonic.py +119 -0
  691. truthound/validators/distribution/outlier.py +178 -0
  692. truthound/validators/distribution/quantile.py +80 -0
  693. truthound/validators/distribution/range.py +254 -0
  694. truthound/validators/distribution/set.py +125 -0
  695. truthound/validators/distribution/statistical.py +459 -0
  696. truthound/validators/drift/__init__.py +79 -0
  697. truthound/validators/drift/base.py +427 -0
  698. truthound/validators/drift/multi_feature.py +401 -0
  699. truthound/validators/drift/numeric.py +395 -0
  700. truthound/validators/drift/psi.py +446 -0
  701. truthound/validators/drift/statistical.py +510 -0
  702. truthound/validators/enterprise.py +1658 -0
  703. truthound/validators/geospatial/__init__.py +80 -0
  704. truthound/validators/geospatial/base.py +97 -0
  705. truthound/validators/geospatial/boundary.py +238 -0
  706. truthound/validators/geospatial/coordinate.py +351 -0
  707. truthound/validators/geospatial/distance.py +399 -0
  708. truthound/validators/geospatial/polygon.py +665 -0
  709. truthound/validators/i18n/__init__.py +308 -0
  710. truthound/validators/i18n/bidi.py +571 -0
  711. truthound/validators/i18n/catalogs.py +570 -0
  712. truthound/validators/i18n/dialects.py +763 -0
  713. truthound/validators/i18n/extended_catalogs.py +549 -0
  714. truthound/validators/i18n/formatting.py +1434 -0
  715. truthound/validators/i18n/loader.py +1020 -0
  716. truthound/validators/i18n/messages.py +521 -0
  717. truthound/validators/i18n/plural.py +683 -0
  718. truthound/validators/i18n/protocols.py +855 -0
  719. truthound/validators/i18n/tms.py +1162 -0
  720. truthound/validators/localization/__init__.py +53 -0
  721. truthound/validators/localization/base.py +122 -0
  722. truthound/validators/localization/chinese.py +362 -0
  723. truthound/validators/localization/japanese.py +275 -0
  724. truthound/validators/localization/korean.py +524 -0
  725. truthound/validators/memory/__init__.py +94 -0
  726. truthound/validators/memory/approximate_knn.py +506 -0
  727. truthound/validators/memory/base.py +547 -0
  728. truthound/validators/memory/sgd_online.py +719 -0
  729. truthound/validators/memory/streaming_ecdf.py +753 -0
  730. truthound/validators/ml_feature/__init__.py +54 -0
  731. truthound/validators/ml_feature/base.py +249 -0
  732. truthound/validators/ml_feature/correlation.py +299 -0
  733. truthound/validators/ml_feature/leakage.py +344 -0
  734. truthound/validators/ml_feature/null_impact.py +270 -0
  735. truthound/validators/ml_feature/scale.py +264 -0
  736. truthound/validators/multi_column/__init__.py +89 -0
  737. truthound/validators/multi_column/arithmetic.py +284 -0
  738. truthound/validators/multi_column/base.py +231 -0
  739. truthound/validators/multi_column/comparison.py +273 -0
  740. truthound/validators/multi_column/consistency.py +312 -0
  741. truthound/validators/multi_column/statistical.py +299 -0
  742. truthound/validators/optimization/__init__.py +164 -0
  743. truthound/validators/optimization/aggregation.py +563 -0
  744. truthound/validators/optimization/covariance.py +556 -0
  745. truthound/validators/optimization/geo.py +626 -0
  746. truthound/validators/optimization/graph.py +587 -0
  747. truthound/validators/optimization/orchestrator.py +970 -0
  748. truthound/validators/optimization/profiling.py +1312 -0
  749. truthound/validators/privacy/__init__.py +223 -0
  750. truthound/validators/privacy/base.py +635 -0
  751. truthound/validators/privacy/ccpa.py +670 -0
  752. truthound/validators/privacy/gdpr.py +728 -0
  753. truthound/validators/privacy/global_patterns.py +604 -0
  754. truthound/validators/privacy/plugins.py +867 -0
  755. truthound/validators/profiling/__init__.py +52 -0
  756. truthound/validators/profiling/base.py +175 -0
  757. truthound/validators/profiling/cardinality.py +312 -0
  758. truthound/validators/profiling/entropy.py +391 -0
  759. truthound/validators/profiling/frequency.py +455 -0
  760. truthound/validators/pushdown_support.py +660 -0
  761. truthound/validators/query/__init__.py +91 -0
  762. truthound/validators/query/aggregate.py +346 -0
  763. truthound/validators/query/base.py +246 -0
  764. truthound/validators/query/column.py +249 -0
  765. truthound/validators/query/expression.py +274 -0
  766. truthound/validators/query/result.py +323 -0
  767. truthound/validators/query/row_count.py +264 -0
  768. truthound/validators/referential/__init__.py +80 -0
  769. truthound/validators/referential/base.py +395 -0
  770. truthound/validators/referential/cascade.py +391 -0
  771. truthound/validators/referential/circular.py +563 -0
  772. truthound/validators/referential/foreign_key.py +624 -0
  773. truthound/validators/referential/orphan.py +485 -0
  774. truthound/validators/registry.py +112 -0
  775. truthound/validators/schema/__init__.py +41 -0
  776. truthound/validators/schema/column_count.py +142 -0
  777. truthound/validators/schema/column_exists.py +80 -0
  778. truthound/validators/schema/column_order.py +82 -0
  779. truthound/validators/schema/column_pair.py +85 -0
  780. truthound/validators/schema/column_pair_set.py +195 -0
  781. truthound/validators/schema/column_type.py +94 -0
  782. truthound/validators/schema/multi_column.py +53 -0
  783. truthound/validators/schema/multi_column_aggregate.py +175 -0
  784. truthound/validators/schema/referential.py +274 -0
  785. truthound/validators/schema/table_schema.py +91 -0
  786. truthound/validators/schema_validator.py +219 -0
  787. truthound/validators/sdk/__init__.py +250 -0
  788. truthound/validators/sdk/builder.py +680 -0
  789. truthound/validators/sdk/decorators.py +474 -0
  790. truthound/validators/sdk/enterprise/__init__.py +211 -0
  791. truthound/validators/sdk/enterprise/docs.py +725 -0
  792. truthound/validators/sdk/enterprise/fuzzing.py +659 -0
  793. truthound/validators/sdk/enterprise/licensing.py +709 -0
  794. truthound/validators/sdk/enterprise/manager.py +543 -0
  795. truthound/validators/sdk/enterprise/resources.py +628 -0
  796. truthound/validators/sdk/enterprise/sandbox.py +766 -0
  797. truthound/validators/sdk/enterprise/signing.py +603 -0
  798. truthound/validators/sdk/enterprise/templates.py +865 -0
  799. truthound/validators/sdk/enterprise/versioning.py +659 -0
  800. truthound/validators/sdk/templates.py +757 -0
  801. truthound/validators/sdk/testing.py +807 -0
  802. truthound/validators/security/__init__.py +181 -0
  803. truthound/validators/security/redos/__init__.py +182 -0
  804. truthound/validators/security/redos/core.py +861 -0
  805. truthound/validators/security/redos/cpu_monitor.py +593 -0
  806. truthound/validators/security/redos/cve_database.py +791 -0
  807. truthound/validators/security/redos/ml/__init__.py +155 -0
  808. truthound/validators/security/redos/ml/base.py +785 -0
  809. truthound/validators/security/redos/ml/datasets.py +618 -0
  810. truthound/validators/security/redos/ml/features.py +359 -0
  811. truthound/validators/security/redos/ml/models.py +1000 -0
  812. truthound/validators/security/redos/ml/predictor.py +507 -0
  813. truthound/validators/security/redos/ml/storage.py +632 -0
  814. truthound/validators/security/redos/ml/training.py +571 -0
  815. truthound/validators/security/redos/ml_analyzer.py +937 -0
  816. truthound/validators/security/redos/optimizer.py +674 -0
  817. truthound/validators/security/redos/profiler.py +682 -0
  818. truthound/validators/security/redos/re2_engine.py +709 -0
  819. truthound/validators/security/redos.py +886 -0
  820. truthound/validators/security/sql_security.py +1247 -0
  821. truthound/validators/streaming/__init__.py +126 -0
  822. truthound/validators/streaming/base.py +292 -0
  823. truthound/validators/streaming/completeness.py +210 -0
  824. truthound/validators/streaming/mixin.py +575 -0
  825. truthound/validators/streaming/range.py +308 -0
  826. truthound/validators/streaming/sources.py +846 -0
  827. truthound/validators/string/__init__.py +57 -0
  828. truthound/validators/string/casing.py +158 -0
  829. truthound/validators/string/charset.py +96 -0
  830. truthound/validators/string/format.py +501 -0
  831. truthound/validators/string/json.py +77 -0
  832. truthound/validators/string/json_schema.py +184 -0
  833. truthound/validators/string/length.py +104 -0
  834. truthound/validators/string/like_pattern.py +237 -0
  835. truthound/validators/string/regex.py +202 -0
  836. truthound/validators/string/regex_extended.py +435 -0
  837. truthound/validators/table/__init__.py +88 -0
  838. truthound/validators/table/base.py +78 -0
  839. truthound/validators/table/column_count.py +198 -0
  840. truthound/validators/table/freshness.py +362 -0
  841. truthound/validators/table/row_count.py +251 -0
  842. truthound/validators/table/schema.py +333 -0
  843. truthound/validators/table/size.py +285 -0
  844. truthound/validators/timeout/__init__.py +102 -0
  845. truthound/validators/timeout/advanced/__init__.py +247 -0
  846. truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
  847. truthound/validators/timeout/advanced/prediction.py +773 -0
  848. truthound/validators/timeout/advanced/priority.py +618 -0
  849. truthound/validators/timeout/advanced/redis_backend.py +770 -0
  850. truthound/validators/timeout/advanced/retry.py +721 -0
  851. truthound/validators/timeout/advanced/sampling.py +788 -0
  852. truthound/validators/timeout/advanced/sla.py +661 -0
  853. truthound/validators/timeout/advanced/telemetry.py +804 -0
  854. truthound/validators/timeout/cascade.py +477 -0
  855. truthound/validators/timeout/deadline.py +657 -0
  856. truthound/validators/timeout/degradation.py +525 -0
  857. truthound/validators/timeout/distributed.py +597 -0
  858. truthound/validators/timeseries/__init__.py +89 -0
  859. truthound/validators/timeseries/base.py +326 -0
  860. truthound/validators/timeseries/completeness.py +617 -0
  861. truthound/validators/timeseries/gap.py +485 -0
  862. truthound/validators/timeseries/monotonic.py +310 -0
  863. truthound/validators/timeseries/seasonality.py +422 -0
  864. truthound/validators/timeseries/trend.py +510 -0
  865. truthound/validators/uniqueness/__init__.py +59 -0
  866. truthound/validators/uniqueness/approximate.py +475 -0
  867. truthound/validators/uniqueness/distinct_values.py +253 -0
  868. truthound/validators/uniqueness/duplicate.py +118 -0
  869. truthound/validators/uniqueness/primary_key.py +140 -0
  870. truthound/validators/uniqueness/unique.py +191 -0
  871. truthound/validators/uniqueness/within_record.py +599 -0
  872. truthound/validators/utils.py +756 -0
  873. truthound-1.0.8.dist-info/METADATA +474 -0
  874. truthound-1.0.8.dist-info/RECORD +877 -0
  875. truthound-1.0.8.dist-info/WHEEL +4 -0
  876. truthound-1.0.8.dist-info/entry_points.txt +2 -0
  877. truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
@@ -0,0 +1,723 @@
1
+ """OIDC Token Verification Utilities.
2
+
3
+ This module provides utilities for verifying GitHub Actions OIDC tokens
4
+ using JWKS (JSON Web Key Set) from GitHub's OIDC provider.
5
+
6
+ Features:
7
+ - JWKS fetching and caching
8
+ - JWT signature verification
9
+ - Claims validation
10
+ - Token expiration checking
11
+
12
+ Note: Token verification requires the 'cryptography' package:
13
+ pip install cryptography
14
+
15
+ Example:
16
+ >>> from truthound.secrets.oidc.github import verify_token, GitHubActionsJWKS
17
+ >>>
18
+ >>> # Verify a token
19
+ >>> result = verify_token(token_string, audience="sts.amazonaws.com")
20
+ >>> if result.is_valid:
21
+ ... print(f"Token verified for: {result.claims.repository}")
22
+ >>> else:
23
+ ... print(f"Verification failed: {result.error}")
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import base64
29
+ import hashlib
30
+ import json
31
+ import logging
32
+ import time
33
+ from dataclasses import dataclass, field
34
+ from datetime import datetime, timedelta
35
+ from typing import Any
36
+ from abc import ABC, abstractmethod
37
+
38
+ from truthound.secrets.oidc.github.claims import (
39
+ GitHubActionsClaims,
40
+ parse_github_claims,
41
+ )
42
+
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+
47
+ # GitHub Actions OIDC endpoints
48
+ GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com"
49
+ GITHUB_OIDC_JWKS_URL = f"{GITHUB_OIDC_ISSUER}/.well-known/jwks"
50
+ GITHUB_OIDC_CONFIG_URL = f"{GITHUB_OIDC_ISSUER}/.well-known/openid-configuration"
51
+
52
+
53
+ # =============================================================================
54
+ # JWKS Verification
55
+ # =============================================================================
56
+
57
+
58
+ @dataclass
59
+ class JWK:
60
+ """JSON Web Key representation.
61
+
62
+ Attributes:
63
+ kty: Key type (RSA, EC).
64
+ kid: Key ID.
65
+ alg: Algorithm.
66
+ use: Key usage (sig).
67
+ n: RSA modulus (base64url).
68
+ e: RSA exponent (base64url).
69
+ x: EC X coordinate (base64url).
70
+ y: EC Y coordinate (base64url).
71
+ crv: EC curve name.
72
+ """
73
+
74
+ kty: str
75
+ kid: str = ""
76
+ alg: str = ""
77
+ use: str = ""
78
+ n: str = "" # RSA
79
+ e: str = "" # RSA
80
+ x: str = "" # EC
81
+ y: str = "" # EC
82
+ crv: str = "" # EC
83
+
84
+ @classmethod
85
+ def from_dict(cls, data: dict[str, Any]) -> "JWK":
86
+ """Create JWK from dictionary."""
87
+ return cls(
88
+ kty=data.get("kty", ""),
89
+ kid=data.get("kid", ""),
90
+ alg=data.get("alg", ""),
91
+ use=data.get("use", ""),
92
+ n=data.get("n", ""),
93
+ e=data.get("e", ""),
94
+ x=data.get("x", ""),
95
+ y=data.get("y", ""),
96
+ crv=data.get("crv", ""),
97
+ )
98
+
99
+ def get_public_key(self) -> Any:
100
+ """Convert JWK to cryptography public key.
101
+
102
+ Returns:
103
+ RSAPublicKey or EllipticCurvePublicKey.
104
+
105
+ Raises:
106
+ ImportError: If cryptography is not installed.
107
+ ValueError: If key type is not supported.
108
+ """
109
+ try:
110
+ from cryptography.hazmat.primitives.asymmetric import rsa, ec
111
+ from cryptography.hazmat.backends import default_backend
112
+ except ImportError as e:
113
+ raise ImportError(
114
+ "Token verification requires 'cryptography' package. "
115
+ "Install with: pip install cryptography"
116
+ ) from e
117
+
118
+ if self.kty == "RSA":
119
+ return self._build_rsa_key()
120
+ elif self.kty == "EC":
121
+ return self._build_ec_key()
122
+ else:
123
+ raise ValueError(f"Unsupported key type: {self.kty}")
124
+
125
+ def _build_rsa_key(self) -> Any:
126
+ """Build RSA public key from JWK parameters."""
127
+ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
128
+ from cryptography.hazmat.backends import default_backend
129
+
130
+ # Decode base64url values
131
+ n_bytes = self._base64url_decode(self.n)
132
+ e_bytes = self._base64url_decode(self.e)
133
+
134
+ # Convert to integers
135
+ n_int = int.from_bytes(n_bytes, byteorder="big")
136
+ e_int = int.from_bytes(e_bytes, byteorder="big")
137
+
138
+ # Create public key
139
+ public_numbers = RSAPublicNumbers(e_int, n_int)
140
+ return public_numbers.public_key(default_backend())
141
+
142
+ def _build_ec_key(self) -> Any:
143
+ """Build EC public key from JWK parameters."""
144
+ from cryptography.hazmat.primitives.asymmetric.ec import (
145
+ EllipticCurvePublicNumbers,
146
+ SECP256R1,
147
+ SECP384R1,
148
+ SECP521R1,
149
+ )
150
+ from cryptography.hazmat.backends import default_backend
151
+
152
+ # Map curve names
153
+ curves = {
154
+ "P-256": SECP256R1(),
155
+ "P-384": SECP384R1(),
156
+ "P-521": SECP521R1(),
157
+ }
158
+
159
+ curve = curves.get(self.crv)
160
+ if not curve:
161
+ raise ValueError(f"Unsupported curve: {self.crv}")
162
+
163
+ # Decode coordinates
164
+ x_bytes = self._base64url_decode(self.x)
165
+ y_bytes = self._base64url_decode(self.y)
166
+
167
+ x_int = int.from_bytes(x_bytes, byteorder="big")
168
+ y_int = int.from_bytes(y_bytes, byteorder="big")
169
+
170
+ public_numbers = EllipticCurvePublicNumbers(x_int, y_int, curve)
171
+ return public_numbers.public_key(default_backend())
172
+
173
+ @staticmethod
174
+ def _base64url_decode(value: str) -> bytes:
175
+ """Decode base64url value."""
176
+ # Add padding if needed
177
+ padding = 4 - len(value) % 4
178
+ if padding != 4:
179
+ value += "=" * padding
180
+ return base64.urlsafe_b64decode(value)
181
+
182
+
183
+ @dataclass
184
+ class JWKS:
185
+ """JSON Web Key Set.
186
+
187
+ Attributes:
188
+ keys: List of JWK objects.
189
+ fetched_at: When the JWKS was fetched.
190
+ expires_at: When the cached JWKS expires.
191
+ """
192
+
193
+ keys: list[JWK] = field(default_factory=list)
194
+ fetched_at: datetime = field(default_factory=datetime.now)
195
+ expires_at: datetime | None = None
196
+
197
+ @classmethod
198
+ def from_dict(cls, data: dict[str, Any], cache_ttl: int = 3600) -> "JWKS":
199
+ """Create JWKS from dictionary."""
200
+ keys = [JWK.from_dict(k) for k in data.get("keys", [])]
201
+ now = datetime.now()
202
+ return cls(
203
+ keys=keys,
204
+ fetched_at=now,
205
+ expires_at=now + timedelta(seconds=cache_ttl),
206
+ )
207
+
208
+ def get_key(self, kid: str) -> JWK | None:
209
+ """Get key by key ID."""
210
+ for key in self.keys:
211
+ if key.kid == kid:
212
+ return key
213
+ return None
214
+
215
+ @property
216
+ def is_expired(self) -> bool:
217
+ """Check if cached JWKS is expired."""
218
+ if self.expires_at is None:
219
+ return False
220
+ return datetime.now() >= self.expires_at
221
+
222
+
223
+ class JWKSVerifier(ABC):
224
+ """Abstract base class for JWKS-based verification."""
225
+
226
+ @abstractmethod
227
+ def get_jwks(self, force_refresh: bool = False) -> JWKS:
228
+ """Get JWKS (potentially cached).
229
+
230
+ Args:
231
+ force_refresh: Force refresh from source.
232
+
233
+ Returns:
234
+ JWKS instance.
235
+ """
236
+ pass
237
+
238
+ @abstractmethod
239
+ def verify(
240
+ self,
241
+ token: str,
242
+ audience: str | list[str] | None = None,
243
+ ) -> "TokenVerificationResult":
244
+ """Verify a JWT token.
245
+
246
+ Args:
247
+ token: JWT token string.
248
+ audience: Expected audience(s).
249
+
250
+ Returns:
251
+ TokenVerificationResult.
252
+ """
253
+ pass
254
+
255
+
256
+ class GitHubActionsJWKS(JWKSVerifier):
257
+ """GitHub Actions OIDC JWKS verifier.
258
+
259
+ Fetches and caches the JWKS from GitHub Actions OIDC provider,
260
+ and provides token verification.
261
+
262
+ Example:
263
+ >>> verifier = GitHubActionsJWKS()
264
+ >>> result = verifier.verify(token, audience="sts.amazonaws.com")
265
+ >>> if result.is_valid:
266
+ ... print(f"Valid token for: {result.claims.repository}")
267
+ """
268
+
269
+ def __init__(
270
+ self,
271
+ *,
272
+ jwks_url: str = GITHUB_OIDC_JWKS_URL,
273
+ issuer: str = GITHUB_OIDC_ISSUER,
274
+ cache_ttl_seconds: int = 3600,
275
+ request_timeout: float = 30.0,
276
+ ) -> None:
277
+ """Initialize verifier.
278
+
279
+ Args:
280
+ jwks_url: URL to fetch JWKS from.
281
+ issuer: Expected token issuer.
282
+ cache_ttl_seconds: JWKS cache TTL.
283
+ request_timeout: HTTP request timeout.
284
+ """
285
+ self._jwks_url = jwks_url
286
+ self._issuer = issuer
287
+ self._cache_ttl = cache_ttl_seconds
288
+ self._request_timeout = request_timeout
289
+ self._cached_jwks: JWKS | None = None
290
+
291
+ def get_jwks(self, force_refresh: bool = False) -> JWKS:
292
+ """Get JWKS from GitHub Actions OIDC provider.
293
+
294
+ Args:
295
+ force_refresh: Force refresh from server.
296
+
297
+ Returns:
298
+ JWKS instance.
299
+ """
300
+ # Check cache
301
+ if not force_refresh and self._cached_jwks:
302
+ if not self._cached_jwks.is_expired:
303
+ return self._cached_jwks
304
+
305
+ # Fetch from server
306
+ import urllib.request
307
+ import urllib.error
308
+
309
+ request = urllib.request.Request(
310
+ self._jwks_url,
311
+ headers={"Accept": "application/json"},
312
+ )
313
+
314
+ try:
315
+ with urllib.request.urlopen(
316
+ request, timeout=self._request_timeout
317
+ ) as response:
318
+ data = json.loads(response.read())
319
+ self._cached_jwks = JWKS.from_dict(data, self._cache_ttl)
320
+ return self._cached_jwks
321
+
322
+ except urllib.error.URLError as e:
323
+ logger.error(f"Failed to fetch JWKS: {e}")
324
+ # Return cached if available, even if expired
325
+ if self._cached_jwks:
326
+ return self._cached_jwks
327
+ raise
328
+
329
+ def verify(
330
+ self,
331
+ token: str,
332
+ audience: str | list[str] | None = None,
333
+ ) -> "TokenVerificationResult":
334
+ """Verify a GitHub Actions OIDC token.
335
+
336
+ Args:
337
+ token: JWT token string.
338
+ audience: Expected audience(s).
339
+
340
+ Returns:
341
+ TokenVerificationResult with verification status.
342
+ """
343
+ try:
344
+ # Parse token header to get key ID
345
+ header = self._parse_header(token)
346
+ kid = header.get("kid", "")
347
+ alg = header.get("alg", "RS256")
348
+
349
+ # Get JWKS and find key
350
+ jwks = self.get_jwks()
351
+ jwk = jwks.get_key(kid)
352
+
353
+ if jwk is None:
354
+ # Try refreshing JWKS
355
+ jwks = self.get_jwks(force_refresh=True)
356
+ jwk = jwks.get_key(kid)
357
+
358
+ if jwk is None:
359
+ return TokenVerificationResult(
360
+ is_valid=False,
361
+ error=f"Key not found: {kid}",
362
+ )
363
+
364
+ # Verify signature
365
+ if not self._verify_signature(token, jwk, alg):
366
+ return TokenVerificationResult(
367
+ is_valid=False,
368
+ error="Signature verification failed",
369
+ )
370
+
371
+ # Parse and validate claims
372
+ payload = self._parse_payload(token)
373
+ claims = parse_github_claims(payload)
374
+
375
+ # Check expiration
376
+ if claims.is_expired:
377
+ return TokenVerificationResult(
378
+ is_valid=False,
379
+ error="Token is expired",
380
+ claims=claims,
381
+ )
382
+
383
+ # Check issuer
384
+ if claims.issuer != self._issuer:
385
+ return TokenVerificationResult(
386
+ is_valid=False,
387
+ error=f"Invalid issuer: {claims.issuer}",
388
+ claims=claims,
389
+ )
390
+
391
+ # Check audience
392
+ if audience:
393
+ audiences = audience if isinstance(audience, list) else [audience]
394
+ claim_audiences = (
395
+ claims.audience
396
+ if isinstance(claims.audience, list)
397
+ else [claims.audience]
398
+ )
399
+
400
+ if not any(aud in claim_audiences for aud in audiences):
401
+ return TokenVerificationResult(
402
+ is_valid=False,
403
+ error=f"Invalid audience: {claims.audience}",
404
+ claims=claims,
405
+ )
406
+
407
+ return TokenVerificationResult(
408
+ is_valid=True,
409
+ claims=claims,
410
+ )
411
+
412
+ except Exception as e:
413
+ logger.error(f"Token verification failed: {e}")
414
+ return TokenVerificationResult(
415
+ is_valid=False,
416
+ error=str(e),
417
+ )
418
+
419
+ def _parse_header(self, token: str) -> dict[str, Any]:
420
+ """Parse JWT header."""
421
+ parts = token.split(".")
422
+ if len(parts) != 3:
423
+ raise ValueError("Invalid JWT format")
424
+
425
+ header_b64 = parts[0]
426
+ padding = 4 - len(header_b64) % 4
427
+ if padding != 4:
428
+ header_b64 += "=" * padding
429
+
430
+ header_json = base64.urlsafe_b64decode(header_b64)
431
+ return json.loads(header_json)
432
+
433
+ def _parse_payload(self, token: str) -> dict[str, Any]:
434
+ """Parse JWT payload."""
435
+ parts = token.split(".")
436
+ if len(parts) != 3:
437
+ raise ValueError("Invalid JWT format")
438
+
439
+ payload_b64 = parts[1]
440
+ padding = 4 - len(payload_b64) % 4
441
+ if padding != 4:
442
+ payload_b64 += "=" * padding
443
+
444
+ payload_json = base64.urlsafe_b64decode(payload_b64)
445
+ return json.loads(payload_json)
446
+
447
+ def _verify_signature(self, token: str, jwk: JWK, alg: str) -> bool:
448
+ """Verify JWT signature using JWK."""
449
+ try:
450
+ from cryptography.hazmat.primitives import hashes
451
+ from cryptography.hazmat.primitives.asymmetric import padding, utils, ec
452
+ from cryptography.exceptions import InvalidSignature
453
+ except ImportError:
454
+ logger.warning(
455
+ "cryptography not available, skipping signature verification"
456
+ )
457
+ return True # Skip verification if cryptography not available
458
+
459
+ parts = token.split(".")
460
+ if len(parts) != 3:
461
+ return False
462
+
463
+ # Data to verify (header.payload)
464
+ message = f"{parts[0]}.{parts[1]}".encode()
465
+
466
+ # Decode signature
467
+ sig_b64 = parts[2]
468
+ padding_len = 4 - len(sig_b64) % 4
469
+ if padding_len != 4:
470
+ sig_b64 += "=" * padding_len
471
+ signature = base64.urlsafe_b64decode(sig_b64)
472
+
473
+ # Get public key
474
+ public_key = jwk.get_public_key()
475
+
476
+ try:
477
+ if alg.startswith("RS"):
478
+ # RSA algorithms
479
+ hash_alg = self._get_hash_algorithm(alg)
480
+ public_key.verify(
481
+ signature,
482
+ message,
483
+ padding.PKCS1v15(),
484
+ hash_alg,
485
+ )
486
+ elif alg.startswith("ES"):
487
+ # ECDSA algorithms
488
+ hash_alg = self._get_hash_algorithm(alg)
489
+ # Convert DER signature to raw if needed
490
+ public_key.verify(
491
+ signature,
492
+ message,
493
+ ec.ECDSA(hash_alg),
494
+ )
495
+ else:
496
+ logger.warning(f"Unsupported algorithm: {alg}")
497
+ return False
498
+
499
+ return True
500
+
501
+ except InvalidSignature:
502
+ return False
503
+
504
+ def _get_hash_algorithm(self, alg: str) -> Any:
505
+ """Get hash algorithm for JWT algorithm."""
506
+ from cryptography.hazmat.primitives import hashes
507
+
508
+ if alg in ("RS256", "ES256"):
509
+ return hashes.SHA256()
510
+ elif alg in ("RS384", "ES384"):
511
+ return hashes.SHA384()
512
+ elif alg in ("RS512", "ES512"):
513
+ return hashes.SHA512()
514
+ else:
515
+ return hashes.SHA256()
516
+
517
+
518
+ # =============================================================================
519
+ # Token Verifier
520
+ # =============================================================================
521
+
522
+
523
+ @dataclass
524
+ class TokenVerificationResult:
525
+ """Result of token verification.
526
+
527
+ Attributes:
528
+ is_valid: Whether token is valid.
529
+ claims: Parsed claims (if successful).
530
+ error: Error message (if failed).
531
+ """
532
+
533
+ is_valid: bool
534
+ claims: GitHubActionsClaims | None = None
535
+ error: str | None = None
536
+
537
+ def __bool__(self) -> bool:
538
+ return self.is_valid
539
+
540
+
541
+ class TokenVerifier:
542
+ """Token verification with configurable policies.
543
+
544
+ Example:
545
+ >>> verifier = TokenVerifier(
546
+ ... allowed_repositories=["owner/repo"],
547
+ ... allowed_branches=["main"],
548
+ ... require_environment=True,
549
+ ... )
550
+ >>> result = verifier.verify(token, audience="sts.amazonaws.com")
551
+ """
552
+
553
+ def __init__(
554
+ self,
555
+ *,
556
+ jwks_verifier: JWKSVerifier | None = None,
557
+ allowed_repositories: list[str] | None = None,
558
+ allowed_branches: list[str] | None = None,
559
+ allowed_environments: list[str] | None = None,
560
+ allowed_actors: list[str] | None = None,
561
+ require_environment: bool = False,
562
+ deny_pull_requests: bool = False,
563
+ verify_signature: bool = True,
564
+ ) -> None:
565
+ """Initialize verifier.
566
+
567
+ Args:
568
+ jwks_verifier: JWKS verifier instance.
569
+ allowed_repositories: List of allowed repository patterns.
570
+ allowed_branches: List of allowed branch names.
571
+ allowed_environments: List of allowed environment names.
572
+ allowed_actors: List of allowed actor names.
573
+ require_environment: Require deployment environment.
574
+ deny_pull_requests: Deny pull request events.
575
+ verify_signature: Enable signature verification.
576
+ """
577
+ self._jwks_verifier = jwks_verifier or GitHubActionsJWKS()
578
+ self._allowed_repositories = allowed_repositories
579
+ self._allowed_branches = allowed_branches
580
+ self._allowed_environments = allowed_environments
581
+ self._allowed_actors = allowed_actors
582
+ self._require_environment = require_environment
583
+ self._deny_pull_requests = deny_pull_requests
584
+ self._verify_signature = verify_signature
585
+
586
+ def verify(
587
+ self,
588
+ token: str,
589
+ audience: str | list[str] | None = None,
590
+ ) -> TokenVerificationResult:
591
+ """Verify token with policy checks.
592
+
593
+ Args:
594
+ token: JWT token string.
595
+ audience: Expected audience(s).
596
+
597
+ Returns:
598
+ TokenVerificationResult.
599
+ """
600
+ # First, verify signature and basic claims
601
+ if self._verify_signature:
602
+ result = self._jwks_verifier.verify(token, audience)
603
+ if not result.is_valid:
604
+ return result
605
+ claims = result.claims
606
+ else:
607
+ # Parse claims without verification
608
+ from truthound.secrets.oidc.github.claims import parse_github_claims
609
+ payload = self._parse_payload(token)
610
+ claims = parse_github_claims(payload)
611
+
612
+ if claims is None:
613
+ return TokenVerificationResult(
614
+ is_valid=False,
615
+ error="Failed to parse claims",
616
+ )
617
+
618
+ # Apply policy checks
619
+ errors: list[str] = []
620
+
621
+ # Check repository
622
+ if self._allowed_repositories:
623
+ if not any(
624
+ claims.matches_repository(repo)
625
+ for repo in self._allowed_repositories
626
+ ):
627
+ errors.append(
628
+ f"Repository '{claims.repository}' not in allowed list"
629
+ )
630
+
631
+ # Check branch
632
+ if self._allowed_branches and claims.branch_name:
633
+ if claims.branch_name not in self._allowed_branches:
634
+ errors.append(
635
+ f"Branch '{claims.branch_name}' not in allowed list"
636
+ )
637
+
638
+ # Check environment
639
+ if self._require_environment and not claims.environment:
640
+ errors.append("Deployment environment required")
641
+
642
+ if self._allowed_environments and claims.environment:
643
+ if claims.environment not in self._allowed_environments:
644
+ errors.append(
645
+ f"Environment '{claims.environment}' not in allowed list"
646
+ )
647
+
648
+ # Check actors
649
+ if self._allowed_actors:
650
+ if claims.actor not in self._allowed_actors:
651
+ errors.append(
652
+ f"Actor '{claims.actor}' not in allowed list"
653
+ )
654
+
655
+ # Check pull requests
656
+ if self._deny_pull_requests and claims.is_pull_request:
657
+ errors.append("Pull request events are denied")
658
+
659
+ if errors:
660
+ return TokenVerificationResult(
661
+ is_valid=False,
662
+ claims=claims,
663
+ error="; ".join(errors),
664
+ )
665
+
666
+ return TokenVerificationResult(
667
+ is_valid=True,
668
+ claims=claims,
669
+ )
670
+
671
+ def _parse_payload(self, token: str) -> dict[str, Any]:
672
+ """Parse JWT payload without verification."""
673
+ parts = token.split(".")
674
+ if len(parts) != 3:
675
+ raise ValueError("Invalid JWT format")
676
+
677
+ payload_b64 = parts[1]
678
+ padding = 4 - len(payload_b64) % 4
679
+ if padding != 4:
680
+ payload_b64 += "=" * padding
681
+
682
+ payload_json = base64.urlsafe_b64decode(payload_b64)
683
+ return json.loads(payload_json)
684
+
685
+
686
+ # =============================================================================
687
+ # Convenience Functions
688
+ # =============================================================================
689
+
690
+
691
+ def verify_token(
692
+ token: str,
693
+ audience: str | list[str] | None = None,
694
+ *,
695
+ verify_signature: bool = True,
696
+ ) -> TokenVerificationResult:
697
+ """Verify a GitHub Actions OIDC token.
698
+
699
+ Args:
700
+ token: JWT token string.
701
+ audience: Expected audience(s).
702
+ verify_signature: Enable signature verification.
703
+
704
+ Returns:
705
+ TokenVerificationResult.
706
+
707
+ Example:
708
+ >>> result = verify_token(token, audience="sts.amazonaws.com")
709
+ >>> if result.is_valid:
710
+ ... print(f"Valid for: {result.claims.repository}")
711
+ """
712
+ verifier = TokenVerifier(verify_signature=verify_signature)
713
+ return verifier.verify(token, audience)
714
+
715
+
716
+ def get_jwks() -> JWKS:
717
+ """Fetch GitHub Actions OIDC JWKS.
718
+
719
+ Returns:
720
+ JWKS instance.
721
+ """
722
+ verifier = GitHubActionsJWKS()
723
+ return verifier.get_jwks()