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
120 changes: 82 additions & 38 deletions evaluation_plans/osps/build_release/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,49 +174,95 @@ func releaseHasUniqueIdentifier(payloadData interface{}, _ map[string]*layer4.Ch
return layer4.Passed, "All releases found have a unique name"
}

func getLinks(data data.Payload) []string {
func getLinksFromProjectDocumentation(data data.Payload) (urls []string) {
doc := data.Insights.Project.Documentation
if doc == nil {
return urls
}
if doc.DetailedGuide != nil {
urls = append(urls, doc.DetailedGuide.String())
}
if doc.CodeOfConduct != nil {
urls = append(urls, doc.CodeOfConduct.String())
}
if doc.QuickstartGuide != nil {
urls = append(urls, doc.QuickstartGuide.String())
}
if doc.ReleaseProcess != nil {
urls = append(urls, doc.ReleaseProcess.String())
}
if doc.SignatureVerification != nil {
urls = append(urls, doc.SignatureVerification.String())
}
return urls
}

func getLinks(data data.Payload) (links []string) {
si := data.Insights
links := []string{
si.Header.URL,
si.Header.ProjectSISource,
si.Project.Homepage,
si.Project.Roadmap,
si.Project.Funding,
si.Project.Documentation.DetailedGuide,
si.Project.Documentation.CodeOfConduct,
si.Project.Documentation.QuickstartGuide,
si.Project.Documentation.ReleaseProcess,
si.Project.Documentation.SignatureVerification,
si.Project.Vulnerability.BugBountyProgram,
si.Project.Vulnerability.SecurityPolicy,
si.Repository.URL,
si.Repository.License.URL,
si.Repository.Security.Assessments.Self.Evidence,

if len(si.Header.URL.String()) > 0 {
links = append(links, si.Header.URL.String())
}
if data.RepositoryMetadata.OrganizationBlogURL() != nil {
links = append(links, *data.RepositoryMetadata.OrganizationBlogURL())

if si.Header.ProjectSISource != nil && len(si.Header.ProjectSISource.String()) > 0 {
links = append(links, si.Header.ProjectSISource.String())
}
for _, repo := range si.Project.Repositories {
links = append(links, repo.URL)

if si.Project != nil {
for _, repo := range si.Project.Repositories {
links = append(links, repo.Url.String())
}
links = append(links, getLinksFromProjectDocumentation(data)...)
if si.Project.HomePage != nil {
links = append(links, si.Project.HomePage.String())
}
if si.Project.Roadmap != nil {
links = append(links, si.Project.Roadmap.String())
}
if si.Project.Funding != nil {
links = append(links, si.Project.Funding.String())
}

if si.Project.VulnerabilityReporting.BugBountyProgram != nil {
links = append(links, si.Project.VulnerabilityReporting.BugBountyProgram.String())
}
if si.Project.VulnerabilityReporting.SecurityPolicy != nil {
links = append(links, si.Project.VulnerabilityReporting.SecurityPolicy.String())
}
}
if si.Repository != nil {
if len(si.Repository.Url.String()) > 0 {
links = append(links, si.Repository.Url.String())
}
if len(si.Repository.License.Url.String()) > 0 {
links = append(links, si.Repository.License.Url.String())
}

for _, repo := range si.Repository.Security.Assessments.ThirdParty {
links = append(links, repo.Evidence)
for _, tool := range si.Repository.SecurityPosture.Tools {
links = append(links, tool.Results.Adhoc.Location.String())
links = append(links, tool.Results.CI.Location.String())
links = append(links, tool.Results.Release.Location.String())
}
for _, repo := range si.Repository.SecurityPosture.Assessments.ThirdPartyAssessment {
links = append(links, repo.Evidence.String())
}
if si.Repository.SecurityPosture.Assessments.Self.Evidence != nil {
links = append(links, si.Repository.SecurityPosture.Assessments.Self.Evidence.String())
}
}

for _, tool := range si.Repository.Security.Tools {
links = append(links, tool.Results.Adhoc.Location)
links = append(links, tool.Results.CI.Location)
links = append(links, tool.Results.Release.Location)
if data.RepositoryMetadata != nil && data.RepositoryMetadata.OrganizationBlogURL() != nil {
links = append(links, *data.RepositoryMetadata.OrganizationBlogURL())
}

return links
}

func insecureURI(uri string) bool {
if !strings.HasPrefix(uri, "https://") ||
!strings.HasPrefix(uri, "ssh:") ||
!strings.HasPrefix(uri, "git:") ||
!strings.HasPrefix(uri, "git@") {
if strings.HasPrefix(uri, "https://") ||
strings.HasPrefix(uri, "ssh:") ||
strings.HasPrefix(uri, "git:") ||
strings.HasPrefix(uri, "git@") {
return false
}
return true
Expand Down Expand Up @@ -260,7 +306,7 @@ func insightsHasSlsaAttestation(payloadData interface{}, _ map[string]*layer4.Ch
return layer4.Unknown, message
}

attestations := data.Insights.Repository.Release.Attestations
attestations := data.Insights.Repository.ReleaseDetails.Attestations

for _, attestation := range attestations {
if attestation.PredicateURI == "https://slsa.dev/provenance/v1" {
Expand All @@ -275,17 +321,15 @@ func distributionPointsUseHTTPS(payloadData interface{}, _ map[string]*layer4.Ch
if message != "" {
return layer4.Unknown, message
}

distributionPoints := data.Insights.Repository.Release.DistributionPoints

if len(distributionPoints) == 0 {
if data.Insights.Repository.ReleaseDetails == nil || (data.Insights.Repository.ReleaseDetails != nil && len(data.Insights.Repository.ReleaseDetails.DistributionPoints) == 0) {
return layer4.NotApplicable, "No official distribution points found in Security Insights data"
}
distributionPoints := data.Insights.Repository.ReleaseDetails.DistributionPoints

var badURIs []string
for _, point := range distributionPoints {
if insecureURI(point.URI) {
badURIs = append(badURIs, point.URI)
if insecureURI(point.Uri) {
badURIs = append(badURIs, point.Uri)
}
}
if len(badURIs) > 0 {
Expand Down
71 changes: 46 additions & 25 deletions evaluation_plans/osps/build_release/steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
"slices"
"testing"

"github.com/ossf/si-tooling/v2/si"
"github.com/revanite-io/pvtr-github-repo/data"
"github.com/rhysd/actionlint"
"github.com/stretchr/testify/assert"
)


var goodWorkflowFile =
`name: OSPS Baseline Scan
var goodWorkflowFile = `name: OSPS Baseline Scan

on: [workflow_dispatch]

Expand All @@ -38,9 +38,7 @@ jobs:
-v ${{ github.workspace }}/docker_output:/evaluation_results \
eddieknight/pvtr-github-repo:latest`


var badWorkflowFile =
`name: OSPS Baseline Scan
var badWorkflowFile = `name: OSPS Baseline Scan

on: [workflow_dispatch]

Expand All @@ -66,25 +64,23 @@ jobs:
-v ${{ github.workspace }}/docker_output:/evaluation_results \
eddieknight/pvtr-github-repo:latest`


type testingData struct {
expectedResult bool
workflowFile string
expectedResult bool
workflowFile string
assertionMessage string
}

func TestCicdSanitizedInputParameters(t *testing.T) {

func TestCicdSanitizedInputParameters (t * testing.T) {

testData := []testingData {
testData := []testingData{
{
expectedResult: false,
workflowFile: badWorkflowFile,
expectedResult: false,
workflowFile: badWorkflowFile,
assertionMessage: "Untrusted input not detected",
},
{
expectedResult: true,
workflowFile: goodWorkflowFile,
expectedResult: true,
workflowFile: goodWorkflowFile,
assertionMessage: "Untrusted input detected where it should not have been",
},
}
Expand All @@ -100,11 +96,9 @@ func TestCicdSanitizedInputParameters (t * testing.T) {
}
}


func TestVariableExtraction(t *testing.T) {

var testScript =
`echo ${{github.event.issue.title }}
var testScript = `echo ${{github.event.issue.title }}
if ${{ github.event.commits.arbitrary.data.message}} -ne 0
then
echo "Checkout report image" ${{ githubnodotevent.commits.arbitrary.data.message}}
Expand All @@ -115,9 +109,8 @@ func TestVariableExtraction(t *testing.T) {

assert.Equal(t, slices.Contains(varNames, "github.event.issue.title"), true, "Variable extraction failed")
assert.Equal(t, slices.Contains(varNames, "github.event.commits.arbitrary.data.message"), true, "Variable extraction failed")

}

}

func TestMultipleVariables(t *testing.T) {

Expand All @@ -129,17 +122,45 @@ func TestMultipleVariables(t *testing.T) {

}


func TestRegex ( t * testing.T ) {
func TestRegex(t *testing.T) {

expression, err := regexp.Compile(regex)
if err != nil {
t.Errorf("Error compiling regex: %v", err)
return
}

assert.Equal(t, expression.Match([]byte("github.event.issue.title")), true, "regex match failed" )
assert.Equal(t, expression.Match([]byte("github.event.commits.arbitrary.data.message")), true, "regex match failed" )
assert.Equal(t, expression.Match([]byte("github.event.issue.title")), true, "regex match failed")
assert.Equal(t, expression.Match([]byte("github.event.commits.arbitrary.data.message")), true, "regex match failed")
}

func TestGetLinks(t *testing.T) {
payload := data.Payload{
RestData: &data.RestData{
Insights: si.SecurityInsights{
Header: si.Header{},
Repository: &si.Repository{},
},
},
}
links := getLinks(payload)
assert.Equal(t, len(links), 0, "getLinks should return an empty slice when no links are present")
}

func TestInsecureURI(t *testing.T) {
testData := []struct {
input string
expected bool
}{
{"http://example.com", true},
{"https://example.com", false},
{"ftp://example.com", true},
{"mailto:", true},
}
for _, data := range testData {
t.Run(data.input, func(t *testing.T) {
result := insecureURI(data.input)
assert.Equal(t, result, data.expected, fmt.Sprintf("Expected %v for input %s", data.expected, data.input))
})
}
}
2 changes: 1 addition & 1 deletion evaluation_plans/osps/docs/evaluations.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func OSPS_DO_06() (evaluation *layer4.ControlEvaluation) {
[]layer4.AssessmentStep{
reusable_steps.HasMadeReleases,
reusable_steps.HasSecurityInsightsFile,
hasDependencyManagementPolicy,
reusable_steps.HasDependencyManagementPolicy,
},
)

Expand Down
23 changes: 5 additions & 18 deletions evaluation_plans/osps/docs/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ func hasUserGuides(payloadData interface{}, _ map[string]*layer4.Change) (result
if message != "" {
return layer4.Unknown, message
}

if data.Insights.Project.Documentation.DetailedGuide == "" {
doc := data.Insights.Project.Documentation
if doc == nil || doc.DetailedGuide == nil || len(doc.DetailedGuide.String()) == 0 {
return layer4.Failed, "User guide was NOT specified in Security Insights data"
}

Expand All @@ -39,7 +39,7 @@ func acceptsVulnReports(payloadData interface{}, _ map[string]*layer4.Change) (r
return layer4.Unknown, message
}

if data.Insights.Project.Vulnerability.ReportsAccepted {
if data.Insights.Project.VulnerabilityReporting.ReportsAccepted {
return layer4.Passed, "Repository accepts vulnerability reports"
}

Expand All @@ -51,23 +51,10 @@ func hasSignatureVerificationGuide(payloadData interface{}, _ map[string]*layer4
if message != "" {
return layer4.Unknown, message
}

if data.Insights.Project.Documentation.SignatureVerification == "" {
doc := data.Insights.Project.Documentation
if doc == nil || doc.SignatureVerification == nil || len(doc.SignatureVerification.String()) == 0 {
return layer4.Failed, "Signature verification guide was NOT specified in Security Insights data"
}

return layer4.Passed, "Signature verification guide was specified in Security Insights data"
}

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

if data.Insights.Repository.Documentation.DependencyManagement == "" {
return layer4.Failed, "Dependency management policy was NOT specified in Security Insights data"
}

return layer4.Passed, "Dependency management policy was specified in Security Insights data"
}
Loading
Loading