Conversation
- Load now uses viper.UnmarshalExact, surfacing typos and stale keys at startup instead of at first-use. - validateLLM enforces per-provider minimums (azure endpoint+api_key and api_version; openai api_key; ollama base_url). Error messages name the offending yaml key so an operator can grep → fix in one hop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Config.Redact() returns a deep copy with api_key fields cleared. Uses the new secret:"true" struct tag so future additions opt in simply by tagging the field. Audited existing log sites; none logged the full Config struct, so no handler changes were required — helper remains available for future code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sets a strict CSP matching the air-gapped deployment posture on every non-OPTIONS response: default-src 'self' everywhere; inline scripts banned; WASM allowed for shiki; inline styles allowed for Tailwind/ shadcn; frame-ancestors 'none' to block clickjacking. Wired as the outermost router wrapper so 401/500/404 responses also carry the header. Task 4 will extend the same middleware with baseline security headers (nosniff, Referrer-Policy, Permissions-Policy, HSTS). Playwright smoke not run locally; CI Playwright job will catch any CSP-triggered regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends securityHeadersMiddleware with: - X-Content-Type-Options: nosniff - Referrer-Policy: strict-origin-when-cross-origin - Permissions-Policy: disables camera/mic/geo/payment/usb - Strict-Transport-Security: 1y max-age + includeSubDomains, gated by server.hsts_enabled (default off) so HTTP-only dev stays unaffected. Operators behind a TLS terminator or direct TLS should set DOCSIQ_SERVER_HSTS_ENABLED=true. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an additive helper next to the existing RequestIDFromContext: ContextLogger(ctx) returns slog.Default() enriched with req_id=<id> when the context carries one from loggingMiddleware. Refactored 4 representative error-path log sites in handlers.go (writeError 5xx path, upload invalid-filename, upload path-traversal, oversize upload rejection) to use it. The rest of the codebase keeps using slog.Default() directly — a wider sweep is tracked as tech debt for the observability sweep (Block 4). Also gofmt'd config.go (drift was pre-existing on main; fixed in-place since this file was touched in Task 1/2/4). Note: the roadmap's 2.5 bullet mentioned ULID. The existing request_id.go generates 16-char hex from crypto/rand which is collision-safe for the air-gapped single-binary deploy. Not worth changing as part of this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Block 2 of the docsiq production-polish roadmap. Five independent server-side changes that harden the HTTP boundary and config loader.
securityHeadersMiddlewaresets a strict CSP on every non-OPTIONS response, wired as the outermost router wrapper so 401/404/500 responses also carry it.X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy(disables camera/mic/geo/payment/usb), andStrict-Transport-Securitygated byserver.hsts_enabled(default off; operators behind TLS setDOCSIQ_SERVER_HSTS_ENABLED=true).Config.Redact()+secret:"true"struct tag on everyapi_keyfield; reflective deep-copy zeroes tagged strings. Audited existing log sites — no whole-struct log sites found, so no log-call changes were required. Helper is available for future code.viper.UnmarshalExactrejects unknown/stale YAML keys at startup;validateLLMenforces per-provider minimums (Azure endpoint + api_key + api_version; OpenAI api_key; Ollama base_url)."none"and empty provider are accepted as explicit opt-out.ContextLogger(ctx)helper returnsslog.Default().With("req_id", id)when the context carries a request ID. Refactored 4 representative error-path log sites inhandlers.go(writeError 5xx, upload invalid-filename, upload path-traversal, oversize upload rejection). Full sweep deferred to Block 4 observability work.Plan deviations
validateLLMaccepts"none"in addition to""(existing code treatsprovider: "none"as explicit LLM opt-out with a test —TestProviderNone— which the plan's validator would have broken).TestLoad_ValidatesLLMProvider/ollama_missing_base_urluses explicitbase_url: ""in YAML because the defaultollama.base_url = http://localhost:11434makes "plain absence of key" a passing config.config_file_load_yamlsubtest loaded an Azure config with no endpoint/api_key; updated it to include both so the new validator passes.internal/config/config.go(drift pre-dated Block 2; fixed because Tasks 1/2/4 were all touching this file).Test plan
CGO_ENABLED=1 go test -tags sqlite_fts5 -timeout 300s ./...- all packages passCGO_ENABLED=1 go test -tags sqlite_fts5 -race ./internal/api/ ./internal/config/- passgo vet -tags sqlite_fts5 ./...- cleangofmt -lon touched files - cleancd ui && npm test -- --run- 54/54 passmainpost-merge (gates CSP against the SPA)Follow-ups (later blocks)
slog.*sites in handlers toContextLogger(r.Context()).