Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.2.0 now with added OWASP #305

Merged
merged 4 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ come say hi!

## Documentation

---
🔥 **New in** `v.2.0` 🔥 : [OWASP API rules](https://quobix.com/vacuum/rules/owasp/) are now available out of the box.

[Learn more about enabling OWASP API rules](https://quobix.com/vacuum/rulesets/owasp/).

---

### [Quick Start Guide 🚀](https://quobix.com/vacuum/start)

See all the documentation at https://quobix.com/vacuum
Expand Down Expand Up @@ -88,6 +95,7 @@ See all the documentation at https://quobix.com/vacuum
- [Operations & Paths](https://quobix.com/vacuum/rules/operations/)
- [Validation](https://quobix.com/vacuum/rules/validation/)
- [Security](https://quobix.com/vacuum/rules/security/)
- [OWASP](https://quobix.com/vacuum/rules/owasp/)
- [Functions](https://quobix.com/vacuum/functions/)
- [Core Functions](https://quobix.com/vacuum/functions/core/)
- [OpenAPI Functions](https://quobix.com/vacuum/functions/openapi/)
Expand Down
12 changes: 7 additions & 5 deletions cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ func GetLintCommand() *cobra.Command {
cats = append(cats, model.RuleCategories[model.CategoryOperations])
case model.CategoryTags:
cats = append(cats, model.RuleCategories[model.CategoryTags])
case model.CategoryOWASP:
cats = append(cats, model.RuleCategories[model.CategoryOWASP])
default:
cats = model.RuleCategoriesOrdered
}
Expand Down Expand Up @@ -231,7 +233,7 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip
// we just render the entire table, all rows.
var tableData [][]string
if !snippets {
tableData = [][]string{{"Line / Column", "Severity", "Message", "Path"}}
tableData = [][]string{{"Line / Column", "Severity", "Message", "Rule", "Path"}}
}
for i, r := range results {

Expand All @@ -241,7 +243,7 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip
break
}
if snippets {
tableData = [][]string{{"Line / Column", "Severity", "Message", "Path"}}
tableData = [][]string{{"Line / Column", "Severity", "Message", "Rule", "Path"}}
}
startLine := 0
startCol := 0
Expand All @@ -258,8 +260,8 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip
p = fmt.Sprintf("%s...", r.Path[:60])
}

if len(r.Message) > 100 {
m = fmt.Sprintf("%s...", r.Message[:100])
if len(r.Message) > 180 {
m = fmt.Sprintf("%s...", r.Message[:180])
}

sev := "nope"
Expand All @@ -280,7 +282,7 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip
continue // only show errors
}

tableData = append(tableData, []string{start, sev, m, p})
tableData = append(tableData, []string{start, sev, m, r.Rule.Id, p})

if snippets && !silent {
_ = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render()
Expand Down
20 changes: 19 additions & 1 deletion functions/core/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package core

import (
"fmt"
"strings"

"github.com/daveshanley/vacuum/model"
"github.com/daveshanley/vacuum/parser"
Expand Down Expand Up @@ -126,6 +127,8 @@ func (sch Schema) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext)
return results
}

var bannedErrors = []string{"if-then failed", "if-else failed", "allOf failed", "oneOf failed"}

func validateNodeAgainstSchema(schema *highBase.Schema, field *yaml.Node,
context model.RuleFunctionContext, x int) []model.RuleFunctionResult {

Expand Down Expand Up @@ -153,7 +156,22 @@ func validateNodeAgainstSchema(schema *highBase.Schema, field *yaml.Node,
if p, ok := context.Given.(string); ok {
r.Path = fmt.Sprintf("%s[%d]", p, x)
}
results = append(results, r)
if p, ok := context.Given.([]string); ok {
r.Path = fmt.Sprintf("%s[%d]", p[0], x)
}

banned := false
for g := range bannedErrors {
if strings.Contains(schemaErrors[c].Reason, bannedErrors[g]) {
banned = true
continue
}
}

if !banned {
results = append(results, r)
}

}
return results
}
2 changes: 1 addition & 1 deletion functions/core/truthy.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (t *Truthy) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext)
pathValue = fmt.Sprintf("%s[%d]", pathValue, x)
}

if !utils.IsNodeMap(fieldNode) && !utils.IsNodeArray(fieldNodeValue) {
if !utils.IsNodeMap(fieldNode) && !utils.IsNodeArray(fieldNodeValue) && !utils.IsNodeMap(fieldNodeValue) {
var endNode *yaml.Node
if len(node.Content) > 0 {
endNode = node.Content[len(node.Content)-1]
Expand Down
6 changes: 2 additions & 4 deletions functions/owasp/header_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ type HeaderDefinition struct {
func (m message) String() string {
oout := ""
for _, headerSet := range m.headersSets {
oout += "{" + strings.Join(headerSet, ", ") + "}"
oout += "{" + strings.Join(headerSet, ", ") + "} "
}

return fmt.Sprintf(`response with code %d, must contain one of the defined 'headers' set:
{%s}`, m.responseCode, oout,
)
return fmt.Sprintf(`Response with code %d, must contain one of the defined 'headers': {%s}`, m.responseCode, oout)
}

// GetSchema returns a model.RuleFunctionSchema defining the schema of the HeaderDefinition rule.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/gizak/termui/v3 v3.1.0
github.com/json-iterator/go v1.1.12
github.com/mitchellh/mapstructure v1.5.0
github.com/pb33f/libopenapi v0.9.0
github.com/pb33f/libopenapi v0.9.2
github.com/pb33f/libopenapi-validator v0.0.8
github.com/pterm/pterm v0.12.62
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/pb33f/libopenapi v0.9.0 h1:JVTt4alhUdgf4R4ByGCPsPowzu2vOZyKsuNqJq4DQFg=
github.com/pb33f/libopenapi v0.9.0/go.mod h1:lvUmCtjgHUGVj6WzN3I5/CS9wkXtyN3Ykjh6ZZP5lrI=
github.com/pb33f/libopenapi v0.9.2 h1:QFxTgTSmW9mnXhQ+myignBh19ZPkFRxnAvbPnFcesDs=
github.com/pb33f/libopenapi v0.9.2/go.mod h1:lvUmCtjgHUGVj6WzN3I5/CS9wkXtyN3Ykjh6ZZP5lrI=
github.com/pb33f/libopenapi-validator v0.0.8 h1:ydeoKZ8VMnrN+ORaXP64IQCqgbIMxv7EkhukbW72e0U=
github.com/pb33f/libopenapi-validator v0.0.8/go.mod h1:uAF035zrQxpAdaoZuZZyy7eh7+Fjw3ovv4bOAPjt97U=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
Expand Down
8 changes: 8 additions & 0 deletions model/rule_categories.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ func init() {
Description: "Validation rules make sure that certain characters or patterns have not been used that may cause" +
"issues when rendering in different types of applications.",
}
RuleCategories[CategoryOWASP] = &RuleCategory{
Id: CategoryOWASP,
Name: "OWASP",
Description: "OWASP API rules check against the most common security risks in web applications " +
"and APIs. These rules check for common security risks and mistakes. ",
}

RuleCategories[CategoryAll] = &RuleCategory{
Id: CategoryAll,
Name: "All Categories",
Expand All @@ -74,5 +81,6 @@ func init() {
RuleCategories[CategoryDescriptions],
RuleCategories[CategorySecurity],
RuleCategories[CategoryExamples],
RuleCategories[CategoryOWASP],
)
}
1 change: 1 addition & 0 deletions model/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
CategorySecurity = "security"
CategoryTags = "tags"
CategoryValidation = "validation"
CategoryOWASP = "OWASP"
CategoryAll = "all"
)

Expand Down
48 changes: 48 additions & 0 deletions motor/rule_applicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package motor

import (
"fmt"
"net/url"
"sync"

Expand Down Expand Up @@ -260,6 +261,8 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult {
ruleWaitGroup.Wait()
}

ruleResults = *removeDuplicates(&ruleResults)

return &RuleSetExecutionResult{
RuleSetExecution: execution,
Results: ruleResults,
Expand Down Expand Up @@ -411,3 +414,48 @@ func buildResults(ctx ruleContext, ruleAction model.RuleAction, nodes []*yaml.No
}
return ctx.ruleResults
}

type seenResult struct {
location string
message string
}

func removeDuplicates(results *[]model.RuleFunctionResult) *[]model.RuleFunctionResult {
seen := make(map[string][]*seenResult)
var newResults []model.RuleFunctionResult
for _, result := range *results {
if result.RuleId == "" && result.Rule != nil && result.Rule.Id != "" {
result.RuleId = result.Rule.Id
}
if r, ok := seen[result.RuleId]; !ok {
if result.StartNode != nil {
seen[result.RuleId] = []*seenResult{
{
fmt.Sprintf("%d:%d", result.StartNode.Line, result.StartNode.Column),
result.Message,
},
}
newResults = append(newResults, result)
}
} else {
stopNowPlease:
for _, line := range r {
if line.location == fmt.Sprintf("%d:%d", result.StartNode.Line, result.StartNode.Column) &&
line.message == result.Message {
break stopNowPlease
}
if result.StartNode != nil {
seen[result.RuleId] = []*seenResult{
{
fmt.Sprintf("%d:%d", result.StartNode.Line, result.StartNode.Column),
result.Message,
},
}
newResults = append(newResults, result)
}
}
}
}

return &newResults
}
2 changes: 1 addition & 1 deletion motor/rule_applicator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ func TestApplyRules_Length_Description_BadConfig(t *testing.T) {
}
results := ApplyRulesToRuleSet(rse)
assert.Len(t, results.Errors, 0)
assert.Len(t, results.Results, 1)
assert.Len(t, results.Results, 0)

}

Expand Down
5 changes: 5 additions & 0 deletions motor/rule_composer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ func (rc *RuleComposer) ComposeRuleSet(ruleset []byte) (*rulesets.RuleSet, error
// check builtinFunctions exist for rules defined
for k, v := range rs.Rules {

// map ID if it's not been set.
if v.Id == "" {
v.Id = k
}

if v.Then != nil {

var ruleAction model.RuleAction
Expand Down
2 changes: 1 addition & 1 deletion motor/rule_tests/openapi_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ paths:
rules := rs.GenerateOpenAPIDefaultRuleSet()
lintExecution := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{RuleSet: rules, Spec: []byte(badDoc)})
assert.Len(t, lintExecution.Errors, 0)
assert.Len(t, lintExecution.Results, 71)
assert.Len(t, lintExecution.Results, 69)

for n := 0; n < len(lintExecution.Results); n++ {
assert.NotNil(t, lintExecution.Results[n].Path)
Expand Down
Loading