sqlguard-ninja 0.5.0__tar.gz → 0.6.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.
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/CHANGELOG.md +23 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/PKG-INFO +30 -4
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/README.md +19 -3
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/THIRD_PARTY_NOTICES.md +23 -1
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/development/IMPLEMENTATION_PROGRESS.md +2 -1
- sqlguard_ninja-0.6.0/docs/development/NEXT_ACTIONS.md +6 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/development/TEST_EVIDENCE.md +3 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/development/progress.json +8 -5
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/CI_INTEGRATION.md +22 -0
- sqlguard_ninja-0.6.0/docs/guides/OBSERVABILITY.md +133 -0
- sqlguard_ninja-0.6.0/docs/releases/v0.6.0.md +24 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/security/THREAT_MODEL.md +1 -0
- sqlguard_ninja-0.6.0/examples/observability/docker-compose.yml +47 -0
- sqlguard_ninja-0.6.0/examples/observability/grafana/provisioning/dashboards/sqlguard.yml +9 -0
- sqlguard_ninja-0.6.0/examples/observability/grafana/provisioning/datasources/prometheus.yml +9 -0
- sqlguard_ninja-0.6.0/examples/observability/prometheus.yml +6 -0
- sqlguard_ninja-0.6.0/examples/observability/smoke_check.py +42 -0
- sqlguard_ninja-0.6.0/examples/observability/synthetic_traffic.py +80 -0
- sqlguard_ninja-0.6.0/examples/sql/support_queries.sql +22 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/pyproject.toml +7 -2
- sqlguard_ninja-0.6.0/src/sqlguard_ninja/_version.py +1 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/cli/main.py +93 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/audit.py +42 -5
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/checkpoint.py +30 -2
- sqlguard_ninja-0.6.0/src/sqlguard_ninja/observability/__init__.py +17 -0
- sqlguard_ninja-0.6.0/src/sqlguard_ninja/observability/metrics.py +121 -0
- sqlguard_ninja-0.6.0/src/sqlguard_ninja/observability/telemetry.py +148 -0
- sqlguard_ninja-0.6.0/src/sqlguard_ninja/reporting/sarif.py +195 -0
- sqlguard_ninja-0.6.0/tests/data/sarif-schema-2.1.0.json +3389 -0
- sqlguard_ninja-0.6.0/tests/unit/test_grafana_dashboard.py +84 -0
- sqlguard_ninja-0.6.0/tests/unit/test_observability.py +381 -0
- sqlguard_ninja-0.5.0/docs/development/NEXT_ACTIONS.md +0 -6
- sqlguard_ninja-0.5.0/src/sqlguard_ninja/_version.py +0 -1
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/.gitignore +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/LICENSE +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/baselines/native.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/external/README.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/LABEL_CHANGES.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/01_destructive.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/02_multi_statement_and_formatting.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/03_table_and_column_access.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/04_privacy.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/05_tenant_isolation.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/06_cte_nested_unicode.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/07_legitimate_lookalike_cost_dialect.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/08_lineage.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/cases/09_mysql.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/generated/grant_matrix.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/generated/label_matrix.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/generated/tenant_matrix.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/generated/transforms.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/datasets/v1/manifest.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/policies/retail_saas.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/agent-sql-guard.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/agent-sql-guard.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/krish2105-sqlguard.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/krish2105-sqlguard.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/native.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/native.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/sql-guardrail.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.0.0/sql-guardrail.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/agent-sql-guard.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/agent-sql-guard.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/agent-sql-guard.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/krish2105-sqlguard.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/krish2105-sqlguard.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/krish2105-sqlguard.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/native.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/native.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/native.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/sql-guardrail.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/sql-guardrail.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.1.0/sql-guardrail.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/agent-sql-guard.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/agent-sql-guard.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/agent-sql-guard.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/krish2105-sqlguard.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/krish2105-sqlguard.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/krish2105-sqlguard.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/native.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/native.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/native.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/sql-guardrail.csv +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/sql-guardrail.json +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/benchmarks/results/v1.2.0/sql-guardrail.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/architecture/ARCHITECTURE_DECISIONS.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/benchmarks/RESULTS_v1.1.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/benchmarks/RESULTS_v1.2.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/benchmarks/RESULTS_v1.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/development/ARCHITECTURE_MAP.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/development/DECISION_LOG.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/development/UPGRADE_MASTER_PLAN.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/AGENT_INTEGRATION.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/BENCHMARK_BIAS_REVIEW.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/BENCHMARK_METHODOLOGY.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/DATABASE_COMPATIBILITY.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/MCP_INTEGRATION.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/POLICY_AS_CODE.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/POLICY_REFERENCE.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/guides/QUERY_COST.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/release/FINAL_TECHNICAL_AUDIT.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/releases/v0.1.0.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/releases/v0.1.1.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/releases/v0.2.0.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/releases/v0.3.0.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/releases/v0.4.0.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/releases/v0.5.0.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/research/COMPETITIVE_ANALYSIS.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/docs/security/STATIC_ANALYSIS_LIMITS.md +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/_shared.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/fastapi/app.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/github-actions/sql-security.yml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/langchain/guarded_tool_demo.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/langgraph/real_llm_agent.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/langgraph/sql_agent_demo.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/postgresql/plan_inspection_demo.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/sdk/quickstart.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/examples/sqlite/text_to_sql_demo.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/adapters.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/builtin.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/compare.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/dataset.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/fixtures.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/metrics.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/runner.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/translate.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/workers/_protocol.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/workers/agent_sql_guard_worker.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/workers/krish_sqlguard_worker.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/benchmarking/workers/sql_guardrail_worker.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/cli/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/cli/config.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/cli/templates.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/analysis.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/findings.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/guard.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/lineage.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/access.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/base.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/catalog.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/functions.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/statements.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/structure.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/core/rules/tenant.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/executors.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/fastapi.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/langchain.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/langgraph.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/integrations/mysql.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/mcp_server.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/inheritance.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/model.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/templates/analytics_readonly.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/templates/minimal.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/templates/multi_tenant_saas.yaml +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/policy/tools.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/privacy/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/privacy/rules.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/privacy/suggest.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/py.typed +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/query_cost/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/query_cost/postgres.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/query_cost/static.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/reporting/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/reporting/markdown.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/src/sqlguard_ninja/tenancy.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/conftest.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/test_corpus_sqlite_validation.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/test_external_adapters.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/test_frameworks.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/test_mcp_server.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/test_mysql.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/integration/test_postgres.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/pg_support.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/property/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/property/test_guard_properties.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/security/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/security/test_agent_boundaries.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/security/test_corpus_native.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/security/test_tenant_rewrite.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/__init__.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/helpers.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_benchmark_engine.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_cli.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_dataset.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_docs.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_experimental_dialects.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_function_policy_mechanics.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_lineage.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_mysql_binding.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_policy.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_policy_inheritance.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_policy_tools.py +0 -0
- {sqlguard_ninja-0.5.0 → sqlguard_ninja-0.6.0}/tests/unit/test_rules.py +0 -0
|
@@ -3,6 +3,29 @@
|
|
|
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.6.0] — 2026-10-24
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
* **Observability** (`sqlguard_ninja.observability`, extras `[otel]` and `[prometheus]`):
|
|
10
|
+
* `Telemetry` and `InstrumentedGuard` emit OpenTelemetry spans `sqlguard.checkpoint` and
|
|
11
|
+
`sqlguard.validate`.
|
|
12
|
+
* `GuardMetrics` exports Prometheus counters and histograms for decisions, findings,
|
|
13
|
+
latency, errors, resource-limit rejections, checkpoint outcomes and policy identity.
|
|
14
|
+
* `SQLCheckpoint(telemetry=...)` instruments its guard automatically.
|
|
15
|
+
* **SARIF 2.1.0**: `sqlninja scan PATH... --format sarif|json|text --fail-on deny|review|never`.
|
|
16
|
+
It validates every statement in SQL files, with stable fingerprints. Tests and CI validate the
|
|
17
|
+
output against the OASIS schema.
|
|
18
|
+
* **Grafana dashboard** `deploy/grafana/sqlguard-ninja.json`, and a Prometheus + Grafana +
|
|
19
|
+
synthetic-traffic stack (`examples/observability/`). A new CI job runs that stack.
|
|
20
|
+
* Audit events gain `schema_version` and `rate_limited`. New
|
|
21
|
+
`SQLCheckpoint(principal_hmac_key=...)` option to pseudonymise principals.
|
|
22
|
+
|
|
23
|
+
### Security
|
|
24
|
+
* `redact_sql` now redacts hex, bit, byte (`E''`), dollar-quoted, Unicode (`U&''`) and
|
|
25
|
+
national (`N''`) strings and whole `INTERVAL` expressions. Previously these could reach
|
|
26
|
+
`sql_redacted` in audit logs. Comments are dropped, and if any literal token survives,
|
|
27
|
+
redacted SQL is withheld. See threat model T19.
|
|
28
|
+
|
|
6
29
|
## [0.5.0] — 2026-10-24
|
|
7
30
|
|
|
8
31
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sqlguard-ninja
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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,6 +30,8 @@ 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'
|
|
35
37
|
Provides-Extra: dev
|
|
@@ -37,10 +39,14 @@ Requires-Dist: build>=1.2; extra == 'dev'
|
|
|
37
39
|
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
38
40
|
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
39
41
|
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
42
|
+
Requires-Dist: jsonschema>=4.20; extra == 'dev'
|
|
40
43
|
Requires-Dist: langchain-core>=0.3; extra == 'dev'
|
|
41
44
|
Requires-Dist: langgraph>=0.2; extra == 'dev'
|
|
42
45
|
Requires-Dist: mcp<3,>=2.3; extra == 'dev'
|
|
43
46
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
47
|
+
Requires-Dist: opentelemetry-api>=1.25; extra == 'dev'
|
|
48
|
+
Requires-Dist: opentelemetry-sdk>=1.25; extra == 'dev'
|
|
49
|
+
Requires-Dist: prometheus-client>=0.20; extra == 'dev'
|
|
44
50
|
Requires-Dist: psycopg[binary]>=3.1; extra == 'dev'
|
|
45
51
|
Requires-Dist: pymysql>=1.1; extra == 'dev'
|
|
46
52
|
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
@@ -58,8 +64,12 @@ Provides-Extra: mcp
|
|
|
58
64
|
Requires-Dist: mcp<3,>=2.3; extra == 'mcp'
|
|
59
65
|
Provides-Extra: mysql
|
|
60
66
|
Requires-Dist: pymysql>=1.1; extra == 'mysql'
|
|
67
|
+
Provides-Extra: otel
|
|
68
|
+
Requires-Dist: opentelemetry-api>=1.25; extra == 'otel'
|
|
61
69
|
Provides-Extra: postgres
|
|
62
70
|
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
|
|
71
|
+
Provides-Extra: prometheus
|
|
72
|
+
Requires-Dist: prometheus-client>=0.20; extra == 'prometheus'
|
|
63
73
|
Description-Content-Type: text/markdown
|
|
64
74
|
|
|
65
75
|
# SQLGuard Ninja
|
|
@@ -290,6 +300,20 @@ The four tools are `validate_sql`, `explain_policy_decision`, `inspect_query_cos
|
|
|
290
300
|
to a database, and it exposes no execution tool. Tested with the official MCP Python SDK
|
|
291
301
|
client over stdio. See [MCP_INTEGRATION.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/MCP_INTEGRATION.md).
|
|
292
302
|
|
|
303
|
+
## Observability and SARIF
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
python -m pip install "sqlguard-ninja[otel,prometheus]"
|
|
307
|
+
sqlninja scan sql/ --policy policy.yaml --role reporting --tenant 1 --format sarif --output sqlninja.sarif
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`Telemetry(tracer=..., metrics=GuardMetrics())` adds OpenTelemetry spans and Prometheus metrics
|
|
311
|
+
to the checkpoint: decisions, findings by rule, latency, errors, resource-limit rejections and
|
|
312
|
+
checkpoint outcomes. By default no signal contains SQL text, literals, request-context values,
|
|
313
|
+
tenant ids or principals, and tests check this with canary values. A Grafana dashboard
|
|
314
|
+
(`deploy/grafana/`) and a synthetic-traffic Prometheus/Grafana stack are included. SARIF output
|
|
315
|
+
validates against the OASIS 2.1.0 schema. See [OBSERVABILITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md).
|
|
316
|
+
|
|
293
317
|
## Query cost analysis
|
|
294
318
|
|
|
295
319
|
Every validation includes an explainable static score that uses the catalog's
|
|
@@ -371,9 +395,9 @@ the [benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/m
|
|
|
371
395
|
|
|
372
396
|
## Roadmap
|
|
373
397
|
|
|
374
|
-
Next: restricted-function and system-catalog benchmark cases, an independent
|
|
375
|
-
corpus,
|
|
376
|
-
writes,
|
|
398
|
+
Next: restricted-function and system-catalog benchmark cases (issue #5), an independent
|
|
399
|
+
held-out corpus (issue #6), an interactive demo and a hardened deployment reference. Later:
|
|
400
|
+
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
401
|
|
|
378
402
|
## Links
|
|
379
403
|
|
|
@@ -391,6 +415,8 @@ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://
|
|
|
391
415
|
* [Agent integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md)
|
|
392
416
|
* [Query cost](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md)
|
|
393
417
|
* [CI integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/CI_INTEGRATION.md)
|
|
418
|
+
* [Observability and SARIF](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md)
|
|
419
|
+
* [Database compatibility](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DATABASE_COMPATIBILITY.md)
|
|
394
420
|
* [Benchmark methodology](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md)
|
|
395
421
|
* [Threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md)
|
|
396
422
|
* [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,20 @@ 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
|
+
## Observability and SARIF
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
python -m pip install "sqlguard-ninja[otel,prometheus]"
|
|
233
|
+
sqlninja scan sql/ --policy policy.yaml --role reporting --tenant 1 --format sarif --output sqlninja.sarif
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`Telemetry(tracer=..., metrics=GuardMetrics())` adds OpenTelemetry spans and Prometheus metrics
|
|
237
|
+
to the checkpoint: decisions, findings by rule, latency, errors, resource-limit rejections and
|
|
238
|
+
checkpoint outcomes. By default no signal contains SQL text, literals, request-context values,
|
|
239
|
+
tenant ids or principals, and tests check this with canary values. A Grafana dashboard
|
|
240
|
+
(`deploy/grafana/`) and a synthetic-traffic Prometheus/Grafana stack are included. SARIF output
|
|
241
|
+
validates against the OASIS 2.1.0 schema. See [OBSERVABILITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md).
|
|
242
|
+
|
|
229
243
|
## Query cost analysis
|
|
230
244
|
|
|
231
245
|
Every validation includes an explainable static score that uses the catalog's
|
|
@@ -307,9 +321,9 @@ the [benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/m
|
|
|
307
321
|
|
|
308
322
|
## Roadmap
|
|
309
323
|
|
|
310
|
-
Next: restricted-function and system-catalog benchmark cases, an independent
|
|
311
|
-
corpus,
|
|
312
|
-
writes,
|
|
324
|
+
Next: restricted-function and system-catalog benchmark cases (issue #5), an independent
|
|
325
|
+
held-out corpus (issue #6), an interactive demo and a hardened deployment reference. Later:
|
|
326
|
+
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
327
|
|
|
314
328
|
## Links
|
|
315
329
|
|
|
@@ -327,6 +341,8 @@ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://
|
|
|
327
341
|
* [Agent integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md)
|
|
328
342
|
* [Query cost](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md)
|
|
329
343
|
* [CI integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/CI_INTEGRATION.md)
|
|
344
|
+
* [Observability and SARIF](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/OBSERVABILITY.md)
|
|
345
|
+
* [Database compatibility](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/DATABASE_COMPATIBILITY.md)
|
|
330
346
|
* [Benchmark methodology](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md)
|
|
331
347
|
* [Threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md)
|
|
332
348
|
* [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
|
|
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,10 @@ 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 |
|
|
24
29
|
|
|
25
30
|
psycopg is LGPL-3.0. SQLGuard Ninja only imports it at runtime when the `postgres` extra is
|
|
26
31
|
installed. It is not modified or bundled, which is compatible with distributing SQLGuard Ninja
|
|
@@ -33,6 +38,23 @@ mypy (MIT), build (MIT). `pgserver` is optionally used for local PostgreSQL test
|
|
|
33
38
|
declared dependency, and its package metadata on PyPI does not declare a licence. It bundles
|
|
34
39
|
PostgreSQL binaries (PostgreSQL License).
|
|
35
40
|
|
|
41
|
+
Tests also use jsonschema (MIT) and opentelemetry-sdk (Apache-2.0).
|
|
42
|
+
|
|
43
|
+
## Test fixture: SARIF 2.1.0 JSON schema
|
|
44
|
+
|
|
45
|
+
`tests/data/sarif-schema-2.1.0.json` is the unmodified OASIS SARIF 2.1.0 (Errata 01) schema,
|
|
46
|
+
downloaded from
|
|
47
|
+
https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json
|
|
48
|
+
(SHA-256 `c3b4bb2d6093897483348925aaa73af03b3e3f4bd4ca38cef26dcb4212a2682e`). It is used only to
|
|
49
|
+
validate SARIF output in tests and is not part of the wheel.
|
|
50
|
+
|
|
51
|
+
> Copyright © OASIS Open 2020. All Rights Reserved. This document and translations of it may be
|
|
52
|
+
> copied and furnished to others, and derivative works that comment on or otherwise explain it
|
|
53
|
+
> or assist in its implementation may be prepared, copied, published, and distributed, in whole
|
|
54
|
+
> or in part, without restriction of any kind, provided that the above copyright notice and this
|
|
55
|
+
> section are included on all such copies and derivative works. (OASIS IPR Policy notice; see
|
|
56
|
+
> https://www.oasis-open.org/policies-guidelines/ipr/)
|
|
57
|
+
|
|
36
58
|
## External validators used for benchmarking only
|
|
37
59
|
|
|
38
60
|
Installed into isolated virtual environments by `scripts/setup_external_adapters.py`, never
|
|
@@ -10,4 +10,5 @@
|
|
|
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 |
|
|
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 | VERIFIED (local, CI pending) | see PR | full gate 1,916 passed, 94% | redaction leak (hex/E''/$$/U&/N''/INTERVAL) found and fixed (T19) |
|
|
@@ -0,0 +1,6 @@
|
|
|
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.6.0 (observability) PR after CI incl. the new observability job; add that job to required checks; release; verify PyPI + fresh `[otel,prometheus]` install.
|
|
5
|
+
3. Phase 6: interactive demo on the FastAPI backend (three roles, editor, explanations, lineage, benchmark dashboard, browser e2e tests).
|
|
6
|
+
4. Phases 7-8: Docker/Kubernetes reference, public demo (hosting needs maintainer authorization).
|
|
@@ -16,3 +16,6 @@
|
|
|
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 |
|
|
@@ -62,25 +62,28 @@
|
|
|
62
62
|
{
|
|
63
63
|
"id": 5,
|
|
64
64
|
"name": "Database dialects",
|
|
65
|
-
"status": "
|
|
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": "
|
|
79
|
-
"release": "v0.
|
|
79
|
+
"status": "VERIFIED",
|
|
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
|
+
],
|
|
84
87
|
"blockers": []
|
|
85
88
|
},
|
|
86
89
|
{
|
|
@@ -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,133 @@
|
|
|
1
|
+
# Observability
|
|
2
|
+
|
|
3
|
+
SQLGuard Ninja can emit OpenTelemetry traces, Prometheus metrics, structured JSON audit
|
|
4
|
+
events and SARIF reports. All of these are optional, and none are imported by the core.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
python -m pip install "sqlguard-ninja[otel,prometheus]"
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Privacy contract
|
|
11
|
+
|
|
12
|
+
By default, **no signal contains**:
|
|
13
|
+
|
|
14
|
+
* SQL text, literal values or comments
|
|
15
|
+
* request context values (for example API keys or other attributes)
|
|
16
|
+
* the tenant id (in traces and metrics)
|
|
17
|
+
* the principal (in traces and metrics)
|
|
18
|
+
* internal finding `detail`
|
|
19
|
+
|
|
20
|
+
Signals carry the decision, rule ids, bounded labels and correlation identifiers. The
|
|
21
|
+
correlation identifiers are the decision id, the policy fingerprint and revision, the
|
|
22
|
+
literal-insensitive query fingerprint and the correlation (trace) id. Exception messages are
|
|
23
|
+
never recorded, because they can quote SQL; spans record only the exception type.
|
|
24
|
+
|
|
25
|
+
`tests/unit/test_observability.py` checks this. It sends queries containing a canary value
|
|
26
|
+
through the checkpoint in many literal forms and checks the canary appears in none of the
|
|
27
|
+
signals. The forms include:
|
|
28
|
+
|
|
29
|
+
* strings, comments, dollar-quoted, `E''`, `U&''`, `N''` and hex strings
|
|
30
|
+
* `INTERVAL` text and MySQL double-quoted strings
|
|
31
|
+
* unterminated and multi-statement input
|
|
32
|
+
|
|
33
|
+
The signals checked are audit JSON, logging output, exported spans and the Prometheus
|
|
34
|
+
exposition. The test also puts the canary in request-context secrets.
|
|
35
|
+
|
|
36
|
+
## Tracing (OpenTelemetry)
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from opentelemetry import trace
|
|
40
|
+
from sqlguard_ninja import Guard, Policy
|
|
41
|
+
from sqlguard_ninja.integrations import SQLCheckpoint
|
|
42
|
+
from sqlguard_ninja.observability import GuardMetrics, Telemetry
|
|
43
|
+
|
|
44
|
+
telemetry = Telemetry(tracer=trace.get_tracer("my-app"), metrics=GuardMetrics())
|
|
45
|
+
checkpoint = SQLCheckpoint(Guard(Policy.from_file("policy.yaml")), telemetry=telemetry)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The checkpoint instruments its guard automatically. To instrument a guard used on its own,
|
|
49
|
+
call `telemetry.instrument(guard)`. Configure exporters and sampling with the OpenTelemetry
|
|
50
|
+
SDK as usual.
|
|
51
|
+
|
|
52
|
+
| Span | Attributes |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `sqlguard.checkpoint` | `sqlguard.mode`, `.decision`, `.decision_id`, `.authorized`, `.enforced`, `.rate_limited`, `.tenant_filter_rewrite`, `.correlation_id`, `.attempt` |
|
|
55
|
+
| `sqlguard.validate` (child) | `sqlguard.decision`, `.decision_id`, `.role`, `.dialect`, `.policy.name`, `.policy.revision`, `.policy.fingerprint`, `.findings.count`, `.findings.rule_ids`, `.query_fingerprint`; on exceptions `error.type` and an ERROR status |
|
|
56
|
+
|
|
57
|
+
## Metrics (Prometheus)
|
|
58
|
+
|
|
59
|
+
`GuardMetrics()` uses a private `CollectorRegistry`. To use your own, pass
|
|
60
|
+
`GuardMetrics(registry=...)`. Expose the metrics with `metrics.render()` (returns the body and
|
|
61
|
+
content type) or with `prometheus_client.start_http_server(port, registry=metrics.registry)`.
|
|
62
|
+
|
|
63
|
+
| Metric | Labels | Meaning |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `sqlguard_decisions_total` | `decision`, `role`, `dialect`, `policy` | validations |
|
|
66
|
+
| `sqlguard_findings_total` | `rule_id`, `action` | findings per rule |
|
|
67
|
+
| `sqlguard_validation_duration_seconds` | `decision` | histogram |
|
|
68
|
+
| `sqlguard_validation_errors_total` | `error` (exception type) | exceptions during validation |
|
|
69
|
+
| `sqlguard_limit_rejections_total` | `rule_id` | length, AST size, structure and cost limits |
|
|
70
|
+
| `sqlguard_checkpoint_outcomes_total` | `mode`, `outcome` | `allowed`, `blocked`, `approved`, `rejected`, `audit_only`, `rate_limited` |
|
|
71
|
+
| `sqlguard_policy_info` | `policy`, `revision`, `fingerprint` | always 1 |
|
|
72
|
+
|
|
73
|
+
Label cardinality is bounded. A role not defined in the policy is reported as `unknown`.
|
|
74
|
+
|
|
75
|
+
## Audit events
|
|
76
|
+
|
|
77
|
+
`SQLCheckpoint(audit_sink=...)` writes one `AuditEvent` per decision. It works with
|
|
78
|
+
`JsonlAuditSink`, `LoggingAuditSink`, or your own sink. Each event carries:
|
|
79
|
+
|
|
80
|
+
* `schema_version` (`sqlninja.audit/1`), `decision_id`, `correlation_id`
|
|
81
|
+
* `policy_revision`, `policy_fingerprint`
|
|
82
|
+
* `sql_sha256`, `query_fingerprint`
|
|
83
|
+
* `sql_redacted`: literals become `?` and comments are dropped. It is withheld (`null`) if the
|
|
84
|
+
SQL cannot be parsed, or if any literal token survives redaction.
|
|
85
|
+
* `rate_limited`, plus the rule ids and actions of the findings
|
|
86
|
+
|
|
87
|
+
Audit events do record the principal and tenant, because that is their purpose. Pass
|
|
88
|
+
`principal_hmac_key=` to store an HMAC-SHA256 pseudonym of the principal instead. Treat audit
|
|
89
|
+
logs as sensitive. If a short query embeds a guessable secret, its `sql_sha256` can be
|
|
90
|
+
brute-forced. Use `query_fingerprint` where an identifier is enough.
|
|
91
|
+
|
|
92
|
+
## SARIF
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
sqlninja scan sql/ --policy policy.yaml --role reporting --tenant 1 --format sarif --output sqlninja.sarif
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`scan` validates every statement in `*.sql` files and splits them with the dialect tokenizer.
|
|
99
|
+
Each finding becomes a SARIF result:
|
|
100
|
+
|
|
101
|
+
* located at its statement
|
|
102
|
+
* level by action: `error` for deny, `warning` for review, `note` for warn
|
|
103
|
+
* carrying a `partialFingerprints` entry that is stable when only literals change
|
|
104
|
+
|
|
105
|
+
No code snippet is copied into the log. The output validates against the official OASIS SARIF
|
|
106
|
+
2.1.0 schema, both in the tests and in CI. To upload to GitHub code scanning, see
|
|
107
|
+
[CI_INTEGRATION.md](CI_INTEGRATION.md).
|
|
108
|
+
|
|
109
|
+
## Grafana dashboard and synthetic telemetry
|
|
110
|
+
|
|
111
|
+
`deploy/grafana/sqlguard-ninja.json` is a Grafana dashboard. It shows:
|
|
112
|
+
|
|
113
|
+
* decisions per second
|
|
114
|
+
* deny ratio and errors
|
|
115
|
+
* p50/p95/p99 latency
|
|
116
|
+
* top rules
|
|
117
|
+
* checkpoint outcomes
|
|
118
|
+
* resource-limit rejections
|
|
119
|
+
* decisions by role
|
|
120
|
+
* loaded policies
|
|
121
|
+
|
|
122
|
+
To see it with synthetic traffic:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
docker compose -f examples/observability/docker-compose.yml up -d --wait
|
|
126
|
+
# Grafana (read-only, anonymous): http://127.0.0.1:3000/d/sqlguard-ninja
|
|
127
|
+
# Prometheus: http://127.0.0.1:9090
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The traffic generator replays the bundled synthetic corpus through an instrumented
|
|
131
|
+
checkpoint. It executes nothing. CI starts this stack and checks two things: Prometheus has
|
|
132
|
+
scraped `sqlguard_decisions_total`, and Grafana has provisioned the dashboard and can query
|
|
133
|
+
Prometheus.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SQLGuard Ninja v0.6.0
|
|
2
|
+
|
|
3
|
+
## Highlights
|
|
4
|
+
|
|
5
|
+
* **OpenTelemetry tracing and Prometheus metrics.** Install the optional extras `[otel]` and `[prometheus]`. Pass `SQLCheckpoint(telemetry=Telemetry(tracer=..., metrics=GuardMetrics()))` to get spans for checkpoint and validation decisions. Metrics cover decisions, findings by rule, latency, errors, resource-limit rejections and checkpoint outcomes, all with bounded labels.
|
|
6
|
+
* **Privacy by default.** Spans, metrics and audit events contain no SQL text, literals, comments, request-context values or internal detail. Traces and metrics also omit tenant ids and principals. Canary tests check this across many literal forms.
|
|
7
|
+
* **SARIF 2.1.0.** `sqlninja scan` validates every statement in SQL files and writes SARIF for code scanning. The output validates against the OASIS schema in the tests and in CI.
|
|
8
|
+
* **Grafana dashboard.** It ships with a local Prometheus + Grafana stack fed by synthetic traffic, which CI starts and checks.
|
|
9
|
+
|
|
10
|
+
## Security fix
|
|
11
|
+
|
|
12
|
+
* Audit redaction previously let hex, bit, `E''`, dollar-quoted, `U&''` and `N''` strings, and `INTERVAL` text, through into `sql_redacted`. All are now redacted, and redacted SQL is withheld if any literal token survives. If you keep audit logs from earlier versions, review them for inlined secrets.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
* All benchmark results are still internal (issues #5 and #6). SQL Server and Oracle remain parser-level experimental.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
python -m pip install "sqlguard-ninja[otel,prometheus]==0.6.0"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
PyPI: https://pypi.org/project/sqlguard-ninja/0.6.0/
|
|
@@ -58,6 +58,7 @@ flowchart LR
|
|
|
58
58
|
| T4 | Parser ambiguity | Comment and string tricks, nested comments, dollar quoting | sqlglot dialect parsing. The corpus includes formatting and comment cases validated against real engines. Unparseable input is denied. | Unknown tokenizer differences. Report them as vulnerabilities. |
|
|
59
59
|
| T5 | Dialect-specific bypasses | MySQL `/*! */` executable comments, SQLite `ATTACH`/`PRAGMA` | Executable-comment rule for the MySQL family. SQLite session statements are denied. | Untested dialects are experimental. |
|
|
60
60
|
| T17 | MCP client or model escalating privileges | A model passes `role` or `tenant` arguments, or probes `get_policy_capabilities` for hidden tables | Identity fixed at server start from operator config; tool schemas have no identity arguments; capabilities list only granted, non-denied columns; no execution or credentials in the server | Anyone who can edit the MCP client configuration chooses the identity |
|
|
61
|
+
| T19 | Secrets leaking through logs, traces, metrics or SARIF | A model inlines a credential or customer value as a literal or comment, or an exception message quotes SQL | Redaction of every literal form, with comments dropped; output is re-tokenized and withheld if any literal survives; telemetry carries ids and bounded labels only; spans record exception types only; SARIF copies no snippet; canary tests | `sql_sha256` is brute-forceable for short secrets; custom sinks and rules can add their own leaks |
|
|
61
62
|
| T18 | MySQL parser differential through `sql_mode` | Under `ANSI_QUOTES` or `NO_BACKSLASH_ESCAPES` the server reads a validated string differently (e.g. `"card_number"` as a column, not a string) | `mysql_executor` checks `@@SESSION.sql_mode` and refuses those flags; placeholder values with backslashes are refused | Custom executors must call `check_sql_mode` themselves; global `sql_mode` changes between the check and the statement are not detected |
|
|
62
63
|
| T6a | Unsafe automatic tenant filtering | A rewrite that changes meaning or masks a query naming another tenant | Rewrite is opt-in, bind-parameter only, refused on any non-tenant finding and on RIGHT/FULL-join ownership ambiguity; the rewritten SQL is re-validated and is the executed string | Rewritten SQL is sqlglot-normalised; RLS remains authoritative |
|
|
63
64
|
| T6b | Silent policy relaxation through inheritance or edits | A child role or edit quietly widens access | Sticky explicit denies (load error on conflict); `policy diff --fail-on-relaxation`; fingerprint and revision in every decision | Reviewers must still read the diff |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Local observability stack with synthetic traffic: Prometheus + Grafana + traffic generator.
|
|
2
|
+
# Synthetic data only; nothing is executed against a database. Ports bind to 127.0.0.1.
|
|
3
|
+
#
|
|
4
|
+
# docker compose -f examples/observability/docker-compose.yml up -d --wait
|
|
5
|
+
# open http://127.0.0.1:3000/d/sqlguard-ninja (anonymous read-only viewer)
|
|
6
|
+
services:
|
|
7
|
+
traffic:
|
|
8
|
+
image: python:3.12-slim
|
|
9
|
+
working_dir: /src
|
|
10
|
+
volumes: ["../..:/src:ro"]
|
|
11
|
+
command: >-
|
|
12
|
+
sh -c "pip install --no-cache-dir --target /tmp/deps '/src[prometheus]' >/dev/null &&
|
|
13
|
+
PYTHONPATH=/tmp/deps python /src/examples/observability/synthetic_traffic.py --host 0.0.0.0 --port 9464"
|
|
14
|
+
healthcheck:
|
|
15
|
+
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:9464/metrics')"]
|
|
16
|
+
interval: 5s
|
|
17
|
+
retries: 60
|
|
18
|
+
read_only: true
|
|
19
|
+
tmpfs: ["/tmp:exec,mode=1777"] # pip installs compiled wheels here
|
|
20
|
+
user: "65534:65534"
|
|
21
|
+
environment: {HOME: /tmp, PIP_CACHE_DIR: /tmp/pip}
|
|
22
|
+
prometheus:
|
|
23
|
+
image: prom/prometheus:v3.13.5@sha256:aafa9d694e6a883c02db516d9f6e5b84e14195f49a2a6456a4cca815edda72b9
|
|
24
|
+
volumes: ["./prometheus.yml:/etc/prometheus/prometheus.yml:ro"]
|
|
25
|
+
ports: ["127.0.0.1:9090:9090"]
|
|
26
|
+
depends_on: {traffic: {condition: service_healthy}}
|
|
27
|
+
healthcheck:
|
|
28
|
+
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:9090/-/ready"]
|
|
29
|
+
interval: 5s
|
|
30
|
+
retries: 30
|
|
31
|
+
grafana:
|
|
32
|
+
image: grafana/grafana:13.0.11@sha256:2abd2114bb65e4d6f0614206b7c882c1e7c6a992827ba08396aa5afc23ca07f5
|
|
33
|
+
environment:
|
|
34
|
+
GF_AUTH_ANONYMOUS_ENABLED: "true"
|
|
35
|
+
GF_AUTH_ANONYMOUS_ORG_ROLE: Viewer
|
|
36
|
+
GF_AUTH_DISABLE_LOGIN_FORM: "true"
|
|
37
|
+
GF_USERS_ALLOW_SIGN_UP: "false"
|
|
38
|
+
GF_ANALYTICS_REPORTING_ENABLED: "false"
|
|
39
|
+
volumes:
|
|
40
|
+
- ./grafana/provisioning:/etc/grafana/provisioning:ro
|
|
41
|
+
- ../../deploy/grafana:/var/lib/grafana/dashboards:ro
|
|
42
|
+
ports: ["127.0.0.1:3000:3000"]
|
|
43
|
+
depends_on: {prometheus: {condition: service_healthy}}
|
|
44
|
+
healthcheck:
|
|
45
|
+
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/api/health"]
|
|
46
|
+
interval: 5s
|
|
47
|
+
retries: 30
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""CI smoke check for the observability stack: Prometheus scraped the synthetic metrics
|
|
2
|
+
and Grafana provisioned the dashboard with a working Prometheus datasource."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import urllib.parse
|
|
8
|
+
import urllib.request
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get(url: str) -> dict:
|
|
12
|
+
with urllib.request.urlopen(url, timeout=10) as r:
|
|
13
|
+
return json.load(r)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> int:
|
|
17
|
+
q = urllib.parse.quote("sum(sqlguard_decisions_total)")
|
|
18
|
+
for _ in range(40):
|
|
19
|
+
data = get(f"http://127.0.0.1:9090/api/v1/query?query={q}")["data"]["result"]
|
|
20
|
+
if data and float(data[0]["value"][1]) > 0:
|
|
21
|
+
break
|
|
22
|
+
time.sleep(3)
|
|
23
|
+
else:
|
|
24
|
+
print("Prometheus has no sqlguard_decisions_total samples")
|
|
25
|
+
return 1
|
|
26
|
+
dash = get("http://127.0.0.1:3000/api/dashboards/uid/sqlguard-ninja")["dashboard"]
|
|
27
|
+
exprs = [t["expr"] for p in dash["panels"] for t in p["targets"]]
|
|
28
|
+
ds = urllib.parse.quote(
|
|
29
|
+
exprs[0].replace("$__rate_interval", "1m").replace('{policy=~"$policy"}', "")
|
|
30
|
+
)
|
|
31
|
+
proxied = get(
|
|
32
|
+
f"http://127.0.0.1:3000/api/datasources/proxy/uid/prometheus/api/v1/query?query={ds}"
|
|
33
|
+
)
|
|
34
|
+
print(
|
|
35
|
+
f"decisions={data[0]['value'][1]} panels={len(dash['panels'])}",
|
|
36
|
+
f"grafana_query={proxied['status']}",
|
|
37
|
+
)
|
|
38
|
+
return 0 if proxied["status"] == "success" else 1
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
sys.exit(main())
|