sqlguard-ninja 0.1.0__tar.gz → 0.1.1__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 (125) hide show
  1. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/CHANGELOG.md +20 -0
  2. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/PKG-INFO +27 -24
  3. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/README.md +26 -23
  4. sqlguard_ninja-0.1.1/benchmarks/datasets/external/README.md +28 -0
  5. sqlguard_ninja-0.1.1/docs/guides/BENCHMARK_BIAS_REVIEW.md +74 -0
  6. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/BENCHMARK_METHODOLOGY.md +2 -0
  7. sqlguard_ninja-0.1.1/docs/releases/v0.1.1.md +29 -0
  8. sqlguard_ninja-0.1.1/docs/security/STATIC_ANALYSIS_LIMITS.md +49 -0
  9. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/security/THREAT_MODEL.md +4 -0
  10. sqlguard_ninja-0.1.1/src/sqlguard_ninja/_version.py +1 -0
  11. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_docs.py +13 -0
  12. sqlguard_ninja-0.1.0/src/sqlguard_ninja/_version.py +0 -1
  13. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/.gitignore +0 -0
  14. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/LICENSE +0 -0
  15. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/THIRD_PARTY_NOTICES.md +0 -0
  16. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/baselines/native.json +0 -0
  17. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/01_destructive.yaml +0 -0
  18. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/02_multi_statement_and_formatting.yaml +0 -0
  19. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/03_table_and_column_access.yaml +0 -0
  20. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/04_privacy.yaml +0 -0
  21. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/05_tenant_isolation.yaml +0 -0
  22. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/06_cte_nested_unicode.yaml +0 -0
  23. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/07_legitimate_lookalike_cost_dialect.yaml +0 -0
  24. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/grant_matrix.yaml +0 -0
  25. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/label_matrix.yaml +0 -0
  26. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/tenant_matrix.yaml +0 -0
  27. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/transforms.yaml +0 -0
  28. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/manifest.yaml +0 -0
  29. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/policies/retail_saas.yaml +0 -0
  30. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/agent-sql-guard.json +0 -0
  31. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/agent-sql-guard.md +0 -0
  32. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/krish2105-sqlguard.json +0 -0
  33. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/krish2105-sqlguard.md +0 -0
  34. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/native.json +0 -0
  35. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/native.md +0 -0
  36. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/sql-guardrail.json +0 -0
  37. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/sql-guardrail.md +0 -0
  38. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/architecture/ARCHITECTURE_DECISIONS.md +0 -0
  39. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/benchmarks/RESULTS_v1.md +0 -0
  40. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/AGENT_INTEGRATION.md +0 -0
  41. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/CI_INTEGRATION.md +0 -0
  42. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/POLICY_REFERENCE.md +0 -0
  43. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/QUERY_COST.md +0 -0
  44. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/release/FINAL_TECHNICAL_AUDIT.md +0 -0
  45. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/releases/v0.1.0.md +0 -0
  46. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/research/COMPETITIVE_ANALYSIS.md +0 -0
  47. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/_shared.py +0 -0
  48. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/fastapi/app.py +0 -0
  49. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/github-actions/sql-security.yml +0 -0
  50. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/langchain/guarded_tool_demo.py +0 -0
  51. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/langgraph/real_llm_agent.py +0 -0
  52. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/langgraph/sql_agent_demo.py +0 -0
  53. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/postgresql/plan_inspection_demo.py +0 -0
  54. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/sdk/quickstart.py +0 -0
  55. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/sqlite/text_to_sql_demo.py +0 -0
  56. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/pyproject.toml +0 -0
  57. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/__init__.py +0 -0
  58. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/__init__.py +0 -0
  59. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/adapters.py +0 -0
  60. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/builtin.py +0 -0
  61. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/compare.py +0 -0
  62. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/dataset.py +0 -0
  63. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/fixtures.py +0 -0
  64. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/metrics.py +0 -0
  65. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/runner.py +0 -0
  66. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/translate.py +0 -0
  67. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/_protocol.py +0 -0
  68. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/agent_sql_guard_worker.py +0 -0
  69. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/krish_sqlguard_worker.py +0 -0
  70. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/sql_guardrail_worker.py +0 -0
  71. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/__init__.py +0 -0
  72. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/config.py +0 -0
  73. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/main.py +0 -0
  74. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/templates.py +0 -0
  75. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/__init__.py +0 -0
  76. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/analysis.py +0 -0
  77. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/findings.py +0 -0
  78. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/guard.py +0 -0
  79. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/__init__.py +0 -0
  80. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/access.py +0 -0
  81. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/base.py +0 -0
  82. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/catalog.py +0 -0
  83. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/functions.py +0 -0
  84. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/statements.py +0 -0
  85. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/structure.py +0 -0
  86. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/tenant.py +0 -0
  87. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/__init__.py +0 -0
  88. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/audit.py +0 -0
  89. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/checkpoint.py +0 -0
  90. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/executors.py +0 -0
  91. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/fastapi.py +0 -0
  92. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/langchain.py +0 -0
  93. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/langgraph.py +0 -0
  94. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/policy/__init__.py +0 -0
  95. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/policy/model.py +0 -0
  96. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/privacy/__init__.py +0 -0
  97. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/privacy/rules.py +0 -0
  98. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/privacy/suggest.py +0 -0
  99. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/py.typed +0 -0
  100. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/query_cost/__init__.py +0 -0
  101. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/query_cost/postgres.py +0 -0
  102. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/query_cost/static.py +0 -0
  103. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/reporting/__init__.py +0 -0
  104. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/reporting/markdown.py +0 -0
  105. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/__init__.py +0 -0
  106. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/conftest.py +0 -0
  107. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/__init__.py +0 -0
  108. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_corpus_sqlite_validation.py +0 -0
  109. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_external_adapters.py +0 -0
  110. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_frameworks.py +0 -0
  111. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_postgres.py +0 -0
  112. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/pg_support.py +0 -0
  113. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/property/__init__.py +0 -0
  114. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/property/test_guard_properties.py +0 -0
  115. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/security/__init__.py +0 -0
  116. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/security/test_agent_boundaries.py +0 -0
  117. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/security/test_corpus_native.py +0 -0
  118. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/__init__.py +0 -0
  119. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/helpers.py +0 -0
  120. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_benchmark_engine.py +0 -0
  121. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_cli.py +0 -0
  122. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_dataset.py +0 -0
  123. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_function_policy_mechanics.py +0 -0
  124. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_policy.py +0 -0
  125. {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_rules.py +0 -0
@@ -3,6 +3,26 @@
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.1.1] — 2026-10-24
7
+
8
+ Documentation and governance patch. No changes to policy decisions.
9
+
10
+ ### Fixed
11
+ * README links were relative and broke on the PyPI project page. They are now absolute
12
+ GitHub URLs, and a test keeps relative links out of the README.
13
+
14
+ ### Added
15
+ * `docs/security/STATIC_ANALYSIS_LIMITS.md`: limits of static analysis, compensating
16
+ controls, and a dialect support matrix (supported / experimental / untested).
17
+ * `docs/guides/BENCHMARK_BIAS_REVIEW.md`: quantified review of corpus v1 bias
18
+ (co-development, generated labels, input distribution, outcome imbalance).
19
+ * `benchmarks/datasets/external/`: intake rules for independently sourced, held-out
20
+ evaluation sets (no cases yet).
21
+ * `.github/CODEOWNERS`, and branch protection on `main` (pull requests and CI required).
22
+
23
+ ### Unchanged known limitations
24
+ * No restricted-function or system-catalog benchmark cases; no external dataset yet.
25
+
6
26
  ## [0.1.0] — 2026-10-24
7
27
 
8
28
  First release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sqlguard-ninja
3
- Version: 0.1.0
3
+ Version: 0.1.1
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
@@ -76,7 +76,7 @@ side by side.
76
76
 
77
77
  > **One layer, not the only one.** Static SQL inspection cannot guarantee database security.
78
78
  > Use a read-only least-privilege role, row-level security and statement timeouts as well.
79
- > See the [threat model](docs/security/THREAT_MODEL.md).
79
+ > See the [threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md).
80
80
 
81
81
  ## What it does
82
82
 
@@ -92,7 +92,7 @@ side by side.
92
92
  Not yet included: governed writes (all writes are denied in v0.1); benchmark cases for
93
93
  restricted functions and system catalogs (the rules exist, but the corpus does not cover
94
94
  them); dialects beyond PostgreSQL and SQLite (they parse, but are untested). Full list:
95
- [KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md).
95
+ [KNOWN_LIMITATIONS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/KNOWN_LIMITATIONS.md).
96
96
 
97
97
  ## Architecture
98
98
 
@@ -121,7 +121,7 @@ flowchart LR
121
121
  ```
122
122
 
123
123
  Design decisions and their trade-offs are recorded in
124
- [ARCHITECTURE_DECISIONS.md](docs/architecture/ARCHITECTURE_DECISIONS.md).
124
+ [ARCHITECTURE_DECISIONS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/architecture/ARCHITECTURE_DECISIONS.md).
125
125
 
126
126
  ## Five-minute quick start
127
127
 
@@ -203,7 +203,7 @@ ctx = RequestContext(principal=user.id, role=user.role, tenant_id=user.tenant_id
203
203
  app.invoke(state, config={"configurable": {CONTEXT_KEY: ctx}}) # nodes cannot alter config
204
204
  ```
205
205
 
206
- More in [AGENT_INTEGRATION.md](docs/guides/AGENT_INTEGRATION.md): the LangChain tool (its
206
+ More in [AGENT_INTEGRATION.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md): the LangChain tool (its
207
207
  schema accepts only `query`), the FastAPI router, review-mode approvals and audit sinks.
208
208
 
209
209
  ## Privacy policies
@@ -244,7 +244,7 @@ REVIEW:
244
244
  Labels also apply to paths inside JSON columns (`profile->'health'->>'allergies'`).
245
245
  `sqlninja policy suggest` proposes labels from column names for a human to review; those
246
246
  suggestions are never enforced. This is a technical control, not a certification of GDPR
247
- compliance. Full semantics: [POLICY_REFERENCE.md](docs/guides/POLICY_REFERENCE.md).
247
+ compliance. Full semantics: [POLICY_REFERENCE.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/POLICY_REFERENCE.md).
248
248
 
249
249
  ## Query cost analysis
250
250
 
@@ -268,7 +268,7 @@ report = PostgresPlanInspector(guard, lambda: psycopg.connect(READER_DSN)).inspe
268
268
  report.total_cost, report.plan_rows, report.static.score, report.findings
269
269
  ```
270
270
 
271
- See [QUERY_COST.md](docs/guides/QUERY_COST.md) and `examples/postgresql/plan_inspection_demo.py`.
271
+ See [QUERY_COST.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md) and `examples/postgresql/plan_inspection_demo.py`.
272
272
 
273
273
  ## Benchmark
274
274
 
@@ -280,7 +280,7 @@ sqlninja benchmark compare --baseline benchmarks/baselines/native.json --candida
280
280
  ```
281
281
 
282
282
  Results for corpus v1.0.0 (Windows 11, Python 3.11, 1,002 evaluations). Details and raw files:
283
- [RESULTS_v1.md](docs/benchmarks/RESULTS_v1.md).
283
+ [RESULTS_v1.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/benchmarks/RESULTS_v1.md).
284
284
 
285
285
  | Validator | Recall | Precision | FPR | Recall / FPR within declared capability | p95 ms |
286
286
  |---|---:|---:|---:|---:|---:|
@@ -294,7 +294,7 @@ every disagreement was triaged by fixing the engine or correcting a label (both
294
294
  100% shows consistency with the documented semantics, not independent detection quality. The
295
295
  other tools have smaller policy models (no roles, labels, aggregate-only access or review), so
296
296
  the "within capability" column is the fairer comparison. Methodology and threats to validity:
297
- [BENCHMARK_METHODOLOGY.md](docs/guides/BENCHMARK_METHODOLOGY.md).
297
+ [BENCHMARK_METHODOLOGY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md).
298
298
 
299
299
  ## How it relates to existing tools
300
300
 
@@ -304,7 +304,7 @@ thorough read-only validator with a tautology catch-all, which this project does
304
304
  duplicate. [agent-sql-guard](https://github.com/sakura-sky/sql-guard) has a careful
305
305
  schema-less PII denylist. What SQLGuard Ninja adds: role- and label-based policies, the agent
306
306
  checkpoint contract, a shared benchmark with isolated adapters, planner inspection, and the
307
- CI gate. Full comparison: [COMPETITIVE_ANALYSIS.md](docs/research/COMPETITIVE_ANALYSIS.md).
307
+ CI gate. Full comparison: [COMPETITIVE_ANALYSIS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/research/COMPETITIVE_ANALYSIS.md).
308
308
 
309
309
  ## Security limitations
310
310
 
@@ -318,13 +318,14 @@ CI gate. Full comparison: [COMPETITIVE_ANALYSIS.md](docs/research/COMPETITIVE_AN
318
318
  but those two categories are not measured.
319
319
 
320
320
  Use database grants, row-level security and timeouts as the authoritative controls. See the
321
- [threat model](docs/security/THREAT_MODEL.md) and [KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md).
321
+ [threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md), [limits of static analysis](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md),
322
+ the [benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_BIAS_REVIEW.md) and [KNOWN_LIMITATIONS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/KNOWN_LIMITATIONS.md).
322
323
 
323
324
  ## Roadmap
324
325
 
325
326
  Next: restricted-function and system-catalog benchmark cases, an independent held-out
326
327
  corpus, lineage-aware aggregate checks, and tenant predicate push-down. Later: governed
327
- writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](ROADMAP.md).
328
+ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md).
328
329
 
329
330
  ## Links
330
331
 
@@ -332,18 +333,20 @@ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](ROADMAP.
332
333
  * Releases: https://github.com/Pratik653/SQLGuard_Ninjapy/releases
333
334
  * PyPI: https://pypi.org/project/sqlguard-ninja/
334
335
  * Issues: https://github.com/Pratik653/SQLGuard_Ninjapy/issues
335
- * Security policy: [SECURITY.md](SECURITY.md) · Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
336
+ * Security policy: [SECURITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/SECURITY.md) · Contributing: [CONTRIBUTING.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/CONTRIBUTING.md)
336
337
 
337
338
  ## Documentation
338
339
 
339
- * [Policy reference](docs/guides/POLICY_REFERENCE.md)
340
- * [Agent integration](docs/guides/AGENT_INTEGRATION.md)
341
- * [Query cost](docs/guides/QUERY_COST.md)
342
- * [CI integration](docs/guides/CI_INTEGRATION.md)
343
- * [Benchmark methodology](docs/guides/BENCHMARK_METHODOLOGY.md)
344
- * [Threat model](docs/security/THREAT_MODEL.md)
345
- * [Architecture decisions](docs/architecture/ARCHITECTURE_DECISIONS.md)
346
- * [Known limitations](KNOWN_LIMITATIONS.md) · [Roadmap](ROADMAP.md) · [Changelog](CHANGELOG.md)
340
+ * [Policy reference](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/POLICY_REFERENCE.md)
341
+ * [Agent integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md)
342
+ * [Query cost](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md)
343
+ * [CI integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/CI_INTEGRATION.md)
344
+ * [Benchmark methodology](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md)
345
+ * [Threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md)
346
+ * [Limits of static SQL analysis and dialect support](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md)
347
+ * [Benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_BIAS_REVIEW.md)
348
+ * [Architecture decisions](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/architecture/ARCHITECTURE_DECISIONS.md)
349
+ * [Known limitations](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/KNOWN_LIMITATIONS.md) · [Roadmap](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md) · [Changelog](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/CHANGELOG.md)
347
350
 
348
351
  ## Supported versions
349
352
 
@@ -361,10 +364,10 @@ pytest # PostgreSQL tests run if SQLNINJA_PG_DSN is set
361
364
  python scripts/generate_corpus.py --check
362
365
  ```
363
366
 
364
- See [CONTRIBUTING.md](CONTRIBUTING.md). Report vulnerabilities privately per
365
- [SECURITY.md](SECURITY.md).
367
+ See [CONTRIBUTING.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/CONTRIBUTING.md). Report vulnerabilities privately per
368
+ [SECURITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/SECURITY.md).
366
369
 
367
370
  ## Licence
368
371
 
369
372
  Apache-2.0. No third-party code is vendored; dependencies and their licences are listed in
370
- [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). All benchmark data is synthetic.
373
+ [THIRD_PARTY_NOTICES.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/THIRD_PARTY_NOTICES.md). All benchmark data is synthetic.
@@ -20,7 +20,7 @@ side by side.
20
20
 
21
21
  > **One layer, not the only one.** Static SQL inspection cannot guarantee database security.
22
22
  > Use a read-only least-privilege role, row-level security and statement timeouts as well.
23
- > See the [threat model](docs/security/THREAT_MODEL.md).
23
+ > See the [threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md).
24
24
 
25
25
  ## What it does
26
26
 
@@ -36,7 +36,7 @@ side by side.
36
36
  Not yet included: governed writes (all writes are denied in v0.1); benchmark cases for
37
37
  restricted functions and system catalogs (the rules exist, but the corpus does not cover
38
38
  them); dialects beyond PostgreSQL and SQLite (they parse, but are untested). Full list:
39
- [KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md).
39
+ [KNOWN_LIMITATIONS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/KNOWN_LIMITATIONS.md).
40
40
 
41
41
  ## Architecture
42
42
 
@@ -65,7 +65,7 @@ flowchart LR
65
65
  ```
66
66
 
67
67
  Design decisions and their trade-offs are recorded in
68
- [ARCHITECTURE_DECISIONS.md](docs/architecture/ARCHITECTURE_DECISIONS.md).
68
+ [ARCHITECTURE_DECISIONS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/architecture/ARCHITECTURE_DECISIONS.md).
69
69
 
70
70
  ## Five-minute quick start
71
71
 
@@ -147,7 +147,7 @@ ctx = RequestContext(principal=user.id, role=user.role, tenant_id=user.tenant_id
147
147
  app.invoke(state, config={"configurable": {CONTEXT_KEY: ctx}}) # nodes cannot alter config
148
148
  ```
149
149
 
150
- More in [AGENT_INTEGRATION.md](docs/guides/AGENT_INTEGRATION.md): the LangChain tool (its
150
+ More in [AGENT_INTEGRATION.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md): the LangChain tool (its
151
151
  schema accepts only `query`), the FastAPI router, review-mode approvals and audit sinks.
152
152
 
153
153
  ## Privacy policies
@@ -188,7 +188,7 @@ REVIEW:
188
188
  Labels also apply to paths inside JSON columns (`profile->'health'->>'allergies'`).
189
189
  `sqlninja policy suggest` proposes labels from column names for a human to review; those
190
190
  suggestions are never enforced. This is a technical control, not a certification of GDPR
191
- compliance. Full semantics: [POLICY_REFERENCE.md](docs/guides/POLICY_REFERENCE.md).
191
+ compliance. Full semantics: [POLICY_REFERENCE.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/POLICY_REFERENCE.md).
192
192
 
193
193
  ## Query cost analysis
194
194
 
@@ -212,7 +212,7 @@ report = PostgresPlanInspector(guard, lambda: psycopg.connect(READER_DSN)).inspe
212
212
  report.total_cost, report.plan_rows, report.static.score, report.findings
213
213
  ```
214
214
 
215
- See [QUERY_COST.md](docs/guides/QUERY_COST.md) and `examples/postgresql/plan_inspection_demo.py`.
215
+ See [QUERY_COST.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md) and `examples/postgresql/plan_inspection_demo.py`.
216
216
 
217
217
  ## Benchmark
218
218
 
@@ -224,7 +224,7 @@ sqlninja benchmark compare --baseline benchmarks/baselines/native.json --candida
224
224
  ```
225
225
 
226
226
  Results for corpus v1.0.0 (Windows 11, Python 3.11, 1,002 evaluations). Details and raw files:
227
- [RESULTS_v1.md](docs/benchmarks/RESULTS_v1.md).
227
+ [RESULTS_v1.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/benchmarks/RESULTS_v1.md).
228
228
 
229
229
  | Validator | Recall | Precision | FPR | Recall / FPR within declared capability | p95 ms |
230
230
  |---|---:|---:|---:|---:|---:|
@@ -238,7 +238,7 @@ every disagreement was triaged by fixing the engine or correcting a label (both
238
238
  100% shows consistency with the documented semantics, not independent detection quality. The
239
239
  other tools have smaller policy models (no roles, labels, aggregate-only access or review), so
240
240
  the "within capability" column is the fairer comparison. Methodology and threats to validity:
241
- [BENCHMARK_METHODOLOGY.md](docs/guides/BENCHMARK_METHODOLOGY.md).
241
+ [BENCHMARK_METHODOLOGY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md).
242
242
 
243
243
  ## How it relates to existing tools
244
244
 
@@ -248,7 +248,7 @@ thorough read-only validator with a tautology catch-all, which this project does
248
248
  duplicate. [agent-sql-guard](https://github.com/sakura-sky/sql-guard) has a careful
249
249
  schema-less PII denylist. What SQLGuard Ninja adds: role- and label-based policies, the agent
250
250
  checkpoint contract, a shared benchmark with isolated adapters, planner inspection, and the
251
- CI gate. Full comparison: [COMPETITIVE_ANALYSIS.md](docs/research/COMPETITIVE_ANALYSIS.md).
251
+ CI gate. Full comparison: [COMPETITIVE_ANALYSIS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/research/COMPETITIVE_ANALYSIS.md).
252
252
 
253
253
  ## Security limitations
254
254
 
@@ -262,13 +262,14 @@ CI gate. Full comparison: [COMPETITIVE_ANALYSIS.md](docs/research/COMPETITIVE_AN
262
262
  but those two categories are not measured.
263
263
 
264
264
  Use database grants, row-level security and timeouts as the authoritative controls. See the
265
- [threat model](docs/security/THREAT_MODEL.md) and [KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md).
265
+ [threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md), [limits of static analysis](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md),
266
+ the [benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_BIAS_REVIEW.md) and [KNOWN_LIMITATIONS.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/KNOWN_LIMITATIONS.md).
266
267
 
267
268
  ## Roadmap
268
269
 
269
270
  Next: restricted-function and system-catalog benchmark cases, an independent held-out
270
271
  corpus, lineage-aware aggregate checks, and tenant predicate push-down. Later: governed
271
- writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](ROADMAP.md).
272
+ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md).
272
273
 
273
274
  ## Links
274
275
 
@@ -276,18 +277,20 @@ writes, more tested dialects, and SARIF output. Details in [ROADMAP.md](ROADMAP.
276
277
  * Releases: https://github.com/Pratik653/SQLGuard_Ninjapy/releases
277
278
  * PyPI: https://pypi.org/project/sqlguard-ninja/
278
279
  * Issues: https://github.com/Pratik653/SQLGuard_Ninjapy/issues
279
- * Security policy: [SECURITY.md](SECURITY.md) · Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
280
+ * Security policy: [SECURITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/SECURITY.md) · Contributing: [CONTRIBUTING.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/CONTRIBUTING.md)
280
281
 
281
282
  ## Documentation
282
283
 
283
- * [Policy reference](docs/guides/POLICY_REFERENCE.md)
284
- * [Agent integration](docs/guides/AGENT_INTEGRATION.md)
285
- * [Query cost](docs/guides/QUERY_COST.md)
286
- * [CI integration](docs/guides/CI_INTEGRATION.md)
287
- * [Benchmark methodology](docs/guides/BENCHMARK_METHODOLOGY.md)
288
- * [Threat model](docs/security/THREAT_MODEL.md)
289
- * [Architecture decisions](docs/architecture/ARCHITECTURE_DECISIONS.md)
290
- * [Known limitations](KNOWN_LIMITATIONS.md) · [Roadmap](ROADMAP.md) · [Changelog](CHANGELOG.md)
284
+ * [Policy reference](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/POLICY_REFERENCE.md)
285
+ * [Agent integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/AGENT_INTEGRATION.md)
286
+ * [Query cost](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/QUERY_COST.md)
287
+ * [CI integration](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/CI_INTEGRATION.md)
288
+ * [Benchmark methodology](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_METHODOLOGY.md)
289
+ * [Threat model](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/THREAT_MODEL.md)
290
+ * [Limits of static SQL analysis and dialect support](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md)
291
+ * [Benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_BIAS_REVIEW.md)
292
+ * [Architecture decisions](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/architecture/ARCHITECTURE_DECISIONS.md)
293
+ * [Known limitations](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/KNOWN_LIMITATIONS.md) · [Roadmap](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/ROADMAP.md) · [Changelog](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/CHANGELOG.md)
291
294
 
292
295
  ## Supported versions
293
296
 
@@ -305,10 +308,10 @@ pytest # PostgreSQL tests run if SQLNINJA_PG_DSN is set
305
308
  python scripts/generate_corpus.py --check
306
309
  ```
307
310
 
308
- See [CONTRIBUTING.md](CONTRIBUTING.md). Report vulnerabilities privately per
309
- [SECURITY.md](SECURITY.md).
311
+ See [CONTRIBUTING.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/CONTRIBUTING.md). Report vulnerabilities privately per
312
+ [SECURITY.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/SECURITY.md).
310
313
 
311
314
  ## Licence
312
315
 
313
316
  Apache-2.0. No third-party code is vendored; dependencies and their licences are listed in
314
- [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). All benchmark data is synthetic.
317
+ [THIRD_PARTY_NOTICES.md](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/THIRD_PARTY_NOTICES.md). All benchmark data is synthetic.
@@ -0,0 +1,28 @@
1
+ # External evaluation datasets — intake requirements
2
+
3
+ This directory is reserved for evaluation sets that were **not** written by SQLGuard Ninja's
4
+ authors. It is intentionally empty in v0.1.1. See
5
+ [`docs/guides/BENCHMARK_BIAS_REVIEW.md`](../../../docs/guides/BENCHMARK_BIAS_REVIEW.md) for
6
+ why it exists.
7
+
8
+ ## Rules
9
+
10
+ 1. **Independent provenance.** Each dataset lives in its own sub-directory with a
11
+ `PROVENANCE.md` that names the author or organisation, the creation method, the date, the
12
+ source URL or commit, and how labels were assigned and reviewed.
13
+ 2. **Licence first.** The dataset's licence must allow redistribution under, or alongside,
14
+ Apache-2.0. Record the SPDX identifier in `PROVENANCE.md` and `THIRD_PARTY_NOTICES.md`.
15
+ No dataset with an unknown or restrictive licence is merged.
16
+ 3. **Synthetic data only.** No real personal data, customer data or credentials.
17
+ 4. **Held out.** External cases must never be used to tune the engine. If a case reveals an
18
+ engine bug, fix it, add a *separate* regression case under `benchmarks/datasets/v1`, and
19
+ leave the external case and its label unchanged.
20
+ 5. **Same schema format** as `benchmarks/datasets/v1` (a `manifest.yaml` with `case_files`
21
+ and a policy). Every case uses
22
+ `provenance: external:<dataset-name>@<version>` so reports break it out separately.
23
+ 6. **Label validation.** `allow` cases must execute on the fixture databases, as in v1.
24
+ 7. **Separate reporting.** Results are published per dataset and never merged into the v1
25
+ headline numbers.
26
+
27
+ Proposals: open a "Benchmark case" issue first so licence and provenance can be checked
28
+ before any data is submitted.
@@ -0,0 +1,74 @@
1
+ # Benchmark bias review (corpus v1.0.0)
2
+
3
+ _Written 2026-10-24 for v0.1.1. Every number below was computed from the corpus with
4
+ `load_dataset("benchmarks/datasets/v1")`. The purpose is to say plainly how much the v1
5
+ results can be trusted, and what an external evaluation set must fix._
6
+
7
+ ## Summary
8
+
9
+ The v1 corpus measures **consistency with SQLGuard Ninja's documented policy semantics**. It
10
+ does not measure real-world detection ability. The native 100% score should not be quoted
11
+ without this caveat. The four biases below are structural, not incidental.
12
+
13
+ ## Findings
14
+
15
+ ### B1. Co-development and single authorship (high)
16
+
17
+ The engine and all 530 cases were written by the same author in the same development
18
+ session. Every disagreement between them was resolved by changing one of the two, and those
19
+ changes are logged in IMPLEMENTATION_STATUS.md. A corpus tuned this way always converges
20
+ toward 100% for its own engine, so the score carries no information about cases the author
21
+ did not think of.
22
+
23
+ ### B2. Generated labels restate the engine's semantics (high, for 51% of cases)
24
+
25
+ 268 of the 530 unique cases (51%) are generated. The generator does not import the engine,
26
+ but its oracle encodes the **same** semantics (grant lookup, label resolution, tenant-pin
27
+ rules). The `label_matrix` (43 cases) and `tenant_matrix` (70) agree with the engine by
28
+ construction whenever both implement the specification correctly. That checks the
29
+ implementation against the specification. It cannot show that the specification is the
30
+ right policy.
31
+
32
+ ### B3. Narrow input distribution (medium)
33
+
34
+ | Measure | Value | Why it matters |
35
+ |---|---|---|
36
+ | Cases using the `alias.column` style | 424 / 530 (80%) | LLMs often emit unqualified columns, where the analyser's fail-closed path for unresolvable columns dominates |
37
+ | Leading keyword in upper case | 426 / 530 | formatting variety comes mostly from 7 transform templates |
38
+ | Median SQL length | 64 characters (max 346) | real text-to-SQL output is often longer, with more joins |
39
+ | Cases with two or more JOINs | 1 | multi-join reasoning is barely exercised |
40
+ | Tenant context | tenant 1 in 528 / 530 | value-handling bugs specific to other ids would go unseen |
41
+ | Literal tenant pin vs bind parameter | 297 vs 8 | parameterised queries are under-represented |
42
+ | Role mix | `support_analyst` in 355 / 530 | other roles' policies are lightly covered |
43
+
44
+ ### B4. Outcome and category imbalance (medium)
45
+
46
+ Of the 1,002 evaluations, 568 are deny, 420 allow and **14 review** (8 unique cases). Metrics
47
+ for the review outcome are therefore not statistically meaningful. Two policy categories,
48
+ restricted functions and system catalogs, have **no cases at all**.
49
+
50
+ ### B5. Single synthetic schema and policy (medium)
51
+
52
+ All cases use one ten-table retail schema with explicit labels. Results may not transfer to
53
+ schemas with views, many-to-many joins, wide tables or naming conventions unlike this one.
54
+
55
+ ### B6. Ground truth is policy semantics, not harm (by design)
56
+
57
+ Labels encode what the *policy* says, for example the scope-local tenant pin and
58
+ reference-based aggregate-only checks. A safe query can be labelled deny. That is documented,
59
+ but it means "false positive" here is measured against the policy, not against actual risk.
60
+
61
+ ## What reduces these biases
62
+
63
+ 1. **An external evaluation set** with independent provenance, kept separate from v1 and
64
+ never used to tune the engine. Requirements: [`benchmarks/datasets/external/README.md`](../../benchmarks/datasets/external/README.md).
65
+ 2. Reporting external results **separately** (the `by_provenance` breakdown already does this)
66
+ and never merging them into the headline v1 figure.
67
+ 3. More schemas and policies, LLM-generated SQL captured from real text-to-SQL runs against
68
+ the fixture schema, and larger review and multi-join slices.
69
+
70
+ ## Status
71
+
72
+ No external dataset is included in v0.1.1. One was requested, but this release does not add
73
+ new benchmark cases. It ships the intake process and these findings; the work is tracked as a
74
+ GitHub issue. Adding cases written by the engine's own author would not reduce B1 in any case.
@@ -100,6 +100,8 @@ decision is not `allow`.
100
100
 
101
101
  ## Threats to validity
102
102
 
103
+ A quantified review is in [BENCHMARK_BIAS_REVIEW.md](BENCHMARK_BIAS_REVIEW.md).
104
+
103
105
  * The corpus and the native engine were co-developed by the same authors. Native results
104
106
  show consistency with the stated semantics, not independent detection quality.
105
107
  * Some semantics are deliberately strict (reference-based privacy, scope-local tenant pins).
@@ -0,0 +1,29 @@
1
+ # SQLGuard Ninja v0.1.1
2
+
3
+ Documentation and governance patch release. **The enforcement behaviour is identical to v0.1.0.**
4
+ v0.1.0 remains published and unchanged on PyPI.
5
+
6
+ ## Fixed
7
+ * The README's documentation links now work on the PyPI project page (they were relative in 0.1.0).
8
+
9
+ ## Added
10
+ * [Limits of static SQL analysis and dialect support](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/security/STATIC_ANALYSIS_LIMITS.md)
11
+ * [Benchmark bias review](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/docs/guides/BENCHMARK_BIAS_REVIEW.md):
12
+ the v1 results reflect consistency with the engine's own semantics, not independent detection quality.
13
+ * [External dataset intake rules](https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/benchmarks/datasets/external/README.md); no external cases yet.
14
+ * CODEOWNERS and branch protection on `main`.
15
+
16
+ ## Known limitations (unchanged)
17
+ * No benchmark cases for restricted functions (server file access, sleep, dynamic SQL,
18
+ administrative functions) or system catalogs (`pg_catalog`, `information_schema`,
19
+ `sqlite_master`, `sqlite_schema`). The rules ship and run, but their detection is not measured.
20
+ * Writes are denied. Only PostgreSQL and SQLite are supported. No guarantee against SQL injection or data leakage.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ python -m pip install sqlguard-ninja==0.1.1
26
+ python -m pip install "sqlguard-ninja[all]==0.1.1"
27
+ ```
28
+
29
+ PyPI: https://pypi.org/project/sqlguard-ninja/0.1.1/
@@ -0,0 +1,49 @@
1
+ # Limits of static SQL analysis
2
+
3
+ SQLGuard Ninja decides by **reading** SQL, never by running it. That makes it fast,
4
+ deterministic and safe to run before execution. It also puts hard limits on what it can
5
+ know. This page lists those limits so deployers can put the right controls around it.
6
+
7
+ ## What a static analyser cannot know
8
+
9
+ | Limit | Consequence | Compensating control |
10
+ |---|---|---|
11
+ | **Parser differential:** sqlglot's parse can differ from the database engine's parse for unusual syntax | A query the guard reads as harmless could mean something else to the server | Only use tested dialects; unparseable input is denied; least-privilege DB role |
12
+ | **Object definitions:** views, functions, triggers and rules are opaque | A permitted view or function can read or change data the policy would deny | Grant only vetted views; `functions.mode: allowlist`; no SECURITY DEFINER functions for the agent role |
13
+ | **Runtime data:** values, row counts, NULL patterns | Aggregates and filters can still leak information across many permitted queries (inference) | Small-cohort review, rate limits, auditing, minimum-group-size controls in the database or app |
14
+ | **Session state:** `search_path`, settings, temporary objects created elsewhere | An unqualified name may resolve to a different object at runtime | Fixed `search_path` for the agent role; deny session statements (default) |
15
+ | **Cost:** actual rows, cache state, concurrency | Static scores and planner estimates can be wrong in both directions | `statement_timeout`, connection limits, resource groups |
16
+ | **Intent:** why the query was written | A legitimate query can serve a harmful goal | Human review mode for sensitive labels; audit trail |
17
+ | **Writes:** governed DML is out of scope | All writes are denied in v0.1 | Separate, explicitly designed write paths |
18
+
19
+ The guard **fails closed** where it detects uncertainty: parse errors, incomplete qualification,
20
+ unattributable columns, `*` over unknown sources and unknown tables are denied by default. It
21
+ cannot detect uncertainty it does not know about. That is why the controls in the right-hand
22
+ column are required, not optional.
23
+
24
+ ## Dialect support
25
+
26
+ | Dialect | Status | Evidence |
27
+ |---|---|---|
28
+ | PostgreSQL | **Supported.** Tested against PostgreSQL 16. | Corpus cases; every `allow` case executed on PostgreSQL 16 as a SELECT-only role (locally and in CI); plan inspection tested |
29
+ | SQLite | **Supported.** | Corpus cases; every `allow` case executed on the fixture database; SQLite-specific session statements denied |
30
+ | MySQL family | **Experimental.** | Executable-comment rule only; no corpus cases; not tested |
31
+ | Other sqlglot dialects (Snowflake, BigQuery, T-SQL, ...) | **Untested.** | Parsing may work. Policy behaviour, quoting and catalog rules are not verified. |
32
+
33
+ Treat "experimental" and "untested" as unsupported for security decisions.
34
+ Identifier-case handling follows each dialect's rules through sqlglot. For example, quoted
35
+ identifiers are case-sensitive in PostgreSQL but not in SQLite, and the corpus covers both.
36
+
37
+ ## Coverage of the benchmark
38
+
39
+ The benchmark does **not** contain restricted-function or system-catalog cases in v1.0.0.
40
+ Those rules exist and run by default, but their detection rate is not measured. See
41
+ [BENCHMARK_BIAS_REVIEW.md](../guides/BENCHMARK_BIAS_REVIEW.md) for what the existing results
42
+ can and cannot show.
43
+
44
+ ## Non-claims
45
+
46
+ SQLGuard Ninja does **not** guarantee protection against SQL injection, prompt injection or
47
+ data leakage, and it does not certify compliance with GDPR or any other regulation. It is one
48
+ defence-in-depth layer between an LLM and a database whose own permissions, row-level security
49
+ and resource limits remain authoritative. See the [threat model](THREAT_MODEL.md).
@@ -78,6 +78,10 @@ flowchart LR
78
78
  * Store audit events in append-only storage. Treat `sql_redacted` as potentially sensitive.
79
79
  * Protect policy files and baselines with code review (CODEOWNERS).
80
80
 
81
+ ## Further reading
82
+
83
+ * [Limits of static SQL analysis and dialect support](STATIC_ANALYSIS_LIMITS.md)
84
+
81
85
  ## Reporting vulnerabilities
82
86
 
83
87
  See [`SECURITY.md`](../../SECURITY.md).
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -87,6 +87,7 @@ def test_policy_reference_custom_rule():
87
87
 
88
88
 
89
89
  LINK = re.compile(r"\]\(([^)#\s]+)(#[^)]*)?\)")
90
+ REPO_BLOB = "https://github.com/Pratik653/SQLGuard_Ninjapy/blob/main/"
90
91
 
91
92
 
92
93
  @pytest.mark.parametrize("doc", DOCS, ids=lambda p: str(p.relative_to(ROOT)))
@@ -94,6 +95,11 @@ def test_relative_links_resolve(doc: Path):
94
95
  text = doc.read_text(encoding="utf-8")
95
96
  missing = []
96
97
  for target, _ in LINK.findall(text):
98
+ if target.startswith(REPO_BLOB):
99
+ # Absolute links into this repository must still point at real files.
100
+ if not (ROOT / target.removeprefix(REPO_BLOB)).exists():
101
+ missing.append(target)
102
+ continue
97
103
  if re.match(r"^[a-z]+:", target):
98
104
  continue # external URL
99
105
  if not (doc.parent / target).resolve().exists():
@@ -128,3 +134,10 @@ def test_readme_privacy_yaml_fragment_is_valid():
128
134
  for role, spec in fragment["roles"].items():
129
135
  for label, access in spec["labels"].items():
130
136
  assert full["roles"][role]["labels"][label] == access
137
+
138
+
139
+ def test_readme_has_no_relative_links():
140
+ """The README is the PyPI long description; relative links break there (0.1.0 defect)."""
141
+ text = (ROOT / "README.md").read_text(encoding="utf-8")
142
+ relative = [t for t, _ in LINK.findall(text) if not re.match(r"^[a-z]+:", t)]
143
+ assert relative == []
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"
File without changes