Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,57 @@
# SQLbase
An advanced and automated sql injector that replace the work from a 1:1 sandboxed operation to a real life one in just 1 simple command.
# SQLbase – Security Scanning Toolkit

Cross-platform (Linux, Windows, macOS) security scanning: SQL injection scanning, dynamic testing, code fixing, and remediation. Uses `pathlib` and portable paths throughout.

## Install

```bash
python -m venv .venv
# Linux/macOS:
.venv/bin/activate
# Windows:
.venv\Scripts\activate
pip install -r requirements.txt
pip install -e .
```

Or run without install (from repo root):

```bash
# Linux/macOS/Windows (same commands)
PYTHONPATH=. python -m sqlbase scan .
PYTHONPATH=. python -m sqlbase predict .
PYTHONPATH=. python -m sqlbase remediate SQL_INJECTION python
```

## Usage

```bash
# Scan path for SQL injection patterns (file or directory)
python -m sqlbase scan [path] [-o report.json] [--fail-on-findings]

# Predict vulnerability likelihood (heuristic/ML-ready)
python -m sqlbase predict [path]

# Get remediation for a vulnerability type and language
python -m sqlbase remediate SQL_INJECTION python
```

Programmatic use:

```python
from pathlib import Path
from sqlbase.scanner import SQLInjectionScanner
from sqlbase.tester import DynamicSQLiTester
from sqlbase.fixer import SqliCodeFixer
from sqlbase.remediation import RemediationKnowledgeBase
from sqlbase.injector import SecurityPatternInjector
from sqlbase.predictor import VulnerabilityPredictor

scanner = SQLInjectionScanner()
for v in scanner.scan_path(Path("src")):
print(v["file"], v["line"], v["type"])
```

## CI

GitHub Actions: `.github/workflows/security-scan.yml` runs on **ubuntu-latest**, **windows-latest**, and **macos-latest** on push/PR.
1 change: 1 addition & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# SQLbase – cross-platform security scanning toolkit
66 changes: 66 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
CLI entrypoint: python -m sqlbase [scan|predict|remediate] ...
Cross-platform: Linux, Windows, macOS.
"""
import argparse
import json
import sys
from pathlib import Path


def cmd_scan(args) -> int:
from sqlbase.scanner import SQLInjectionScanner
scanner = SQLInjectionScanner()
path = Path(args.path).resolve()
results = scanner.scan_path(path, extensions=args.extensions)
out = json.dumps(results, indent=2)
if args.output:
Path(args.output).write_text(out, encoding="utf-8")
print(f"Wrote {len(results)} findings to {args.output}", file=sys.stderr)
else:
print(out)
return 0 if not args.fail_on_findings or len(results) == 0 else 1


def cmd_predict(args) -> int:
from sqlbase.predictor import VulnerabilityPredictor
predictor = VulnerabilityPredictor()
result = predictor.predict_vulnerability_likelihood(Path(args.path))
print(json.dumps(result, indent=2))
return 0


def cmd_remediate(args) -> int:
from sqlbase.remediation import RemediationKnowledgeBase
kb = RemediationKnowledgeBase()
r = kb.get_remediation(args.type, args.language)
print(json.dumps(r, indent=2))
return 0


def main() -> int:
parser = argparse.ArgumentParser(description="SQLbase security toolkit")
sub = parser.add_subparsers(dest="command", required=True)
# scan
p_scan = sub.add_parser("scan", help="Scan path for SQL injection patterns")
p_scan.add_argument("path", nargs="?", default=".", help="File or directory to scan")
p_scan.add_argument("-o", "--output", metavar="FILE", help="Write JSON report to FILE (cross-platform path)")
p_scan.add_argument("--fail-on-findings", action="store_true", help="Exit 1 if any finding")
p_scan.add_argument("--extensions", nargs="+", default=[".py", ".java", ".js", ".ts", ".php", ".rb", ".go", ".cs"], help="File extensions")
p_scan.set_defaults(func=cmd_scan)
# predict
p_predict = sub.add_parser("predict", help="Predict vulnerability likelihood")
p_predict.add_argument("path", nargs="?", default=".", help="File or directory")
p_predict.set_defaults(func=cmd_predict)
# remediate
p_rem = sub.add_parser("remediate", help="Get remediation for vulnerability type + language")
p_rem.add_argument("type", help="e.g. SQL_INJECTION")
p_rem.add_argument("language", help="e.g. python, java")
p_rem.set_defaults(func=cmd_remediate)

args = parser.parse_args()
return args.func(args)


if __name__ == "__main__":
sys.exit(main())
65 changes: 65 additions & 0 deletions filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Security filter stubs for intercepting filter pattern. Cross-platform.
"""
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional


class BaseFilter(ABC):
@abstractmethod
def apply(self, data: Any) -> Any:
pass

@abstractmethod
def name(self) -> str:
pass


class SQLInjectionFilter(BaseFilter):
def name(self) -> str:
return "SQLi"

def apply(self, data: Any) -> Any:
if isinstance(data, str):
return data.replace("'", "''").replace("\\", "\\\\")
if isinstance(data, dict):
return {k: self.apply(v) for k, v in data.items()}
if isinstance(data, (list, tuple)):
return type(data)(self.apply(x) for x in data)
return data


class XSSFilter(BaseFilter):
def name(self) -> str:
return "XSS"

def apply(self, data: Any) -> Any:
if isinstance(data, str):
return (
data.replace("&", "&")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace('"', "&quot;")
.replace("'", "&#x27;")
)
if isinstance(data, dict):
return {k: self.apply(v) for k, v in data.items()}
if isinstance(data, (list, tuple)):
return type(data)(self.apply(x) for x in data)
return data


class CommandInjectionFilter(BaseFilter):
def name(self) -> str:
return "CommandInjection"

def apply(self, data: Any) -> Any:
if isinstance(data, str):
for char in [";", "|", "&", "$", "`", "\n", "\r"]:
data = data.replace(char, "")
return data
if isinstance(data, dict):
return {k: self.apply(v) for k, v in data.items()}
if isinstance(data, (list, tuple)):
return type(data)(self.apply(x) for x in data)
return data
51 changes: 51 additions & 0 deletions fixer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
SQL injection code fixer. Supports Java and Python. Cross-platform.
"""
import re
from typing import Optional


class SqliCodeFixer:
def fix_concatenation(self, code_snippet: str, language: str) -> str:
language = (language or "").strip().lower()
if language == "java":
return self.fix_java_sqli(code_snippet)
if language in ("python", "py"):
return self.fix_python_sqli(code_snippet)
return code_snippet

def fix_java_sqli(self, code: str) -> str:
# Pattern: String sql = "SELECT ..." + userInput;
pattern = r'(\w+)\s*=\s*["\'](SELECT\s+.*?)["\']\s*\+\s*(\w+)'
replacement = (
r'String \1 = "\2 ?";\n'
r'PreparedStatement stmt = conn.prepareStatement(\1);\n'
r'stmt.setString(1, \3);'
)
result = re.sub(pattern, replacement, code, flags=re.IGNORECASE)
# Also: "SELECT ..." + var + " ..."
pattern2 = r'["\'](SELECT\s+.*?)\s*\+\s*(\w+)\s*\+\s*["\'](.*?)["\']'
if not re.search(pattern2, result, re.IGNORECASE):
return result
result = re.sub(
pattern2,
r'"\1 ? \3"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, \2);',
result,
flags=re.IGNORECASE,
)
return result

def fix_python_sqli(self, code: str) -> str:
# cursor.execute("SELECT ... " + user_id)
pattern = r'\.(execute|executemany)\s*\(\s*["\']([^"\']*?)["\']\s*\+\s*(\w+)'
replacement = r'.\1("\2 %s", (\3,))'
result = re.sub(pattern, replacement, code)
# cursor.execute("SELECT ... %s" % user_id) -> parameterized
pattern2 = r'\.(execute|executemany)\s*\(\s*["\']([^"\']*?)["\']\s*%\s*(\w+)'
replacement2 = r'.\1("\2", (\3,))'
result = re.sub(pattern2, replacement2, result)
# f"SELECT ... {var}"
pattern3 = r'\.(execute|executemany)\s*\(\s*f["\']([^"\']*)\{(\w+)\}([^"\']*)["\']\s*\)'
replacement3 = r'.\1("\2%s\3", (\4,))'
result = re.sub(pattern3, replacement3, result)
return result
78 changes: 78 additions & 0 deletions injector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Security pattern injector: applies intercepting filters and injects filter calls.
Cross-platform: uses pathlib for Linux, Windows, macOS.
"""
from pathlib import Path
from typing import Dict, Optional

from sqlbase.filters import (
BaseFilter,
SQLInjectionFilter,
XSSFilter,
CommandInjectionFilter,
)


class FilterManager:
def __init__(self, filters: Dict[str, BaseFilter]) -> None:
self.filters = filters

def apply_all(self, data):
for _name, f in self.filters.items():
data = f.apply(data)
return data


class SecurityPatternInjector:
def __init__(self) -> None:
self.filter_manager: Optional[FilterManager] = None

def apply_intercepting_filter(self, project_path: str | Path) -> None:
filters: Dict[str, BaseFilter] = {
"SQLi": SQLInjectionFilter(),
"XSS": XSSFilter(),
"CommandInjection": CommandInjectionFilter(),
}
self.filter_manager = self.generate_filter_manager(filters)
self.inject_filter_calls(Path(project_path))

def generate_filter_manager(self, filters: Dict[str, BaseFilter]) -> FilterManager:
return FilterManager(filters)

def inject_filter_calls(self, project_path: Path) -> None:
project_path = Path(project_path).resolve()
if not project_path.exists():
return
# Write a small bootstrap module that projects can import to get FilterManager
bootstrap_dir = project_path / ".security_filters"
bootstrap_dir.mkdir(exist_ok=True)
bootstrap_file = bootstrap_dir / "filter_manager.py"
content = '''"""
Auto-generated security filter bootstrap. Cross-platform.
Import: from .security_filters.filter_manager import get_filter_manager
"""
from pathlib import Path

_filters = None

def get_filter_manager():
global _filters
if _filters is None:
from sqlbase.filters import SQLInjectionFilter, XSSFilter, CommandInjectionFilter
from sqlbase.injector import FilterManager
_filters = FilterManager({
"SQLi": SQLInjectionFilter(),
"XSS": XSSFilter(),
"CommandInjection": CommandInjectionFilter(),
})
return _filters

def apply_security_filters(data):
return get_filter_manager().apply_all(data)
'''
bootstrap_file.write_text(content, encoding="utf-8")
init_file = bootstrap_dir / "__init__.py"
init_file.write_text(
"from .filter_manager import get_filter_manager, apply_security_filters\n",
encoding="utf-8",
)
Loading