package cc.unitmesh.agent.policy import cc.unitmesh.agent.orchestrator.ToolExecutionContext import cc.unitmesh.agent.state.ToolCall import cc.unitmesh.agent.tool.ToolType /** * Default implementation of PolicyEngine * Provides basic security policies for common tools */ class DefaultPolicyEngine : PolicyEngine { private val rules = mutableListOf() init { // Initialize with default rules initializeDefaultRules() } override fun checkPermission(toolCall: ToolCall, context: ToolExecutionContext): PolicyDecision { return checkPermissionDetailed(toolCall, context).decision } override fun checkPermissionDetailed( toolCall: ToolCall, context: ToolExecutionContext ): PolicyDecisionResult { // Sort rules by priority (higher first) val sortedRules = rules.sortedByDescending { it.priority } for (rule in sortedRules) { if (matchesRule(toolCall, rule)) { return PolicyDecisionResult( decision = rule.decision, reason = "Matched rule: ${rule.name} - ${rule.description}", riskLevel = rule.riskLevel, metadata = mapOf( "rule_name" to rule.name, "tool_name" to toolCall.toolName ) ) } } // Default: allow with low risk return PolicyDecisionResult( decision = PolicyDecision.ALLOW, reason = "No matching rules found, using default allow policy", riskLevel = RiskLevel.LOW ) } override fun addRule(rule: PolicyRule) { rules.add(rule) } override fun removeRule(ruleName: String) { rules.removeAll { it.name == ruleName } } override fun getRules(): List { return rules.toList() } override fun clearRules() { rules.clear() } override fun isToolAllowed(toolName: String): Boolean { val dummyCall = ToolCall.create(toolName, emptyMap()) val dummyContext = ToolExecutionContext() return checkPermission(dummyCall, dummyContext) != PolicyDecision.DENY } override fun getToolRiskLevel(toolName: String): RiskLevel { val dummyCall = ToolCall.create(toolName, emptyMap()) val dummyContext = ToolExecutionContext() return checkPermissionDetailed(dummyCall, dummyContext).riskLevel } /** * Check if a tool call matches a policy rule */ private fun matchesRule(toolCall: ToolCall, rule: PolicyRule): Boolean { // Check tool name pattern val toolNameRegex = Regex(rule.toolPattern) if (!toolNameRegex.matches(toolCall.toolName)) { return false } // Check parameter rules for (paramRule in rule.parameterRules) { if (!matchesParameterRule(toolCall, paramRule)) { return false } } return true } /** * Check if a tool call matches a parameter rule */ private fun matchesParameterRule(toolCall: ToolCall, paramRule: ParameterRule): Boolean { val paramValue = toolCall.params[paramRule.parameterName] // Check required parameters if (paramRule.required && paramValue == null) { return false } // Check forbidden parameters if (paramRule.forbidden && paramValue != null) { return false } // Check value pattern if specified if (paramRule.valuePattern != null && paramValue != null) { val valueRegex = Regex(paramRule.valuePattern) if (!valueRegex.matches(paramValue)) { return false } } return true } /** * Initialize default security rules */ private fun initializeDefaultRules() { // File system tools - generally safe addRule(PolicyRule( name = "allow_read_file", description = "Allow reading files", toolPattern = ToolType.ReadFile.name, decision = PolicyDecision.ALLOW, riskLevel = RiskLevel.LOW, priority = 10 )) addRule(PolicyRule( name = "allow_write_file", description = "Allow writing files", toolPattern = ToolType.WriteFile.name, decision = PolicyDecision.ALLOW, riskLevel = RiskLevel.MEDIUM, priority = 10 )) addRule(PolicyRule( name = "allow_glob", description = "Allow file globbing", toolPattern = ToolType.Glob.name, decision = PolicyDecision.ALLOW, riskLevel = RiskLevel.LOW, priority = 10 )) addRule(PolicyRule( name = "allow_grep", description = "Allow text searching", toolPattern = ToolType.Grep.name, decision = PolicyDecision.ALLOW, riskLevel = RiskLevel.LOW, priority = 10 )) // Shell commands - higher risk, but allowed for coding tasks addRule(PolicyRule( name = "allow_shell", description = "Allow shell commands", toolPattern = ToolType.Shell.name, decision = PolicyDecision.ALLOW, riskLevel = RiskLevel.HIGH, priority = 5 )) } }