Skip to content

CSRF 保護の復元と CI の実効化(総括レビュー対応)#30

Merged
dendencat merged 6 commits into
mainfrom
fix/csrf-and-ci-hardening
Jul 18, 2026
Merged

CSRF 保護の復元と CI の実効化(総括レビュー対応)#30
dendencat merged 6 commits into
mainfrom
fix/csrf-and-ci-hardening

Conversation

@dendencat

Copy link
Copy Markdown
Owner

概要

waste audit 後の総括レビューで挙がった構造的問題のうち、コードで対処可能な 4 点を修正。今回から体制を再編し、実装は Claude が直接行い、Codex が独立レビューを実施(blocker 2 件・minor 2 件を検出し反映済み)。

変更内容(コミット単位)

  1. CSRF 保護の復元(セキュリティ): login_view / preview_markdown_view から @csrf_exempt を削除し、エディタのプレビュー fetch に X-CSRFToken を付与。さらに「テストを通すためにテンプレートから {% csrf_token %} を条件で抜く」{% if not IS_TESTING %} ハックを発見・撤去(専用の context processor ごと削除)。CSRF 強制テスト 4 件を追加

  2. CI テストの Postgres 化: これまで Postgres サービスを起動しながらテストは SQLite で走っていた。TEST_DB_ENGINE=postgres で本番と同じエンジンでテストを実行(ローカルは従来どおり SQLite で高速)。両エンジンで全 27 件パスを確認済み

  3. compose スモークジョブ追加: 整合した .env を生成 → db/redis/django を実ビルドで起動 → healthcheck 待ち → /health/ /ready/ / /articles/ の 200 を検証。「本番構成が一度も通しで動いたことがない」状態の構造的な再発防止。publish(build ジョブ)は smoke を通過しないと実行されない

  4. AI 残骸の掃除: typo ファイル assitant.agent.prompt.md 削除、copilot-instructions.md を Copilot が実際に読む .github/ 直下へ移動

  5. 独立レビュー反映: manage.py test 時に SQLite へ強制上書きする隠れブロックを IS_TESTING に統合して撤去(副次効果で pytest が 65s→6s に高速化)、build ジョブを smoke に依存させ、CSRF テストを強化

検証

  • pytest: SQLite で 27 件パス、Postgres(compose db)でも全件パス
  • YAML 構文検証済み。smoke ジョブは本 PR の CI 実行自体が検証になる

🤖 Generated with Claude Code

dendencat and others added 6 commits July 19, 2026 00:10
Remove @csrf_exempt from login_view and preview_markdown_view, and send
X-CSRFToken from the editor's preview fetch (token read from the form's
hidden input, same pattern as dashboard.html).

Also remove the {% if not IS_TESTING %} guards that stripped
{% csrf_token %} out of the login and editor forms during tests — the
hack existed only to make token-less test POSTs pass, which is exactly
backwards. The testing_mode context processor existed solely for that
guard and is deleted with it. New tests assert both endpoints reject
token-less POSTs with enforce_csrf_checks enabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The test job has always started a postgres service container but the
suite silently ran on in-memory SQLite via the IS_TESTING branch, so
migrations, indexes, and type behaviour were never exercised on the
production engine. TEST_DB_ENGINE=postgres now opts the test settings
into the service container; local pytest keeps the fast SQLite default.

Verified: full suite passes on both engines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Until now no pipeline ever ran the deployable stack end-to-end, which
let a mutually inconsistent .env, an unbuildable nginx image, and a
healthcheck that structurally failed under DEBUG=False all go unnoticed
for months. The smoke job generates a self-consistent .env, brings up
db + redis + django with compose, waits for the container healthcheck,
and asserts /health/, /ready/, / and /articles/ return 200. nginx stays
out because its config needs TLS certificates that only exist on the
production host.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Delete assitant.agent.prompt.md (typo'd filename, referenced nowhere)
and move copilot-instructions.md out of .github/workflows/ — Actions
ignores non-workflow files there, while .github/copilot-instructions.md
is the location Copilot actually reads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Fold the legacy 'test' in sys.argv override into IS_TESTING and delete
  it; it silently forced manage.py test back onto SQLite, defeating
  TEST_DB_ENGINE. The MD5 password hasher moves into the shared testing
  block (pytest now benefits too: suite runtime dropped 65s -> 6s).
- Gate the build/publish job on the smoke job, not just unit tests.
- Strengthen CSRF tests: prove login works with the form token and guard
  the editor's X-CSRFToken header against template regressions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On a fresh checkout the static/media named volumes initialize root-owned,
and the entrypoint - running as appuser under set -e - dies on chown,
restarting forever. Pre-create /app/static and /app/media owned by
appuser in the image so first-use volumes inherit that ownership, and
make the entrypoint chowns best-effort like the chmods next to them.
Long-lived local volumes masked this; the CI smoke job caught it on its
first run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dendencat
dendencat merged commit d134f43 into main Jul 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant