diff --git a/.flaskenv b/.flaskenv
deleted file mode 100644
index 3ac4157..0000000
--- a/.flaskenv
+++ /dev/null
@@ -1,12 +0,0 @@
-CONTACT_EMAIL=[contact email]
-CONTACT_PHONE=[contact phone]
-DEPARTMENT_NAME=[name of department]
-DEPARTMENT_URL=[url of department]
-FLASK_APP=govuk-frontend-flask.py
-FLASK_DEBUG=True
-FLASK_RUN_PORT=5000
-REDIS_URL=memory://
-SECRET_KEY=3e48b821144547db5f22c7357431a093489450fcc4aad992ab9e1dd1a9d3b40d
-SERVICE_NAME=[name of service]
-SERVICE_PHASE=[phase]
-SERVICE_URL=[url of service]
\ No newline at end of file
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 93941b8..04d6ccb 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -13,17 +13,23 @@ name: "CodeQL"
on:
push:
- branches: [ "main" ]
+ branches: [ "main", "develop" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- - cron: '17 2 * * 4'
+ - cron: '40 16 * * 6'
jobs:
analyze:
name: Analyze
- runs-on: ubuntu-latest
+ # Runner size impacts CodeQL analysis time. To learn more, please see:
+ # - https://gh.io/recommended-hardware-resources-for-running-codeql
+ # - https://gh.io/supported-runners-and-hardware-resources
+ # - https://gh.io/using-larger-runners
+ # Consider using larger runners for possible analysis time improvements.
+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
+ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
@@ -32,8 +38,10 @@ jobs:
strategy:
fail-fast: false
matrix:
- language: [ 'javascript', 'python' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ language: [ 'javascript-typescript', 'python' ]
+ # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]
+ # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both
+ # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
@@ -48,12 +56,12 @@ jobs:
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
-
- # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+
+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
@@ -61,12 +69,14 @@ jobs:
# âšī¸ Command-line programs to run using the OS shell.
# đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- # If the Autobuild fails above, remove it and uncomment the following three lines.
+ # If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
- # echo "Run, Build Application using script"
- # ./location_of_script_within_repo/buildscript.sh
+ # echo "Run, Build Application using script"
+ # ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index fe461b4..b0dedc4 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -17,4 +17,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
- uses: actions/dependency-review-action@v2
+ uses: actions/dependency-review-action@v3
diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
index b45ab00..4c9361d 100644
--- a/.github/workflows/python-app.yml
+++ b/.github/workflows/python-app.yml
@@ -1,38 +1,44 @@
+# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
+
name: Python application
on:
push:
- branches: [main]
+ branches: [ "main" ]
pull_request:
- branches: [main]
+ branches: [ "main" ]
+
+permissions:
+ contents: read
jobs:
build:
runs-on: ubuntu-latest
- strategy:
- matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11"]
-
steps:
- uses: actions/checkout@v3
- - name: Set up Python ${{ matrix.python-version }}
+ - name: Set up Python 3.11
uses: actions/setup-python@v4
with:
- python-version: ${{ matrix.python-version }}
+ python-version: 3.11
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install safety flake8 black bandit
+ pip install -r requirements_dev.txt
pip install -r requirements.txt
- name: Check dependencies for known security vulnerabilities
run: safety check -r requirements.txt
- name: Check code for potential security vulnerabilities
- run: bandit -r .
+ run: bandit -r . -x /tests
- name: Check code formatting
- run: black . -l 120 --check
+ run: |
+ black . -t py311 -l 120 --check
+ isort . -c
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings.
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics
+ # - name: Test with pytest
+ # run: python -m pytest --cov=app --cov-report=term-missing --cov-branch
diff --git a/.gitignore b/.gitignore
index 10b4b63..6a27a5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
-# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,osx,linux,jetbrains+all,visualstudio,visualstudiocode,python,flask,git,venv
-# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,osx,linux,jetbrains+all,visualstudio,visualstudiocode,python,flask,git,venv
+# Created by https://www.toptal.com/developers/gitignore/api/git,osx,venv,flask,linux,macos,python,windows,certificates,visualstudio,jetbrains+all,visualstudiocode
+# Edit at https://www.toptal.com/developers/gitignore?templates=git,osx,venv,flask,linux,macos,python,windows,certificates,visualstudio,jetbrains+all,visualstudiocode
### GOV.UK Frontend ###
app/static/fonts*
@@ -8,6 +8,14 @@ app/static/images*
app/static/VERSION.txt
govuk_components*
+### certificates ###
+*.pem
+*.key
+*.crt
+*.cer
+*.der
+*.priv
+
### Flask ###
instance/*
!instance/.gitignore
@@ -872,4 +880,4 @@ FodyWeavers.xsd
### VisualStudio Patch ###
# Additional files built by Visual Studio
-# End of https://www.toptal.com/developers/gitignore/api/windows,macos,osx,linux,jetbrains+all,visualstudio,visualstudiocode,python,flask,git,venv
+# End of https://www.toptal.com/developers/gitignore/api/git,osx,venv,flask,linux,macos,python,windows,certificates,visualstudio,jetbrains+all,visualstudiocode
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..d08219e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,21 @@
+FROM python:3.11-slim
+
+RUN useradd containeruser
+
+WORKDIR /home/containeruser
+
+COPY app app
+COPY govuk-frontend-flask.py config.py docker-entrypoint.sh requirements.txt ./
+RUN pip install -r requirements.txt \
+ && chmod +x docker-entrypoint.sh \
+ && chown -R containeruser:containeruser ./
+
+# Set environment variables
+ENV FLASK_APP=govuk-frontend-flask.py \
+ PYTHONDONTWRITEBYTECODE=1 \
+ PYTHONUNBUFFERED=1
+
+USER containeruser
+
+EXPOSE 8000
+ENTRYPOINT ["./docker-entrypoint.sh"]
\ No newline at end of file
diff --git a/README.md b/README.md
index ca8f3ca..fe50e01 100644
--- a/README.md
+++ b/README.md
@@ -15,11 +15,7 @@ The app is provided intentionally bare, with just the essential parts that all s
### Required
-- Python 3.8.x or higher
-
-### Optional
-
-- Redis 4.0.x or higher (for rate limiting, otherwise in-memory storage is used)
+- Docker
## Getting started
@@ -27,25 +23,9 @@ The app is provided intentionally bare, with just the essential parts that all s
[Create a new repository](https://github.com/LandRegistry/govuk-frontend-flask/generate) using this template, with the same directory structure and files. Then clone a local copy of your newly created repository.
-### Create venv and install requirements
-
-```shell
-python3 -m venv venv
-source venv/bin/activate
-pip3 install -r requirements.txt ; pip3 install -r requirements_dev.txt
-```
-
-### Get GOV.UK Frontend assets
-
-For convenience a shell script has been provided to download and extract the GOV.UK Frontend distribution assets
-
-```shell
-./build.sh
-```
-
### Set local environment variables
-In the `.flaskenv` file you will find a number of environment variables. These are injected as global variables into the app and pre-populated into page templates as appropriate. Enter your specific information for the following:
+In the `compose.yml` file you will find a number of environment variables. These are injected as global variables into the app and pre-populated into page templates as appropriate. Enter your specific service information for the following:
- CONTACT_EMAIL
- CONTACT_PHONE
@@ -55,13 +35,25 @@ In the `.flaskenv` file you will find a number of environment variables. These a
- SERVICE_PHASE
- SERVICE_URL
-### Run app
+You must also set a new unique `SECRET_KEY`, which is used to securely sign the session cookie and CSRF tokens. It should be a long random `bytes` or `str`. You can use the output of this Python comand to generate a new key:
+
+```shell
+python -c 'import secrets; print(secrets.token_hex())'
+```
+
+### Get the latest GOV.UK Frontend assets
+
+```shell
+./build.sh
+```
+
+### Run containers
```shell
-flask run
+docker compose up --build
```
-You should now have the app running on
+You should now have the app running on . Accept the browsers security warning due to the self-signed HTTPS certificate to continue.
## Demos
diff --git a/app/demos/forms.py b/app/demos/forms.py
index 1580eb0..5e77265 100644
--- a/app/demos/forms.py
+++ b/app/demos/forms.py
@@ -350,20 +350,29 @@ class ConditionalRevealForm(FlaskForm):
class AutocompleteForm(FlaskForm):
# Manually added list here, but could be dynamically assigned in server route
countries = [
+ "Argentina",
+ "Australia",
+ "Brazil",
"Canada",
"China",
"France",
"Germany",
"India",
+ "Indonesia",
"Italy",
"Japan",
+ "Mexico",
+ "Russia",
+ "Saudi Arabia",
+ "South Africa",
"South Korea",
+ "Turkey",
"United Kingdom",
"United States",
]
country = StringField(
- "Country",
+ "G20 Countries",
widget=GovTextInput(),
validators=[InputRequired(message="Enter a country")],
description="Start typing and select a suggestion",
diff --git a/compose.yml b/compose.yml
new file mode 100644
index 0000000..39957dc
--- /dev/null
+++ b/compose.yml
@@ -0,0 +1,28 @@
+services:
+ web:
+ container_name: govuk-frontend-flask
+ build: .
+ restart: always
+ environment:
+ - CONTACT_EMAIL=[contact email]
+ - CONTACT_PHONE=[contact phone]
+ - DEPARTMENT_NAME=[name of department]
+ - DEPARTMENT_URL=[url of department]
+ - REDIS_URL=redis://cache:6379
+ - SECRET_KEY=4f378500459bb58fecf903ea3c113069f11f150b33388f56fc89f7edce0e6a84
+ - SERVICE_NAME=[name of service]
+ - SERVICE_PHASE=[phase]
+ - SERVICE_URL=[url of service]
+ ports:
+ - "8000:8000"
+ volumes:
+ - .:/home/containeruser
+ depends_on:
+ - cache
+ cache:
+ container_name: redis
+ image: redis:7.0-alpine
+ restart: always
+ ports:
+ - 6379:6379
+
\ No newline at end of file
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100755
index 0000000..5f961b1
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+openssl req -new -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365 -subj "/C=GB/ST=Devon/L=Plymouth/O=HM Land Registry/OU=DDaT/CN=localhost"
+exec gunicorn --reload --certfile cert.pem --keyfile key.pem -b :8000 --access-logfile - --error-logfile - govuk-frontend-flask:app
\ No newline at end of file
diff --git a/requirements.in b/requirements.in
index a85de70..15b2d57 100644
--- a/requirements.in
+++ b/requirements.in
@@ -1,13 +1,11 @@
cssmin
email_validator
+flask
flask-assets
flask-compress
-flask-limiter
+flask-limiter[redis]
flask-talisman
-flask
govuk-frontend-jinja
govuk-frontend-wtf
gunicorn
jsmin
-python-dotenv
-redis
diff --git a/requirements.txt b/requirements.txt
index ac85327..7f80f69 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,7 @@
#
# pip-compile requirements.in
#
-blinker==1.6.2
+blinker==1.6.3
# via flask
brotli==1.1.0
# via flask-compress
@@ -18,7 +18,7 @@ deprecated==1.2.14
# via limits
dnspython==2.4.2
# via email-validator
-email-validator==2.0.0.post2
+email-validator==2.1.0.post1
# via -r requirements.in
flask==3.0.0
# via
@@ -28,11 +28,11 @@ flask==3.0.0
# flask-limiter
# flask-wtf
# govuk-frontend-wtf
-flask-assets==2.0
+flask-assets==2.1.0
# via -r requirements.in
flask-compress==1.14
# via -r requirements.in
-flask-limiter==3.5.0
+flask-limiter[redis]==3.5.0
# via -r requirements.in
flask-talisman==1.1.0
# via -r requirements.in
@@ -61,7 +61,7 @@ jinja2==3.1.2
# govuk-frontend-wtf
jsmin==3.0.1
# via -r requirements.in
-limits==3.6.0
+limits[redis]==3.6.0
# via flask-limiter
markdown-it-py==3.0.0
# via rich
@@ -80,10 +80,8 @@ packaging==23.2
# limits
pygments==2.16.1
# via rich
-python-dotenv==1.0.0
- # via -r requirements.in
redis==5.0.1
- # via -r requirements.in
+ # via limits
rich==13.6.0
# via flask-limiter
typing-extensions==4.8.0
@@ -92,11 +90,11 @@ typing-extensions==4.8.0
# limits
webassets==2.0
# via flask-assets
-werkzeug==3.0.0
+werkzeug==3.0.1
# via flask
wrapt==1.15.0
# via deprecated
-wtforms==3.0.1
+wtforms==3.1.0
# via
# flask-wtf
# govuk-frontend-wtf
diff --git a/requirements_dev.txt b/requirements_dev.txt
index f030411..a7f7dd1 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -8,13 +8,13 @@ attrs==23.1.0
# via flake8-bugbear
bandit==1.7.5
# via -r requirements_dev.in
-black==23.9.1
+black==23.10.1
# via -r requirements_dev.in
build==1.0.3
# via pip-tools
certifi==2023.7.22
# via requests
-charset-normalizer==3.3.0
+charset-normalizer==3.3.1
# via requests
click==8.1.7
# via
@@ -23,7 +23,9 @@ click==8.1.7
# pur
# safety
coverage[toml]==7.3.2
- # via pytest-cov
+ # via
+ # coverage
+ # pytest-cov
dparse==0.6.3
# via safety
flake8==6.1.0
@@ -32,9 +34,9 @@ flake8==6.1.0
# pep8-naming
flake8-bugbear==23.9.16
# via -r requirements_dev.in
-gitdb==4.0.10
+gitdb==4.0.11
# via gitpython
-gitpython==3.1.37
+gitpython==3.1.40
# via bandit
idna==3.4
# via requests
@@ -71,7 +73,7 @@ pluggy==1.3.0
# via pytest
pur==7.3.1
# via -r requirements_dev.in
-pycodestyle==2.11.0
+pycodestyle==2.11.1
# via flake8
pyflakes==3.1.0
# via flake8
@@ -79,7 +81,7 @@ pygments==2.16.1
# via rich
pyproject-hooks==1.0.0
# via build
-pytest==7.4.2
+pytest==7.4.3
# via pytest-cov
pytest-cov==4.1.0
# via -r requirements_dev.in
@@ -89,9 +91,9 @@ requests==2.31.0
# via safety
rich==13.6.0
# via bandit
-ruamel-yaml==0.17.33
+ruamel-yaml==0.18.2
# via safety
-ruamel-yaml-clib==0.2.7
+ruamel-yaml-clib==0.2.8
# via ruamel-yaml
safety==2.3.4
# via -r requirements_dev.in
@@ -99,7 +101,7 @@ smmap==5.0.1
# via gitdb
stevedore==5.1.0
# via bandit
-urllib3==2.0.6
+urllib3==2.0.7
# via requests
wheel==0.41.2
# via pip-tools