fix(portal): keep technical health out of security view

This commit is contained in:
igor04091968
2026-06-04 23:33:40 +03:00
parent afc40fd3ca
commit 36fbcb1d63
3 changed files with 44 additions and 26 deletions
+35 -25
View File
@@ -2240,33 +2240,30 @@ fn agent_quality_explain(quality: &AgentQuality) -> AgentQualityExplain {
fn command_json_source(name: &str, command: &str, timeout: Duration) -> SourceStatus { fn command_json_source(name: &str, command: &str, timeout: Duration) -> SourceStatus {
match run_shell(command, timeout) { match run_shell(command, timeout) {
Ok((stdout, stderr, success)) => { Ok((stdout, stderr, success)) => match serde_json::from_str::<Value>(&stdout) {
if !success { Ok(payload) => SourceStatus {
return SourceStatus { ok: payload_bool(&payload, "/ok").unwrap_or(true),
ok: false, status: status_from_payload(&payload),
status: "FAIL".to_string(), summary: source_summary(name, &payload),
summary: format!("{name} command returned non-zero status"), error: if success || stderr.trim().is_empty() {
error: Some(stderr.trim().to_string()), None
payload: None, } else {
}; Some(stderr.trim().to_string())
}
match serde_json::from_str::<Value>(&stdout) {
Ok(payload) => SourceStatus {
ok: payload_bool(&payload, "/ok").unwrap_or(true),
status: status_from_payload(&payload),
summary: source_summary(name, &payload),
error: None,
payload: Some(payload),
}, },
Err(err) => SourceStatus { payload: Some(payload),
ok: false, },
status: "FAIL".to_string(), Err(err) => SourceStatus {
summary: format!("{name} returned invalid JSON"), ok: false,
error: Some(err.to_string()), status: "FAIL".to_string(),
payload: None, summary: if success {
format!("{name} returned invalid JSON")
} else {
format!("{name} command returned non-zero status")
}, },
} error: Some(err.to_string()),
} payload: None,
},
},
Err(err) => SourceStatus { Err(err) => SourceStatus {
ok: false, ok: false,
status: "FAIL".to_string(), status: "FAIL".to_string(),
@@ -9352,6 +9349,19 @@ mod tests {
assert!(err.to_string().contains("command timed out")); assert!(err.to_string().contains("command timed out"));
} }
#[test]
fn command_json_source_accepts_valid_json_with_nonzero_exit() {
let source = command_json_source(
"detmir_status",
"printf '%s' '{\"severity\":\"OK\",\"ok_for_operator\":false}'; exit 2",
Duration::from_secs(1),
);
assert!(source.ok);
assert_eq!(source.status, "OK");
assert!(source.payload.is_some());
assert!(!source.summary.contains("command returned non-zero"));
}
#[test] #[test]
fn agent_quality_history_counts_seven_ok_days() { fn agent_quality_history_counts_seven_ok_days() {
let (_dir, path) = write_history( let (_dir, path) = write_history(
@@ -1214,7 +1214,7 @@ function renderSecurityView(data, report, extras = {}) {
${renderInvestigationPacks(report?.risk_incident_candidates)} ${renderInvestigationPacks(report?.risk_incident_candidates)}
<section class="dashboard-band security-band"> <section class="dashboard-band security-band">
<div class="band-head"><h3>Расследования</h3><span class="muted">ручная проверка, решения и материалы</span></div> <div class="band-head"><h3>Расследования</h3><span class="muted">ручная проверка, решения и материалы</span></div>
${renderIncidentsList(extras.incidents || data.incidents || [])} ${renderDlpIncidentsList(extras.incidents || data.incidents || [])}
</section> </section>
`; `;
} }
+8
View File
@@ -289,6 +289,14 @@ async function main() {
"Связь рисков и активности", "Связь рисков и активности",
].every((marker) => containsText(securityText, marker)), ].every((marker) => containsText(securityText, marker)),
}); });
checks.push({
name: "security_role_hides_technical_health_sources",
ok:
!containsText(securityText, "detmir_check")
&& !containsText(securityText, "detmir_status")
&& !containsText(securityText, "command failed")
&& !containsText(securityText, "command returned non-zero"),
});
checks.push({ checks.push({
name: "security_events_security_text", name: "security_events_security_text",
ok: !expectedSecurityText || containsText(securityText, expectedSecurityText), ok: !expectedSecurityText || containsText(securityText, expectedSecurityText),