🛡️ Sentinel: [MEDIUM] Fix SQL template injection risk #289
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Minimal CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| # Cancel in-progress runs for same PR | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| test: | |
| name: Tests | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ['3.10', '3.11', '3.12'] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install pytest-cov | |
| pip install -e ".[dev]" | |
| - name: Run tests | |
| run: | | |
| pytest -n0 tests/ -xvs --tb=short --cov=src --cov-report=xml | |
| env: | |
| PYTHONPATH: src | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@v4 | |
| if: matrix.python-version == '3.11' | |
| with: | |
| files: ./coverage.xml | |
| flags: unittests | |
| name: codecov-umbrella | |
| fail_ci_if_error: false | |
| security: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| - name: Install security tools | |
| run: | | |
| python -m pip install bandit safety pip-audit | |
| - name: Run Bandit (SAST) | |
| id: bandit | |
| run: | | |
| bandit -r src/ -f json -o bandit-report.json || true | |
| continue-on-error: true | |
| - name: Run Safety (Dependency Check) | |
| id: safety | |
| run: | | |
| safety check --json > safety-report.json || true | |
| continue-on-error: true | |
| - name: Run Pip-audit | |
| id: pip-audit | |
| run: | | |
| pip-audit --desc --format json > pip-audit-report.json || true | |
| continue-on-error: true | |
| - name: Parse security reports | |
| run: | | |
| python << 'EOF' | |
| import json | |
| import sys | |
| # Bandit | |
| try: | |
| with open('bandit-report.json') as f: | |
| bandit = json.load(f) | |
| print(f"Bandit: {bandit.get('metrics', {}).get('SEVERITY.HIGH', 0)} HIGH issues") | |
| except: | |
| pass | |
| # Safety | |
| try: | |
| with open('safety-report.json') as f: | |
| safety = json.load(f) | |
| vulns = safety.get('vulnerabilities', []) | |
| print(f"Safety: {len(vulns)} known vulnerabilities") | |
| except: | |
| pass | |
| # Pip-audit | |
| try: | |
| with open('pip-audit-report.json') as f: | |
| pip_audit = json.load(f) | |
| deps = pip_audit.get('dependencies', []) | |
| vuln_deps = [d for d in deps if d.get('vulnerabilities')] | |
| print(f"Pip-audit: {len(vuln_deps)} vulnerable packages") | |
| except: | |
| pass | |
| EOF | |
| - name: Upload security reports | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: security-reports | |
| path: | | |
| bandit-report.json | |
| safety-report.json | |
| pip-audit-report.json | |
| - name: Comment PR with security results | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const issue_number = context.issue.number; | |
| const fs = require('fs'); | |
| const parseJsonSafe = (filepath) => { | |
| try { | |
| if (fs.existsSync(filepath)) { | |
| const content = fs.readFileSync(filepath, 'utf8'); | |
| if (!content.trim()) return {}; | |
| const start = content.indexOf('{'); | |
| const end = content.lastIndexOf('}'); | |
| if (start !== -1 && end !== -1) { | |
| return JSON.parse(content.substring(start, end + 1)); | |
| } | |
| } | |
| } catch (e) { console.error(`Error parsing ${filepath}:`, e); } | |
| return {}; | |
| }; | |
| let body = '## đź”’ Security Scan Results\\n\\n'; | |
| body += '### Bandit (SAST)\\n'; | |
| body += `- ${(parseJsonSafe('bandit-report.json').metrics || {})['SEVERITY.HIGH'] || 0} HIGH severity issues\\n\\n`; | |
| body += '### Safety (Dependencies)\\n'; | |
| body += `- ${(parseJsonSafe('safety-report.json').vulnerabilities || []).length || 0} known vulnerabilities\\n\\n`; | |
| body += '### Pip-audit (Dependencies)\\n'; | |
| body += `- ${(parseJsonSafe('pip-audit-report.json').dependencies || []).filter(d => d.vulnerabilities?.length)?.length || 0} vulnerable packages\\n`; | |
| core.summary.addHeading('đź”’ Security Scan Results'); | |
| core.summary.addRaw(body); | |
| core.summary.write(); | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| lint: | |
| name: Code Quality | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| - name: Install linting tools | |
| run: | | |
| python -m pip install ruff black mypy | |
| - name: Run Ruff (Fast Python Linter) | |
| run: | | |
| ruff check src/ tests/ --output-format=github | |
| continue-on-error: true | |
| - name: Check formatting (Black) | |
| run: | | |
| black --check src/ tests/ --diff | |
| continue-on-error: true | |
| - name: Type checking (MyPy) | |
| run: | | |
| mypy src/ --ignore-missing-imports || true | |
| continue-on-error: true | |
| build: | |
| name: Build Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[gguf]" | |
| - name: Verify import | |
| run: | | |
| python -c "import ledgermind; print('âś“ Imports work')" | |
| - name: Create distribution | |
| run: | | |
| pip install build | |
| python -m build |