Skip to content

Commit

Permalink
Add OSPS Baseline Level 1 rules.
Browse files Browse the repository at this point in the history
This change adds all currently implemented rule types for OSPS
Baseline Level 1.

Some rules were copy-pasted from rules like
e.g. `branch_protection_allow_deletions` in order to (a) be able to
change them independently and (b) change the name to something
descriptive in the scope of Security Baseline. We generally do not
foster this, but in this case we deemed simplicity was preferable to
avoiding duplication.

Along the rules themselves, tests were added to new, existing ones,
and their copies.

Fixes stacklok/minder-stories#198
  • Loading branch information
blkt committed Jan 10, 2025
1 parent 632390f commit 50f2fb9
Show file tree
Hide file tree
Showing 25 changed files with 698 additions and 33 deletions.
10 changes: 10 additions & 0 deletions data-sources/ghapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,13 @@ rest:
type: string
repo:
type: string
repo:
endpoint: https://api.github.com/repos/{owner}/{repo}
parse: json
input_schema:
type: object
properties:
owner:
type: string
repo:
type: string
49 changes: 49 additions & 0 deletions rule-types/github/branch_protection_allow_deletions.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tests:
- name: "force push not allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "pass"
http:
status: 200
body: '{"allow_deletions": {"enabled": false}}'
- name: "force push allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 200
body: '{"allow_deletions": {"enabled": true}}'
- name: "not found"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 404
body: '{"woot": "woot"}'
- name: "internal error"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 502
body: '{"woot": "woot"}'
49 changes: 49 additions & 0 deletions rule-types/github/branch_protection_allow_force_pushes.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tests:
- name: "force push not allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "pass"
http:
status: 200
body: '{"allow_force_pushes": {"enabled": false}}'
- name: "force push allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 200
body: '{"allow_force_pushes": {"enabled": true}}'
- name: "not found"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 404
body: '{"woot": "woot"}'
- name: "internal error"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 502
body: '{"woot": "woot"}'
68 changes: 35 additions & 33 deletions rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,50 +131,52 @@ func TestRuleTypes(t *testing.T) {

require.NoError(t, os.Setenv("REGO_ENABLE_PRINT", "true"))

// iterate rule types directory
err := walkRuleTypesTests(t, func(t *testing.T, rt *minderv1.RuleType, tc *RuleTest, rtDataPath string) {
var opts []tkv1.Option
if rt.Def.Ingest.Type == "git" {
opts = append(opts, gitTestOpts(t, tc, rtDataPath))
} else if rt.Def.Ingest.Type == "rest" {
opts = append(opts, httpTestOpts(t, tc, rtDataPath))
} else {
t.Skipf("Unsupported ingest type %s", rt.Def.Ingest.Type)
}
for _, folder := range []string{"rule-types", "security-baseline/rule-types"} {
// iterate rule types directory
err := walkRuleTypesTests(t, folder, func(t *testing.T, rt *minderv1.RuleType, tc *RuleTest, rtDataPath string) {
var opts []tkv1.Option
if rt.Def.Ingest.Type == "git" {
opts = append(opts, gitTestOpts(t, tc, rtDataPath))
} else if rt.Def.Ingest.Type == "rest" {
opts = append(opts, httpTestOpts(t, tc, rtDataPath))
} else {
t.Skipf("Unsupported ingest type %s", rt.Def.Ingest.Type)
}

ztw := zerolog.NewTestWriter(t)
zerolog.SetGlobalLevel(zerolog.DebugLevel)
ctx := zerolog.New(ztw).With().Timestamp().Logger().WithContext(context.Background())
ztw := zerolog.NewTestWriter(t)
zerolog.SetGlobalLevel(zerolog.DebugLevel)
ctx := zerolog.New(ztw).With().Timestamp().Logger().WithContext(context.Background())

tk := tkv1.NewTestKit(opts...)
rte, err := rtengine.NewRuleTypeEngine(ctx, rt, tk, nil)
require.NoError(t, err)
tk := tkv1.NewTestKit(opts...)
rte, err := rtengine.NewRuleTypeEngine(ctx, rt, tk, nil)
require.NoError(t, err)

val := rte.GetRuleInstanceValidator()
require.NoError(t, val.ValidateRuleDefAgainstSchema(tc.Def), "Failed to validate rule definition against schema")
require.NoError(t, val.ValidateParamsAgainstSchema(tc.Params), "Failed to validate params against schema")
val := rte.GetRuleInstanceValidator()
require.NoError(t, val.ValidateRuleDefAgainstSchema(tc.Def), "Failed to validate rule definition against schema")
require.NoError(t, val.ValidateParamsAgainstSchema(tc.Params), "Failed to validate params against schema")

if tk.ShouldOverrideIngest() {
rte.WithCustomIngester(tk)
}
if tk.ShouldOverrideIngest() {
rte.WithCustomIngester(tk)
}

_, err = rte.Eval(ctx, tc.Entity.Entity, tc.Def, tc.Params, tkv1.NewVoidResultSink())
if tc.Expect == ExpectPass {
require.NoError(t, err)
} else {
require.Error(t, err)
}
})
_, err = rte.Eval(ctx, tc.Entity.Entity, tc.Def, tc.Params, tkv1.NewVoidResultSink())
if tc.Expect == ExpectPass {
require.NoError(t, err)
} else {
require.Error(t, err)
}
})

if err != nil {
t.Error(err)
if err != nil {
t.Error(err)
}
}
}

func walkRuleTypesTests(t *testing.T, testfunc RuleTypeTestFunc) error {
func walkRuleTypesTests(t *testing.T, folder string, testfunc RuleTypeTestFunc) error {
t.Helper()

return filepath.Walk("rule-types", func(path string, info os.FileInfo, err error) error {
return filepath.Walk(folder, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions security-baseline/data-sources/ghapi.yaml
49 changes: 49 additions & 0 deletions security-baseline/rule-types/github/osps-ac-03.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tests:
- name: "force push not allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "pass"
http:
status: 200
body: '{"allow_force_pushes": {"enabled": false}}'
- name: "force push allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 200
body: '{"allow_force_pushes": {"enabled": true}}'
- name: "not found"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 404
body: '{"woot": "woot"}'
- name: "internal error"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 502
body: '{"woot": "woot"}'
38 changes: 38 additions & 0 deletions security-baseline/rule-types/github/osps-ac-03.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
version: v1
release_phase: alpha
type: rule-type
name: osps-ac-04
display_name: Prevent overwriting git history
short_failure_message: Force pushes are allowed
severity:
value: info
context:
provider: github
description: Disallow force pushes to the branch
guidance: |
Ensure that the appropriate setting is disabled for the branch
protection rule.
This setting prevents users with push access to force push to the
branch.
For more information, see [GitHub's
documentation](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule).
def:
in_entity: repository
rule_schema: {}
ingest:
type: rest
rest:
endpoint: '/repos/{{.Entity.Owner}}/{{.Entity.Name}}/branches/{{ .Entity.DefaultBranch }}/protection'
parse: json
fallback:
- http_code: 404
body: |
{"http_status": 404, "message": "Not Protected"}
eval:
type: jq
jq:
- ingested:
def: ".allow_force_pushes.enabled"
constant: false
49 changes: 49 additions & 0 deletions security-baseline/rule-types/github/osps-ac-04.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tests:
- name: "force push not allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "pass"
http:
status: 200
body: '{"allow_deletions": {"enabled": false}}'
- name: "force push allowed"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 200
body: '{"allow_deletions": {"enabled": true}}'
- name: "not found"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 404
body: '{"woot": "woot"}'
- name: "internal error"
def: {}
params: {}
entity:
type: repository
entity:
owner: "mindersec"
name: "minder"
expect: "fail"
http:
status: 502
body: '{"woot": "woot"}'
37 changes: 37 additions & 0 deletions security-baseline/rule-types/github/osps-ac-04.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
version: v1
release_phase: alpha
type: rule-type
name: osps-ac-04
display_name: Prevent permanent branch deletion
short_failure_message: Branch protection allows deletions
severity:
value: info
context:
provider: github
description: Prevents the branch from being deleted
guidance: |
Ensure that the "Allow deletions" setting is disabled for the branch
protection rule.
Prevent users from deleting matching branches.
For more information, see [GitHub's
documentation](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule).
def:
in_entity: repository
rule_schema: {}
ingest:
type: rest
rest:
endpoint: '/repos/{{.Entity.Owner}}/{{.Entity.Name}}/branches/{{ .Entity.DefaultBranch }}/protection'
parse: json
fallback:
- http_code: 404
body: |
{"http_status": 404, "message": "Not Protected"}
eval:
type: jq
jq:
- ingested:
def: ".allow_deletions.enabled"
constant: false
Loading

0 comments on commit 50f2fb9

Please sign in to comment.