This is a large squashed commit covering two batches of prior uncommitted
work plus a full security-audit remediation pass, kept together because
go.mod/go.sum and several shared files (main.go, handler.go) were touched
by both and splitting risked non-building intermediate commits.
Features (built earlier, previously uncommitted):
- Local username/password login for single-tenant deployments with no
SSO configured (api/localauth, alerting/internal/sessioncheck,
sentryctl users, web/src/routes/login, metadata migrations 0040/0041).
- Remotely-editable additional log file paths for agents, on top of
their existing primary source (api/agents, agent/sentry-agent
extra-file-path diffing, web agent config UI).
- IPv4/IPv6 addresses reported alongside other host system metrics.
Security audit remediation (this pass, all live-verified in production):
- Critical: block ClickHouse SSRF table functions (url/remote/file/s3/...)
in the raw-SQL query escape hatch.
- High: deny sensitive paths and require Admin to add agent
extra_file_paths (Editor could previously point an agent at /etc/shadow
or an SSH key); alerting webhook targets now validate against
internal/metadata/loopback addresses, both at creation and send time;
alerting's session middleware now enforces an Editor+ floor on
mutating requests instead of "any authenticated session"; bumped
goxmldsig to close a SAML signature-verification bypass (GO-2026-4753).
- Medium: per-IP login rate limiting; security response headers
(HSTS/CSP/nosniff/X-Frame-Options/Referrer-Policy/Permissions-Policy)
on web/nginx.conf; a DevCredentialWarnings check in every Go service's
config loader, logging loudly at startup if a deployment is still on
docker-compose.yml's literal dev-only credentials; dependency bumps
(golang.org/x/text, grpc, x/net, quick-xml, h2) across every affected
Go module and both Rust crates, including a previously-uncovered x/net
vulnerability in deploy/operator; a new security-scan.yml CI workflow
running cargo-deny/govulncheck/npm-audit, mirroring the existing
license-compliance.yml matrix shape.
- Low: removed sentryctl's plaintext --password flag (shell
history/`ps` exposure) in favor of stdin and a --password-stdin flag
for reset-password's optional specific-password path; a dummy bcrypt
comparison closes a login response-time username-enumeration
side-channel.
Closes the threat model's headline finding for the SQL query path:
enterprise/internal/tenantprovision does real CREATE DATABASE/USER/GRANT
against ClickHouse, and enterprise/internal/chrunner is a per-tenant
connection registry implementing api's SQLRunner interface, resolving
the tenant from the authenticated request identity -- never a
caller-suppliable parameter. Both are wired into a new binary,
enterprise/cmd/enterprise-api, alongside the unchanged single-tenant
api/cmd/api, since AGPL core can never import enterprise/ and Go's own
internal/ package visibility rules meant enterprise/ couldn't implement
core's SQLRunner interface without importing the package that defines
it. That required moving api/internal/{authz,queryapi,dashboards,
querylang/executor,searchclient,httpserver} out of internal/ -- the
minimal set enterprise-api needs to import; querylang's compiler
internals (planner/lexer/parser/ast/ir) and api's own config stay
internal, since nothing outside api needs them directly.
Also finally wires enterprise/internal/audit into queryapi.AuditLogger
(nil since Phase 4 task 4) via a new adapter, and adds live-ClickHouse
integration tests for two of the four adversarial probes named in
docs/phase-4-isolation-design.md's verification plan.
Corrected several overclaims in the docs while writing this up: an
earlier claim that rbacstore's CRUD was "verified against a live
Postgres" was never actually true in this environment (only
internal/audit was, earlier in this phase, before Docker access was
lost) -- threat-model.md, phase-4-runbook.md, CLAUDE.md, and
enterprise/README.md all now distinguish "a real integration test
exists" from "this was confirmed against a live database."
Still not built: Tantivy/free-text tenant isolation
(enterprise/internal/searchclient), and any deployment-topology
mechanism that actually routes traffic to enterprise-api instead of
plain api -- both binaries exist side by side today with nothing
enforcing or flagging which one a deployment runs.
Replaces the separate SQL-only /query and text-only /search endpoints
with one pipe-syntax query language (plus raw SQL escape hatch) that
compiles to a single IR and execution plan across both backends, so a
query like `message:"connection refused" | stats count by host` runs
as one request instead of two disjoint tools.
- api/internal/querylang: lexer -> ast -> parser -> ir -> planner ->
executor, each layer independently tested.
- Execution generalizes Phase 1's proven Tantivy-prefilter pattern
into a 4-way routing table (pure ClickHouse / text-only / text +
aggregation / raw SQL passthrough).
- Unified web query page and `sentryctl query`, both hitting the same
POST /query endpoint.
- Benchmarked against a real 1,022,000-row dataset
(hack/benchmark-fixture); caught and fixed a real bug where the
Tantivy prefilter cap (10,000) produced an IN-clause exceeding
ClickHouse's default max_query_size -- lowered to 5,000, documented
in docs/query-language-design.md and docs/phase-2-runbook.md.
- docs/query-language-reference.md: customer-facing syntax reference.