Files
Zhichang Yu f58fae5fb7 feat(go-agent): Ported retrieval node, added Keenable web search tool (#16396)
Ported retrieval node, added Keenable web search tool
- [x] New Feature (non-breaking change which adds functionality)
2026-06-29 09:45:16 +08:00

151 lines
3.6 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package security
import (
"regexp"
"strings"
)
// Finding represents a security issue found during scanning
type Finding struct {
PatternID string // Rule ID
Severity string // critical | high | medium | low
Category string // exfiltration | injection | destructive | persistence | network | obfuscation
File string // File path where found
Line int // Line number
Match string // The matched text
Description string // Human-readable description
}
// ScanResult represents the result of a security scan
type ScanResult struct {
SkillName string
Source string
TrustLevel string // builtin | trusted | community
Verdict string // safe | caution | dangerous
Findings []Finding
}
// Scanner performs security scans on skill content
type Scanner struct {
patterns []ThreatPattern
}
// NewScanner creates a new security scanner
func NewScanner() *Scanner {
return &Scanner{
patterns: ThreatPatterns,
}
}
// ScanSkill scans skill files for security threats
func (s *Scanner) ScanSkill(skillName, source, trustLevel string, files map[string][]byte) *ScanResult {
var allFindings []Finding
for filename, content := range files {
findings := s.scanFile(filename, string(content))
allFindings = append(allFindings, findings...)
}
verdict := s.determineVerdict(allFindings)
return &ScanResult{
SkillName: skillName,
Source: source,
TrustLevel: trustLevel,
Verdict: verdict,
Findings: allFindings,
}
}
// scanFile scans a single file for threats
func (s *Scanner) scanFile(filename, content string) []Finding {
var findings []Finding
lines := strings.Split(content, "\n")
for _, pattern := range s.patterns {
re, err := regexp.Compile("(?i:" + pattern.Pattern + ")")
if err != nil {
continue
}
for i, line := range lines {
if matches := re.FindString(line); matches != "" {
findings = append(findings, Finding{
PatternID: pattern.PatternID,
Severity: pattern.Severity,
Category: pattern.Category,
File: filename,
Line: i + 1,
Match: strings.TrimSpace(matches),
Description: pattern.Description,
})
}
}
}
return findings
}
// determineVerdict determines the overall verdict based on findings
func (s *Scanner) determineVerdict(findings []Finding) string {
if len(findings) == 0 {
return "safe"
}
hasCritical := false
hasHigh := false
for _, f := range findings {
if f.Severity == "critical" {
hasCritical = true
} else if f.Severity == "high" {
hasHigh = true
}
}
if hasCritical {
return "dangerous"
}
if hasHigh {
return "caution"
}
return "caution"
}
// HasCriticalChecks if any finding is critical severity
func (r *ScanResult) HasCritical() bool {
for _, f := range r.Findings {
if f.Severity == "critical" {
return true
}
}
return false
}
// CountBySeverity counts findings by severity level
func (r *ScanResult) CountBySeverity(severity string) int {
count := 0
for _, f := range r.Findings {
if f.Severity == severity {
count++
}
}
return count
}