Skip to content
This repository has been archived by the owner on Jul 19, 2023. It is now read-only.

Commit

Permalink
feat: Add validate flow on local directory if path was given
Browse files Browse the repository at this point in the history
* validate without fetch

* add comment

* validate without fetch

* add comment

* template to read local data as github data

* fetch local data as github data and validate on it

* add hierarchy for github vs gitlab

* fetch gitlab files from local

* fetch gitlab files from local

* iterate only on cicd relevant to the scm

* change validate flags and running options

* refactor path handling

* refactor before PR

* Cr fixes

* Cr fixes
  • Loading branch information
OriYosef authored Sep 29, 2022
1 parent 378dc88 commit 6017a2d
Show file tree
Hide file tree
Showing 13 changed files with 424 additions and 32 deletions.
9 changes: 6 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/allero-io/allero/cmd/validate"
"github.com/allero-io/allero/cmd/version"
"github.com/allero-io/allero/pkg/configurationManager"
localConnector "github.com/allero-io/allero/pkg/connectors/local"
"github.com/allero-io/allero/pkg/posthog"
"github.com/allero-io/allero/pkg/rulesConfig"
"github.com/fatih/color"
Expand Down Expand Up @@ -38,6 +39,7 @@ var CliVersion string

func init() {
configurationManager := configurationManager.New()
localRepositoriesClient := localConnector.New()

posthogClient, _ := posthog.New(&posthog.PosthogClientDependencies{
ConfigurationManager: configurationManager,
Expand All @@ -54,9 +56,10 @@ func init() {
}))

rootCmd.AddCommand(validate.New(&validate.ValidateCommandDependencies{
ConfigurationManager: configurationManager,
PosthogClient: posthogClient,
RulesConfig: rulesConfig,
ConfigurationManager: configurationManager,
PosthogClient: posthogClient,
RulesConfig: rulesConfig,
LocalRepositoriesClient: localRepositoriesClient,
}))

rootCmd.AddCommand(version.New(&version.VersionCommandDependencies{
Expand Down
57 changes: 40 additions & 17 deletions cmd/validate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/allero-io/allero/pkg/configurationManager"
localConnector "github.com/allero-io/allero/pkg/connectors/local"
"github.com/allero-io/allero/pkg/mapStructureEncoder"
"github.com/allero-io/allero/pkg/posthog"
"github.com/allero-io/allero/pkg/resultsPrinter"
Expand All @@ -18,14 +19,16 @@ var (
)

type ValidateCommandDependencies struct {
RulesConfig *rulesConfig.RulesConfig
ConfigurationManager *configurationManager.ConfigurationManager
PosthogClient *posthog.PosthogClient
RulesConfig *rulesConfig.RulesConfig
ConfigurationManager *configurationManager.ConfigurationManager
PosthogClient *posthog.PosthogClient
LocalRepositoriesClient *localConnector.LocalConnector
}

type ValidateCommandFlags struct {
output string
ignoreToken bool
type validateCommandOptions struct {
output string
ignoreToken bool
localPathToValidate string
}

type wrapper struct {
Expand All @@ -44,12 +47,15 @@ func (w *wrapper) Run(f func(cmd *cobra.Command, args []string) error) func(cmd
func New(deps *ValidateCommandDependencies) *cobra.Command {
cmdWrap := wrapper{}
var policiesCmd = &cobra.Command{
Use: "validate",
Short: "Validate set of default rules",
Long: "Validate set of default rules over all fetched data",
Example: `allero validate`,
Use: "validate [OPTIONAL] PATH",
Short: "Validate set of default rules",
Long: "Validate set of default rules over fetched data or the given path",
Example: `allero validate Validate over fetched repositories
allero validate . Validate over current directory
allero validate ~/my-repo-dir Validate over local directory`,
SilenceUsage: true,
SilenceErrors: true,
Args: cobra.MaximumNArgs(1),
PreRun: func(cmd *cobra.Command, cmdArgs []string) {
args := make(map[string]any)
args["Args"] = cmdArgs
Expand All @@ -66,10 +72,15 @@ func New(deps *ValidateCommandDependencies) *cobra.Command {
}

ignoreToken := cmd.Flag("ignore-token").Value.String() == "true"
localPathToValidate := ""
if len(args) > 0 {
localPathToValidate = args[0]
}

validateCommandFlags := &ValidateCommandFlags{
output: output,
ignoreToken: ignoreToken,
validateCommandFlags := &validateCommandOptions{
output: output,
ignoreToken: ignoreToken,
localPathToValidate: localPathToValidate,
}

return execute(deps, validateCommandFlags)
Expand Down Expand Up @@ -105,8 +116,20 @@ func validateOutputFlag(output string) bool {
return false
}

func execute(deps *ValidateCommandDependencies, flags *ValidateCommandFlags) error {
err := deps.RulesConfig.Initialize()
func execute(deps *ValidateCommandDependencies, option *validateCommandOptions) error {
var err error
if option.localPathToValidate != "" {
err = deps.LocalRepositoriesClient.Get(option.localPathToValidate)
if err == nil {
fmt.Printf("Running validation over %s\n", option.localPathToValidate)
deps.RulesConfig.ReadLocalData()
}
}
if err != nil {
return err
}

err = deps.RulesConfig.Initialize()
if err != nil {
return err
}
Expand All @@ -123,7 +146,7 @@ func execute(deps *ValidateCommandDependencies, flags *ValidateCommandFlags) err
hasToken := false
selectedRuleIds := make(map[int]bool)

if !flags.ignoreToken {
if !option.ignoreToken {
selectedRuleIds, err = deps.RulesConfig.GetSelectedRuleIds()
if err != nil {
return err
Expand Down Expand Up @@ -185,7 +208,7 @@ func execute(deps *ValidateCommandDependencies, flags *ValidateCommandFlags) err
summary.URL = deps.ConfigurationManager.TokenGenerationUrl
}

err = resultsPrinter.PrintResults(ruleResultsById, summary, flags.output)
err = resultsPrinter.PrintResults(ruleResultsById, summary, option.output, option.localPathToValidate != "")
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/connectors/github/githubConnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ func (gc *GithubConnector) getWorkflowFilesEntities(repo *github.Repository) (ch
}

for _, cicdPlatform := range connectors.SUPPORTED_CICD_PLATFORMS {
if !cicdPlatform.GithubValid {
continue
}
relevantFilesPaths := gc.matchedFiles(tree, cicdPlatform.RelevantFilesRegex)
for _, filePath := range relevantFilesPaths {
workflowFilesEntitiesChan <- &PipelineFile{
Expand Down
13 changes: 9 additions & 4 deletions pkg/connectors/gitlab/gitlabConnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gitlabConnector
import (
"encoding/json"
"fmt"
"path"
"regexp"
"strings"

Expand Down Expand Up @@ -70,7 +71,7 @@ func (gc *GitlabConnector) processPipelineFiles(gitlabJsonObject map[string]*Git
for workflowFile := range workflowFilesChan {
byteContent, _, err := gc.client.RepositoryFiles.GetRawFile(project.ID, workflowFile.Filename, &gitlab.GetRawFileOptions{})
if err != nil {
processingError = fmt.Errorf("failed to get content for file %s from repository %s", workflowFile.Filename, project.PathWithNamespace)
processingError = fmt.Errorf("failed to get content for file %s from repository %s", workflowFile.RelativePath, project.PathWithNamespace)
continue
}

Expand All @@ -95,7 +96,7 @@ func (gc *GitlabConnector) processPipelineFiles(gitlabJsonObject map[string]*Git
} else if workflowFile.Origin == "jfrog_pipelines" {
gitlabJsonObject[project.Namespace.Name].Projects[project.Path].JfrogPipelines[escapedFilename] = workflowFile
} else {
processingError = fmt.Errorf("unsupported CICD platform %s for file %s from repository %s", workflowFile.Origin, workflowFile.Filename, project.PathWithNamespace)
processingError = fmt.Errorf("unsupported CICD platform %s for file %s from repository %s", workflowFile.Origin, workflowFile.RelativePath, project.PathWithNamespace)
continue
}
}
Expand All @@ -117,11 +118,15 @@ func (gc *GitlabConnector) getPipelineFiles(project *gitlab.Project) (chan *Pipe
}

for _, cicdPlatform := range connectors.SUPPORTED_CICD_PLATFORMS {
if !cicdPlatform.GitlabValid {
continue
}
relevantFilesPaths := gc.matchedFiles(treeNodes, cicdPlatform.RelevantFilesRegex)
for _, filePath := range relevantFilesPaths {
pipelineFilesChan <- &PipelineFile{
Filename: filePath,
Origin: cicdPlatform.Name,
RelativePath: filePath,
Filename: path.Base(filePath),
Origin: cicdPlatform.Name,
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/connectors/gitlab/gitlabJson.type.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ type GitlabProject struct {
}

type PipelineFile struct {
Filename string `json:"filename"`
Origin string `json:"origin"`
Content map[string]interface{} `json:"content"`
RelativePath string `json:"relativePath"`
Filename string `json:"filename"`
Origin string `json:"origin"`
Content map[string]interface{} `json:"content"`
}
69 changes: 69 additions & 0 deletions pkg/connectors/local/localConnector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package localConnector

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

githubConnector "github.com/allero-io/allero/pkg/connectors/github"
gitlabConnector "github.com/allero-io/allero/pkg/connectors/gitlab"
"github.com/allero-io/allero/pkg/fileManager"
)

type LocalConnector struct {
absoluteRootPath string
}

func New() *LocalConnector {
return &LocalConnector{
absoluteRootPath: "",
}
}

func (lc *LocalConnector) Get(path string) error {
abs, err := filepath.Abs(path)
if err != nil {
return err
}
lc.absoluteRootPath = abs
var localJsonObject LocalRoot
githubJsonObject := make(map[string]*githubConnector.GithubOwner)
err = lc.getLocalGithub(githubJsonObject)
if err != nil {
return err
}
localJsonObject.GithubData = githubJsonObject

gitlabJsonObject := make(map[string]*gitlabConnector.GitlabGroup)
err = lc.getLocalGitlab(gitlabJsonObject)
if err != nil {
return err
}
localJsonObject.GitlabData = gitlabJsonObject

localJson, err := json.MarshalIndent(localJsonObject, "", " ")
if err != nil {
return err
}

alleroHomedir := fileManager.GetAlleroHomedir()
return fileManager.WriteToFile(fmt.Sprintf("%s/repo_files/local.json", alleroHomedir), localJson)
}

func (lc *LocalConnector) walkAndMatchedFiles(dir string, regex string) ([]string, error) {

var allFiles []string
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
if matched, _ := regexp.MatchString(regex, path); matched {
relativePath := strings.TrimPrefix(path, lc.absoluteRootPath+"/")
allFiles = append(allFiles, relativePath)
}

return nil
})

return allFiles, err
}
119 changes: 119 additions & 0 deletions pkg/connectors/local/localGithubConnector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package localConnector

import (
"encoding/json"
"fmt"
"path"
"path/filepath"

"github.com/allero-io/allero/pkg/connectors"
githubConnector "github.com/allero-io/allero/pkg/connectors/github"
"github.com/allero-io/allero/pkg/fileManager"
)

func (lc *LocalConnector) getLocalGithub(githubJsonObject map[string]*githubConnector.GithubOwner) error {
err := lc.addRootPathAsNewRepo(githubJsonObject)
if err != nil {
return err
}

escapedRepoName := connectors.EscapeJsonKey(lc.absoluteRootPath)
err = lc.processGithubWorkflowFiles(githubJsonObject, escapedRepoName)
if err != nil {
fmt.Println(err)
return err
}
return nil

}

func (lc *LocalConnector) addRootPathAsNewRepo(githubJsonObject map[string]*githubConnector.GithubOwner) error {
githubJsonObject["local_owner"] = &githubConnector.GithubOwner{
Name: "sudo",
Type: "local_github",
ID: 0,
Repositories: make(map[string]*githubConnector.GithubRepository),
}

escapedRepoName := connectors.EscapeJsonKey(lc.absoluteRootPath)

githubJsonObject["local_owner"].Repositories[escapedRepoName] = &githubConnector.GithubRepository{
Name: escapedRepoName,
FullName: escapedRepoName,
ID: 0,
ProgrammingLanguages: nil,
GithubActionsWorkflows: make(map[string]*githubConnector.PipelineFile),
JfrogPipelines: make(map[string]*githubConnector.PipelineFile),
}

return nil
}

func (lc *LocalConnector) processGithubWorkflowFiles(githubJsonObject map[string]*githubConnector.GithubOwner, repoName string) error {
workflowFilesChan, _ := lc.getWorkflowFilesEntities(repoName)
var processingError error

for workflowFile := range workflowFilesChan {
fullPath := filepath.Join(lc.absoluteRootPath, workflowFile.RelativePath)
content, err := fileManager.ReadFile(fullPath)
if err != nil {
processingError = fmt.Errorf("failed to get content for file %s", fullPath)
continue
}

jsonContentBytes, err := connectors.YamlToJson(content)
if err != nil {
processingError = err
continue
}

jsonContent := make(map[string]interface{})
err = json.Unmarshal(jsonContentBytes, &jsonContent)
if err != nil {
processingError = err
continue
}

workflowFile.Content = jsonContent
escapedFilename := connectors.EscapeJsonKey(workflowFile.Filename)

if workflowFile.Origin == "github_actions" {
githubJsonObject["local_owner"].Repositories[repoName].GithubActionsWorkflows[escapedFilename] = workflowFile
} else if workflowFile.Origin == "jfrog_pipelines" {
githubJsonObject["local_owner"].Repositories[repoName].JfrogPipelines[escapedFilename] = workflowFile
} else {
processingError = fmt.Errorf("unsupported CICD platform %s for file %s from repository %s", workflowFile.Origin, workflowFile.RelativePath, repoName)
continue
}
}

return processingError
}

func (lc *LocalConnector) getWorkflowFilesEntities(repoName string) (chan *githubConnector.PipelineFile, error) {
workflowFilesEntitiesChan := make(chan *githubConnector.PipelineFile)

var getEntitiesErr error
go func() {
defer close(workflowFilesEntitiesChan)

for _, cicdPlatform := range connectors.SUPPORTED_CICD_PLATFORMS {
if !cicdPlatform.GithubValid {
continue
}
relevantFilesPaths, err := lc.walkAndMatchedFiles(lc.absoluteRootPath, cicdPlatform.RelevantFilesRegex)
if err != nil {
return
}
for _, filePath := range relevantFilesPaths {
workflowFilesEntitiesChan <- &githubConnector.PipelineFile{
RelativePath: filePath,
Filename: path.Base(filePath),
Origin: cicdPlatform.Name,
}
}
}
}()

return workflowFilesEntitiesChan, getEntitiesErr
}
Loading

0 comments on commit 6017a2d

Please sign in to comment.