{"id":49313364,"url":"https://github.com/pawansingh3889/sql-guard","last_synced_at":"2026-04-26T14:00:45.038Z","repository":{"id":349891496,"uuid":"1204360599","full_name":"Pawansingh3889/sql-guard","owner":"Pawansingh3889","description":"Fast rule-based SQL linter on PyPI (sql-sop). 38 rules, 149 tests, libCST injection scanner, SARIF output, browser playground. Pre-commit hook + GitHub Action. 500+ monthly downloads.","archived":false,"fork":false,"pushed_at":"2026-04-26T12:07:12.000Z","size":160,"stargazers_count":1,"open_issues_count":8,"forks_count":4,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T14:00:36.630Z","etag":null,"topics":["cli","code","code-q","developer-tools","github-actions","linter","pre-c","python","sql","sql-lint"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/sql-sop/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Pawansingh3889.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":"GOVERNANCE.md","roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-08T00:04:57.000Z","updated_at":"2026-04-26T12:21:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"755d879a-9c3e-4cb9-b95d-7e59e76b78e5","html_url":"https://github.com/Pawansingh3889/sql-guard","commit_stats":null,"previous_names":["pawansingh3889/sql-guard"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/Pawansingh3889/sql-guard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pawansingh3889%2Fsql-guard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pawansingh3889%2Fsql-guard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pawansingh3889%2Fsql-guard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pawansingh3889%2Fsql-guard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Pawansingh3889","download_url":"https://codeload.github.com/Pawansingh3889/sql-guard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pawansingh3889%2Fsql-guard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32299644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T09:34:17.070Z","status":"ssl_error","status_checked_at":"2026-04-26T09:34:00.993Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","code","code-q","developer-tools","github-actions","linter","pre-c","python","sql","sql-lint"],"created_at":"2026-04-26T14:00:32.625Z","updated_at":"2026-04-26T14:00:45.030Z","avatar_url":"https://github.com/Pawansingh3889.png","language":"Python","readme":"# sql-sop\n\n[![CI](https://github.com/Pawansingh3889/sql-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/Pawansingh3889/sql-guard/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/sql-sop)](https://pypi.org/project/sql-sop/)\n[![Python](https://img.shields.io/pypi/pyversions/sql-sop)](https://pypi.org/project/sql-sop/)\n[![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)\n[![Marketplace](https://img.shields.io/badge/GitHub%20Marketplace-sql--sop-a07aff?logo=githubactions\u0026logoColor=white)](https://github.com/marketplace/actions/sql-sop)\n[![Playground](https://img.shields.io/badge/playground-try%20online-a07aff)](https://pawansingh3889.github.io/sql-guard/)\n[![Downloads](https://img.shields.io/pypi/dm/sql-sop?color=a07aff)](https://pypi.org/project/sql-sop/)\n[![codecov](https://codecov.io/gh/Pawansingh3889/sql-guard/branch/main/graph/badge.svg)](https://codecov.io/gh/Pawansingh3889/sql-guard)\n## Links\n- [GitHub](https://github.com/Pawansingh3889/sql-guard)\n- [PyPI](https://pypi.org/project/sql-sop/)\n- [Download Stats](https://pypistats.org/packages/sql-sop)\n- Install: `pip install sql-sop`\n- [Profile](https://github.com/Pawansingh3889)\n- **Contributing:** [`CONTRIBUTING.md`](CONTRIBUTING.md) · [`GOVERNANCE.md`](GOVERNANCE.md) · [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) · [`SECURITY.md`](SECURITY.md) · [`NOTICE`](NOTICE)\n\n## Why Does This Exist?\n\nOne bad SQL query can delete production data, expose customer records, or bring down a database. Most teams only find out after the damage is done. sql-sop catches dangerous patterns automatically — before the query ever runs — in 0.08 seconds.\n\n### Key Numbers\n\n| | |\n|---|---|\n| Rules | 38 (10 errors, 23 warnings, 5 Python-source) |\n| Tests | 149 |\n| Coverage | 86% |\n| Scan speed | 0.08s across 200 files |\n| PyPI downloads | 500+/month |\n| Version | 0.6.1 |\n\n### Fluent API (v0.2.0)\n\n```python\nfrom sql_guard import SqlGuard\n\nresult = SqlGuard().enable(\"E001\", \"W001\").scan(\"DELETE FROM users\")\nprint(result.passed)    # False\nprint(result.summary()) # \"1 error, 0 warnings in 1 statement\"\n```\n\n---\n\nFast, rule-based SQL linter. 38 rules (33 SQL + 5 Python), including 5 T-SQL-specific rules for SQL Server shops. Inline disable, project config, git-changed-only mode, and SARIF output for GitHub Code Scanning. 500+ monthly downloads on PyPI.\n\nCatches dangerous SQL before it reaches production -- DELETE without WHERE, UPDATE without WHERE, SQL injection patterns, SELECT *, and 20 more. Runs as a **CLI tool**, **pre-commit hook**, and **GitHub Action**.\n\nUsed in production data pipelines to lint SQL before it reaches manufacturing ERP databases. Prevents dangerous patterns like DELETE without WHERE from running against production SI Integreater tables.\n\nFor deeper AI-powered analysis, pair with [SQL Ops Reviewer](https://github.com/Pawansingh3889/sql-ops-reviewer).\n\n---\n\n## Quick start\n\n**If sql-sop catches a real bug for you, a GitHub star is the easiest way\nto help — it makes the project more discoverable for people with the same\nproblem.**\n\n```bash\npip install sql-sop\nsql-sop check .\n\n# Also scan .py files for SQL hazards in execute()/read_sql() calls:\npip install \"sql-sop[python]\"\nsql-sop check . --include-python\n```\n\n```\nqueries/create_orders.sql\n  L3:  ERROR [E001] DELETE without WHERE clause -- this will delete all rows\n         -\u003e Add a WHERE clause to limit affected rows\n  L7:  WARN  [W001] SELECT * -- specify columns explicitly\n         -\u003e Replace with: SELECT col1, col2, col3 FROM ...\n\nFound 2 issues (1 error, 1 warning) in 1 file (0.001s)\n```\n\n## The two-layer SQL quality pipeline\n\nMost teams have **no SQL review process**. Some use an AI linter. The problem: AI is slow, expensive, and overkill for catching `DELETE FROM users;`.\n\nsql-sop and SQL Ops Reviewer solve this together:\n\n```\n                    ┌─────────────────────────────────────┐\n                    │         YOUR SQL FILE                │\n                    └──────────────┬──────────────────────┘\n                                   │\n          ┌────────────────────────┼────────────────────────┐\n          │                        │                        │\n          ▼                        │                        │\n   LAYER 1: PRE-COMMIT             │              LAYER 2: CI\n   ─────────────────               │              ──────────\n   sql-guard                       │              SQL Ops Reviewer\n                                   │\n   When: before every commit       │              When: on every PR\n   Speed: \u003c0.2 seconds             │              Speed: 10-40 seconds\n   How: regex pattern matching     │              How: Ollama LLM analysis\n   Needs: nothing (pure Python)    │              Needs: 4-7 GB (AI model)\n   Catches: 80% of issues          │              Catches: remaining 20%\n                                   │\n   ✓ DELETE without WHERE          │              ✓ wrong JOIN type\n   ✓ SELECT *                      │              ✓ business logic errors\n   ✓ SQL injection patterns        │              ✓ schema-aware suggestions\n   ✓ missing LIMIT                 │              ✓ cross-table consistency\n   ✓ DROP without IF EXISTS        │              ✓ performance rewrites\n          │                        │                        │\n          ▼                        │                        ▼\n   commit blocked or passes        │              PR comment with findings\n          │                        │                        │\n          └────────────────────────┼────────────────────────┘\n                                   │\n                                   ▼\n                         CLEAN SQL IN PRODUCTION\n```\n\n**Layer 1 (sql-guard)** is a smoke detector -- always on, instant, catches fire fast.\n**Layer 2 (SQL Ops Reviewer)** is a fire inspector -- thorough, comes on every PR.\n\nYou want both.\n\n---\n\n## Set up the full pipeline (5 minutes)\n\n### Step 1: Pre-commit hook (Layer 1)\n\n```yaml\n# .pre-commit-config.yaml\nrepos:\n  - repo: https://github.com/Pawansingh3889/sql-guard\n    rev: v0.5.0\n    hooks:\n      - id: sql-guard\n        args: [--severity, error]  # only block on errors locally\n```\n\n```bash\npip install pre-commit\npre-commit install\n```\n\nNow every `git commit` with `.sql` changes runs sql-guard automatically. Errors block the commit. Warnings are shown but don't block.\n\n### Step 2: GitHub Actions (Layer 1 + Layer 2)\n\n```yaml\n# .github/workflows/sql-quality.yml\nname: SQL Quality\non:\n  pull_request:\n    paths: ['**/*.sql']\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  # Layer 1: fast rule check (~2 seconds)\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: Pawansingh3889/sql-guard@v1\n        with:\n          severity: warning\n\n  # Layer 2: deep AI review (~30 seconds, runs after lint passes)\n  review:\n    needs: lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: Pawansingh3889/sql-ops-reviewer@v1\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n```\n\nThat's it. Two files. Every SQL change gets:\n1. Instant rule-based lint (sql-guard)\n2. Deep AI review with fix suggestions (SQL Ops Reviewer)\n\n### Step 3 (optional): CLI for manual checks\n\n```bash\npip install sql-sop\n\nsql-sop check .                          # scan current directory\nsql-sop check queries/ --severity error  # errors only\nsql-sop check . --fail-fast              # stop on first error\nsql-sop check . --disable E002 W008      # skip specific rules\nsql-sop list-rules                       # show every registered rule\n```\n\n---\n\n## Rules\n\n### Errors (block commit by default)\n\n| ID | Name | What it catches |\n|---|---|---|\n| E001 | `delete-without-where` | `DELETE FROM orders;` -- deletes all rows |\n| E002 | `drop-without-if-exists` | `DROP TABLE users;` -- fails if table missing |\n| E003 | `grant-revoke` | `GRANT SELECT ON users TO public;` -- privilege escalation |\n| E004 | `string-concat-in-where` | `WHERE id = '' + @input` -- SQL injection |\n| E005 | `insert-without-columns` | `INSERT INTO t VALUES (...)` -- breaks on schema change |\n| E006 | `update-without-where` | `UPDATE orders SET status = 'x';` -- overwrites every row |\n| E007 | `alter-add-not-null-no-default` | `ALTER TABLE t ADD c INT NOT NULL;` -- locks table for full rewrite |\n| E008 | `drop-column` | `ALTER TABLE t DROP COLUMN c;` -- irreversible, breaks subscribers |\n\n### Warnings (advisory by default)\n\n| ID | Name | What it catches |\n|---|---|---|\n| W001 | `select-star` | `SELECT * FROM users` -- pulls unnecessary columns |\n| W002 | `missing-limit` | Unbounded SELECT -- could return millions of rows |\n| W003 | `function-on-column` | `WHERE YEAR(date) = 2024` -- kills index usage |\n| W004 | `missing-alias` | JOIN without table aliases -- hard to read |\n| W005 | `subquery-in-where` | `WHERE x IN (SELECT ...)` -- often slower than JOIN |\n| W006 | `orderby-without-limit` | ORDER BY without LIMIT -- sorts entire result |\n| W007 | `hardcoded-values` | `WHERE amount \u003e 10000` -- use parameters |\n| W008 | `mixed-case-keywords` | `select ... FROM` -- inconsistent casing |\n| W009 | `missing-semicolon` | Statement not terminated with `;` |\n| W010 | `commented-out-code` | `-- SELECT * FROM old_table` -- use version control |\n| W016 | `not-in-with-subquery` | `WHERE id NOT IN (SELECT ...)` -- silently returns 0 rows on NULL |\n| W017 | `leading-wildcard-like` | `WHERE name LIKE '%smith'` -- non-SARGable, full scan |\n| W018 | `or-across-columns` | `WHERE a = 1 OR b = 2` -- defeats single-column indexes |\n| W020 | `truncate-table` | `TRUNCATE TABLE staging;` -- bypasses triggers, resets identity |\n\n### T-SQL (v0.5.0+)\n\nRules targeting SQL Server anti-patterns common in legacy stored procs\nand SSRS datasets. Fire on text patterns that do not appear in BigQuery\nor Postgres code, so they run unconditionally with near-zero false\npositives on non-T-SQL input.\n\n| ID | Name | What it catches |\n|---|---|---|\n| T001 | `with-nolock` | `SELECT * FROM t WITH (NOLOCK)` -- dirty reads |\n| T002 | `xp-cmdshell` | `EXEC xp_cmdshell ...` -- shell-exec surface |\n| T003 | `cursor-declaration` | `DECLARE c CURSOR FOR ...` -- row-by-row processing |\n| T004 | `deprecated-outer-join` | `WHERE a.x *= b.y` -- removed in SQL Server 2012+ |\n| T005 | `create-index-without-online` | `CREATE INDEX ix ON t (...)` -- locks table; add `WITH (ONLINE = ON)` |\n\n### Python scanning (v0.4.0+, opt-in)\n\nEnable with `pip install \"sql-sop[python]\"` and `--include-python`. Uses\nlibCST to walk Python source and extract SQL strings from `.execute()`,\n`.read_sql()`, `sqlalchemy.text(...)` calls and `sql =`/`query =` style\nassignments. Then applies every rule above, plus four that only make\nsense at the Python level:\n\n| ID | Name | What it catches |\n|---|---|---|\n| P001 | `fstring-in-execute` | `cursor.execute(f\"... {user_input}\")` -- SQL injection |\n| P002 | `concat-in-execute` | `cursor.execute(\"...\" + user_input)` -- SQL injection |\n| P003 | `format-in-execute` | `.format()` or `%` interpolation into an execute call |\n| P004 | `bare-variable-in-execute` | `cursor.execute(query)` where `query` is an unchecked variable |\n| P005 | `sqlalchemy-text-fstring` | `sqlalchemy.text(f\"... {var}\")` -- SQL injection on the SQLAlchemy text() surface |\n\n---\n\n## Configuration\n\n### Disable specific rules\n\n```bash\nsql-sop check . --disable E002 W008 W010\n```\n\n### Project config file (`.sql-guard.yml`)\n\nDrop a `.sql-guard.yml` (or `.sql-guard.yaml`) at the repo root. The loader walks up from the current directory; CLI flags merge with and override these settings.\n\n```yaml\ndisable:\n  - W005\n  - T001\nignore:\n  - migrations/legacy/\n  - vendor/\ninclude_python: true\nseverity: warning\n```\n\n### Inline disable comments\n\nSilence a known false positive on a single line, no project-wide override needed:\n\n```sql\nSELECT * FROM lookups; -- sql-guard: disable=W001\nSELECT * FROM users  -- sql-guard: disable=W001,W002\nWHERE name LIKE '%smith';\n\n-- sql-guard: disable-next-line=W017\nSELECT * FROM events WHERE name LIKE '%checkout';\n```\n\nA bare `-- sql-guard: disable` (no equals sign) silences every rule on the line. The same directives work in Python with `#` instead of `--`.\n\n### Lint only changed files\n\nFor pre-commit and CI on big repos:\n\n```bash\nsql-sop check . --changed-only                      # working tree\nsql-sop check . --changed-only --changed-base main  # vs a branch ref\n```\n\nFalls back to a full scan with a warning when not in a git repo.\n\n### Severity filtering\n\n```bash\nsql-sop check . --severity error    # only show errors\nsql-sop check . --severity warning  # show everything (default)\n```\n\n### Fail fast\n\n```bash\nsql-sop check . --fail-fast  # stop after first error found\n```\n\n### SARIF output for GitHub Code Scanning\n\nRender findings inline on PRs in the GitHub Files Changed view:\n\n```bash\nsql-sop check . --format sarif --output results.sarif\n```\n\nIn a GitHub Actions workflow:\n\n```yaml\n- run: sql-sop check . --format sarif --output sql-guard.sarif\n- uses: github/codeql-action/upload-sarif@v3\n  with:\n    sarif_file: sql-guard.sarif\n```\n\n---\n\n## Performance\n\nsql-guard is designed to be fast:\n\n- **Compiled regex** -- patterns compiled once at startup, reused per file\n- **Two-pass scanning** -- single-line rules run first (8 of 20 SQL rules), multi-line parsing only when needed\n- **Line-by-line streaming** -- files read line by line, not loaded entirely into memory\n- **Early exit** -- `--fail-fast` stops on first error\n\n```\nBenchmark: 200 SQL files, 20 SQL rules\n  sql-guard:  0.08 seconds\n  sqlfluff:   45 seconds (560x slower)\n```\n\n---\n\n## Production Use Case\n\nIn a fish production environment, sql-sop runs as a pre-commit hook on all SQL that touches ERP data (RunNumber, OCM_TRANS, OCM_PLU, OCM_TOTALS tables). Combined with read-only database users and Docker isolation, it forms part of a 6-layer safety architecture that prevents accidental writes to the production ERP.\n\n---\n\n## How it compares\n\n| | sql-sop | sqlfluff | sql-lint |\n|---|---|---|---|\n| Rules | 31 (focused) | 800+ (comprehensive) | ~20 |\n| Speed | \u003c0.1s for 200 files | 45s for 200 files | ~2s |\n| Config needed | Zero | Extensive | Minimal |\n| Language | Python | Python | JavaScript |\n| Pre-commit | Yes | Yes | No |\n| GitHub Action | Yes | Community | No |\n| AI integration | Pairs with SQL Ops Reviewer | No | No |\n\nsql-sop is not a replacement for sqlfluff. It's a fast first pass that catches 80% of real issues with zero setup. If you need dialect-specific formatting and 800 rules, use sqlfluff. If you want instant feedback on dangerous SQL, use sql-guard.\n\n---\n\n## Contributing\n\n```bash\ngit clone https://github.com/Pawansingh3889/sql-guard.git\ncd sql-guard\npip install -e \".[dev]\"\npytest\n```\n\n### Adding a new rule\n\n1. Create a class in `sql_guard/rules/errors.py` or `warnings.py`\n2. Inherit from `Rule`, set `id`, `name`, `severity`, `description`\n3. Override `check_line()` for single-line rules or `check_statement()` for multi-line\n4. Add to `ALL_RULES` in `sql_guard/rules/__init__.py`\n5. Add a test in `tests/test_rules.py`\n6. Add a trigger case in `tests/fixtures/`\n\n```python\nclass MyNewRule(Rule):\n    id = \"W011\"\n    name = \"my-rule\"\n    severity = \"warning\"\n    description = \"What this rule catches\"\n    multiline = False\n\n    _pattern = Rule._compile(r\"your regex here\")\n\n    def check_line(self, line, line_number, file):\n        if self._pattern.search(line):\n            return Finding(\n                rule_id=self.id,\n                severity=self.severity,\n                file=file,\n                line=line_number,\n                message=\"What went wrong\",\n                suggestion=\"How to fix it\",\n            )\n        return None\n```\n\nPRs welcome. Keep rules simple, keep patterns fast.\n\n---\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpawansingh3889%2Fsql-guard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpawansingh3889%2Fsql-guard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpawansingh3889%2Fsql-guard/lists"}