fix(ops): defauts surs a la copie, CDN epingles, actions epinglees
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.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# Team Tryouts — environment variables
|
||||
#
|
||||
# Copy to .env and fill in. Every value here is a PRODUCTION-SAFE default:
|
||||
# copying this file and changing nothing gives a locked-down configuration
|
||||
# that refuses to start until the two required secrets are set, rather than
|
||||
# a working one that happens to be wide open (OPS-003).
|
||||
#
|
||||
# The previous version shipped FLASK_DEBUG=true under a heading that said
|
||||
# "fill in the values for production". The Werkzeug debugger executes code
|
||||
# submitted through the browser, so that one line turned a copy-paste into a
|
||||
# remote shell.
|
||||
#
|
||||
# For local development, see the DEVELOPMENT block at the bottom.
|
||||
|
||||
# =============================================================================
|
||||
# Required — the application refuses to start without these
|
||||
# =============================================================================
|
||||
|
||||
# Generate with: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# Never reuse one between environments: this key signs session cookies, so
|
||||
# whoever holds it can forge a session for any account.
|
||||
SECRET_KEY=
|
||||
|
||||
# Expected form: postgresql://user:password@host:5432/database
|
||||
# The psycopg 3 driver is named for you by create_app(); postgresql:// alone
|
||||
# would send SQLAlchemy looking for psycopg2, which is not installed.
|
||||
DATABASE_URL=
|
||||
|
||||
# =============================================================================
|
||||
# Security — these defaults assume HTTPS in front. Do not relax them on a
|
||||
# deployed instance.
|
||||
# =============================================================================
|
||||
|
||||
# Session cookies are only sent over HTTPS.
|
||||
SESSION_COOKIE_SECURE=true
|
||||
|
||||
# Plain HTTP is redirected to HTTPS.
|
||||
FORCE_HTTPS=true
|
||||
|
||||
# The Werkzeug debugger is a remote code execution primitive by design.
|
||||
# Never true on anything reachable from a network you do not control.
|
||||
FLASK_DEBUG=false
|
||||
|
||||
# Inline <script> without a nonce. Off: every block carries one, and turning
|
||||
# this on gives up the protection that would have blocked the stored XSS
|
||||
# (SEC-WEB-001). It exists as an escape hatch, not as a setting to tune.
|
||||
CSP_ALLOW_INLINE_SCRIPT=false
|
||||
|
||||
# Comma-separated origins allowed to call this API cross-site. Empty means
|
||||
# no CORS policy at all, which is correct: the site renders its own HTML on
|
||||
# one origin and needs none.
|
||||
CORS_ALLOWED_ORIGINS=
|
||||
|
||||
# =============================================================================
|
||||
# Networking
|
||||
# =============================================================================
|
||||
|
||||
# Interface Waitress binds. 127.0.0.1 keeps it reachable only through the
|
||||
# local reverse proxy; 0.0.0.0 exposes it directly and is only correct if
|
||||
# something else in front is doing the filtering.
|
||||
HOST=127.0.0.1
|
||||
PORT=5000
|
||||
|
||||
# Whether to believe X-Forwarded-For, and from whom. This decides which IP
|
||||
# the rate limiter and the audit log record.
|
||||
#
|
||||
# (empty) — trust nobody. Correct when nothing proxies the app.
|
||||
# 127.0.0.1 — trust a reverse proxy on this same machine. The usual case.
|
||||
# * — trust everyone. Only ever correct if the app cannot be reached
|
||||
# except through the proxy, at the network level. Otherwise any
|
||||
# caller can claim any IP and walk around the rate limit.
|
||||
#
|
||||
# See docs/deployment.md before changing this (OPS-002).
|
||||
TRUSTED_PROXY=127.0.0.1
|
||||
|
||||
# =============================================================================
|
||||
# Optional — Discord
|
||||
# =============================================================================
|
||||
|
||||
# Leave ENABLE_DISCORD_BOT=false and the token empty to run without Discord.
|
||||
ENABLE_DISCORD_BOT=false
|
||||
DISCORD_BOT_TOKEN=
|
||||
|
||||
# OAuth2, for "Connect Discord" on the sign-up page.
|
||||
# Create an application at https://discord.com/developers/applications
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
DISCORD_REDIRECT_URI=https://your-domain/auth/discord/callback
|
||||
|
||||
# =============================================================================
|
||||
# Optional — storage
|
||||
# =============================================================================
|
||||
|
||||
# Where uploaded contracts live. Empty means `documents/` beside the
|
||||
# application. Set it to a path OUTSIDE the deployment directory if you move
|
||||
# to a release-directory layout, or a deployment will take the documents with
|
||||
# it (OPS-011, app/storage.py).
|
||||
DOCUMENTS_ROOT=
|
||||
|
||||
# Tables are created at startup when missing. Set to false once Alembic owns
|
||||
# the schema (DB-002/DB-004): create_all() never ALTERs, so a column added to
|
||||
# a model is silently absent from an existing database.
|
||||
AUTO_CREATE_TABLES=true
|
||||
|
||||
# =============================================================================
|
||||
# DEVELOPMENT ONLY — the values to change on a laptop, and nowhere else
|
||||
# =============================================================================
|
||||
#
|
||||
# FLASK_DEBUG=true reloader and interactive debugger
|
||||
# SESSION_COOKIE_SECURE=false cookies over plain HTTP
|
||||
# FORCE_HTTPS=false no redirect to HTTPS
|
||||
# DISCORD_REDIRECT_URI=http://localhost:5000/auth/discord/callback
|
||||
#
|
||||
# `python run.py` reads DEV_HOST and DEV_PORT rather than HOST and PORT, so a
|
||||
# development session cannot accidentally inherit a production binding.
|
||||
@@ -1,34 +0,0 @@
|
||||
# Team Tryouts Application - Environment Variables
|
||||
# Copy this file to .env and fill in the values for production
|
||||
|
||||
# Security Configuration
|
||||
# Generate a secure random secret key: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
SECRET_KEY=flask_app_secret_key
|
||||
|
||||
# Set to 'true' in production to enable secure cookies (requires HTTPS)
|
||||
SESSION_COOKIE_SECURE=false
|
||||
FORCE_HTTPS=false
|
||||
|
||||
# Flask Debug Mode - Set to 'true' only in development
|
||||
FLASK_DEBUG=true
|
||||
|
||||
# Discord Bot Token (required for notifications)
|
||||
# This is the UdeS Esports BOT token, it will send notifications to people that have their
|
||||
# Dicord_User_ID in the db / remove if you don't want discord notifs.
|
||||
DISCORD_BOT_TOKEN=my_discord_bot_token
|
||||
|
||||
# Discord OAuth2 Configuration (for "Connect Discord" on sign-up page)
|
||||
# Create an application at https://discord.com/developers/applications
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
DISCORD_REDIRECT_URI=http://localhost:5000/auth/discord/callback
|
||||
|
||||
#where to find the db (hosted on render for now)
|
||||
#Forme attendue : postgresql://utilisateur:motdepasse@hote:5432/base
|
||||
#Le pilote psycopg 3 est nomme automatiquement par create_app().
|
||||
DATABASE_URL=URI_vers_db_posgres
|
||||
|
||||
|
||||
#Where the app will be hosted (corresponds to: localhost:5000 in local)
|
||||
HOST=127.0.0.1
|
||||
PORT=5000
|
||||
@@ -4,7 +4,22 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Team Tryout Management{% endblock %}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
{# Subresource integrity (QUA-004). Without it, whoever controls the CDN
|
||||
controls what runs on every page of this site — and the CSP names these
|
||||
hosts as allowed, so it would not object.
|
||||
|
||||
What SRI does and does not do: it pins this exact file, so the browser
|
||||
refuses a version that has been altered since. It does not prove the
|
||||
file was honest when the hash was taken. This hash is the one cdnjs
|
||||
publishes for the release, not one derived from the copy we downloaded.
|
||||
|
||||
integrity requires crossorigin. Changing the version means changing
|
||||
the hash, or the asset silently stops loading. #}
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||
integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏆</text></svg>">
|
||||
</head>
|
||||
|
||||
@@ -114,8 +114,19 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js"></script>
|
||||
{# The stylesheet that used to sit here — index.global.min.css — does not
|
||||
exist. FullCalendar 6 bundles its styles into the JS, and that file is not
|
||||
in the published package: the link had been answering 404 on every calendar
|
||||
load since the upgrade. A failed stylesheet is silent in the browser, which
|
||||
is why it survived.
|
||||
|
||||
Integrity pins the bundle (QUA-004): this file is executable script from a
|
||||
third party, on the page that shows every match in the club. See the note
|
||||
in layouts/base.html for what SRI does and does not promise. #}
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js"
|
||||
integrity="sha384-WfE/vOHqht3KDj6FvpwQUf3UxEPUHoGJ3w1yZ8rhpLWnVigt8HjXL2zXqtcfS7mf"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"></script>
|
||||
<script nonce="{{ csp_nonce }}">
|
||||
var canScheduleMatches = {% if current_user.can_schedule_matches() %}true{% else %}false{% endif %};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user