Files
2025-09-27 04:55:20 +03:00

409 lines
12 KiB
Go

package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"runtime"
"time"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"github.com/vxcontrol/cloud/anonymizer"
"github.com/vxcontrol/cloud/models"
"github.com/vxcontrol/cloud/sdk"
"github.com/vxcontrol/cloud/system"
)
const (
DefaultMaxRetries = 3
DefaultTimeout = 60 * time.Second
)
type Client struct {
errorReport sdk.CallReqBytesRespBytes
issueCreate sdk.CallReqBytesRespBytes
issueInvestigate sdk.CallReqBytesRespBytes
issueInvestigateWithSteam sdk.CallReqBytesRespReader
anonymizer anonymizer.Anonymizer
}
// ReportError sends an automated error report to support
func (c *Client) ReportError(
ctx context.Context, component models.ComponentType, errorDetails map[string]any,
) error {
// anonymize sensitive data before transmission
if err := c.anonymizer.Anonymize(&errorDetails); err != nil {
return fmt.Errorf("failed to anonymize error details, sending original data: %w", err)
}
errorReq := models.SupportErrorRequest{
Component: component,
Version: "0.0.0",
OS: models.OSType(runtime.GOOS),
Arch: models.ArchType(runtime.GOARCH),
ErrorDetails: errorDetails,
}
data, err := json.Marshal(errorReq)
if err != nil {
return fmt.Errorf("failed to marshal error request: %w", err)
}
responseData, err := c.errorReport(ctx, data)
if err != nil {
return fmt.Errorf("failed to report error: %w", err)
}
var response models.SupportErrorResponse
if err := json.Unmarshal(responseData, &response); err != nil {
return fmt.Errorf("failed to unmarshal error response: %w", err)
}
return nil
}
// CreateSupportIssue creates a support issue with AI assistance
func (c *Client) CreateSupportIssue(
ctx context.Context, component models.ComponentType, errorDetails any, logs []models.SupportLogs,
) (uuid.UUID, error) {
// anonymize error details before transmission
if err := c.anonymizer.Anonymize(&errorDetails); err != nil {
return uuid.Nil, fmt.Errorf("failed to anonymize error details, sending original data: %w", err)
}
// anonymize logs before transmission
if err := c.anonymizer.Anonymize(&logs); err != nil {
return uuid.Nil, fmt.Errorf("failed to anonymize logs, sending original data: %w", err)
}
issueReq := models.SupportIssueRequest{
Component: component,
Version: "0.0.0",
OS: models.OSType(runtime.GOOS),
Arch: models.ArchType(runtime.GOARCH),
Logs: logs,
ErrorDetails: errorDetails,
}
data, err := json.Marshal(issueReq)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to marshal issue request: %w", err)
}
responseData, err := c.issueCreate(ctx, data)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to create issue: %w", err)
}
var response models.SupportIssueResponse
if err := json.Unmarshal(responseData, &response); err != nil {
return uuid.Nil, fmt.Errorf("failed to unmarshal issue response: %w", err)
}
return response.IssueID, nil
}
// InvestigateIssue starts AI-powered investigation of a support issue
func (c *Client) InvestigateIssue(
ctx context.Context, issueID uuid.UUID, userInput string,
) (string, error) {
investigationReq := models.SupportInvestigationRequest{
IssueID: issueID,
UseSteam: false,
UserInput: userInput,
}
data, err := json.Marshal(investigationReq)
if err != nil {
return "", fmt.Errorf("failed to marshal investigation request: %w", err)
}
responseData, err := c.issueInvestigate(ctx, data)
if err != nil {
return "", fmt.Errorf("failed to investigate issue: %w", err)
}
var response models.SupportInvestigationResponse
if err := json.Unmarshal(responseData, &response); err != nil {
return "", fmt.Errorf("failed to unmarshal investigation response: %w", err)
}
return response.Answer, nil
}
// InvestigateIssueWithSteam starts AI-powered investigation of a support issue with steam response
func (c *Client) InvestigateIssueWithSteam(
ctx context.Context, issueID uuid.UUID, userInput string,
) (string, error) {
investigationReq := models.SupportInvestigationRequest{
IssueID: issueID,
UseSteam: true,
UserInput: userInput,
}
data, err := json.Marshal(investigationReq)
if err != nil {
return "", fmt.Errorf("failed to marshal investigation request: %w", err)
}
stream, err := c.issueInvestigateWithSteam(ctx, data)
if err != nil {
return "", fmt.Errorf("failed to investigate issue: %w", err)
}
response, err := io.ReadAll(stream)
if err != nil {
return "", fmt.Errorf("failed to read investigation response: %w", err)
}
return string(response), nil
}
func NewClient(serverHost, licenseKey string) (*Client, error) {
// initialize anonymizer for automatic PII/secrets protection
// automatically masks credentials, emails, IPs, database URLs, API keys, etc.
anon, err := anonymizer.NewAnonymizer(nil)
if err != nil {
return nil, fmt.Errorf("failed to create anonymizer: %w", err)
}
client := Client{
anonymizer: anon,
}
configs := []sdk.CallConfig{
{
Calls: []any{&client.errorReport},
Host: serverHost,
Name: "error_report",
Path: "/api/v1/errors/report",
Method: sdk.CallMethodPOST,
},
{
Calls: []any{&client.issueCreate},
Host: serverHost,
Name: "issue_create",
Path: "/api/v1/issues/create",
Method: sdk.CallMethodPOST,
},
{
Calls: []any{&client.issueInvestigate, &client.issueInvestigateWithSteam},
Host: serverHost,
Name: "issue_investigate",
Path: "/api/v1/issues/investigate",
Method: sdk.CallMethodPOST,
},
}
var buildOptions []sdk.Option
buildOptions = append(buildOptions,
sdk.WithTransport(sdk.DefaultTransport()),
sdk.WithInstallationID(system.GetInstallationID()),
sdk.WithClient("Report-Errors-Example", "1.0.0"),
sdk.WithLogger(sdk.WrapLogrus(logrus.StandardLogger())),
sdk.WithPowTimeout(DefaultTimeout),
sdk.WithMaxRetries(DefaultMaxRetries),
)
if licenseKey != "" {
buildOptions = append(buildOptions, sdk.WithLicenseKey(licenseKey))
}
if err := sdk.Build(configs, buildOptions...); err != nil {
return nil, err
}
return &client, nil
}
func getErrorDetails() map[string]any {
return map[string]any{
"error_type": "connection_timeout",
"message": "Failed to connect to target host admin@company.com with API key sk-1234567890abcdef",
"target_host": "192.168.1.100",
"port": 443,
"timeout_ms": 5000,
"retry_count": 3,
"stack_trace": "goroutine panic at scanner.go:142",
"database_url": "postgres://user:password123@db.internal:5432/app",
"api_endpoint": "https://api.service.com/v1/data?token=abc123xyz789",
"user_email": "john.doe@company.com",
"config_path": "/etc/myapp/config.yml",
}
}
func getComponentsLogs() []models.SupportLogs {
return []models.SupportLogs{
{
Component: models.ComponentTypePentagi,
Logs: []string{
"ERROR: Scanner pattern match timeout after 30s for user john.doe@company.com",
"WARN: Memory usage exceeded 80% threshold, DB connection postgres://admin:secret@db:5432/prod",
"INFO: Switching to backup scanning method with API key sk-abcd1234efgh5678",
"ERROR: Backup method failed - insufficient permissions for file /home/user/.ssh/id_rsa",
"DEBUG: Processing request from IP 10.0.1.50 with session token sess_9876543210",
"TRACE: Credit card validation failed for 4532-1234-5678-9012",
},
},
}
}
func getIssueDetails() map[string]any {
return map[string]any{
"problem_description": "Scanner fails to detect specific vulnerability patterns on server admin@prod.company.com",
"affected_patterns": []string{"CVE-2023-1234", "CVE-2023-5678"},
"environment": "production",
"severity": "high",
"server_config": "aws_access_key_id=AKIAIOSFODNN7EXAMPLE aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"affected_users": []string{"admin@company.com", "support@company.com", "security@company.com"},
"database_info": "mysql://root:supersecret@prod-db.internal:3306/vulnerabilities",
"internal_token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example.token",
}
}
func main() {
var (
serverHost = flag.String("host", "support.pentagi.com", "Cloud server host")
licenseKey = flag.String("license", "", "License key (optional)")
example = flag.String("example", "all", "Example to run: error, issue, investigate, steam, all")
)
flag.Parse()
logrus.SetLevel(logrus.DebugLevel)
logrus.Printf("starting support client")
logrus.Printf("server: %s", *serverHost)
if *licenseKey != "" {
logrus.Printf("license: configured")
}
client, err := NewClient(*serverHost, *licenseKey)
if err != nil {
logrus.Fatalf("failed to create client: %v", err)
}
ctx := context.Background()
switch *example {
case "error":
runErrorReporting(ctx, client)
case "issue":
runIssueCreation(ctx, client)
case "investigate":
runInvestigation(ctx, client)
case "steam":
runSteamInvestigation(ctx, client)
case "all":
runAllExamples(ctx, client)
default:
logrus.Fatalf("unknown example: %s. Use: error, issue, investigate, steam, all", *example)
}
logrus.Println("support client demonstration completed")
}
func runErrorReporting(ctx context.Context, client *Client) {
logrus.Println("=== Automated Error Reporting ===")
if err := client.ReportError(
ctx, models.ComponentTypePentagi, getErrorDetails(),
); err != nil {
logrus.Errorf("failed to report error: %v", err)
return
}
logrus.Println("error reported successfully with data anonymization")
}
func runIssueCreation(ctx context.Context, client *Client) {
logrus.Println("=== Support Issue Creation ===")
issueID, err := client.CreateSupportIssue(
ctx, models.ComponentTypePentagi, getIssueDetails(), getComponentsLogs(),
)
if err != nil {
logrus.Errorf("failed to create support issue: %v", err)
return
}
logrus.WithField("issue_id", issueID).Println("support issue created with data anonymization")
}
func runInvestigation(ctx context.Context, client *Client) {
logrus.Println("=== AI Investigation ===")
// first create an issue
issueID, err := client.CreateSupportIssue(
ctx, models.ComponentTypePentagi, getIssueDetails(), getComponentsLogs(),
)
if err != nil {
logrus.Errorf("failed to create support issue: %v", err)
return
}
logrus.WithField("issue_id", issueID).Println("created issue")
// then investigate
userInput := "The scanner works fine with other CVE patterns but consistently fails on these two specific ones. Could this be related to pattern complexity or memory constraints?"
answer, err := client.InvestigateIssue(ctx, issueID, userInput)
if err != nil {
logrus.Errorf("failed to investigate issue: %v", err)
return
}
logrus.WithField("answer", answer).Println("AI investigation result")
// follow-up investigation
followUpInput := "Based on your analysis, what specific configuration changes would you recommend to resolve the memory constraint issue?"
followUpAnswer, err := client.InvestigateIssue(ctx, issueID, followUpInput)
if err != nil {
logrus.Errorf("failed to investigate follow-up: %v", err)
return
}
logrus.WithFields(logrus.Fields{
"issue_id": issueID,
"answer": followUpAnswer,
}).Println("follow-up recommendations")
}
func runSteamInvestigation(ctx context.Context, client *Client) {
logrus.Println("=== AI Steam Investigation ===")
// first create an issue
issueID, err := client.CreateSupportIssue(
ctx, models.ComponentTypePentagi, getIssueDetails(), getComponentsLogs(),
)
if err != nil {
logrus.Errorf("failed to create support issue: %v", err)
return
}
logrus.WithField("issue_id", issueID).Println("created issue")
// steam investigation
userInput := "Please provide a detailed analysis of the scanner performance issues and step-by-step troubleshooting guide."
steamAnswer, err := client.InvestigateIssueWithSteam(ctx, issueID, userInput)
if err != nil {
logrus.Errorf("failed to steam investigate issue: %v", err)
return
}
logrus.WithFields(logrus.Fields{
"issue_id": issueID,
"answer": steamAnswer,
}).Println("steam investigation result")
}
func runAllExamples(ctx context.Context, client *Client) {
runErrorReporting(ctx, client)
runIssueCreation(ctx, client)
runInvestigation(ctx, client)
runSteamInvestigation(ctx, client)
}