food-market/tests/integration/08-security-headers.spec.ts
nns a80471d0f9
Some checks failed
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker Web / Build + push Web (push) Has been cancelled
Docker Web / Deploy Web on stage (push) Has been cancelled
fix(security): add HSTS header on stage + integration test
Найдено в Sprint 28 security audit: stage отдаёт security-заголовки
(CSP, X-Frame-Options, Referrer-Policy, Permissions-Policy и др.), но
БЕЗ Strict-Transport-Security. HSTS из ASP.NET Core (Program.cs UseHsts)
не срабатывает потому что api за nginx-прокси видит запрос как HTTP
(нет ForwardedHeaders middleware'a; nginx X-Forwarded-Proto не дешифруется).

Простейший фикс: добавить HSTS в deploy/nginx.conf (web-контейнер).
Brower honors HSTS только на HTTPS-ответах — безопасно unconditional.

max-age=2592000 (30 дней), без includeSubDomains и без preload —
pre-emptive consent, можно безопасно убрать. Когда production stack
устаканится и admin.food-market.kz будет подан в hstspreload.org —
увеличить до 31536000 + preload + includeSubDomains.

Verified:
  curl -I https://test.admin.food-market.kz/ | grep -i strict
  > strict-transport-security: max-age=2592000

Integration test 08-security-headers.spec.ts проверяет 7 security-
заголовков на главной + на 404 (always-параметр).

Cert: 10/10 integration tests passed in 1.3m.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 03:35:38 +05:00

48 lines
1.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Sprint 28 — security headers verification.
*
* Поверяет, что ВСЕ нужные security-заголовки выставлены на каждом
* ответе SPA, в т.ч. на 4xx/404. Регрессионная защита: если кто-то
* случайно уберёт `always` или `add_header` — этот тест поймает.
*/
import { expect, test } from '@playwright/test'
import { baseUrl } from '../regression/factories/api-client.js'
const REQUIRED_HEADERS = [
// Sprint 13.
'content-security-policy',
'x-frame-options',
'x-content-type-options',
'referrer-policy',
'permissions-policy',
'x-permitted-cross-domain-policies',
// Sprint 28.
'strict-transport-security',
]
test.describe('27.9 security headers', () => {
test('все обязательные security-заголовки на главной странице', async () => {
const r = await fetch(`${baseUrl}/`)
expect(r.status).toBeLessThan(500)
const headers = Object.fromEntries(
[...r.headers.entries()].map(([k, v]) => [k.toLowerCase(), v]),
)
for (const h of REQUIRED_HEADERS) {
expect(headers[h], `missing header: ${h}`).toBeTruthy()
}
// HSTS специфика.
expect(headers['strict-transport-security']).toMatch(/max-age=\d+/)
})
test('заголовки сохраняются на 404 (always-параметр)', async () => {
const r = await fetch(`${baseUrl}/this-path-does-not-exist-deliberately`)
// Может быть 404 или 200 (SPA fallback) — оба ок.
const headers = Object.fromEntries(
[...r.headers.entries()].map(([k, v]) => [k.toLowerCase(), v]),
)
for (const h of REQUIRED_HEADERS) {
expect(headers[h], `missing header on 404: ${h}`).toBeTruthy()
}
})
})