sqlguard-ninja 0.5.0__tar.gz → 0.7.0__tar.gz

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 (220) hide show
  1. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/.gitignore +4 -0
  2. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/CHANGELOG.md +56 -0
  3. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/PKG-INFO +56 -4
  4. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/README.md +40 -3
  5. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/THIRD_PARTY_NOTICES.md +30 -1
  6. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/architecture/ARCHITECTURE_DECISIONS.md +13 -0
  7. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/development/IMPLEMENTATION_PROGRESS.md +4 -1
  8. sqlguard_ninja-0.7.0/docs/development/NEXT_ACTIONS.md +5 -0
  9. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/development/TEST_EVIDENCE.md +6 -0
  10. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/development/progress.json +24 -14
  11. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/CI_INTEGRATION.md +22 -0
  12. sqlguard_ninja-0.7.0/docs/guides/DEPLOYMENT.md +122 -0
  13. sqlguard_ninja-0.7.0/docs/guides/OBSERVABILITY.md +133 -0
  14. sqlguard_ninja-0.7.0/docs/releases/v0.6.0.md +24 -0
  15. sqlguard_ninja-0.7.0/docs/releases/v0.7.0.md +22 -0
  16. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/security/THREAT_MODEL.md +2 -0
  17. sqlguard_ninja-0.7.0/examples/observability/docker-compose.yml +47 -0
  18. sqlguard_ninja-0.7.0/examples/observability/grafana/provisioning/dashboards/sqlguard.yml +9 -0
  19. sqlguard_ninja-0.7.0/examples/observability/grafana/provisioning/datasources/prometheus.yml +9 -0
  20. sqlguard_ninja-0.7.0/examples/observability/prometheus.yml +6 -0
  21. sqlguard_ninja-0.7.0/examples/observability/smoke_check.py +42 -0
  22. sqlguard_ninja-0.7.0/examples/observability/synthetic_traffic.py +80 -0
  23. sqlguard_ninja-0.7.0/examples/sql/support_queries.sql +22 -0
  24. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/pyproject.toml +10 -2
  25. sqlguard_ninja-0.7.0/src/sqlguard_ninja/_version.py +1 -0
  26. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/cli/main.py +111 -0
  27. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/app.py +195 -0
  28. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/examples.py +222 -0
  29. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/roles.yaml +55 -0
  30. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/sandbox.py +146 -0
  31. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/service.py +316 -0
  32. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/static/app.js +490 -0
  33. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/static/data/benchmark.json +573 -0
  34. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/static/favicon.svg +1 -0
  35. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/static/index.html +147 -0
  36. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/static/styles.css +220 -0
  37. sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo/static/worker.js +38 -0
  38. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/audit.py +42 -5
  39. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/checkpoint.py +30 -2
  40. sqlguard_ninja-0.7.0/src/sqlguard_ninja/observability/__init__.py +17 -0
  41. sqlguard_ninja-0.7.0/src/sqlguard_ninja/observability/metrics.py +121 -0
  42. sqlguard_ninja-0.7.0/src/sqlguard_ninja/observability/telemetry.py +148 -0
  43. sqlguard_ninja-0.7.0/src/sqlguard_ninja/reporting/sarif.py +195 -0
  44. sqlguard_ninja-0.7.0/tests/data/sarif-schema-2.1.0.json +3389 -0
  45. sqlguard_ninja-0.7.0/tests/e2e/conftest.py +84 -0
  46. sqlguard_ninja-0.7.0/tests/e2e/test_demo_e2e.py +165 -0
  47. sqlguard_ninja-0.7.0/tests/e2e/test_static_demo_e2e.py +80 -0
  48. sqlguard_ninja-0.7.0/tests/unit/__init__.py +0 -0
  49. sqlguard_ninja-0.7.0/tests/unit/test_demo.py +274 -0
  50. sqlguard_ninja-0.7.0/tests/unit/test_grafana_dashboard.py +84 -0
  51. sqlguard_ninja-0.7.0/tests/unit/test_observability.py +381 -0
  52. sqlguard_ninja-0.5.0/docs/development/NEXT_ACTIONS.md +0 -6
  53. sqlguard_ninja-0.5.0/src/sqlguard_ninja/_version.py +0 -1
  54. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/LICENSE +0 -0
  55. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/baselines/native.json +0 -0
  56. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/external/README.md +0 -0
  57. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/LABEL_CHANGES.yaml +0 -0
  58. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/01_destructive.yaml +0 -0
  59. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/02_multi_statement_and_formatting.yaml +0 -0
  60. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/03_table_and_column_access.yaml +0 -0
  61. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/04_privacy.yaml +0 -0
  62. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/05_tenant_isolation.yaml +0 -0
  63. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/06_cte_nested_unicode.yaml +0 -0
  64. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/07_legitimate_lookalike_cost_dialect.yaml +0 -0
  65. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/08_lineage.yaml +0 -0
  66. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/cases/09_mysql.yaml +0 -0
  67. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/generated/grant_matrix.yaml +0 -0
  68. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/generated/label_matrix.yaml +0 -0
  69. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/generated/tenant_matrix.yaml +0 -0
  70. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/generated/transforms.yaml +0 -0
  71. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/datasets/v1/manifest.yaml +0 -0
  72. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/policies/retail_saas.yaml +0 -0
  73. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/agent-sql-guard.json +0 -0
  74. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/agent-sql-guard.md +0 -0
  75. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/krish2105-sqlguard.json +0 -0
  76. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/krish2105-sqlguard.md +0 -0
  77. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/native.json +0 -0
  78. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/native.md +0 -0
  79. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/sql-guardrail.json +0 -0
  80. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.0.0/sql-guardrail.md +0 -0
  81. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/agent-sql-guard.csv +0 -0
  82. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/agent-sql-guard.json +0 -0
  83. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/agent-sql-guard.md +0 -0
  84. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/krish2105-sqlguard.csv +0 -0
  85. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/krish2105-sqlguard.json +0 -0
  86. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/krish2105-sqlguard.md +0 -0
  87. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/native.csv +0 -0
  88. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/native.json +0 -0
  89. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/native.md +0 -0
  90. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/sql-guardrail.csv +0 -0
  91. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/sql-guardrail.json +0 -0
  92. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.1.0/sql-guardrail.md +0 -0
  93. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/agent-sql-guard.csv +0 -0
  94. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/agent-sql-guard.json +0 -0
  95. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/agent-sql-guard.md +0 -0
  96. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/krish2105-sqlguard.csv +0 -0
  97. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/krish2105-sqlguard.json +0 -0
  98. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/krish2105-sqlguard.md +0 -0
  99. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/native.csv +0 -0
  100. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/native.json +0 -0
  101. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/native.md +0 -0
  102. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/sql-guardrail.csv +0 -0
  103. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/sql-guardrail.json +0 -0
  104. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/benchmarks/results/v1.2.0/sql-guardrail.md +0 -0
  105. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/benchmarks/RESULTS_v1.1.md +0 -0
  106. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/benchmarks/RESULTS_v1.2.md +0 -0
  107. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/benchmarks/RESULTS_v1.md +0 -0
  108. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/development/ARCHITECTURE_MAP.md +0 -0
  109. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/development/DECISION_LOG.md +0 -0
  110. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/development/UPGRADE_MASTER_PLAN.md +0 -0
  111. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/AGENT_INTEGRATION.md +0 -0
  112. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/BENCHMARK_BIAS_REVIEW.md +0 -0
  113. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/BENCHMARK_METHODOLOGY.md +0 -0
  114. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/DATABASE_COMPATIBILITY.md +0 -0
  115. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/MCP_INTEGRATION.md +0 -0
  116. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/POLICY_AS_CODE.md +0 -0
  117. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/POLICY_REFERENCE.md +0 -0
  118. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/guides/QUERY_COST.md +0 -0
  119. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/release/FINAL_TECHNICAL_AUDIT.md +0 -0
  120. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/releases/v0.1.0.md +0 -0
  121. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/releases/v0.1.1.md +0 -0
  122. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/releases/v0.2.0.md +0 -0
  123. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/releases/v0.3.0.md +0 -0
  124. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/releases/v0.4.0.md +0 -0
  125. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/releases/v0.5.0.md +0 -0
  126. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/research/COMPETITIVE_ANALYSIS.md +0 -0
  127. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/docs/security/STATIC_ANALYSIS_LIMITS.md +0 -0
  128. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/_shared.py +0 -0
  129. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/fastapi/app.py +0 -0
  130. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/github-actions/sql-security.yml +0 -0
  131. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/langchain/guarded_tool_demo.py +0 -0
  132. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/langgraph/real_llm_agent.py +0 -0
  133. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/langgraph/sql_agent_demo.py +0 -0
  134. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/postgresql/plan_inspection_demo.py +0 -0
  135. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/sdk/quickstart.py +0 -0
  136. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/examples/sqlite/text_to_sql_demo.py +0 -0
  137. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/__init__.py +0 -0
  138. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/__init__.py +0 -0
  139. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/adapters.py +0 -0
  140. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/builtin.py +0 -0
  141. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/compare.py +0 -0
  142. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/dataset.py +0 -0
  143. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/fixtures.py +0 -0
  144. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/metrics.py +0 -0
  145. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/runner.py +0 -0
  146. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/translate.py +0 -0
  147. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/workers/_protocol.py +0 -0
  148. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/workers/agent_sql_guard_worker.py +0 -0
  149. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/workers/krish_sqlguard_worker.py +0 -0
  150. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/benchmarking/workers/sql_guardrail_worker.py +0 -0
  151. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/cli/__init__.py +0 -0
  152. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/cli/config.py +0 -0
  153. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/cli/templates.py +0 -0
  154. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/__init__.py +0 -0
  155. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/analysis.py +0 -0
  156. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/findings.py +0 -0
  157. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/guard.py +0 -0
  158. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/lineage.py +0 -0
  159. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/__init__.py +0 -0
  160. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/access.py +0 -0
  161. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/base.py +0 -0
  162. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/catalog.py +0 -0
  163. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/functions.py +0 -0
  164. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/statements.py +0 -0
  165. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/structure.py +0 -0
  166. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/core/rules/tenant.py +0 -0
  167. {sqlguard_ninja-0.5.0/tests → sqlguard_ninja-0.7.0/src/sqlguard_ninja/demo}/__init__.py +0 -0
  168. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/__init__.py +0 -0
  169. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/executors.py +0 -0
  170. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/fastapi.py +0 -0
  171. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/langchain.py +0 -0
  172. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/langgraph.py +0 -0
  173. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/integrations/mysql.py +0 -0
  174. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/mcp_server.py +0 -0
  175. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/__init__.py +0 -0
  176. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/inheritance.py +0 -0
  177. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/model.py +0 -0
  178. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/templates/analytics_readonly.yaml +0 -0
  179. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/templates/minimal.yaml +0 -0
  180. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/templates/multi_tenant_saas.yaml +0 -0
  181. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/policy/tools.py +0 -0
  182. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/privacy/__init__.py +0 -0
  183. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/privacy/rules.py +0 -0
  184. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/privacy/suggest.py +0 -0
  185. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/py.typed +0 -0
  186. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/query_cost/__init__.py +0 -0
  187. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/query_cost/postgres.py +0 -0
  188. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/query_cost/static.py +0 -0
  189. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/reporting/__init__.py +0 -0
  190. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/reporting/markdown.py +0 -0
  191. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/src/sqlguard_ninja/tenancy.py +0 -0
  192. {sqlguard_ninja-0.5.0/tests/integration → sqlguard_ninja-0.7.0/tests}/__init__.py +0 -0
  193. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/conftest.py +0 -0
  194. {sqlguard_ninja-0.5.0/tests/property → sqlguard_ninja-0.7.0/tests/integration}/__init__.py +0 -0
  195. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/integration/test_corpus_sqlite_validation.py +0 -0
  196. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/integration/test_external_adapters.py +0 -0
  197. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/integration/test_frameworks.py +0 -0
  198. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/integration/test_mcp_server.py +0 -0
  199. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/integration/test_mysql.py +0 -0
  200. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/integration/test_postgres.py +0 -0
  201. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/pg_support.py +0 -0
  202. {sqlguard_ninja-0.5.0/tests/security → sqlguard_ninja-0.7.0/tests/property}/__init__.py +0 -0
  203. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/property/test_guard_properties.py +0 -0
  204. {sqlguard_ninja-0.5.0/tests/unit → sqlguard_ninja-0.7.0/tests/security}/__init__.py +0 -0
  205. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/security/test_agent_boundaries.py +0 -0
  206. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/security/test_corpus_native.py +0 -0
  207. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/security/test_tenant_rewrite.py +0 -0
  208. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/helpers.py +0 -0
  209. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_benchmark_engine.py +0 -0
  210. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_cli.py +0 -0
  211. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_dataset.py +0 -0
  212. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_docs.py +0 -0
  213. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_experimental_dialects.py +0 -0
  214. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_function_policy_mechanics.py +0 -0
  215. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_lineage.py +0 -0
  216. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_mysql_binding.py +0 -0
  217. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_policy.py +0 -0
  218. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_policy_inheritance.py +0 -0
  219. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_policy_tools.py +0 -0
  220. {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.7.0}/tests/unit/test_rules.py +0 -0
@@ -28,3 +28,7 @@ htmlcov/
28
28
  !.env.example
29
29
  audit*.jsonl
30
30
  .pytest_tmp/
31
+
32
+ # Static demo build output and download cache
33
+ /.cache/
34
+ /site/
@@ -3,6 +3,62 @@
3
3
  All notable changes are documented here. The project follows
4
4
  [Semantic Versioning](https://semver.org/); before 1.0, minor versions may change the API.
5
5
 
6
+ ## [0.7.0] — 2026-10-24
7
+
8
+ ### Added
9
+ * **Interactive playground** (`sqlguard_ninja.demo`, extra `[demo]`, `sqlninja demo serve`).
10
+ It runs on a synthetic retail database with three enforceable roles: Sales Analyst,
11
+ Customer Support and Store Manager. Features:
12
+ * an SQL editor and 20 example queries; tests check each one's expected decision
13
+ * plain-language explanations with how-to-fix hints
14
+ * lineage (output → base column, column usage with access per role)
15
+ * tenant-isolation demonstrations and a role-permission browser
16
+ * a benchmark dashboard that separates the 1,070 internal evaluations from the 0
17
+ independent ones
18
+ * **Restricted sandbox** for allowed demo queries. The database enforces it: an authorizer
19
+ allowlist, `query_only`, engine limits, a step/time budget and a row cap.
20
+ * **Static build** (`scripts/build_static_demo.py`). The same service runs in the browser with
21
+ Pyodide. Everything is self-hosted and checksum-verified, and the site makes no third-party
22
+ requests.
23
+ * **Deployment references:**
24
+ * a hardened `Dockerfile`: non-root, digest-pinned base, healthcheck
25
+ * a Compose file: read-only, no capabilities, resource limits
26
+ * Kubernetes manifests: restricted security context, probes, NetworkPolicy without
27
+ egress, PDB, example Ingress
28
+ * a manual GitHub Pages workflow
29
+ * **Server hardening:**
30
+ * validated `SQLNINJA_DEMO_*` settings
31
+ * per-client rate limiting, a body cap and 411 on chunked bodies
32
+ * strict CSP and security headers
33
+ * `/healthz` and `/readyz`
34
+ * **Browser end-to-end tests** (Playwright) in server mode and against the offline static
35
+ build, including axe-core accessibility checks, keyboard use, small screens and XSS-safe
36
+ rendering. New CI jobs cover these tests and the container and manifests.
37
+ * ADR-014, threat model T20, `docs/guides/DEPLOYMENT.md`.
38
+
39
+ ## [0.6.0] — 2026-10-24
40
+
41
+ ### Added
42
+ * **Observability** (`sqlguard_ninja.observability`, extras `[otel]` and `[prometheus]`):
43
+ * `Telemetry` and `InstrumentedGuard` emit OpenTelemetry spans `sqlguard.checkpoint` and
44
+ `sqlguard.validate`.
45
+ * `GuardMetrics` exports Prometheus counters and histograms for decisions, findings,
46
+ latency, errors, resource-limit rejections, checkpoint outcomes and policy identity.
47
+ * `SQLCheckpoint(telemetry=...)` instruments its guard automatically.
48
+ * **SARIF 2.1.0**: `sqlninja scan PATH... --format sarif|json|text --fail-on deny|review|never`.
49
+ It validates every statement in SQL files, with stable fingerprints. Tests and CI validate the
50
+ output against the OASIS schema.
51
+ * **Grafana dashboard** `deploy/grafana/sqlguard-ninja.json`, and a Prometheus + Grafana +
52
+ synthetic-traffic stack (`examples/observability/`). A new CI job runs that stack.
53
+ * Audit events gain `schema_version` and `rate_limited`. New
54
+ `SQLCheckpoint(principal_hmac_key=...)` option to pseudonymise principals.
55
+
56
+ ### Security
57
+ * `redact_sql` now redacts hex, bit, byte (`E''`), dollar-quoted, Unicode (`U&''`) and
58
+ national (`N''`) strings and whole `INTERVAL` expressions. Previously these could reach
59
+ `sql_redacted` in audit logs. Comments are dropped, and if any literal token survives,
60
+ redacted SQL is withheld. See threat model T19.
61
+
6
62
  ## [0.5.0] — 2026-10-24
7
63
 
8
64
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sqlguard-ninja
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: Security testing, policy enforcement, agent integration, privacy analysis and CI benchmarking for LLM-generated SQL.
5
5
  Project-URL: Homepage, https://github.com/Pratik653/SQLGuard_Ninjapy
6
6
  Project-URL: Documentation, https://github.com/Pratik653/SQLGuard_Ninjapy/tree/main/docs
@@ -30,23 +30,34 @@ Requires-Dist: fastapi>=0.110; extra == 'all'
30
30
  Requires-Dist: langchain-core>=0.3; extra == 'all'
31
31
  Requires-Dist: langgraph>=0.2; extra == 'all'
32
32
  Requires-Dist: mcp<3,>=2.3; extra == 'all'
33
+ Requires-Dist: opentelemetry-api>=1.25; extra == 'all'
34
+ Requires-Dist: prometheus-client>=0.20; extra == 'all'
33
35
  Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
34
36
  Requires-Dist: pymysql>=1.1; extra == 'all'
37
+ Requires-Dist: uvicorn>=0.30; extra == 'all'
38
+ Provides-Extra: demo
39
+ Requires-Dist: fastapi>=0.110; extra == 'demo'
40
+ Requires-Dist: uvicorn>=0.30; extra == 'demo'
35
41
  Provides-Extra: dev
36
42
  Requires-Dist: build>=1.2; extra == 'dev'
37
43
  Requires-Dist: fastapi>=0.110; extra == 'dev'
38
44
  Requires-Dist: httpx>=0.27; extra == 'dev'
39
45
  Requires-Dist: hypothesis>=6.100; extra == 'dev'
46
+ Requires-Dist: jsonschema>=4.20; extra == 'dev'
40
47
  Requires-Dist: langchain-core>=0.3; extra == 'dev'
41
48
  Requires-Dist: langgraph>=0.2; extra == 'dev'
42
49
  Requires-Dist: mcp<3,>=2.3; extra == 'dev'
43
50
  Requires-Dist: mypy>=1.10; extra == 'dev'
51
+ Requires-Dist: opentelemetry-api>=1.25; extra == 'dev'
52
+ Requires-Dist: opentelemetry-sdk>=1.25; extra == 'dev'
53
+ Requires-Dist: prometheus-client>=0.20; extra == 'dev'
44
54
  Requires-Dist: psycopg[binary]>=3.1; extra == 'dev'
45
55
  Requires-Dist: pymysql>=1.1; extra == 'dev'
46
56
  Requires-Dist: pytest-cov>=5.0; extra == 'dev'
47
57
  Requires-Dist: pytest>=8.0; extra == 'dev'
48
58
  Requires-Dist: ruff>=0.6; extra == 'dev'
49
59
  Requires-Dist: types-pyyaml; extra == 'dev'
60
+ Requires-Dist: uvicorn>=0.30; extra == 'dev'
50
61
  Provides-Extra: fastapi
51
62
  Requires-Dist: fastapi>=0.110; extra == 'fastapi'
52
63
  Provides-Extra: langchain
@@ -58,8 +69,12 @@ Provides-Extra: mcp
58
69
  Requires-Dist: mcp<3,>=2.3; extra == 'mcp'
59
70
  Provides-Extra: mysql
60
71
  Requires-Dist: pymysql>=1.1; extra == 'mysql'
72
+ Provides-Extra: otel
73
+ Requires-Dist: opentelemetry-api>=1.25; extra == 'otel'
61
74
  Provides-Extra: postgres
62
75
  Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
76
+ Provides-Extra: prometheus
77
+ Requires-Dist: prometheus-client>=0.20; extra == 'prometheus'
63
78
  Description-Content-Type: text/markdown
64
79
 
65
80
  # SQLGuard Ninja
@@ -290,6 +305,40 @@ The four tools are `validate_sql`, `explain_policy_decision`, `inspect_query_cos
290
305
  to a database, and it exposes no execution tool. Tested with the official MCP Python SDK
291
306
  client over stdio. See [MCP_INTEGRATION.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/MCP_INTEGRATION.md).
292
307
 
308
+ ## Interactive playground
309
+
310
+ A web app on a synthetic retail database. It has:
311
+
312
+ * three enforceable roles (Sales Analyst, Customer Support, Store Manager)
313
+ * example queries, with explanations and how-to-fix hints
314
+ * lineage, tenant-isolation demonstrations and role permissions
315
+ * a benchmark dashboard that separates internal from independent results
316
+
317
+ ```bash
318
+ python -m pip install "sqlguard-ninja[demo]"
319
+ sqlninja demo serve # http://127.0.0.1:8000
320
+ ```
321
+
322
+ Allowed queries run in a restricted in-memory sandbox. The database itself enforces this
323
+ with an authorizer allowlist, engine limits and a time budget. A static build
324
+ (`scripts/build_static_demo.py`) runs the whole thing in the browser with Pyodide, with no
325
+ server. See [DEPLOYMENT.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DEPLOYMENT.md)
326
+ for Docker, Compose and Kubernetes.
327
+
328
+ ## Observability and SARIF
329
+
330
+ ```bash
331
+ python -m pip install "sqlguard-ninja[otel,prometheus]"
332
+ sqlninja scan sql/ --policy policy.yaml --role reporting --tenant 1 --format sarif --output sqlninja.sarif
333
+ ```
334
+
335
+ `Telemetry(tracer=..., metrics=GuardMetrics())` adds OpenTelemetry spans and Prometheus metrics
336
+ to the checkpoint: decisions, findings by rule, latency, errors, resource-limit rejections and
337
+ checkpoint outcomes. By default no signal contains SQL text, literals, request-context values,
338
+ tenant ids or principals, and tests check this with canary values. A Grafana dashboard
339
+ (`deploy/grafana/`) and a synthetic-traffic Prometheus/Grafana stack are included. SARIF output
340
+ validates against the OASIS 2.1.0 schema. See [OBSERVABILITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md).
341
+
293
342
  ## Query cost analysis
294
343
 
295
344
  Every validation includes an explainable static score that uses the catalog's
@@ -371,9 +420,9 @@ the [benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/m
371
420
 
372
421
  ## Roadmap
373
422
 
374
- Next: restricted-function and system-catalog benchmark cases, an independent held-out
375
- corpus, lineage-aware aggregate checks, and tenant predicate push-down. Later: governed
376
- writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md).
423
+ Next: restricted-function and system-catalog benchmark cases (issue #5), an independent
424
+ held-out corpus (issue #6) and public hosting of the playground. Later:
425
+ governed writes, and database-verified SQL Server and Oracle support. Details in [ROADMAP.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md).
377
426
 
378
427
  ## Links
379
428
 
@@ -391,6 +440,9 @@ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://
391
440
  * [Agent integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md)
392
441
  * [Query cost](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md)
393
442
  * [CI integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/CI_INTEGRATION.md)
443
+ * [Playground and deployment](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DEPLOYMENT.md)
444
+ * [Observability and SARIF](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md)
445
+ * [Database compatibility](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DATABASE_COMPATIBILITY.md)
394
446
  * [Benchmark methodology](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md)
395
447
  * [Threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md)
396
448
  * [Limits of static SQL analysis and dialect support](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md)
@@ -226,6 +226,40 @@ The four tools are `validate_sql`, `explain_policy_decision`, `inspect_query_cos
226
226
  to a database, and it exposes no execution tool. Tested with the official MCP Python SDK
227
227
  client over stdio. See [MCP_INTEGRATION.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/MCP_INTEGRATION.md).
228
228
 
229
+ ## Interactive playground
230
+
231
+ A web app on a synthetic retail database. It has:
232
+
233
+ * three enforceable roles (Sales Analyst, Customer Support, Store Manager)
234
+ * example queries, with explanations and how-to-fix hints
235
+ * lineage, tenant-isolation demonstrations and role permissions
236
+ * a benchmark dashboard that separates internal from independent results
237
+
238
+ ```bash
239
+ python -m pip install "sqlguard-ninja[demo]"
240
+ sqlninja demo serve # http://127.0.0.1:8000
241
+ ```
242
+
243
+ Allowed queries run in a restricted in-memory sandbox. The database itself enforces this
244
+ with an authorizer allowlist, engine limits and a time budget. A static build
245
+ (`scripts/build_static_demo.py`) runs the whole thing in the browser with Pyodide, with no
246
+ server. See [DEPLOYMENT.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DEPLOYMENT.md)
247
+ for Docker, Compose and Kubernetes.
248
+
249
+ ## Observability and SARIF
250
+
251
+ ```bash
252
+ python -m pip install "sqlguard-ninja[otel,prometheus]"
253
+ sqlninja scan sql/ --policy policy.yaml --role reporting --tenant 1 --format sarif --output sqlninja.sarif
254
+ ```
255
+
256
+ `Telemetry(tracer=..., metrics=GuardMetrics())` adds OpenTelemetry spans and Prometheus metrics
257
+ to the checkpoint: decisions, findings by rule, latency, errors, resource-limit rejections and
258
+ checkpoint outcomes. By default no signal contains SQL text, literals, request-context values,
259
+ tenant ids or principals, and tests check this with canary values. A Grafana dashboard
260
+ (`deploy/grafana/`) and a synthetic-traffic Prometheus/Grafana stack are included. SARIF output
261
+ validates against the OASIS 2.1.0 schema. See [OBSERVABILITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md).
262
+
229
263
  ## Query cost analysis
230
264
 
231
265
  Every validation includes an explainable static score that uses the catalog's
@@ -307,9 +341,9 @@ the [benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/m
307
341
 
308
342
  ## Roadmap
309
343
 
310
- Next: restricted-function and system-catalog benchmark cases, an independent held-out
311
- corpus, lineage-aware aggregate checks, and tenant predicate push-down. Later: governed
312
- writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md).
344
+ Next: restricted-function and system-catalog benchmark cases (issue #5), an independent
345
+ held-out corpus (issue #6) and public hosting of the playground. Later:
346
+ governed writes, and database-verified SQL Server and Oracle support. Details in [ROADMAP.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md).
313
347
 
314
348
  ## Links
315
349
 
@@ -327,6 +361,9 @@ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://
327
361
  * [Agent integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md)
328
362
  * [Query cost](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md)
329
363
  * [CI integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/CI_INTEGRATION.md)
364
+ * [Playground and deployment](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DEPLOYMENT.md)
365
+ * [Observability and SARIF](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md)
366
+ * [Database compatibility](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DATABASE_COMPATIBILITY.md)
330
367
  * [Benchmark methodology](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md)
331
368
  * [Threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md)
332
369
  * [Limits of static SQL analysis and dialect support](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md)
@@ -1,7 +1,8 @@
1
1
  # Third-party notices
2
2
 
3
3
  SQLGuard Ninja is licensed under Apache-2.0. **It does not vendor or copy third-party source
4
- code.** The projects below are used as ordinary dependencies, installed by the package
4
+ code.** The only third-party file in the repository is a test fixture, the SARIF schema
5
+ (below). The projects below are used as ordinary dependencies, installed by the package
5
6
  manager under their own licences. Licences were read from installed package metadata on
6
7
  2026-10-24. Always check the version you install.
7
8
 
@@ -21,6 +22,11 @@ manager under their own licences. Licences were read from installed package meta
21
22
  | `langgraph` | langgraph | 1.2.17 | MIT |
22
23
  | `fastapi` | fastapi / starlette | 0.162.0 / 1.9.0 | MIT / BSD-3-Clause |
23
24
  | `postgres` | psycopg (+ psycopg-binary) | 3.3.6 | LGPL-3.0-only |
25
+ | `mcp` | mcp (official MCP Python SDK) | 2.3.0 | MIT |
26
+ | `mysql` | PyMySQL | 1.2.3 | MIT |
27
+ | `otel` | opentelemetry-api | 1.45.1 | Apache-2.0 |
28
+ | `prometheus` | prometheus-client | 0.26.0 | Apache-2.0 AND BSD-2-Clause |
29
+ | `demo` | fastapi / uvicorn | 0.162.0 / 0.54.0 | MIT / BSD-3-Clause |
24
30
 
25
31
  psycopg is LGPL-3.0. SQLGuard Ninja only imports it at runtime when the `postgres` extra is
26
32
  installed. It is not modified or bundled, which is compatible with distributing SQLGuard Ninja
@@ -33,6 +39,29 @@ mypy (MIT), build (MIT). `pgserver` is optionally used for local PostgreSQL test
33
39
  declared dependency, and its package metadata on PyPI does not declare a licence. It bundles
34
40
  PostgreSQL binaries (PostgreSQL License).
35
41
 
42
+ Tests also use jsonschema (MIT), opentelemetry-sdk (Apache-2.0), Playwright and
43
+ pytest-playwright (Apache-2.0), and axe-core 4.10.3 (MPL-2.0), which is downloaded at test time.
44
+
45
+ The **static playground build** (`scripts/build_static_demo.py`, not part of the package)
46
+ bundles Pyodide 0.29.5 (MPL-2.0, including CPython under the PSF License and SQLite, which is
47
+ public domain), sqlglot (MIT), and pydantic, PyYAML and their dependencies as packaged by
48
+ Pyodide. Their licences are listed in the generated `NOTICE.txt`.
49
+
50
+ ## Test fixture: SARIF 2.1.0 JSON schema
51
+
52
+ `tests/data/sarif-schema-2.1.0.json` is the unmodified OASIS SARIF 2.1.0 (Errata 01) schema,
53
+ downloaded from
54
+ https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json
55
+ (SHA-256 `c3b4bb2d6093897483348925aaa73af03b3e3f4bd4ca38cef26dcb4212a2682e`). It is used only to
56
+ validate SARIF output in tests and is not part of the wheel.
57
+
58
+ > Copyright © OASIS Open 2020. All Rights Reserved. This document and translations of it may be
59
+ > copied and furnished to others, and derivative works that comment on or otherwise explain it
60
+ > or assist in its implementation may be prepared, copied, published, and distributed, in whole
61
+ > or in part, without restriction of any kind, provided that the above copyright notice and this
62
+ > section are included on all such copies and derivative works. (OASIS IPR Policy notice; see
63
+ > https://www.oasis-open.org/policies-guidelines/ipr/)
64
+
36
65
  ## External validators used for benchmarking only
37
66
 
38
67
  Installed into isolated virtual environments by `scripts/setup_external_adapters.py`, never
@@ -166,3 +166,16 @@ MySQL with server defaults, so the executor refuses sessions whose `sql_mode` co
166
166
  **Consequences.** This refines ADR-005: the executed string differs from the validated one only
167
167
  in placeholder nodes, which carry server-side values, never user input. Literals and identifiers
168
168
  are not rewritten. MySQL support is limited to the scope the CI service-container tests cover.
169
+
170
+ ## ADR-014 — Playground: one service, static-first hosting, database-enforced sandbox
171
+
172
+ **Decision.** One Python `DemoService` backs both a FastAPI server and a static build in which
173
+ Pyodide runs the same code in the visitor's browser. Allowed queries run only in an in-memory
174
+ synthetic SQLite database. That database enforces read-only access itself: an authorizer
175
+ allowlist, `query_only`, engine limits and a step/time budget. The public deployment is the
176
+ static build on GitHub Pages: there is no server to abuse, no credentials, and no cost. The
177
+ static build self-hosts Pyodide and the wheels and verifies their checksums.
178
+
179
+ **Consequences.** The first static load downloads about 16 MB. The server mode stays available
180
+ for private use behind a proxy (Docker/Compose/Kubernetes references). In-process rate limits
181
+ are per replica.
@@ -10,4 +10,7 @@
10
10
  | 2c Opt-in AST tenant-filter rewrite + checkpoint | VERIFIED (local) | b0b5ade | test_tenant_rewrite (32, executes on SQLite, tenants 1-3) | |
11
11
  | 2d Docs, ADR-011/012, v0.3.0 bump | RELEASED v0.3.0 | 198a308 | full gate 1,804 passed, 94% | |
12
12
  | 3 MCP server (stdio, validation-only) | RELEASED v0.4.0 | ecec4da | 9 tests incl. real SDK client; published-package stdio smoke test | version targets adapted |
13
- | 4 MySQL tested dialect; SQL Server/Oracle experimental; compatibility matrix | VERIFIED (local, CI pending) | see PR | full gate 1,881 passed, 94%; 15 MySQL server tests run in CI | sql_mode parser differential found and closed (T18) |
13
+ | 4 MySQL tested dialect; SQL Server/Oracle experimental; compatibility matrix | RELEASED v0.5.0 | de4d03f | full gate 1,881 passed, 94%; CI MySQL 8.4: 15 passed, 0 skipped | sql_mode parser differential found and closed (T18) |
14
+ | 5 Observability: OTel spans, Prometheus metrics, audit schema, SARIF scan, Grafana + synthetic stack | RELEASED v0.6.0 | 3867989 | full gate 1,916 passed, 94%; CI stack: 224 decisions scraped, Grafana query success | redaction leak (hex/E''/$$/U&/N''/INTERVAL) found and fixed (T19) |
15
+ | 6 Playground: 3 roles, examples, explanations, lineage, tenant demo, permissions, benchmark dashboard; static Pyodide build | VERIFIED (local, CI pending) | see PR | 1,959 passed, 94%; 14 browser tests incl. axe + offline static | database-enforced sandbox |
16
+ | 7 Deployment: Dockerfile, Compose, Kubernetes, health, config validation, rate/body limits | VERIFIED (local tests; container in CI pending) | see PR | manifest and hardening tests | no Docker locally |
@@ -0,0 +1,5 @@
1
+ # Next actions (development program)
2
+
3
+ 1. SQL Server/Oracle real-database tests need vendor licence acceptance (maintainer decision); MySQL function/catalog cases stay under #5.
4
+ 2. Merge the v0.7.0 PR after CI (demo e2e + container jobs); add both jobs to required checks; release; verify PyPI + fresh `[demo]` install and `sqlninja demo serve`.
5
+ 3. Phase 8: public hosting needs maintainer authorization: enable GitHub Pages (Source: GitHub Actions), then run `pages.yml` and verify the real HTTPS URL in an unauthenticated browser.
@@ -16,3 +16,9 @@
16
16
  | 2026-10-24 | v0.4.0 release | PR #20 12/12; main CI on ecec4da success; release.yml run 40117676554 success | wheel and sdist on PyPI, both provenance endpoints 200; 0.1.0-0.3.0 unchanged |
17
17
  | 2026-10-24 | v0.4.0 fresh install + MCP stdio | `pip install --no-cache-dir "sqlguard-ninja[mcp]==0.4.0"` (Python 3.13, outside repo, 4th attempt; CDN lag), then SDK `stdio_client`/`ClientSession` against installed `sqlninja mcp` | 4 tools; allow/deny/cross-tenant deny; 100,001-char input `invalid_input`; forged `role` argument has no effect (deny); capabilities hide denied tables, `read_only: true`; no finding detail leaked |
18
18
  | 2026-10-24 | v0.5.0 candidate (MySQL) | full gate (`ruff`, `mypy`, corpus `--check`, `pytest --cov -m "not mysql"` incl. embedded PostgreSQL, build, `twine check --strict`) | 1,881 passed, 94%; twine PASSED; corpus 1.2.0: 1,070 evaluations, 4 adapters regenerated |
19
+ | 2026-10-24 | v0.5.0 release | PR #22 13/13 (MySQL job: 15 passed, 0 skipped); MySQL job added to required checks; main CI 40119298660 on de4d03f success; release.yml 40119480081 success | wheel and sdist on PyPI, both provenance endpoints 200; 0.1.0-0.4.0 unchanged |
20
+ | 2026-10-24 | v0.5.0 fresh install | `pip install --no-cache-dir "sqlguard-ninja[mysql]==0.5.0"` (Python 3.13, outside repo, `-I`; 2nd attempt) | PyMySQL 1.2.3; dialects include mysql; bundled corpus 1.2.0, 26 MySQL cases 0 mismatches; binding and sql_mode refusals; `o.ID` and card column denied. No MySQL server locally, so execution is evidenced by CI only |
21
+ | 2026-10-24 | v0.6.0 candidate (observability) | full gate (`ruff`, `mypy`, corpus `--check`, `pytest --cov -m "not mysql"`, build, `twine check --strict`) | 1,916 passed, 94%; twine PASSED; SARIF schema-valid; canary privacy tests over 13 literal forms |
22
+ | 2026-10-24 | v0.6.0 release | PR #24 14/14 (observability job: SARIF valid, decisions=224, 9 panels, grafana_query=success; 2nd run after tmpfs noexec fix); job added to required checks; main CI 40127541718 success; release.yml 40127737479 success | wheel + sdist on PyPI, both provenance 200; 0.1.0-0.5.0 unchanged |
23
+ | 2026-10-24 | v0.6.0 fresh install | `pip install --no-cache-dir "sqlguard-ninja[otel,prometheus]==0.6.0"` + opentelemetry-sdk, jsonschema (Python 3.13, outside repo, `-I`) | spans checkpoint+validate; decisions allow 2 / deny 1; canary in literal, hex, comment and context not leaked; redacted SQL correct; `scan` SARIF valid against OASIS schema fetched separately |
24
+ | 2026-10-24 | v0.7.0 candidate (playground + deployment) | full gate + `pytest -m e2e` (Playwright Chromium; static site built with verified downloads; external requests blocked) | 1,959 passed, 94%; 14 e2e passed incl. axe serious/critical = 0, 360/768 px no overflow; build + twine PASSED |
@@ -62,54 +62,64 @@
62
62
  {
63
63
  "id": 5,
64
64
  "name": "Database dialects",
65
- "status": "VERIFIED",
65
+ "status": "RELEASED",
66
66
  "release": "v0.5.0",
67
67
  "issues": [
68
68
  11
69
69
  ],
70
70
  "evidence": [
71
- "MySQL executor/binding/sql_mode guard, 26 corpus cases; 1,881 local tests; CI MySQL job pending"
71
+ "MySQL executor/binding/sql_mode guard, 26 corpus cases; 1,881 local tests; CI MySQL job pending",
72
+ "v0.5.0 on PyPI with provenance; CI MySQL 8.4 15/15; fresh [mysql] install verified"
72
73
  ],
73
74
  "blockers": []
74
75
  },
75
76
  {
76
77
  "id": 6,
77
78
  "name": "Observability and SARIF",
78
- "status": "NOT_STARTED",
79
- "release": "v0.4.0",
79
+ "status": "RELEASED",
80
+ "release": "v0.6.0",
80
81
  "issues": [
81
82
  14
82
83
  ],
83
- "evidence": [],
84
+ "evidence": [
85
+ "OTel/Prometheus/SARIF/Grafana + canary privacy tests; 1,916 local tests; CI pending",
86
+ "v0.6.0 on PyPI with provenance; fresh [otel,prometheus] install verified"
87
+ ],
84
88
  "blockers": []
85
89
  },
86
90
  {
87
91
  "id": 7,
88
92
  "name": "Web dashboard and playground",
89
- "status": "NOT_STARTED",
90
- "release": "v0.5.0",
93
+ "status": "VERIFIED",
94
+ "release": "v0.7.0",
91
95
  "issues": [],
92
- "evidence": [],
96
+ "evidence": [
97
+ "FastAPI + static Pyodide playground; 14 Playwright tests incl. axe; CI pending"
98
+ ],
93
99
  "blockers": []
94
100
  },
95
101
  {
96
102
  "id": 8,
97
103
  "name": "Production deployment",
98
- "status": "NOT_STARTED",
99
- "release": "v0.5.0",
104
+ "status": "VERIFIED",
105
+ "release": "v0.7.0",
100
106
  "issues": [],
101
- "evidence": [],
107
+ "evidence": [
108
+ "Dockerfile/Compose/Kubernetes with hardening tests; container CI pending"
109
+ ],
102
110
  "blockers": []
103
111
  },
104
112
  {
105
113
  "id": 9,
106
114
  "name": "Public zero-API-key demo",
107
- "status": "NOT_STARTED",
115
+ "status": "BLOCKED",
108
116
  "release": "v0.5.0",
109
117
  "issues": [],
110
- "evidence": [],
118
+ "evidence": [
119
+ "static build ready and tested offline; pages.yml manual workflow prepared"
120
+ ],
111
121
  "blockers": [
112
- "public hosting needs maintainer account authorization"
122
+ "needs maintainer authorization to enable GitHub Pages"
113
123
  ]
114
124
  }
115
125
  ]
@@ -11,6 +11,28 @@ git add benchmarks/baseline.json # baselines change only through reviewed commi
11
11
 
12
12
  `sqlninja benchmark baseline` refuses to run when `CI` is set. CI never updates baselines.
13
13
 
14
+ ## SARIF and GitHub code scanning
15
+
16
+ `sqlninja scan` writes SARIF 2.1.0. Upload it with GitHub's own action, pinned to a commit SHA
17
+ you have reviewed. Uploading needs `security-events: write`, and code scanning must be available
18
+ for the repository.
19
+
20
+ ```yaml
21
+ permissions:
22
+ contents: read
23
+ security-events: write
24
+ steps:
25
+ - uses: actions/checkout@<sha>
26
+ - run: pip install sqlguard-ninja
27
+ - run: sqlninja scan sql/ --policy policy.yaml --role reporting --tenant 1 --format sarif --output sqlninja.sarif --fail-on never
28
+ - uses: github/codeql-action/upload-sarif@<sha>
29
+ with:
30
+ sarif_file: sqlninja.sarif
31
+ ```
32
+
33
+ Results are fingerprinted per file, rule and literal-insensitive query, so changing only a
34
+ literal does not open a new alert. The log contains rule messages but no SQL snippet.
35
+
14
36
  ## GitHub Action
15
37
 
16
38
  See [`examples/github-actions/sql-security.yml`](../../examples/github-actions/sql-security.yml).
@@ -0,0 +1,122 @@
1
+ # Playground and deployment
2
+
3
+ The playground is a web app on a synthetic retail database. It has:
4
+
5
+ * three roles: Sales Analyst, Customer Support and Store Manager
6
+ * example queries and plain-language explanations
7
+ * lineage, tenant isolation, role permissions and a benchmark dashboard
8
+
9
+ It needs no login, API key or credentials.
10
+
11
+ It runs in two modes from the same frontend and the same Python `DemoService`:
12
+
13
+ | Mode | How | Where SQL is validated and run |
14
+ |---|---|---|
15
+ | **Static** (recommended for public hosting) | `python scripts/build_static_demo.py site/` | in the visitor's browser, through Pyodide in a Web Worker; no server |
16
+ | **Server** | `pip install "sqlguard-ninja[demo]"` then `sqlninja demo serve` | FastAPI server, restricted in-memory sandbox |
17
+
18
+ ## Execution safety
19
+
20
+ The demo does not rely on the parser alone, or on rolling back a transaction. Only queries
21
+ the policy **allows** run, and they run against an in-memory SQLite database of synthetic
22
+ data. That database enforces its own restrictions (`sqlguard_ninja/demo/sandbox.py`):
23
+
24
+ * `PRAGMA query_only`.
25
+ * An **authorizer** that allows only `SELECT`, reads of the catalog's tables and an allowlist
26
+ of side-effect-free functions. ATTACH, PRAGMA, writes, schema changes, `sqlite_master` and
27
+ `load_extension` are refused by the engine.
28
+ * **Engine limits**: string length, SQL length, expression depth, compound selects, zero
29
+ attached databases, and few variables.
30
+ * A **step and time budget** through the progress handler. Recursive CTEs and cartesian
31
+ products are interrupted.
32
+ * A row cap of 100, plus value truncation.
33
+
34
+ The database holds no real data. In the static build it exists only in the visitor's browser.
35
+ Set `SQLNINJA_DEMO_EXECUTE=false` for a validation-only server.
36
+
37
+ `tests/unit/test_demo.py` checks every one of these refusals.
38
+
39
+ ## Server settings
40
+
41
+ Settings are read from the environment and validated at start-up. Unknown or invalid values
42
+ stop the server.
43
+
44
+ | Variable | Default | Meaning |
45
+ |---|---|---|
46
+ | `SQLNINJA_DEMO_EXECUTE` | `true` | run allowed queries in the sandbox |
47
+ | `SQLNINJA_DEMO_RATE_LIMIT_PER_MINUTE` | `60` | per-client API requests per minute (per process) |
48
+ | `SQLNINJA_DEMO_MAX_BODY_BYTES` | `16384` | request body cap; bodies without `Content-Length` are refused |
49
+ | `SQLNINJA_DEMO_TRUST_PROXY_HEADERS` | `false` | take the client from `X-Forwarded-For`; enable only behind a proxy that overwrites it |
50
+ | `SQLNINJA_DEMO_MAX_TRACKED_CLIENTS` | `10000` | memory bound for the rate limiter |
51
+
52
+ The server also:
53
+
54
+ * sends a strict CSP with no inline script, `frame-ancestors 'none'`, `nosniff`, and
55
+ `no-referrer`
56
+ * disables the OpenAPI and docs pages
57
+ * exposes `/healthz` (liveness) and `/readyz` (the sandbox answers)
58
+ * rejects identity forgery: role and tenant come from fixed enums, and extra fields are
59
+ rejected with 422
60
+
61
+ ## Docker and Compose
62
+
63
+ ```bash
64
+ docker build -t sqlguard-ninja-demo .
65
+ docker compose -f deploy/compose/docker-compose.yml up -d --build --wait
66
+ ```
67
+
68
+ The image is multi-stage, with the base pinned by digest. It runs as non-root (UID 10001) and
69
+ has a `HEALTHCHECK`. The Compose file adds:
70
+
71
+ * a read-only root filesystem with a small tmpfs
72
+ * `cap_drop: ALL` and `no-new-privileges`
73
+ * memory, CPU and PID limits
74
+ * port binding to localhost only
75
+
76
+ CI builds the image and runs it under those restrictions. It then checks that the user is
77
+ UID 10001, that the probes pass, that the CSP header is present, and that one allowed and one
78
+ denied API call behave correctly.
79
+
80
+ ## Kubernetes (example)
81
+
82
+ `deploy/kubernetes/` (Kustomize) contains:
83
+
84
+ * a Deployment:
85
+ * non-root with a RuntimeDefault seccomp profile
86
+ * read-only root filesystem with all capabilities dropped
87
+ * no service-account token
88
+ * resource limits
89
+ * startup, liveness and readiness probes
90
+ * a Service and a PodDisruptionBudget
91
+ * a NetworkPolicy that allows ingress only from the ingress controller and **no egress**
92
+ * an example Ingress with edge rate limits
93
+
94
+ CI validates these manifests against the Kubernetes schemas with `kubernetes-validate --strict`.
95
+ Replace the image reference with the digest you built and scanned. The in-process rate
96
+ limiter is per replica, so keep the edge limit.
97
+
98
+ ## Public hosting (decision)
99
+
100
+ Hosting is recorded as ADR-014. Several free options were compared.
101
+
102
+ **Static options:**
103
+
104
+ * **GitHub Pages** is free for public repositories and serves over HTTPS. It is already
105
+ part of the project's GitHub account, with no new vendor, and the build is a single
106
+ workflow.
107
+ * **Cloudflare Pages** and **Netlify** have free static tiers. Each would need a new
108
+ account and adds a vendor.
109
+
110
+ **Server options:** Render, Fly.io and Koyeb have free or trial container tiers. They add an
111
+ always-on attack surface, can sleep on idle, may change their terms, and some require a
112
+ payment card.
113
+
114
+ **Chosen: the static build on GitHub Pages.** Validation and execution happen in each
115
+ visitor's browser, so there is no shared server to abuse and no database to protect. It runs
116
+ at zero cost. The `_headers` file sets the CSP and related headers on hosts that support it.
117
+ On GitHub Pages, a `<meta>` CSP applies instead.
118
+
119
+ `.github/workflows/pages.yml` builds the site, tests that exact build in Chromium with every
120
+ external request blocked, and deploys it. It runs **only when started manually**, after the
121
+ maintainer enables Pages (Settings → Pages → Source: GitHub Actions). Do not describe the demo
122
+ as live until an unauthenticated browser has loaded the real HTTPS URL and run a query.