Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '(_0!&^^xekahfp=s5(9+^wlq6gvn6z90%i*p+wn^4ir+mvl4lx'
# The key must be provided via the DJANGO_SECRET_KEY environment variable at
# runtime. No literal key value lives in source control -- see
# Polyhydra-Games/projects-hub#56 for the tracking issue and rotation status.
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', '')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Provision the key before making it mandatory

When DJANGO_SECRET_KEY is absent鈥攁s it is for a fresh clone following the README, make install, or rpmanager.sh, none of which provisions or documents this variable鈥攖he expression resolves to an empty string. Django 1.8 rejects an empty SECRET_KEY while initializing settings, so both the installation migration and documented launcher fail before the application can start; add a secure provisioning path before requiring the variable.

Useful? React with 馃憤聽/ 馃憥.


# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
Expand Down
39 changes: 39 additions & 0 deletions scripts/check-no-plaintext-secret-key.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 1 ]]; then
echo "Usage: $0 SETTINGS_FILE" >&2
exit 2
fi

settings_file=$1
if [[ ! -f "$settings_file" ]]; then
echo "Settings file not found: $settings_file" >&2
exit 2
fi

# Flag a Django SECRET_KEY assigned a literal quoted string (a value-shaped
# secret committed to source control), without ever printing the matching
# value. An environment-variable lookup such as
# `SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', '')` or
# `SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')` is not a violation -- only a
# non-empty literal assigned directly to SECRET_KEY is.
readonly literal_secret_key_pattern="^[[:space:]]*SECRET_KEY[[:space:]]*=[[:space:]]*['\"][^'\"]+['\"]"
readonly env_lookup_pattern='os\.(environ|getenv)'

line_number=0
violations=0

while IFS= read -r line || [[ -n "$line" ]]; do
((line_number += 1))
if [[ "$line" =~ $literal_secret_key_pattern ]] && [[ ! "$line" =~ $env_lookup_pattern ]]; then
echo "Hardcoded SECRET_KEY violation at $settings_file:$line_number" >&2
((violations += 1))
fi
done < "$settings_file"

if ((violations > 0)); then
exit 1
fi

echo "No hardcoded SECRET_KEY literal found in $settings_file"
46 changes: 46 additions & 0 deletions scripts/test-no-plaintext-secret-key.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail

script_directory=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
scanner="$script_directory/check-no-plaintext-secret-key.sh"
fixture_directory=$(mktemp -d)
trap 'rm -rf "$fixture_directory"' EXIT

negative_fixture="$fixture_directory/negative_settings.py"
cat > "$negative_fixture" <<'EOF'
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'this-is-a-fake-non-functional-fixture-value'
EOF

if "$scanner" "$negative_fixture" >"$fixture_directory/negative.stdout" 2>"$fixture_directory/negative.stderr"; then
echo "Expected the value-shaped SECRET_KEY fixture to fail" >&2
exit 1
fi

if ! grep -Fxq "Hardcoded SECRET_KEY violation at $negative_fixture:2" "$fixture_directory/negative.stderr"; then
echo "Expected a redacted violation location for the negative fixture" >&2
exit 1
fi

if [[ -s "$fixture_directory/negative.stdout" ]] || grep -Fq 'fake-non-functional-fixture-value' "$fixture_directory/negative.stderr"; then
echo "Scanner output was not redacted" >&2
exit 1
fi

reference_fixture="$fixture_directory/reference_settings.py"
cat > "$reference_fixture" <<'EOF'
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', '')
EOF

if ! "$scanner" "$reference_fixture" >"$fixture_directory/reference.stdout" 2>"$fixture_directory/reference.stderr"; then
echo "Expected the environment-variable reference fixture to pass" >&2
exit 1
fi

if [[ -s "$fixture_directory/reference.stderr" ]]; then
echo "Expected no scanner stderr for the passing fixture" >&2
exit 1
fi

echo "Plaintext SECRET_KEY scanner fixtures passed"
3 changes: 3 additions & 0 deletions scripts/validate.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail

./scripts/test-no-plaintext-secret-key.sh
./scripts/check-no-plaintext-secret-key.sh project/settings.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scan and remove every tracked copy of the key

At this commit, a repository-wide search still finds the same non-placeholder key literal in tracked project/settings.py.old, and tracked project/settings.pyc also embeds it, but this validation invokes the scanner only on project/settings.py. CI therefore passes while checkouts and source archives continue distributing the credential; remove the backup/compiled artifacts and make the check cover all tracked candidate files.

AGENTS.md reference: AGENTS.md:L37-L37

Useful? React with 馃憤聽/ 馃憥.


node --check Gruntfile.js
python3 - <<'PY'
import json
Expand Down
Loading