# QA Audit Report — PR-1: Allow Empty Value in UpdateSetting **Date:** 2026-03-17 **Scope:** Remove `binding:"required"` from `Value` field in `UpdateSettingRequest` **File:** `backend/internal/api/handlers/settings_handler.go` --- # QA Security Audit Report — Rate Limit CI Fix **Audited by**: QA Security Auditor **Date**: 2026-03-17 **Spec reference**: `docs/plans/rate_limit_ci_fix_spec.md` **Files audited**: - `scripts/rate_limit_integration.sh` - `Dockerfile` (GeoIP section, non-CI path) - `.github/workflows/rate-limit-integration.yml` --- ## Pre-Commit Check Results | Check | Command | Result | |-------|---------|--------| | Bash syntax | `bash -n scripts/rate_limit_integration.sh` | ✅ PASS (exit 0) | | Pre-commit hooks | `lefthook run pre-commit` (project uses lefthook; no `.pre-commit-config.yaml`) | ✅ PASS — all 6 hooks passed: `check-yaml`, `actionlint`, `end-of-file-fixer`, `trailing-whitespace`, `dockerfile-check`, `shellcheck` | | Caddy admin API trailing slash (workflow) | `grep -n "2119" .github/workflows/rate-limit-integration.yml` | ✅ PASS — line 71 references `/config/` (trailing slash present) | | Caddy admin API trailing slash (script) | All 6 occurrences of `localhost:2119/config` in script | ✅ PASS — all use `/config/` | --- ## Security Focus Area Results ### 1. Credential Handling — `TMP_COOKIE` **`mktemp` usage**: `TMP_COOKIE=$(mktemp)` at line 208. Creates a file in `/tmp` with `600` permissions via the OS. ✅ SECURE. **Removal on exit**: The `cleanup()` function at line 103 removes the file with `rm -f "${TMP_COOKIE:-}"`. However, `cleanup` is only registered via explicit calls — there is **no `trap cleanup EXIT`**. Only `trap on_failure ERR` is registered (line 108). **Gap**: On 5 early `exit 1` paths after line 208 (login failure L220, auth failure L251, Caddy readiness failure L282, security config failure L299, and handler verification failure L316), `cleanup` is never called. The cookie file is left in `/tmp`. **Severity**: LOW — The cookie contains session credentials for a localhost test server (`ratelimit@example.local` / `password123`, non-production). CI runners are ephemeral and auto-cleaned. Local runs will leave a `/tmp/tmp.XXXXXX` file until next reboot or manual cleanup. **Note**: The exit at line 386 (inside the 429 enforcement failure block) intentionally skips cleanup to leave containers running for manual inspection. This is by design and acceptable. **Recommendation**: Add `trap cleanup EXIT` immediately after `trap on_failure ERR` (line 109) to ensure the cookie file is always removed. --- ### 2. `curl` — Sensitive Values in Command-Line Arguments Cookie file path is passed via `-c ${TMP_COOKIE}` and `-b ${TMP_COOKIE}` (unquoted). No credentials, tokens, or API keys are passed as command-line arguments. All authentication is via the cookie file (read/write by path), which is the correct pattern — cookie values never appear in `ps` output. **Finding (LOW)**: `${TMP_COOKIE}` is unquoted in all 6 curl invocations. `mktemp` on Linux produces paths of the form `/tmp/tmp.XXXXXX` which never contain spaces or shell metacharacters under default `$TMPDIR`. However, under a non-standard `$TMPDIR` (e.g., `/tmp/my dir/`) this would break. This is a portability issue, not a security issue. **Recommendation**: Quote `"${TMP_COOKIE}"` in all curl invocations. --- ### 3. Shell Injection All interpolated values in curl `-d` payloads are either: - Script-level constants (`RATE_LIMIT_REQUESTS=3`, `RATE_LIMIT_WINDOW_SEC=10`, `RATE_LIMIT_BURST=1`, `TEST_DOMAIN=ratelimit.local`, `BACKEND_CONTAINER=ratelimit-backend`) - Values derived from API responses stored in double-quoted variables (`"$CREATE_RESP"`, `"$SEC_CONFIG_RESP"`) No shell injection vector exists. All heredoc expansions (`cat <