Skip to content

Commit

Permalink
mixaudit:chore - improve tests and code cleaning (#897)
Browse files Browse the repository at this point in the history
This commit add some new asserts on successful parsing Mix Audit
results, to verify that all fields of Vulnerability was filled.

Some code organization was also made, and the entities packages was
removed and the mix audit schema output was moved to
mixaudit package.

Updates #718

Signed-off-by: Matheus Alcantara <matheus.alcantara@zup.com.br>
  • Loading branch information
matheusalcantarazup authored Dec 27, 2021
1 parent 8002f7b commit c597528
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 127 deletions.
21 changes: 0 additions & 21 deletions internal/services/formatters/elixir/mixaudit/entities/advisory.go

This file was deleted.

This file was deleted.

19 changes: 0 additions & 19 deletions internal/services/formatters/elixir/mixaudit/entities/result.go

This file was deleted.

41 changes: 17 additions & 24 deletions internal/services/formatters/elixir/mixaudit/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,18 @@ import (
"github.com/ZupIT/horusec-devkit/pkg/enums/tools"
"github.com/ZupIT/horusec-devkit/pkg/utils/logger"

dockerEntities "github.com/ZupIT/horusec/internal/entities/docker"
"github.com/ZupIT/horusec/internal/entities/docker"
"github.com/ZupIT/horusec/internal/enums/images"
"github.com/ZupIT/horusec/internal/helpers/messages"
"github.com/ZupIT/horusec/internal/services/formatters"
"github.com/ZupIT/horusec/internal/services/formatters/elixir/mixaudit/entities"
vulnhash "github.com/ZupIT/horusec/internal/utils/vuln_hash"
)

type Formatter struct {
formatters.IService
}

func NewFormatter(service formatters.IService) formatters.IFormatter {
func NewFormatter(service formatters.IService) *Formatter {
return &Formatter{
service,
}
Expand All @@ -56,15 +55,15 @@ func (f *Formatter) startMixAudit(projectSubPath string) (string, error) {
f.LogDebugWithReplace(messages.MsgDebugToolStartAnalysis, tools.MixAudit, languages.Elixir)

output, err := f.ExecuteContainer(f.getConfigData(projectSubPath))
if err != nil {
if err != nil || output == "" {
return output, err
}

return output, f.parseOutput(output)
}

func (f *Formatter) getConfigData(projectSubPath string) *dockerEntities.AnalysisData {
analysisData := &dockerEntities.AnalysisData{
func (f *Formatter) getConfigData(projectSubPath string) *docker.AnalysisData {
analysisData := &docker.AnalysisData{
CMD: f.GetConfigCMDByFileExtension(projectSubPath, CMD, "mix.lock", tools.MixAudit),
Language: languages.Elixir,
}
Expand All @@ -73,33 +72,27 @@ func (f *Formatter) getConfigData(projectSubPath string) *dockerEntities.Analysi
}

func (f *Formatter) parseOutput(output string) error {
var result entities.Result
var result mixAuditResult

if err := json.Unmarshal([]byte(output), &result); err != nil {
return err
}

for index := range result.Vulnerabilities {
f.AddNewVulnerabilityIntoAnalysis(f.setVulnerabilityData(result.Vulnerabilities, index))
f.AddNewVulnerabilityIntoAnalysis(f.newVulnerability(&result.Vulnerabilities[index]))
}

return nil
}

func (f *Formatter) setVulnerabilityData(
vulnerabilities []entities.Vulnerability, index int) *vulnerability.Vulnerability {
vuln := f.getDefaultVulnerabilitySeverity()
vuln.Severity = severities.High
vuln.Details = vulnerabilities[index].GetDetails()
vuln.Code = f.GetCodeWithMaxCharacters(vulnerabilities[index].Advisory.Package, 0)
vuln.File = f.RemoveSrcFolderFromPath(vulnerabilities[index].Dependency.Lockfile)
vuln = vulnhash.Bind(vuln)
return f.SetCommitAuthor(vuln)
}

func (f *Formatter) getDefaultVulnerabilitySeverity() *vulnerability.Vulnerability {
vulnerabilitySeverity := &vulnerability.Vulnerability{}
vulnerabilitySeverity.SecurityTool = tools.MixAudit
vulnerabilitySeverity.Language = languages.Elixir
return vulnerabilitySeverity
func (f *Formatter) newVulnerability(mixVuln *mixAuditVulnerability) *vulnerability.Vulnerability {
vuln := &vulnerability.Vulnerability{
SecurityTool: tools.MixAudit,
Language: languages.Elixir,
Severity: severities.High,
Details: mixVuln.getDetails(),
Code: f.GetCodeWithMaxCharacters(mixVuln.Advisory.Package, 0),
File: f.RemoveSrcFolderFromPath(mixVuln.Dependency.Lockfile),
}
return f.SetCommitAuthor(vulnhash.Bind(vuln))
}
102 changes: 64 additions & 38 deletions internal/services/formatters/elixir/mixaudit/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,79 +19,73 @@ import (
"testing"

"github.com/ZupIT/horusec-devkit/pkg/entities/analysis"
"github.com/ZupIT/horusec-devkit/pkg/enums/languages"
"github.com/ZupIT/horusec-devkit/pkg/enums/tools"
"github.com/stretchr/testify/assert"

cliConfig "github.com/ZupIT/horusec/config"
"github.com/ZupIT/horusec/config"
"github.com/ZupIT/horusec/internal/entities/toolsconfig"
"github.com/ZupIT/horusec/internal/entities/workdir"
"github.com/ZupIT/horusec/internal/services/formatters"
"github.com/ZupIT/horusec/internal/utils/testutil"
)

func getOutputString() string {
return `{"pass":false,"vulnerabilities":[{"advisory":{"cve":"2019-15160","description":"The SweetXml (aka sweet_xml) package through 0.6.6 for Erlang and Elixir allows attackers to cause a denial of service (resource consumption) via an XML entity expansion attack with an inline DTD.\n","disclosure_date":"2019-08-19","id":"fb810971-a5c6-4268-9bd7-d931f72a87ec","package":"sweet_xml","patched_versions":[],"title":"Inline DTD allows XML bomb attack\n","unaffected_versions":[],"url":"https://github.com/kbrw/sweet_xml/issues/71"},"dependency":{"lockfile":"/src/mix.lock","package":"sweet_xml","version":"0.6.6"}}]}`
}

func TestStartCFlawfinder(t *testing.T) {
t.Run("should success execute container and process output", func(t *testing.T) {
t.Run("should add 1 vulnerability on analysis with no errors", func(t *testing.T) {
dockerAPIControllerMock := testutil.NewDockerMock()
entity := &analysis.Analysis{}
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}

output := getOutputString()

dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil)

service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config)
formatter := NewFormatter(service)
entity := new(analysis.Analysis)

service := formatters.NewFormatterService(entity, dockerAPIControllerMock, newTestConfig(t, entity))
formatter := NewFormatter(service)
formatter.StartAnalysis("")

assert.NotEmpty(t, entity)
assert.Len(t, entity.AnalysisVulnerabilities, 1)

for _, v := range entity.AnalysisVulnerabilities {
vuln := v.Vulnerability

assert.Equal(t, tools.MixAudit, vuln.SecurityTool)
assert.Equal(t, languages.Elixir, vuln.Language)
assert.NotEmpty(t, vuln.Details, "Expected not empty details")
assert.NotEmpty(t, vuln.Code, "Expected not empty code")
assert.NotEmpty(t, vuln.File, "Expected not empty file name")
assert.NotEmpty(t, vuln.Severity, "Expected not empty severity")

}
})

t.Run("should return error when invalid output", func(t *testing.T) {
t.Run("should not add error on analysis when parse empty output", func(t *testing.T) {
dockerAPIControllerMock := testutil.NewDockerMock()
entity := &analysis.Analysis{}
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}
dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", nil)

output := ""
entity := new(analysis.Analysis)

dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(output, nil)

service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config)
service := formatters.NewFormatterService(entity, dockerAPIControllerMock, newTestConfig(t, entity))
formatter := NewFormatter(service)
formatter.StartAnalysis("")

assert.NotPanics(t, func() {
formatter.StartAnalysis("")
})
assert.False(t, entity.HasErrors(), "Expected no errors on analysis")
})

t.Run("should return error when executing container", func(t *testing.T) {
t.Run("should add error on analysis when get error executing container", func(t *testing.T) {
dockerAPIControllerMock := testutil.NewDockerMock()
entity := &analysis.Analysis{}
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}

dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return("", errors.New("test"))

service := formatters.NewFormatterService(entity, dockerAPIControllerMock, config)
entity := new(analysis.Analysis)
service := formatters.NewFormatterService(entity, dockerAPIControllerMock, newTestConfig(t, entity))
formatter := NewFormatter(service)
formatter.StartAnalysis("")

assert.NotPanics(t, func() {
formatter.StartAnalysis("")
})
assert.True(t, entity.HasErrors(), "Expected errors on analysis")
})

t.Run("should not execute tool because it's ignored", func(t *testing.T) {
entity := &analysis.Analysis{}
entity := new(analysis.Analysis)
dockerAPIControllerMock := testutil.NewDockerMock()
config := &cliConfig.Config{}
config.WorkDir = &workdir.WorkDir{}

config := config.New()
config.ToolsConfig = toolsconfig.ToolsConfig{
tools.MixAudit: toolsconfig.Config{
IsToIgnore: true,
Expand All @@ -104,3 +98,35 @@ func TestStartCFlawfinder(t *testing.T) {
formatter.StartAnalysis("")
})
}

func newTestConfig(t *testing.T, analysiss *analysis.Analysis) *config.Config {
cfg := config.New()
cfg.ProjectPath = testutil.CreateHorusecAnalysisDirectory(t, analysiss, testutil.ElixirExample)
return cfg
}

const output = `
{
"pass": false,
"vulnerabilities": [
{
"advisory": {
"cve": "2019-15160",
"description": "The SweetXml (aka sweet_xml) package through 0.6.6 for Erlang and Elixir allows attackers to cause a denial of service (resource consumption) via an XML entity expansion attack with an inline DTD.\n",
"disclosure_date": "2019-08-19",
"id": "fb810971-a5c6-4268-9bd7-d931f72a87ec",
"package": "sweet_xml",
"patched_versions": [],
"title": "Inline DTD allows XML bomb attack\n",
"unaffected_versions": [],
"url": "https://github.com/kbrw/sweet_xml/issues/71"
},
"dependency": {
"lockfile": "/src/mix.lock",
"package": "sweet_xml",
"version": "0.6.6"
}
}
]
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package entities
package mixaudit

import (
"fmt"
"strings"
)

type Vulnerability struct {
Advisory Advisory `json:"advisory"`
Dependency Dependency `json:"dependency"`
type mixAuditResult struct {
Vulnerabilities []mixAuditVulnerability `json:"vulnerabilities"`
}

func (v *Vulnerability) GetDetails() string {
type mixAuditVulnerability struct {
Advisory struct {
Description string `json:"description"`
Package string `json:"package"`
Title string `json:"title"`
} `json:"advisory"`
Dependency struct {
Lockfile string `json:"lockfile"`
Version string `json:"version"`
} `json:"dependency"`
}

func (v *mixAuditVulnerability) getDetails() string {
title := strings.ReplaceAll(v.Advisory.Title, "\n", "")
description := strings.ReplaceAll(v.Advisory.Description, "\n", "")
return fmt.Sprintf("%s\n%s", title, description)
Expand Down

0 comments on commit c597528

Please sign in to comment.