audit: full patch set C1-C10, H1-H8, H12, H14-H16, M6-M9, M13-M21 #43
Workflow file for this run
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: WILLIAM OS CI/CD | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| PYTHON_VERSION: "3.12" | |
| NODE_VERSION: "20" | |
| jobs: | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Install Python deps | |
| working-directory: backend | |
| run: pip install -e ".[dev]" | |
| - name: Python vulnerability scan (fail on high) | |
| working-directory: backend | |
| run: | | |
| pip install pip-audit | |
| pip-audit --progress-spinner off --format json --output /tmp/pip-audit.json || true | |
| python - <<'PY' | |
| import json, re, sys | |
| def parse_score(v): | |
| if v is None: return None | |
| if isinstance(v, (int, float)): return float(v) | |
| if isinstance(v, str): | |
| m = re.search(r"([0-9]+(?:\.[0-9]+)?)", v) | |
| return float(m.group(1)) if m else None | |
| if isinstance(v, dict): | |
| for k in ("score", "cvss", "baseScore", "value"): | |
| s = parse_score(v.get(k)) | |
| if s is not None: return s | |
| if isinstance(v, list): | |
| for i in v: | |
| s = parse_score(i) | |
| if s is not None: return s | |
| return None | |
| with open("/tmp/pip-audit.json") as fh: | |
| report = json.load(fh) | |
| rows = report.get("dependencies", report if isinstance(report, list) else []) | |
| high = [] | |
| for dep in rows: | |
| for vuln in dep.get("vulns", []): | |
| score = None | |
| for key in ("severity", "cvssv3", "cvss_score", "score"): | |
| score = parse_score(vuln.get(key)) | |
| if score is not None: break | |
| if score is not None and score >= 7.0: | |
| high.append((dep.get("name", "?"), vuln.get("id", "?"), score)) | |
| if high: | |
| for p, vid, s in high: print(f"- {p}: {vid} (cvss={s})") | |
| sys.exit(1) | |
| print("No high severity Python vulnerabilities.") | |
| PY | |
| - name: Frontend vulnerability scan | |
| working-directory: frontend/web | |
| run: | | |
| npm ci | |
| npm audit --audit-level=high | |
| - name: Ruff lint | |
| working-directory: backend | |
| run: ruff check . | |
| - name: Ruff format check | |
| working-directory: backend | |
| run: ruff format --check . | |
| - name: MyPy | |
| working-directory: backend | |
| run: mypy app/ --ignore-missing-imports | |
| test: | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: william | |
| POSTGRES_PASSWORD: william | |
| POSTGRES_DB: williamos_test | |
| ports: ["5432:5432"] | |
| options: >- | |
| --health-cmd "pg_isready -U william" | |
| --health-interval 10s --health-timeout 5s --health-retries 5 | |
| redis: | |
| image: redis:7-alpine | |
| ports: ["6379:6379"] | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s --health-timeout 5s --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install deps | |
| working-directory: backend | |
| run: pip install -e ".[dev]" | |
| - name: Create schemas | |
| run: PGPASSWORD=william psql -h localhost -U william -d williamos_test -f scripts/init-schemas.sql | |
| - name: Run tests | |
| working-directory: backend | |
| env: | |
| DATABASE_URL: postgresql+asyncpg://william:william@localhost:5432/williamos_test | |
| REDIS_URL: redis://localhost:6379/0 | |
| ENVIRONMENT: development | |
| JWT_SECRET_KEY: test-secret-key-at-least-32-bytes-long | |
| run: pytest tests/ -v --cov=app --cov-report=xml --cov-report=term-missing | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| file: backend/coverage.xml | |
| fail_ci_if_error: false | |
| security-baseline: | |
| runs-on: ubuntu-latest | |
| needs: test | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: william | |
| POSTGRES_PASSWORD: william | |
| POSTGRES_DB: williamos_security | |
| ports: ["5432:5432"] | |
| options: >- | |
| --health-cmd "pg_isready -U william" | |
| --health-interval 10s --health-timeout 5s --health-retries 5 | |
| redis: | |
| image: redis:7-alpine | |
| ports: ["6379:6379"] | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s --health-timeout 5s --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install | |
| working-directory: backend | |
| run: pip install -e ".[dev]" | |
| - name: Schemas | |
| run: PGPASSWORD=william psql -h localhost -U william -d williamos_security -f scripts/init-schemas.sql | |
| - name: Start API | |
| env: | |
| DATABASE_URL: postgresql+asyncpg://william:william@localhost:5432/williamos_security | |
| REDIS_URL: redis://localhost:6379/0 | |
| ENVIRONMENT: development | |
| JWT_SECRET_KEY: test-secret-key-at-least-32-bytes-long | |
| run: | | |
| cd backend | |
| nohup python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 > /tmp/api.log 2>&1 & | |
| echo $! > /tmp/api.pid | |
| - name: Wait for readiness | |
| run: | | |
| for i in $(seq 1 30); do | |
| curl -fsS http://localhost:8000/health >/dev/null 2>&1 && exit 0 | |
| sleep 2 | |
| done | |
| cat /tmp/api.log; exit 1 | |
| - name: Security baseline | |
| run: bash scripts/security-audit-basics.sh http://localhost:8000 | |
| - name: Stop API | |
| if: always() | |
| run: kill "$(cat /tmp/api.pid)" 2>/dev/null || true | |
| docker: | |
| runs-on: ubuntu-latest | |
| needs: security-baseline | |
| if: github.ref == 'refs/heads/main' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build backend image | |
| run: docker build -t william-os-backend:${{ github.sha }} ./backend |