0f6dd1135e
CI / Check (pull_request) Successful in 10m33s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Closes the loop on M7.2 isolation for paths that don't have a JWT
context: scheduler, webhooks, and the agent's `run_scan` / `run_pr_review`
helpers all now take a `tenant_id` at the boundary and resolve to a
tenant-scoped `Database` via `db_pool.for_tenant_id(...)`. Internal
orchestrators (PipelineOrchestrator, PentestOrchestrator) and pipeline
helpers were already DB-agnostic — they take `db: Database` at
construction and don't care which tenant it points to.
Changes
- DatabasePool::for_tenant_id(&str) — same as for_tenant but accepts
a bare tenant_id. Background paths don't have a full TenantContext.
for_tenant is now a thin wrapper that delegates.
- agent.run_scan(tenant_id, repo_id, trigger) — pulls the tenant
database before constructing the PipelineOrchestrator. Was:
run_scan(repo_id, trigger) reading agent.db.
- agent.run_pr_review(tenant_id, repo_id, ...) — same shape.
- Webhook routes change: /webhook/{tenant_id}/{platform}/{repo_id}.
Tenant is part of the URL path because webhooks arrive without a
JWT — they're authenticated via per-repo HMAC, not the tenant gate.
The dashboard surfaces the full per-tenant URL when the repo is
registered. All three handlers (gitea, github, gitlab) updated.
- scheduler.rs — iterates tenants from $SCHEDULER_TENANT_IDS
(comma-separated env), or DEV_TENANT_ID's `dev` default. Both
scan_all_repos and monitor_cves now run once per configured
tenant. M7.2-D will replace this static config with a pull from
the tenant-registry.
- api/handlers/repos.rs::trigger_scan now passes tenant.0.tenant_id.
What's unchanged because it didn't need to change
- PipelineOrchestrator, PentestOrchestrator: take `db: Database` at
construction — they're tenant-DB-agnostic by design. The caller
picks the tenant DB.
- pipeline/{dedup,graph_build,issue_creation,sbom/mod}.rs,
pentest/{context,report/html/*}.rs, trackers/jira.rs, llm/triage.rs:
take `&Database` or `&mongodb::Database` as args, transitively
tenant-scoped via the caller.
Test plan
- cargo fmt --all clean
- cargo clippy --workspace --exclude compliance-dashboard
-- -D warnings clean
- cargo test -p compliance-core --lib — 7 pass
- cargo test -p compliance-agent --lib — 228 pass
- cargo test -p compliance-agent --test tenant_isolation — 5 pass
- cargo test -p compliance-agent --test tenant_status_middleware
— 6 pass
What's left (PR-D)
- Drop the transitional agent.db field — no remaining call sites
(verified by `grep -rn "agent\.db\b" compliance-agent/src`).
- main.rs / TestServer stop building the legacy Database; only the
pool remains.
- Add cross-tenant admin helpers (list tenants, drop tenant DB) on
the pool for offboarding flows.
- Pull tenants from the tenant-registry instead of an env var.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
43 lines
1.5 KiB
Rust
43 lines
1.5 KiB
Rust
use std::sync::Arc;
|
|
|
|
use axum::routing::post;
|
|
use axum::{Extension, Router};
|
|
|
|
use crate::agent::ComplianceAgent;
|
|
use crate::error::AgentError;
|
|
use crate::webhooks::{gitea, github, gitlab};
|
|
|
|
pub async fn start_webhook_server(agent: &ComplianceAgent) -> Result<(), AgentError> {
|
|
let app = Router::new()
|
|
// Per-tenant per-repo webhook URLs: /webhook/{tenant_id}/{platform}/{repo_id}
|
|
// The tenant_id is resolved from the URL path because webhooks
|
|
// arrive without a JWT — they're authenticated via per-repo HMAC,
|
|
// not via the tenant gate. The dashboard surfaces the full URL
|
|
// including the tenant_id when the repo is registered.
|
|
.route(
|
|
"/webhook/{tenant_id}/github/{repo_id}",
|
|
post(github::handle_github_webhook),
|
|
)
|
|
.route(
|
|
"/webhook/{tenant_id}/gitlab/{repo_id}",
|
|
post(gitlab::handle_gitlab_webhook),
|
|
)
|
|
.route(
|
|
"/webhook/{tenant_id}/gitea/{repo_id}",
|
|
post(gitea::handle_gitea_webhook),
|
|
)
|
|
.layer(Extension(Arc::new(agent.clone())));
|
|
|
|
let addr = "0.0.0.0:3002";
|
|
let listener = tokio::net::TcpListener::bind(addr)
|
|
.await
|
|
.map_err(|e| AgentError::Other(format!("Failed to bind webhook server: {e}")))?;
|
|
|
|
tracing::info!("Webhook server listening on {addr}");
|
|
axum::serve(listener, app)
|
|
.await
|
|
.map_err(|e| AgentError::Other(format!("Webhook server error: {e}")))?;
|
|
|
|
Ok(())
|
|
}
|