mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
feat(config): add command pattern detection tool in exec settings (#1971)
* Add command pattern testing endpoint and UI tool Adds a new API endpoint `/api/config/test-command-patterns` that tests a command against configured whitelist and blacklist patterns, along with a frontend UI component to interactively test patterns. * Only process deny patterns when enableDenyPatterns is true
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useState } from "react"
|
||||
import type { ReactNode } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
@@ -7,6 +8,7 @@ import {
|
||||
type LauncherForm,
|
||||
} from "@/components/config/form-model"
|
||||
import { Field, SwitchCardField } from "@/components/shared-form"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -201,6 +203,56 @@ interface ExecSectionProps {
|
||||
|
||||
export function ExecSection({ form, onFieldChange }: ExecSectionProps) {
|
||||
const { t } = useTranslation()
|
||||
const [testCommand, setTestCommand] = useState("")
|
||||
const [testResult, setTestResult] = useState<{
|
||||
allowed: boolean
|
||||
blocked: boolean
|
||||
matchedWhitelist: string | null
|
||||
matchedBlacklist: string | null
|
||||
} | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const testPatterns = async () => {
|
||||
if (!testCommand.trim()) {
|
||||
setTestResult(null)
|
||||
return
|
||||
}
|
||||
|
||||
const allowPatterns = form.customAllowPatternsText
|
||||
.split("\n")
|
||||
.map((p) => p.trim())
|
||||
.filter((p) => p.length > 0)
|
||||
const denyPatterns = form.enableDenyPatterns
|
||||
? form.customDenyPatternsText
|
||||
.split("\n")
|
||||
.map((p) => p.trim())
|
||||
.filter((p) => p.length > 0)
|
||||
: []
|
||||
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const res = await fetch("/api/config/test-command-patterns", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
allow_patterns: allowPatterns,
|
||||
deny_patterns: denyPatterns,
|
||||
command: testCommand,
|
||||
}),
|
||||
})
|
||||
const data = await res.json()
|
||||
setTestResult({
|
||||
allowed: data.allowed,
|
||||
blocked: data.blocked,
|
||||
matchedWhitelist: data.matched_whitelist ?? null,
|
||||
matchedBlacklist: data.matched_blacklist ?? null,
|
||||
})
|
||||
} catch {
|
||||
setTestResult(null)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfigSectionCard title={t("pages.config.sections.exec")}>
|
||||
@@ -266,6 +318,50 @@ export function ExecSection({ form, onFieldChange }: ExecSectionProps) {
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
label={t("pages.config.pattern_detector_title")}
|
||||
hint={t("pages.config.pattern_detector_hint")}
|
||||
layout="setting-row"
|
||||
controlClassName="md:max-w-md"
|
||||
>
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={testCommand}
|
||||
placeholder={t(
|
||||
"pages.config.pattern_detector_input_placeholder",
|
||||
)}
|
||||
onChange={(e) => setTestCommand(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
testPatterns()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button onClick={testPatterns} disabled={isLoading}>
|
||||
{t("pages.config.pattern_detector_test_button")}
|
||||
</Button>
|
||||
</div>
|
||||
{testResult && (
|
||||
<div
|
||||
className={`rounded-md p-2 text-sm ${
|
||||
testResult.allowed
|
||||
? "bg-green-500/10 text-green-600"
|
||||
: testResult.blocked
|
||||
? "bg-red-500/10 text-red-600"
|
||||
: "bg-muted text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
{testResult.allowed
|
||||
? `${t("pages.config.pattern_detector_result_allowed")}${testResult.matchedWhitelist ? ` (${testResult.matchedWhitelist})` : ""}`
|
||||
: testResult.blocked
|
||||
? `${t("pages.config.pattern_detector_result_blocked")}${testResult.matchedBlacklist ? ` (${testResult.matchedBlacklist})` : ""}`
|
||||
: t("pages.config.pattern_detector_result_no_match")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
label={t("pages.config.exec_timeout_seconds")}
|
||||
hint={t("pages.config.exec_timeout_seconds_hint")}
|
||||
|
||||
Reference in New Issue
Block a user