Quatre taches de la matrice du rapport, toutes sans dependance, qu aucune liste de « ce qui reste » ne reprenait. OPS-003 — app/.env.exemple disait « copiez ce fichier et remplissez les valeurs pour la production », puis posait FLASK_DEBUG=true, SESSION_COOKIE_SECURE=false et FORCE_HTTPS=false. Le debogueur Werkzeug execute du code soumis par le navigateur : cette ligne transformait un copier-coller en shell distant. Chaque valeur est desormais sure a la copie, et le fichier refuse de demarrer tant que les deux secrets obligatoires ne sont pas remplis plutot que de demarrer grand ouvert. Renomme en .env.example : l orthographe francaise ne correspondait pas a l exception !.env.example du .gitignore, donc le fichier n etait suivi que par accident de l ordre des regles. Les deux points de la decision ouverte du §8 tombent d un seul git mv. OPS-002 — trusted_proxy='*' et HOST ne sont plus soudes dans wsgi.py. Les defauts sont **inchanges**, deliberement : choisir sans connaitre la topologie coupe la prod si nginx est ailleurs, ou casse la limitation de debit pour tout le monde si on cesse de croire X-Forwarded-For alors que c etait la seule source d adresses. Ce sont maintenant des variables, les valeurs sures sont dans .env.example pour un nouveau deploiement, et docs/deployment.md donne les quatre topologies avec la valeur de chacune. wsgi.py avertit au demarrage tant que les deux defauts sont en place. Le commentaire de HOST annoncait « bind to localhost by default » a cote d un defaut a 0.0.0.0 : il decrivait l intention pendant que le code faisait l inverse. Il dit maintenant ce qu il fait. QUA-004 — Font Awesome et FullCalendar etaient charges sans empreinte, depuis des hotes que la CSP autorise nommement. Qui controle ces CDN controlait ce qui s execute sur chaque page. Empreintes posees, avec ce que SRI promet et ce qu il ne promet pas ecrit a cote : ca fige le fichier, ca ne prouve pas qu il etait honnete au moment du calcul. **Le CSS de FullCalendar n existait pas.** La v6 embarque ses styles dans le JS et ce fichier n est pas publie : le <link> repondait 404 a chaque ouverture du calendrier depuis la montee de version. Une feuille de style en echec est silencieuse dans le navigateur, c est ce qui l a fait durer. CI-003 — actions epinglees sur un commit, version en commentaire, dans les deux forges. Un tag est un pointeur mobile : deplacer v4 fait executer du code arbitraire dans le job qui detient la cle SSH de production. Ce job recoit aussi enfin un bloc permissions. 517 tests.
122 lines
4.2 KiB
YAML
122 lines
4.2 KiB
YAML
name: CI - Security & Lint
|
|
|
|
on:
|
|
push:
|
|
branches: [main, master]
|
|
pull_request:
|
|
branches: [main, master]
|
|
workflow_dispatch: # Allow manual triggers
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# Least privilege: nothing here writes back to the repository.
|
|
permissions:
|
|
contents: read
|
|
|
|
# Third-party actions are pinned to a commit, with the version in a comment
|
|
# (CI-003). A tag is a moving pointer: whoever can move `v4` runs code in a
|
|
# job that holds this repository's token. The comment is what makes the pin
|
|
# maintainable — a bare 40-character hash tells a reader nothing about
|
|
# whether it is current. Dependabot updates both together.
|
|
|
|
env:
|
|
PYTHON_VERSION: '3.12'
|
|
|
|
jobs:
|
|
security-audit:
|
|
name: Security Audit
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install pip-audit
|
|
run: pip install pip-audit==2.9.0
|
|
|
|
# Previously: `pip-audit --require-hashes --no-deps || pip-audit`.
|
|
# Neither form named the requirements file, so the fallback audited the
|
|
# runner's environment — which contained pip-audit and nothing else.
|
|
# The job passed green while checking none of the application's
|
|
# dependencies. -r makes it audit what the application actually pins.
|
|
- name: Scan declared dependencies for known vulnerabilities
|
|
run: pip-audit -r requirements.txt
|
|
|
|
lint:
|
|
name: Lint with Ruff
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
|
|
- name: Install ruff
|
|
run: pip install ruff==0.14.4
|
|
|
|
# Rule selection and per-file ignores live in pyproject.toml. Before it
|
|
# existed, this step ran ruff's bare defaults with no configuration.
|
|
- name: Run ruff linter
|
|
run: ruff check . --output-format=github
|
|
|
|
# Enabled now that the repository has been formatted once, in its own
|
|
# commit (QUA-002). Reaching this step before that would have failed on
|
|
# 72 of 76 files for reasons unrelated to correctness.
|
|
- name: Check formatting
|
|
run: ruff format --check .
|
|
|
|
security-scan:
|
|
name: Security Scan
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install app dependencies
|
|
run: pip install -r requirements.txt -r requirements-dev.txt
|
|
|
|
# The path was `security_scan.py`, but the script lives under
|
|
# app/supporting_scripts/. The step had therefore failed on every run
|
|
# since the file was moved.
|
|
- name: Run security scan
|
|
env:
|
|
SECRET_KEY: ${{ secrets.CI_SECRET_KEY || 'test-key-not-for-production-1234567890' }}
|
|
FLASK_DEBUG: 'false'
|
|
run: python app/supporting_scripts/security_scan.py --skip-http
|
|
|
|
test:
|
|
name: Tests
|
|
runs-on: ubuntu-latest
|
|
needs: [security-audit, lint]
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
cache: 'pip'
|
|
|
|
- name: Install dependencies
|
|
run: pip install -r requirements.txt -r requirements-dev.txt
|
|
|
|
# Previously an `echo` guarded by continue-on-error: the job reported
|
|
# success without executing anything. The suite needs no environment
|
|
# variables and no database server: create_app() takes its configuration
|
|
# as an argument and the fixtures use a temporary SQLite file.
|
|
- name: Run tests
|
|
run: pytest --cov=app --cov-report=term-missing --cov-report=xml
|