feat(onboarding): input validation + editable targets
CI / Check (pull_request) Successful in 5m34s
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

Two gaps surfaced while testing: bad input (a pasted label in a git URL, a
slash in the name) was only discovered at scan time, and there was no way to
fix a target once created.

Validation (client-side, shared by the wizard and the editor):
- `validate_target_name` — non-empty, no stray spaces, no slashes (the name is
  used as the clone directory).
- `validate_artifact_ref` — per-kind checks (git URL shape, http(s) for live
  URLs, image-ref/path for the rest). The wizard shows the error inline and
  disables Next / + Add until it's clean.

Editing:
- `PATCH /api/v1/targets/{id}` now accepts an `artifacts` replacement.
- New `update_target` server fn + an Edit modal on the Targets page: change
  name, type, and add/remove artifacts (same validation), then Save.

Robustness:
- `GitOps::clone_or_fetch` sanitizes the repo name into one filesystem-safe
  directory segment, so a slash (or other path-hostile char) in a name can
  never nest or break the clone path again (+ unit test).
- Drive-by: `sbom` license summary uses `sort_by_key(Reverse(..))`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-07-13 19:51:47 +02:00
co-authored by Claude Opus 4.8
parent 613847d4d3
commit 96763b7fe9
6 changed files with 374 additions and 6 deletions
@@ -75,6 +75,9 @@ pub struct UpdateTargetRequest {
pub scan_config: Option<TargetScanConfig>,
pub compliance_profile: Option<ComplianceProfile>,
pub scan_schedule: Option<String>,
/// Replace the target's artifacts wholesale (used by the dashboard editor).
#[serde(default)]
pub artifacts: Option<Vec<ArtifactInput>>,
}
/// One applicable-scan option, serialized for the wizard.
@@ -214,6 +217,13 @@ pub async fn update_target(
if let Some(ss) = req.scan_schedule {
set.insert("scan_schedule", ss);
}
if let Some(arts) = req.artifacts {
let built: Vec<Artifact> = arts.iter().map(ArtifactInput::build).collect();
set.insert(
"artifacts",
to_bson(&built).map_err(|_| StatusCode::BAD_REQUEST)?,
);
}
db.onboarded_targets()
.update_one(doc! { "_id": oid }, doc! { "$set": set })
+1 -1
View File
@@ -282,7 +282,7 @@ pub async fn license_summary(
}
})
.collect();
summaries.sort_by(|a, b| b.count.cmp(&a.count));
summaries.sort_by_key(|s| std::cmp::Reverse(s.count));
Ok(Json(ApiResponse {
data: summaries,