import React from "react"; import { useSelector } from "react-redux"; import { IoAlertCircle } from "react-icons/io5"; import { useTranslation } from "react-i18next"; import { Editor, Monaco } from "@monaco-editor/react"; import { editor } from "monaco-editor"; import { Button, Select, SelectItem } from "@nextui-org/react"; import { useMutation } from "@tanstack/react-query"; import { RootState } from "#/store"; import { ActionSecurityRisk, SecurityAnalyzerLog, } from "#/state/security-analyzer-slice"; import { useScrollToBottom } from "#/hooks/use-scroll-to-bottom"; import { I18nKey } from "#/i18n/declaration"; import toast from "#/utils/toast"; import InvariantLogoIcon from "./assets/logo"; import { getFormattedDateTime } from "#/utils/gget-formatted-datetime"; import { downloadJSON } from "#/utils/download-json"; import InvariantService from "#/api/invariant-service"; import { useGetPolicy } from "#/hooks/query/use-get-policy"; import { useGetRiskSeverity } from "#/hooks/query/use-get-risk-severity"; import { useGetTraces } from "#/hooks/query/use-get-traces"; type SectionType = "logs" | "policy" | "settings"; function SecurityInvariant() { const { t } = useTranslation(); const { logs } = useSelector((state: RootState) => state.securityAnalyzer); const [activeSection, setActiveSection] = React.useState("logs"); const [policy, setPolicy] = React.useState(""); const [selectedRisk, setSelectedRisk] = React.useState( ActionSecurityRisk.MEDIUM, ); const logsRef = React.useRef(null); useGetPolicy({ onSuccess: setPolicy }); useGetRiskSeverity({ onSuccess: (riskSeverity) => { setSelectedRisk( riskSeverity === 0 ? ActionSecurityRisk.LOW : riskSeverity || ActionSecurityRisk.MEDIUM, ); }, }); const { refetch: exportTraces } = useGetTraces({ onSuccess: (traces) => { toast.info(t(I18nKey.INVARIANT$TRACE_EXPORTED_MESSAGE)); const filename = `openhands-trace-${getFormattedDateTime()}.json`; downloadJSON(traces, filename); }, }); const { mutate: updatePolicy } = useMutation({ mutationFn: (variables: { policy: string }) => InvariantService.updatePolicy(variables.policy), onSuccess: () => { toast.info(t(I18nKey.INVARIANT$POLICY_UPDATED_MESSAGE)); }, }); const { mutate: updateRiskSeverity } = useMutation({ mutationFn: (variables: { riskSeverity: number }) => InvariantService.updateRiskSeverity(variables.riskSeverity), onSuccess: () => { toast.info(t(I18nKey.INVARIANT$SETTINGS_UPDATED_MESSAGE)); }, }); useScrollToBottom(logsRef); const getRiskColor = React.useCallback((risk: ActionSecurityRisk) => { switch (risk) { case ActionSecurityRisk.LOW: return "text-green-500"; case ActionSecurityRisk.MEDIUM: return "text-yellow-500"; case ActionSecurityRisk.HIGH: return "text-red-500"; case ActionSecurityRisk.UNKNOWN: default: return "text-gray-500"; } }, []); const getRiskText = React.useCallback( (risk: ActionSecurityRisk) => { switch (risk) { case ActionSecurityRisk.LOW: return t(I18nKey.SECURITY_ANALYZER$LOW_RISK); case ActionSecurityRisk.MEDIUM: return t(I18nKey.SECURITY_ANALYZER$MEDIUM_RISK); case ActionSecurityRisk.HIGH: return t(I18nKey.SECURITY_ANALYZER$HIGH_RISK); case ActionSecurityRisk.UNKNOWN: default: return t(I18nKey.SECURITY_ANALYZER$UNKNOWN_RISK); } }, [t], ); const handleEditorDidMount = React.useCallback( (_: editor.IStandaloneCodeEditor, monaco: Monaco): void => { monaco.editor.defineTheme("my-theme", { base: "vs-dark", inherit: true, rules: [], colors: { "editor.background": "#171717", }, }); monaco.editor.setTheme("my-theme"); }, [], ); const sections: Record = { logs: ( <>

{t(I18nKey.INVARIANT$LOG_LABEL)}

{logs.map((log: SecurityAnalyzerLog, index: number) => (

{log.content} {(log.confirmation_state === "awaiting_confirmation" || log.confirmed_changed) && ( )}

{getRiskText(log.security_risk)}

))}
), policy: ( <>

{t(I18nKey.INVARIANT$POLICY_LABEL)}

setPolicy(value || "")} />
), settings: ( <>

{t(I18nKey.INVARIANT$SETTINGS_LABEL)}

{t(I18nKey.INVARIANT$ASK_CONFIRMATION_RISK_SEVERITY_LABEL)}

), }; return (
{t(I18nKey.INVARIANT$INVARIANT_ANALYZER_LABEL)}

{t(I18nKey.INVARIANT$INVARIANT_ANALYZER_MESSAGE)}{" "} {t(I18nKey.INVARIANT$CLICK_TO_LEARN_MORE_LABEL)}


    setActiveSection("logs")} > {t(I18nKey.INVARIANT$LOG_LABEL)}
    setActiveSection("policy")} > {t(I18nKey.INVARIANT$POLICY_LABEL)}
    setActiveSection("settings")} > {t(I18nKey.INVARIANT$SETTINGS_LABEL)}
{sections[activeSection as SectionType]}
); } export default SecurityInvariant;