Skip to content
Draft
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
20 changes: 20 additions & 0 deletions data/rest-data.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type RestData struct {
WorkflowPermissions WorkflowPermissions
Insights si.SecurityInsights
Releases []ReleaseData
SecurityAdvisories []SecurityAdvisory
Rulesets []Ruleset
contents RepoContent
ghClient *github.Client
Expand Down Expand Up @@ -66,6 +67,15 @@ type WorkflowPermissions struct {
CanApprovePullRequest bool `json:"can_approve_pull_request_reviews"`
}

type SecurityAdvisory struct {
GhsaId string `json:"ghsa_id"`
CveId string `json:"cve_id"`
Summary string `json:"summary"`
Severity string `json:"severity"`
State string `json:"state"`
PublishedAt string `json:"published_at"`
}

var APIBase = "https://api.github.com"

func (r *RestData) Setup() error {
Expand All @@ -77,6 +87,7 @@ func (r *RestData) Setup() error {
r.loadSecurityInsights()
_ = r.getWorkflowPermissions()
_ = r.getReleases()
_ = r.getSecurityAdvisories()
return nil
}

Expand Down Expand Up @@ -353,6 +364,15 @@ func (r *RestData) getWorkflowPermissions() error {
return err
}

func (r *RestData) getSecurityAdvisories() error {
endpoint := fmt.Sprintf("%s/repos/%s/%s/security-advisories?state=published", APIBase, r.owner, r.repo)
responseData, err := r.MakeApiCall(endpoint, true)
if err != nil {
return err
}
return json.Unmarshal(responseData, &r.SecurityAdvisories)
}

func (r *RestData) GetRulesets(branchName string) []Ruleset {
endpoint := fmt.Sprintf("%s/repos/%s/%s/rules/branches/%s", APIBase, r.owner, r.repo, branchName)
responseData, err := r.MakeApiCall(endpoint, true)
Expand Down
3 changes: 2 additions & 1 deletion evaluation_plans/osps/vuln_management/evaluations.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ func OSPS_VM_04() (evaluation *layer4.ControlEvaluation) {
"Maturity Level 3",
},
[]layer4.AssessmentStep{
reusable_steps.NotImplemented,
reusable_steps.IsActive,
hasPublicVulnerabilityDisclosure,
},
)

Expand Down
13 changes: 13 additions & 0 deletions evaluation_plans/osps/vuln_management/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@ func hasVulnerabilityDisclosurePolicy(payloadData any, _ map[string]*layer4.Chan
return layer4.Passed, "Vulnerability disclosure policy was specified in Security Insights data"
}

func hasPublicVulnerabilityDisclosure(payloadData any, _ map[string]*layer4.Change) (result layer4.Result, message string) {
data, message := reusable_steps.VerifyPayload(payloadData)
if message != "" {
return layer4.Unknown, message
}

if data.SecurityAdvisories != nil {
return layer4.Passed, "Security advisory publishing is enabled"
}

return layer4.Failed, "Security advisory publishing is not enabled"
}

func hasPrivateVulnerabilityReporting(payloadData any, _ map[string]*layer4.Change) (result layer4.Result, message string) {
data, message := reusable_steps.VerifyPayload(payloadData)
if message != "" {
Expand Down
78 changes: 78 additions & 0 deletions evaluation_plans/osps/vuln_management/steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,84 @@ func TestHasVulnerabilityDisclosurePolicy(t *testing.T) {
}
}

func TestHasPublicVulnerabilityDisclosure(t *testing.T) {
tests := []struct {
name string
payloadData any
apiResponse []byte
apiError error
expectedResult layer4.Result
expectedMessage string
}{
{
name: "Security advisory publishing is enabled with advisories",
expectedResult: layer4.Passed,
expectedMessage: "Security advisory publishing is enabled",
payloadData: data.Payload{
RestData: &data.RestData{
SecurityAdvisories: []data.SecurityAdvisory{
{
GhsaId: "GHSA-1234-5678-9012",
CveId: "CVE-2024-12345",
Summary: "Test advisory",
Severity: "high",
State: "published",
},
},
},
GraphqlRepoData: &data.GraphqlRepoData{},
},
apiResponse: []byte(`[{"ghsa_id":"GHSA-1234-5678-9012","cve_id":"CVE-2024-12345","summary":"Test advisory","severity":"high","state":"published","published_at":"2024-01-01T00:00:00Z"}]`),
apiError: nil,
},
{
name: "Security advisory publishing is enabled with no advisories",
expectedResult: layer4.Passed,
expectedMessage: "Security advisory publishing is enabled",
payloadData: data.Payload{
RestData: &data.RestData{
SecurityAdvisories: []data.SecurityAdvisory{},
},
GraphqlRepoData: &data.GraphqlRepoData{},
},
apiResponse: []byte(`[]`),
apiError: nil,
},
{
name: "Security advisory publishing is not enabled",
expectedResult: layer4.Failed,
expectedMessage: "Security advisory publishing is not enabled",
payloadData: data.Payload{
RestData: &data.RestData{
SecurityAdvisories: nil,
},
GraphqlRepoData: &data.GraphqlRepoData{},
},
apiResponse: []byte(`[]`),
apiError: nil,
},
{
name: "Invalid payload",
expectedResult: layer4.Unknown,
expectedMessage: "Malformed assessment: expected payload type data.Payload, got string (invalid_payload)",
payloadData: "invalid_payload",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if payload, ok := test.payloadData.(data.Payload); ok {
payload = data.NewPayloadWithHTTPMock(payload, test.apiResponse, 200, test.apiError)
test.payloadData = payload
}

result, message := hasPublicVulnerabilityDisclosure(test.payloadData, nil)
assert.Equal(t, test.expectedResult, result)
assert.Equal(t, test.expectedMessage, message)
})
}
}

func TestHasPrivateVulnerabilityReporting(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading