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
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:
co-authored by
Claude Opus 4.8
parent
613847d4d3
commit
96763b7fe9
@@ -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 })
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user