Skip to content

Commit

Permalink
Pipeline + detection example fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
frikky committed Aug 19, 2024
1 parent 17323e0 commit cfd6112
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 140 deletions.
12 changes: 8 additions & 4 deletions db-connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -6824,6 +6824,11 @@ func SetWorkflowQueue(ctx context.Context, executionRequest ExecutionRequest, en
env = strings.ReplaceAll(env, " ", "-")
nameKey := fmt.Sprintf("workflowqueue-%s", env)

if project.Environment == "cloud" {
//log.Printf("[DEBUG] Adding execution to queue: %s", nameKey)
}


// New struct, to not add body, author etc
if project.DbType == "opensearch" {
data, err := json.Marshal(executionRequest)
Expand Down Expand Up @@ -8823,8 +8828,9 @@ func GetHook(ctx context.Context, hookId string) (*Hook, error) {
if err == nil && len(hook.Id) > 0 {
return hook, nil
} else {
//log.Printf("[ERROR] Failed unmarshalling cache for hook: %s", err)
return hook, errors.New(fmt.Sprintf("No good cache for hook %s", hookId))
if len(hook.Id) == 0 && len(cacheData) > 0 {
return hook, errors.New(fmt.Sprintf("No good cache for hook %s", hookId))
}
}
} else {
//log.Printf("[DEBUG] Failed getting cache for hook: %s", err)
Expand All @@ -8834,10 +8840,8 @@ func GetHook(ctx context.Context, hookId string) (*Hook, error) {

var err error
if project.DbType == "opensearch" {
//log.Printf("GETTING ES USER %s",
res, err := project.Es.Get(strings.ToLower(GetESIndexPrefix(nameKey)), hookId)
if err != nil {
log.Printf("[WARNING] Error for %s: %s", cacheKey, err)
log.Printf("[WARNING] Error for %s: %s", cacheKey, err)
return &Hook{}, err
}
Expand Down
197 changes: 179 additions & 18 deletions detection.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import (
"sort"
"strings"
"time"
"errors"

uuid "github.com/satori/go.uuid"
"gopkg.in/yaml.v2"
)

Expand All @@ -33,9 +35,9 @@ func HandleTenzirHealthUpdate(resp http.ResponseWriter, request *http.Request) {
fmt.Fprintf(resp, "Failed to decode JSON: %v", err)
return
}

ctx := context.Background()
status := healthUpdate.Status

result, err := GetDisabledRules(ctx)
if (err != nil && err.Error() == "rules doesn't exist") || err == nil {
result.IsTenzirActive = status
Expand All @@ -52,6 +54,7 @@ func HandleTenzirHealthUpdate(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
return
}

resp.WriteHeader(500)
resp.Write([]byte(`{"success": false}`))
return
Expand Down Expand Up @@ -98,6 +101,8 @@ func HandleGetDetectionRules(resp http.ResponseWriter, request *http.Request) {
return
}

log.Printf("[DEBUG] Loaded %d files for user %s from namespace %s", len(files), user.Username, detectionType)

disabledRules, err := GetDisabledRules(ctx)
if err != nil && err.Error() != "rules doesn't exist" {
log.Printf("[ERROR] Failed to get disabled rules: %s", err)
Expand All @@ -110,12 +115,6 @@ func HandleGetDetectionRules(resp http.ResponseWriter, request *http.Request) {
return files[i].UpdatedAt > files[j].UpdatedAt
})

type DetectionResponse struct {
DetectionInfo []DetectionFileInfo `json:"detection_info"`
FolderDisabled bool `json:"folder_disabled"`
IsConnectorActive bool `json:"is_connector_active"`
}

var sigmaFileInfo []DetectionFileInfo

for _, file := range files {
Expand All @@ -127,7 +126,7 @@ func HandleGetDetectionRules(resp http.ResponseWriter, request *http.Request) {

if file.Encrypted {
if project.Environment == "cloud" || file.StorageArea == "google_storage" {
log.Printf("[ERROR] No namespace handler for cloud decryption!")
log.Printf("[ERROR] No namespace handler for cloud decryption (detection)!")
//continue
} else {
Openfile, err := os.Open(file.DownloadPath)
Expand Down Expand Up @@ -214,8 +213,12 @@ func HandleGetDetectionRules(resp http.ResponseWriter, request *http.Request) {
}

response := DetectionResponse{
DetectionInfo: sigmaFileInfo,
FolderDisabled: disabledRules.DisabledFolder,
DetectionName: detectionType,
Category: "",
OrgId: user.ActiveOrg.Id,

DetectionInfo: sigmaFileInfo,
FolderDisabled: disabledRules.DisabledFolder,
IsConnectorActive: isTenzirAlive,
}

Expand Down Expand Up @@ -333,7 +336,7 @@ func HandleToggleRule(resp http.ResponseWriter, request *http.Request) {
execType = "ENABLE_SIGMA_FILE"
}

err = SetExecRequest(ctx, execType, file.Filename)
err = SetDetectionOrborusRequest(ctx, user.ActiveOrg.Id, execType, file.Filename, "SIGMA", "SHUFFLE_DISCOVER")
if err != nil {
log.Printf("[ERROR] Failed setting workflow queue for env: %s", err)
resp.WriteHeader(500)
Expand All @@ -351,6 +354,21 @@ func HandleFolderToggle(resp http.ResponseWriter, request *http.Request) {
return
}

user, err := HandleApiAuthentication(resp, request)
if err != nil {
log.Printf("[WARNING] Api authentication failed in toggle folder: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}

if user.Role == "org-reader" {
log.Printf("[WARNING] Org-reader doesn't have access to toggle folder: %s (%s)", user.Username, user.Id)
resp.WriteHeader(403)
resp.Write([]byte(`{"success": false, "reason": "Read only user"}`))
return
}

location := strings.Split(request.URL.String(), "/")
if location[1] != "api" || len(location) < 6 {
log.Printf("Path too short or incorrect: %s", request.URL.String())
Expand Down Expand Up @@ -396,7 +414,7 @@ func HandleFolderToggle(resp http.ResponseWriter, request *http.Request) {
execType = "CATEGORY_UPDATE"
}

err = SetExecRequest(ctx, execType, "")
err = SetDetectionOrborusRequest(ctx, user.ActiveOrg.Id, execType, "", "SIGMA", "SHUFFLE_DISCOVER")
if err != nil {
log.Printf("[ERROR] Failed setting workflow queue for env: %s", err)
resp.WriteHeader(500)
Expand Down Expand Up @@ -456,7 +474,7 @@ func enableRule(file File) error {
if innerFile.Id == file.Id {
resp.Files = append(resp.Files[:i], resp.Files[i+1:]...)
found = true
break
break
}
}

Expand Down Expand Up @@ -552,7 +570,7 @@ func HandleSaveSelectedRules(resp http.ResponseWriter, request *http.Request) {
triggerId := location[4]

selectedRules := SelectedDetectionRules{}

decoder := json.NewDecoder(request.Body)
err = decoder.Decode(&selectedRules)
if err != nil {
Expand All @@ -564,10 +582,10 @@ func HandleSaveSelectedRules(resp http.ResponseWriter, request *http.Request) {

err = StoreSelectedRules(request.Context(), triggerId, selectedRules)
if err != nil {
log.Printf("[ERROR] Error storing selected rules for %s: %s", triggerId, err)
resp.WriteHeader(http.StatusInternalServerError)
resp.Write([]byte(`{"success": false}`))
return
log.Printf("[ERROR] Error storing selected rules for %s: %s", triggerId, err)
resp.WriteHeader(http.StatusInternalServerError)
resp.Write([]byte(`{"success": false}`))
return
}

responseData, err := json.Marshal(selectedRules)
Expand All @@ -581,3 +599,146 @@ func HandleSaveSelectedRules(resp http.ResponseWriter, request *http.Request) {
resp.WriteHeader(http.StatusOK)
resp.Write(responseData)
}

// FIXME: Should be generic - not just for SIEM/Sigma
// E.g. try for Email/Sublime
func HandleDetectionAutoConnect(resp http.ResponseWriter, request *http.Request) {
cors := HandleCors(resp, request)
if cors {
return
}

user, err := HandleApiAuthentication(resp, request)
if err != nil {
log.Printf("[WARNING] Api authentication failed in conenct siem: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}

if user.Role == "org-reader" {
resp.WriteHeader(403)
resp.Write([]byte(`{"success": false, "reason": "Org reader does not have permission to connect to SIEM"}`))
return
}

// Check if url is /api/v1/detections/siem/
location := strings.Split(request.URL.String(), "/")
if len(location) < 5 {
log.Printf("[WARNING] Path too short: %d", len(location))
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}

detectionType := strings.ToLower(location[4])
if detectionType == "siem" {
log.Printf("[AUDIT] User '%s' (%s) is trying to connect to SIEM", user.Username, user.Id)

ctx := GetContext(request)
execType := "START_TENZIR"
err = SetDetectionOrborusRequest(ctx, user.ActiveOrg.Id, execType, "", "SIGMA", "SHUFFLE_DISCOVER")
if err != nil {
if strings.Contains(strings.ToLower(err.Error()), "must be started") {
resp.WriteHeader(200)
resp.Write([]byte(`{"success": true, "reason": "Please start the environment by running the relevant command.", "action": "environment_start"}`))
return
}


log.Printf("[ERROR] Failed setting workflow queue for env: %s", err)
if strings.Contains(strings.ToLower(err.Error()), "no valid environments") {
resp.WriteHeader(400)
resp.Write([]byte(`{"success": false, "reason": "No valid environments found. Go to /admin?tab=environments to create one.", "action": "environment_create"}`))
return
}

resp.WriteHeader(500)
resp.Write([]byte(`{"success": false}`))
return
}
} else {
resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Detection Type '%s' not implemented"}`, detectionType)))
}

resp.WriteHeader(200)
resp.Write([]byte(`{"success": true}`))
}

func SetDetectionOrborusRequest(ctx context.Context, orgId, execType, fileName, executionSource, environmentName string) error {
if len(orgId) == 0 {
return fmt.Errorf("No org ID provided")
}

environments, err := GetEnvironments(ctx, orgId)
if err != nil {
log.Printf("[ERROR] Failed to get environments: %s", err)
return err
}

selectedEnvironments := []Environment{}
for _, env := range environments {
if env.Archived {
continue
}

if env.Name == "cloud" || env.Name == "Cloud" {
continue
}

if env.Name != environmentName && environmentName != "SHUFFLE_DISCOVER" {
continue
}

selectedEnvironments = append(selectedEnvironments, env)
}

log.Printf("[DEBUG] Found %d potentially valid environment(s)", len(selectedEnvironments))

/*
if len(selectedEnvironments) == 0 || environmentName == "SHUFFLE_DISCOVER" {
// FIXME: Get based on the Organisation. This is only tested onprem so far, so there's a lot to do to make this stable ROFL
log.Printf("[DEBUG] Automatically discovering the right environment from '%s'", environmentName)
}
*/

if len(selectedEnvironments) == 0 {
log.Printf("[ERROR] No valid environments found")
return fmt.Errorf("No valid environments found")
}

deployedToActiveEnv := false
for _, env := range selectedEnvironments {
execRequest := ExecutionRequest{
Type: execType,
ExecutionId: uuid.NewV4().String(),
ExecutionSource: executionSource,
ExecutionArgument: fileName,
Priority: 11,
}

parsedEnv := fmt.Sprintf("%s_%s", strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(env.Name, " ", "-"), "_", "-")), orgId)
if project.Environment != "cloud" {
parsedEnv = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(env.Name, " ", "-"), "_", "-"))
}

err = SetWorkflowQueue(ctx, execRequest, parsedEnv)
if err != nil {
log.Printf("[ERROR] Failed to set workflow queue: %s", err)
return err
} else {
if env.RunningIp != "" {
deployedToActiveEnv = true
}
}
}

if !deployedToActiveEnv {
return errors.New("This environment must be started first. Please start the environment by running it onprem")
}

go DeleteCache(ctx, fmt.Sprintf("environments_%s", orgId))

return nil
}
Loading

0 comments on commit cfd6112

Please sign in to comment.