mirror of
https://github.com/TronoSfera/Law.git
synced 2026-05-18 10:03:45 +03:00
add security test 04
This commit is contained in:
parent
9eeecb48a3
commit
61dc621501
15 changed files with 241 additions and 13 deletions
|
|
@ -91,6 +91,7 @@ OTP_SMS_MIN_BALANCE=20
|
|||
# Email OTP / fallback
|
||||
# EMAIL_PROVIDER: dummy | smtp | service
|
||||
# ----------------------------------------------------------------------------
|
||||
EMAIL_SERVICE_ENABLED=true
|
||||
EMAIL_PROVIDER=service
|
||||
EMAIL_SERVICE_URL=http://email-service:8010
|
||||
OTP_EMAIL_FALLBACK_ENABLED=true
|
||||
|
|
@ -133,3 +134,12 @@ CLAMAV_ENABLED=true
|
|||
CLAMAV_HOST=clamav
|
||||
CLAMAV_PORT=3310
|
||||
CLAMAV_TIMEOUT_SECONDS=20
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Security scheduler (dedicated periodic smoke entity)
|
||||
# ----------------------------------------------------------------------------
|
||||
SECURITY_SCHEDULER_INTERVAL_SECONDS=900
|
||||
SECURITY_SCHEDULER_INTERNAL_BASE_URL=http://frontend
|
||||
SECURITY_SCHEDULER_EXTERNAL_DOMAINS=ruakb.ru,ruakb.online
|
||||
SECURITY_SCHEDULER_SKIP_DOCKER_CHECKS=1
|
||||
SECURITY_SCHEDULER_RUN_INCIDENT_ON_FAIL=1
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y build-essential && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y build-essential curl openssl ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY . .
|
||||
|
|
|
|||
15
Makefile
15
Makefile
|
|
@ -4,7 +4,7 @@
|
|||
prod-up prod-down prod-logs prod-ps prod-migrate \
|
||||
prod-secrets-generate prod-secrets-apply \
|
||||
prod-minio-tls-init incident-checklist rotate-encryption-kid reencrypt-active-kid \
|
||||
security-smoke prod-security-audit \
|
||||
security-smoke prod-security-audit prod-security-scheduler-up prod-security-scheduler-logs \
|
||||
prod-cert-init prod-cert-renew \
|
||||
check-prod-files check-cert-files \
|
||||
run migrate test seed-quotes
|
||||
|
|
@ -44,6 +44,8 @@ help:
|
|||
@echo " incident-checklist - Create PDn incident checklist markdown report"
|
||||
@echo " security-smoke - Run security smoke checks and create report"
|
||||
@echo " prod-security-audit - Full production security audit/repair workflow"
|
||||
@echo " prod-security-scheduler-up - Start/update dedicated security scheduler service"
|
||||
@echo " prod-security-scheduler-logs - Tail security scheduler logs"
|
||||
@echo " rotate-encryption-kid - Add new KID key pair to .env and switch active KID"
|
||||
@echo " reencrypt-active-kid - Re-encrypt historical encrypted fields using active KID"
|
||||
@echo " prod-cert-init - Initial Let's Encrypt issue (nginx only 80 during bootstrap)"
|
||||
|
|
@ -130,6 +132,17 @@ prod-security-audit: check-cert-files
|
|||
LOCAL_SMOKE_CANDIDATES="$(LOCAL_SMOKE_CANDIDATES)" \
|
||||
./scripts/ops/prod_security_audit.sh
|
||||
|
||||
prod-security-scheduler-up: check-prod-files
|
||||
@echo "[SEC] Checking MinIO TLS bundle"
|
||||
@if [ ! -f deploy/tls/minio/ca.crt ] || ! openssl x509 -in deploy/tls/minio/ca.crt -noout >/dev/null 2>&1 || [ ! -f deploy/tls/minio/public.crt ] || ! openssl x509 -in deploy/tls/minio/public.crt -noout >/dev/null 2>&1 || [ ! -f deploy/tls/minio/private.key ]; then \
|
||||
echo "[SEC] MinIO TLS bundle missing/invalid -> regenerating"; \
|
||||
MINIO_TLS_OVERWRITE=true ./scripts/ops/minio_tls_bootstrap.sh; \
|
||||
fi
|
||||
$(PROD_COMPOSE) up -d --build --force-recreate security-scheduler
|
||||
|
||||
prod-security-scheduler-logs: check-prod-files
|
||||
$(PROD_COMPOSE) logs -f --tail=200 security-scheduler
|
||||
|
||||
rotate-encryption-kid:
|
||||
./scripts/ops/rotate_encryption_kid.sh --env-file .env
|
||||
|
||||
|
|
|
|||
34
README.md
34
README.md
|
|
@ -169,7 +169,8 @@ OTP sending is implemented through a dedicated SMS service layer (`app/services/
|
|||
Public auth mode can be selected via environment:
|
||||
```bash
|
||||
PUBLIC_AUTH_MODE=sms # sms | email | sms_or_email | totp
|
||||
EMAIL_PROVIDER=dummy # dummy | smtp
|
||||
EMAIL_SERVICE_ENABLED=true # false -> email-service stays healthy but sending is disabled
|
||||
EMAIL_PROVIDER=dummy # dummy | smtp | service
|
||||
EMAIL_SERVICE_URL=http://email-service:8010
|
||||
INTERNAL_SERVICE_TOKEN=change_me_internal_service_token
|
||||
OTP_EMAIL_FALLBACK_ENABLED=true
|
||||
|
|
@ -204,11 +205,17 @@ OTP_EMAIL_TEMPLATE=Ваш код подтверждения: {code}
|
|||
|
||||
For dedicated email microservice (recommended in production):
|
||||
```bash
|
||||
EMAIL_SERVICE_ENABLED=true
|
||||
EMAIL_PROVIDER=service
|
||||
EMAIL_SERVICE_URL=http://email-service:8010
|
||||
INTERNAL_SERVICE_TOKEN=<strong-random-token>
|
||||
```
|
||||
|
||||
To keep infrastructure healthy but disable email sending temporarily:
|
||||
```bash
|
||||
EMAIL_SERVICE_ENABLED=false
|
||||
```
|
||||
|
||||
Admin/Lawyer TOTP endpoints:
|
||||
- `GET /api/admin/auth/totp/status`
|
||||
- `POST /api/admin/auth/totp/setup`
|
||||
|
|
@ -371,6 +378,31 @@ You can override:
|
|||
make prod-security-audit LOCAL_SMOKE_CANDIDATES="https://127.0.0.1,http://127.0.0.1"
|
||||
```
|
||||
|
||||
## Dedicated security scheduler entity
|
||||
`security-scheduler` is a separate container service that runs periodic smoke checks automatically.
|
||||
|
||||
Start/update:
|
||||
```bash
|
||||
make prod-security-scheduler-up
|
||||
```
|
||||
|
||||
Logs:
|
||||
```bash
|
||||
make prod-security-scheduler-logs
|
||||
```
|
||||
|
||||
Main env knobs:
|
||||
```bash
|
||||
SECURITY_SCHEDULER_INTERVAL_SECONDS=900
|
||||
SECURITY_SCHEDULER_INTERNAL_BASE_URL=http://frontend
|
||||
SECURITY_SCHEDULER_EXTERNAL_DOMAINS=ruakb.ru,ruakb.online
|
||||
SECURITY_SCHEDULER_SKIP_DOCKER_CHECKS=1
|
||||
SECURITY_SCHEDULER_RUN_INCIDENT_ON_FAIL=1
|
||||
```
|
||||
|
||||
Scheduler script:
|
||||
- `/Users/tronosfera/Develop/Law/scripts/ops/security_scheduler.sh`
|
||||
|
||||
## Container health and alerting
|
||||
Docker Compose is configured with:
|
||||
- `restart: unless-stopped` for core services
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ class Settings(BaseSettings):
|
|||
OTP_AUTOTEST_FORCE_MOCK_SMS: bool = True
|
||||
PUBLIC_AUTH_MODE: str = "sms" # sms | email | sms_or_email | totp
|
||||
EMAIL_PROVIDER: str = "dummy" # dummy | smtp
|
||||
EMAIL_SERVICE_ENABLED: bool = True
|
||||
EMAIL_SERVICE_URL: str = "http://email-service:8010"
|
||||
INTERNAL_SERVICE_TOKEN: str = "change_me_internal_service_token"
|
||||
SMTP_HOST: str = ""
|
||||
|
|
|
|||
|
|
@ -17,16 +17,24 @@ class InternalEmailSend(BaseModel):
|
|||
|
||||
@app.on_event("startup")
|
||||
def _validate_security_config_on_startup() -> None:
|
||||
if not bool(getattr(settings, "EMAIL_SERVICE_ENABLED", True)):
|
||||
return
|
||||
validate_production_security_or_raise("email-service")
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
return {"status": "ok", "service": "email-service"}
|
||||
return {
|
||||
"status": "ok",
|
||||
"service": "email-service",
|
||||
"enabled": bool(getattr(settings, "EMAIL_SERVICE_ENABLED", True)),
|
||||
}
|
||||
|
||||
|
||||
@app.post("/internal/send-otp")
|
||||
def internal_send_otp(payload: InternalEmailSend, x_internal_token: str | None = Header(default=None)):
|
||||
if not bool(getattr(settings, "EMAIL_SERVICE_ENABLED", True)):
|
||||
raise HTTPException(status_code=503, detail="Email service disabled")
|
||||
expected = str(settings.INTERNAL_SERVICE_TOKEN or "").strip()
|
||||
if not expected:
|
||||
raise HTTPException(status_code=500, detail="INTERNAL_SERVICE_TOKEN не настроен")
|
||||
|
|
|
|||
|
|
@ -139,6 +139,9 @@ def _send_via_email_service(*, email: str, subject: str, body: str) -> dict[str,
|
|||
|
||||
|
||||
def send_otp_email_message(*, email: str, code: str, purpose: str, track_number: str | None = None) -> dict[str, Any]:
|
||||
if not bool(getattr(settings, "EMAIL_SERVICE_ENABLED", True)):
|
||||
raise EmailDeliveryError("Email-рассылка отключена (EMAIL_SERVICE_ENABLED=false)")
|
||||
|
||||
normalized_email = _normalize_email(email)
|
||||
if not normalized_email:
|
||||
raise EmailDeliveryError("Некорректный email")
|
||||
|
|
@ -163,6 +166,17 @@ def send_otp_email_message(*, email: str, code: str, purpose: str, track_number:
|
|||
|
||||
|
||||
def email_provider_health() -> dict[str, Any]:
|
||||
if not bool(getattr(settings, "EMAIL_SERVICE_ENABLED", True)):
|
||||
return {
|
||||
"provider": "disabled",
|
||||
"status": "ok",
|
||||
"mode": "disabled",
|
||||
"dev_mode": bool(_otp_dev_mode_enabled()),
|
||||
"can_send": False,
|
||||
"checks": {"email_service_enabled": False},
|
||||
"issues": ["EMAIL_SERVICE_ENABLED=false: Email-рассылка отключена"],
|
||||
}
|
||||
|
||||
provider = str(settings.EMAIL_PROVIDER or "dummy").strip().lower()
|
||||
if _otp_dev_mode_enabled():
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ echo $? # 0=OK, >0=ALERT
|
|||
| SEC-14 | Контроль уязвимостей в CI | `.github/workflows/security-ci.yml` (`bandit`, `pip-audit`, `trivy`) | GitHub Actions: workflow `security-ci` (PR/push/schedule/manual). Локальная валидация YAML: `ruby -e "require 'yaml'; YAML.load_file('.github/workflows/security-ci.yml')"` |
|
||||
| SEC-15 | Регулярный security smoke | `scripts/ops/security_smoke.sh` | `make security-smoke` или `./scripts/ops/security_smoke.sh https://ruakb.online`; проверять отчет `reports/security/security-smoke-<timestamp>.md` |
|
||||
| SEC-OPS | Полный прод-аудит безопасности | `scripts/ops/prod_security_audit.sh`, `Makefile` target `prod-security-audit` | `make prod-security-audit DOMAIN=... WWW_DOMAIN=... SECOND_DOMAIN=... SECOND_WWW_DOMAIN=... LETSENCRYPT_EMAIL=...` (`AUTO_CERT_INIT=1` при необходимости автоподнятия LE cert) |
|
||||
| SEC-AUTO | Автоматизация security-smoke отдельной сущностью | `scripts/ops/security_scheduler.sh`, service `security-scheduler` в compose | `make prod-security-scheduler-up`; далее `make prod-security-scheduler-logs` и проверка health статуса контейнера |
|
||||
| P49 | Клиентский UI запросов (куратор/смена юриста) | e2e `e2e/tests/service_requests_flow.spec.js`, `e2e/tests/public_client_flow.spec.js` | `docker compose run --rm --no-deps -e E2E_BASE_URL=http://frontend e2e playwright test --config=playwright.config.js e2e/tests/service_requests_flow.spec.js e2e/tests/public_client_flow.spec.js` |
|
||||
| P50 | Админ UI: вкладка `Запросы` + topbar индикатор | `tests/admin/test_metrics_templates.py`, `tests/admin/test_service_requests.py`, e2e `e2e/tests/admin_role_flow.spec.js`, `e2e/tests/service_requests_flow.spec.js` | `docker compose exec -T backend python -m unittest tests.admin.test_metrics_templates tests.admin.test_service_requests -v` + Playwright прогон указанных spec |
|
||||
| P51 | Тесты контура запросов | backend: `tests/admin/test_service_requests.py`, `tests/admin/test_metrics_templates.py`, `tests/test_public_requests.py`; e2e: `e2e/tests/service_requests_flow.spec.js` | `docker compose exec -T backend python -m unittest tests.admin.test_service_requests tests.admin.test_metrics_templates tests.test_public_requests -v` + `docker compose run --rm --no-deps -e E2E_BASE_URL=http://frontend e2e playwright test --config=playwright.config.js e2e/tests/service_requests_flow.spec.js` |
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ services:
|
|||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
security-scheduler:
|
||||
volumes:
|
||||
- ./reports:/app/reports
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
# Production: keep official ClamAV image on x86_64 hosts.
|
||||
clamav:
|
||||
platform: linux/amd64
|
||||
|
|
|
|||
|
|
@ -72,6 +72,12 @@ services:
|
|||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
security-scheduler:
|
||||
volumes:
|
||||
- ./reports:/app/reports
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
# Production: keep official ClamAV image on x86_64 hosts.
|
||||
clamav:
|
||||
platform: linux/amd64
|
||||
|
|
|
|||
|
|
@ -122,6 +122,30 @@ services:
|
|||
command: ["celery","-A","app.workers.celery_app:celery_app","beat","-l","INFO"]
|
||||
volumes: [".:/app"]
|
||||
|
||||
security-scheduler:
|
||||
build: .
|
||||
container_name: law-security-scheduler
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
depends_on:
|
||||
frontend:
|
||||
condition: service_healthy
|
||||
backend:
|
||||
condition: service_healthy
|
||||
chat-service:
|
||||
condition: service_healthy
|
||||
email-service:
|
||||
condition: service_healthy
|
||||
command: ["bash", "-lc", "./scripts/ops/security_scheduler.sh"]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "test -f /tmp/security_scheduler_heartbeat && [ $(( $(date +%s) - $(cat /tmp/security_scheduler_heartbeat) )) -lt 1800 ]"]
|
||||
interval: 60s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
volumes:
|
||||
- .:/app
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
container_name: law-db
|
||||
|
|
|
|||
|
|
@ -10,14 +10,29 @@ OVERWRITE="${MINIO_TLS_OVERWRITE:-false}"
|
|||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
if [[ "$OVERWRITE" != "true" ]]; then
|
||||
for required in ca.crt ca.key public.crt private.key; do
|
||||
if [[ -f "$OUT_DIR/$required" ]]; then
|
||||
echo "[ERROR] $OUT_DIR/$required already exists. Set MINIO_TLS_OVERWRITE=true to regenerate." >&2
|
||||
prepare_output_path() {
|
||||
local path="$1"
|
||||
if [[ -d "$path" ]]; then
|
||||
if [[ "$OVERWRITE" == "true" ]]; then
|
||||
rm -rf "$path"
|
||||
return 0
|
||||
fi
|
||||
echo "[ERROR] $path is a directory. Set MINIO_TLS_OVERWRITE=true to replace it." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [[ -f "$path" ]]; then
|
||||
if [[ "$OVERWRITE" == "true" ]]; then
|
||||
rm -f "$path"
|
||||
return 0
|
||||
fi
|
||||
echo "[ERROR] $path already exists. Set MINIO_TLS_OVERWRITE=true to regenerate." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for required in ca.crt ca.key public.crt private.key; do
|
||||
prepare_output_path "$OUT_DIR/$required"
|
||||
done
|
||||
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "[ERROR] openssl not found" >&2
|
||||
|
|
|
|||
|
|
@ -62,7 +62,19 @@ ensure_env_file() {
|
|||
ensure_minio_tls_bundle() {
|
||||
if file_missing "deploy/tls/minio/public.crt" || file_missing "deploy/tls/minio/private.key" || file_missing "deploy/tls/minio/ca.crt"; then
|
||||
log "MinIO TLS bundle is missing -> generating"
|
||||
./scripts/ops/minio_tls_bootstrap.sh
|
||||
MINIO_TLS_OVERWRITE=true ./scripts/ops/minio_tls_bootstrap.sh
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! openssl x509 -in "deploy/tls/minio/ca.crt" -noout >/dev/null 2>&1; then
|
||||
log "MinIO CA certificate is invalid -> regenerating"
|
||||
MINIO_TLS_OVERWRITE=true ./scripts/ops/minio_tls_bootstrap.sh
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! openssl x509 -in "deploy/tls/minio/public.crt" -noout >/dev/null 2>&1; then
|
||||
log "MinIO public certificate is invalid -> regenerating"
|
||||
MINIO_TLS_OVERWRITE=true ./scripts/ops/minio_tls_bootstrap.sh
|
||||
else
|
||||
log "MinIO TLS bundle present"
|
||||
fi
|
||||
|
|
@ -82,12 +94,13 @@ stack_up_and_migrate() {
|
|||
"${PROD_COMPOSE[@]}" up -d --build --remove-orphans --force-recreate db redis minio clamav
|
||||
|
||||
log "Starting app services (backend/chat/email/worker/beat)"
|
||||
"${PROD_COMPOSE[@]}" up -d --build --remove-orphans --force-recreate backend chat-service email-service worker beat
|
||||
"${PROD_COMPOSE[@]}" up -d --build --remove-orphans --force-recreate backend chat-service email-service worker beat security-scheduler
|
||||
|
||||
log "Waiting app services to become healthy"
|
||||
wait_service_healthy "backend" 60
|
||||
wait_service_healthy "chat-service" 60
|
||||
wait_service_healthy "email-service" 60
|
||||
wait_service_healthy "security-scheduler" 90
|
||||
|
||||
# Force recreate frontend/edge after chat/backend to avoid stale DNS upstream cache in nginx.
|
||||
log "Starting/recreating frontend and edge"
|
||||
|
|
|
|||
76
scripts/ops/security_scheduler.sh
Executable file
76
scripts/ops/security_scheduler.sh
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env bash
|
||||
set -u
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
INTERVAL_SECONDS="${SECURITY_SCHEDULER_INTERVAL_SECONDS:-900}"
|
||||
INTERNAL_BASE_URL="${SECURITY_SCHEDULER_INTERNAL_BASE_URL:-http://frontend}"
|
||||
EXTERNAL_DOMAINS="${SECURITY_SCHEDULER_EXTERNAL_DOMAINS:-ruakb.ru,ruakb.online}"
|
||||
SKIP_DOCKER_CHECKS="${SECURITY_SCHEDULER_SKIP_DOCKER_CHECKS:-1}"
|
||||
RUN_INCIDENT_ON_FAIL="${SECURITY_SCHEDULER_RUN_INCIDENT_ON_FAIL:-1}"
|
||||
HEARTBEAT_FILE="${SECURITY_SCHEDULER_HEARTBEAT_FILE:-/tmp/security_scheduler_heartbeat}"
|
||||
|
||||
log() {
|
||||
echo "[SEC-SCHEDULER] $*"
|
||||
}
|
||||
|
||||
run_smoke() {
|
||||
local url="$1"
|
||||
if SECURITY_SMOKE_SKIP_DOCKER_CHECKS="$SKIP_DOCKER_CHECKS" ./scripts/ops/security_smoke.sh "$url"; then
|
||||
log "smoke ok: ${url}"
|
||||
return 0
|
||||
fi
|
||||
log "smoke failed: ${url}"
|
||||
return 1
|
||||
}
|
||||
|
||||
run_cycle() {
|
||||
local failed=0
|
||||
run_smoke "$INTERNAL_BASE_URL" || failed=1
|
||||
|
||||
IFS=',' read -r -a _domains <<< "$EXTERNAL_DOMAINS"
|
||||
local domain
|
||||
for domain in "${_domains[@]}"; do
|
||||
domain="$(echo "$domain" | xargs)"
|
||||
[[ -z "$domain" ]] && continue
|
||||
run_smoke "https://${domain}" || failed=1
|
||||
done
|
||||
|
||||
date +%s > "$HEARTBEAT_FILE"
|
||||
|
||||
if [[ "$failed" == "1" && "$RUN_INCIDENT_ON_FAIL" == "1" && -x "./scripts/ops/incident_checklist.sh" ]]; then
|
||||
./scripts/ops/incident_checklist.sh \
|
||||
--severity MEDIUM \
|
||||
--category MONITORING_ALERT \
|
||||
--summary "security-scheduler detected smoke check failure" || true
|
||||
fi
|
||||
}
|
||||
|
||||
validate_interval() {
|
||||
if ! [[ "$INTERVAL_SECONDS" =~ ^[0-9]+$ ]] || [[ "$INTERVAL_SECONDS" -lt 60 ]]; then
|
||||
log "invalid SECURITY_SCHEDULER_INTERVAL_SECONDS=${INTERVAL_SECONDS}, fallback to 900"
|
||||
INTERVAL_SECONDS=900
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
validate_interval
|
||||
mkdir -p reports/security reports/incidents
|
||||
log "started: interval=${INTERVAL_SECONDS}s internal=${INTERNAL_BASE_URL} external=${EXTERNAL_DOMAINS}"
|
||||
while true; do
|
||||
local started_at
|
||||
started_at="$(date +%s)"
|
||||
run_cycle
|
||||
local elapsed
|
||||
elapsed="$(( $(date +%s) - started_at ))"
|
||||
local sleep_for
|
||||
sleep_for="$(( INTERVAL_SECONDS - elapsed ))"
|
||||
if (( sleep_for < 1 )); then
|
||||
sleep_for=1
|
||||
fi
|
||||
sleep "$sleep_for"
|
||||
done
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
@ -6,6 +6,7 @@ cd "$ROOT_DIR"
|
|||
|
||||
BASE_URL="${1:-http://localhost:8081}"
|
||||
REPORT_DIR="${REPORT_DIR:-reports/security}"
|
||||
SECURITY_SMOKE_SKIP_DOCKER_CHECKS="${SECURITY_SMOKE_SKIP_DOCKER_CHECKS:-0}"
|
||||
TS_HUMAN="$(date -u +"%Y-%m-%d %H:%M:%S UTC")"
|
||||
TS_FILE="$(date -u +"%Y%m%d-%H%M%S")"
|
||||
REPORT_FILE="${REPORT_DIR}/security-smoke-${TS_FILE}.md"
|
||||
|
|
@ -166,6 +167,10 @@ check_cookie_and_security_flags() {
|
|||
}
|
||||
|
||||
check_compose_service_running() {
|
||||
if [[ "$SECURITY_SMOKE_SKIP_DOCKER_CHECKS" == "1" ]]; then
|
||||
add_warn "docker checks disabled by SECURITY_SMOKE_SKIP_DOCKER_CHECKS=1"
|
||||
return 0
|
||||
fi
|
||||
local service="$1"
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
add_warn "docker is not available: service checks skipped"
|
||||
|
|
@ -182,6 +187,10 @@ check_compose_service_running() {
|
|||
}
|
||||
|
||||
check_db_security_audit_table() {
|
||||
if [[ "$SECURITY_SMOKE_SKIP_DOCKER_CHECKS" == "1" ]]; then
|
||||
add_warn "db checks disabled by SECURITY_SMOKE_SKIP_DOCKER_CHECKS=1"
|
||||
return 0
|
||||
fi
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
add_warn "docker is not available: DB checks skipped"
|
||||
return 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue