sql-sop is a rule-based SQL linter published on PyPI. Contributions are welcome; the rules below keep the project fast, stable, and trustworthy to downstream users.
Before you start, skim:
GOVERNANCE.md— roles, first-PR-wins, why the licence will stay MIT.CODE_OF_CONDUCT.md— behavioural bar.SECURITY.md— false-negative / bypass reports go there, not in a public issue.
Every rule has tests that cover both "fires on bad SQL" and "does
not fire on safe SQL". Rule IDs are public API — E001, W001, etc.
appear in downstream users' pre-commit configs and CI badges. Don't
renumber, don't silently change default severity, don't remove
without a deprecation window.
git clone https://github.com/YOUR_USERNAME/sql-guard.git
cd sql-guard
pip install -e ".[python]"
pytest -q # 78 tests across SQL + Python scanning
ruff check .Try the CLI locally:
sql-sop check tests/fixtures/errors.sql- Find or open an issue. Watch labels
good first issue,help wanted,new-rule. - Claim it by commenting — 7-day soft claim per
GOVERNANCE.md. - Branch.
feature/<short-name>orbugfix/<short-name>. - Code + test.
- Before pushing:
ruff check .,pytest -q. - Open the PR. Conventional commit style, one logical change per commit.
The common case. Walkthrough:
- Pick the next unused ID in the appropriate family (
E00Nfor errors,W0NNfor warnings,S00Nfor structural,P00Nfor Python-scanner rules). - Add the class to the matching file under
sql_guard/rules/. - Register it in
sql_guard/rules/__init__.py(import + append toALL_RULES). - Add a fixture line to
tests/fixtures/errors.sqlortests/fixtures/warnings.sql(or create a new fixture if the pattern is complex). - Add at least two tests in
tests/test_rules.py:- fires on the new fixture line
- does not fire on a tempfile with safe SQL (use the
tmp_pathfixture — seetest_e006_update_with_where_okfor the pattern)
- Update the README rule table and the "Rules" count in the Key Numbers block.
Rule naming conventions:
- kebab-case, verb-oriented (
delete-without-where, notdeleteWithoutWhereorcheck-delete) - message tells the user what's wrong in one line
- suggestion tells them what to do instead
- Python 3.10+.
- Line length 100.
- Type hints on public API.
- Every rule class has a docstring citing the SQL hazard it catches.
This is a breaking change for downstream users. Process:
- Open an issue naming the rule, the reason for removal, and the deprecation window (one minor version minimum).
- Land a warning in the rule's output pointing at the replacement.
- After the deprecation window, the removal lands in a new minor
version with the change in
README.md.
Open an issue with:
- Exact SQL that triggers or fails to trigger
- Rule ID involved (
E001,W001, etc.) - sql-sop version (
sql-sop --version) - Python version, OS
Open an issue describing:
- What SQL pattern should be caught (or stop being caught)
- Why it's dangerous / safe
- Examples of the pattern in real code
Merged PRs land in the git history permanently. The README credits substantial contributors when appropriate.