Skip to content

audit: full patch set C1-C10, H1-H8, H12, H14-H16, M6-M9, M13-M21 #43

audit: full patch set C1-C10, H1-H8, H12, H14-H16, M6-M9, M13-M21

audit: full patch set C1-C10, H1-H8, H12, H14-H16, M6-M9, M13-M21 #43

Workflow file for this run

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