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.
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/CHANGELOG.md +20 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/PKG-INFO +27 -24
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/README.md +26 -23
- sqlguard_ninja-0.1.1/benchmarks/datasets/external/README.md +28 -0
- sqlguard_ninja-0.1.1/docs/guides/BENCHMARK_BIAS_REVIEW.md +74 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/BENCHMARK_METHODOLOGY.md +2 -0
- sqlguard_ninja-0.1.1/docs/releases/v0.1.1.md +29 -0
- sqlguard_ninja-0.1.1/docs/security/STATIC_ANALYSIS_LIMITS.md +49 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/security/THREAT_MODEL.md +4 -0
- sqlguard_ninja-0.1.1/src/sqlguard_ninja/_version.py +1 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_docs.py +13 -0
- sqlguard_ninja-0.1.0/src/sqlguard_ninja/_version.py +0 -1
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/.gitignore +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/LICENSE +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/THIRD_PARTY_NOTICES.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/baselines/native.json +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/01_destructive.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/02_multi_statement_and_formatting.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/03_table_and_column_access.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/04_privacy.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/05_tenant_isolation.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/06_cte_nested_unicode.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/07_legitimate_lookalike_cost_dialect.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/grant_matrix.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/label_matrix.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/tenant_matrix.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/transforms.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/manifest.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/policies/retail_saas.yaml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/agent-sql-guard.json +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/agent-sql-guard.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/krish2105-sqlguard.json +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/krish2105-sqlguard.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/native.json +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/native.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/sql-guardrail.json +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/sql-guardrail.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/architecture/ARCHITECTURE_DECISIONS.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/benchmarks/RESULTS_v1.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/AGENT_INTEGRATION.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/CI_INTEGRATION.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/POLICY_REFERENCE.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/guides/QUERY_COST.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/release/FINAL_TECHNICAL_AUDIT.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/releases/v0.1.0.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/docs/research/COMPETITIVE_ANALYSIS.md +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/_shared.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/fastapi/app.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/github-actions/sql-security.yml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/langchain/guarded_tool_demo.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/langgraph/real_llm_agent.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/langgraph/sql_agent_demo.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/postgresql/plan_inspection_demo.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/sdk/quickstart.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/examples/sqlite/text_to_sql_demo.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/pyproject.toml +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/adapters.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/builtin.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/compare.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/dataset.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/fixtures.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/metrics.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/runner.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/translate.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/_protocol.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/agent_sql_guard_worker.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/krish_sqlguard_worker.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/sql_guardrail_worker.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/config.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/main.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/cli/templates.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/analysis.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/findings.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/guard.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/access.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/base.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/catalog.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/functions.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/statements.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/structure.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/core/rules/tenant.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/audit.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/checkpoint.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/executors.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/fastapi.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/langchain.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/integrations/langgraph.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/policy/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/policy/model.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/privacy/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/privacy/rules.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/privacy/suggest.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/py.typed +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/query_cost/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/query_cost/postgres.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/query_cost/static.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/reporting/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/reporting/markdown.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/conftest.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_corpus_sqlite_validation.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_external_adapters.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_frameworks.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_postgres.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/pg_support.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/property/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/property/test_guard_properties.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/security/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/security/test_agent_boundaries.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/security/test_corpus_native.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/__init__.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/helpers.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_benchmark_engine.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_cli.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_dataset.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_function_policy_mechanics.py +0 -0
- {sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/unit/test_policy.py +0 -0
- {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.
|
|
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)
|
|
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
|
-
* [
|
|
346
|
-
* [
|
|
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)
|
|
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
|
-
* [
|
|
290
|
-
* [
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/01_destructive.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/cases/05_tenant_isolation.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/grant_matrix.yaml
RENAMED
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/label_matrix.yaml
RENAMED
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/tenant_matrix.yaml
RENAMED
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/datasets/v1/generated/transforms.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/agent-sql-guard.json
RENAMED
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/krish2105-sqlguard.json
RENAMED
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/benchmarks/results/v1.0.0/krish2105-sqlguard.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/src/sqlguard_ninja/benchmarking/workers/_protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlguard_ninja-0.1.0 → sqlguard_ninja-0.1.1}/tests/integration/test_corpus_sqlite_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|