mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-05-01 15:22:37 +02:00
- Rename Go module: sentinel-community/gomcp -> syntrex/gomcp (50+ files) - Rename npm package: sentinel-dashboard -> syntrex-dashboard - Update Cargo.toml repository URL to syntrex/syntrex - Update all doc references from DmitrL-dev/AISecurity to syntrex - Add root Makefile (build-all, test-all, lint-all, clean-all) - Add MIT LICENSE - Add .editorconfig (Go/Rust/TS/C cross-language) - Add .github/workflows/ci.yml (Go + Rust + Dashboard) - Add dashboard next.config.ts and .env.example - Clean ARCHITECTURE.md: remove brain/immune/strike/micro-swarm, fix 61->67 engines
92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
package oracle
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/syntrex/gomcp/internal/domain/entropy"
|
|
)
|
|
|
|
// SecretScanResult holds the result of scanning content for secrets.
|
|
type SecretScanResult struct {
|
|
HasSecrets bool `json:"has_secrets"`
|
|
Detections []string `json:"detections,omitempty"`
|
|
MaxEntropy float64 `json:"max_entropy"`
|
|
LineCount int `json:"line_count"`
|
|
ScannerRules int `json:"scanner_rules"`
|
|
}
|
|
|
|
// Common secret patterns (regex).
|
|
var secretPatterns = []*regexp.Regexp{
|
|
regexp.MustCompile(`(?i)(api[_-]?key|apikey)\s*[:=]\s*['"]?([a-zA-Z0-9_\-]{20,})['"]?`),
|
|
regexp.MustCompile(`(?i)(secret|token|password|passwd|pwd)\s*[:=]\s*['"]?([^\s'"]{8,})['"]?`),
|
|
regexp.MustCompile(`(?i)(bearer|authorization)\s+[a-zA-Z0-9_\-.]{20,}`),
|
|
regexp.MustCompile(`(?i)-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----`),
|
|
regexp.MustCompile(`(?i)(aws_access_key_id|aws_secret_access_key)\s*=\s*[A-Za-z0-9/+=]{16,}`),
|
|
regexp.MustCompile(`ghp_[a-zA-Z0-9]{36}`), // GitHub PAT
|
|
regexp.MustCompile(`sk-[a-zA-Z0-9]{32,}`), // OpenAI key
|
|
regexp.MustCompile(`(?i)(mongodb|postgres|mysql)://[^\s]{10,}`), // DB connection strings
|
|
}
|
|
|
|
const (
|
|
// Lines with entropy above this threshold are suspicious.
|
|
lineEntropyThreshold = 4.5
|
|
|
|
// Minimum line length to check (short lines can have high entropy naturally).
|
|
minLineLength = 20
|
|
)
|
|
|
|
// ScanForSecrets checks content for high-entropy strings and known secret patterns.
|
|
// Returns a result indicating whether secrets were detected.
|
|
func ScanForSecrets(content string) *SecretScanResult {
|
|
result := &SecretScanResult{
|
|
ScannerRules: len(secretPatterns),
|
|
}
|
|
|
|
lines := strings.Split(content, "\n")
|
|
result.LineCount = len(lines)
|
|
|
|
// Pass 1: Pattern matching.
|
|
for _, pattern := range secretPatterns {
|
|
if matches := pattern.FindStringSubmatch(content); len(matches) > 0 {
|
|
result.HasSecrets = true
|
|
match := matches[0]
|
|
if len(match) > 30 {
|
|
match = match[:15] + "***REDACTED***"
|
|
}
|
|
result.Detections = append(result.Detections, "PATTERN: "+match)
|
|
}
|
|
}
|
|
|
|
// Pass 2: Entropy-based detection on individual lines.
|
|
for i, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if len(line) < minLineLength {
|
|
continue
|
|
}
|
|
|
|
// Skip comments.
|
|
if strings.HasPrefix(line, "//") || strings.HasPrefix(line, "#") ||
|
|
strings.HasPrefix(line, "*") || strings.HasPrefix(line, "/*") {
|
|
continue
|
|
}
|
|
|
|
ent := entropy.ShannonEntropy(line)
|
|
if ent > result.MaxEntropy {
|
|
result.MaxEntropy = ent
|
|
}
|
|
|
|
if ent > lineEntropyThreshold {
|
|
result.HasSecrets = true
|
|
redacted := line
|
|
if len(redacted) > 40 {
|
|
redacted = redacted[:20] + "***REDACTED***"
|
|
}
|
|
result.Detections = append(result.Detections,
|
|
fmt.Sprintf("ENTROPY: line %d (%.2f bits/char): %s", i+1, ent, redacted))
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|