Skip to content

Commit

Permalink
Merge pull request #2500 from step-security/feature/exclude_pin_actions
Browse files Browse the repository at this point in the history
Skip pinning for actions present in exemption list
  • Loading branch information
shubham-stepsecurity authored Jan 28, 2025
2 parents a324a94 + c6bd1e5 commit 3f42f66
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 11 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (h Handler) Invoke(ctx context.Context, req []byte) ([]byte, error) {
inputYaml = httpRequest.Body
}

fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, inputYaml, dynamoDbSvc)
fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, nil, inputYaml, dynamoDbSvc)

if err != nil {
response = events.APIGatewayProxyResponse{
Expand Down
2 changes: 1 addition & 1 deletion remediation/workflow/hardenrunner/addaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func AddAction(inputYaml, action string, pinActions bool) (string, bool, error)
}

if updated && pinActions {
out, _ = pin.PinAction(action, out)
out, _ = pin.PinAction(action, out, nil)
}

return out, updated, nil
Expand Down
29 changes: 26 additions & 3 deletions remediation/workflow/pin/pinactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

Expand All @@ -13,7 +14,7 @@ import (
"gopkg.in/yaml.v3"
)

func PinActions(inputYaml string) (string, bool, error) {
func PinActions(inputYaml string, exemptedActions []string) (string, bool, error) {
workflow := metadata.Workflow{}
updated := false
err := yaml.Unmarshal([]byte(inputYaml), &workflow)
Expand All @@ -28,7 +29,7 @@ func PinActions(inputYaml string) (string, bool, error) {
for _, step := range job.Steps {
if len(step.Uses) > 0 {
localUpdated := false
out, localUpdated = PinAction(step.Uses, out)
out, localUpdated = PinAction(step.Uses, out, exemptedActions)
updated = updated || localUpdated
}
}
Expand All @@ -37,7 +38,7 @@ func PinActions(inputYaml string) (string, bool, error) {
return out, updated, nil
}

func PinAction(action, inputYaml string) (string, bool) {
func PinAction(action, inputYaml string, exemptedActions []string) (string, bool) {

updated := false
if !strings.Contains(action, "@") || strings.HasPrefix(action, "docker://") {
Expand All @@ -50,6 +51,11 @@ func PinAction(action, inputYaml string) (string, bool) {
leftOfAt := strings.Split(action, "@")
tagOrBranch := leftOfAt[1]

// skip pinning for exempted actions
if actionExists(leftOfAt[0], exemptedActions) {
return inputYaml, updated
}

splitOnSlash := strings.Split(leftOfAt[0], "/")
owner := splitOnSlash[0]
repo := splitOnSlash[1]
Expand Down Expand Up @@ -188,3 +194,20 @@ func getSemanticVersion(client *github.Client, owner, repo, tagOrBranch, commitS
}
return tagOrBranch, nil
}

// Function to check if an action matches any pattern in the list
func actionExists(actionName string, patterns []string) bool {
for _, pattern := range patterns {
// Use filepath.Match to match the pattern
matched, err := filepath.Match(pattern, actionName)
if err != nil {
// Handle invalid patterns
fmt.Printf("Error matching pattern: %v\n", err)
continue
}
if matched {
return true
}
}
return false
}
8 changes: 5 additions & 3 deletions remediation/workflow/pin/pinactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,9 @@ func TestPinActions(t *testing.T) {
})

tests := []struct {
fileName string
wantUpdated bool
fileName string
wantUpdated bool
exemptedActions []string
}{
{fileName: "alreadypinned.yml", wantUpdated: false},
{fileName: "branch.yml", wantUpdated: true},
Expand All @@ -276,6 +277,7 @@ func TestPinActions(t *testing.T) {
{fileName: "actionwithcomment.yml", wantUpdated: true},
{fileName: "repeatedactionwithcomment.yml", wantUpdated: true},
{fileName: "immutableaction-1.yml", wantUpdated: true},
{fileName: "exemptaction.yml", wantUpdated: true, exemptedActions: []string{"actions/checkout", "rohith/*"}},
}
for _, tt := range tests {
input, err := ioutil.ReadFile(path.Join(inputDirectory, tt.fileName))
Expand All @@ -284,7 +286,7 @@ func TestPinActions(t *testing.T) {
log.Fatal(err)
}

output, gotUpdated, err := PinActions(string(input))
output, gotUpdated, err := PinActions(string(input), tt.exemptedActions)
if tt.wantUpdated != gotUpdated {
t.Errorf("test failed wantUpdated %v did not match gotUpdated %v", tt.wantUpdated, gotUpdated)
}
Expand Down
4 changes: 2 additions & 2 deletions remediation/workflow/secureworkflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const (
HardenRunnerActionName = "Harden Runner"
)

func SecureWorkflow(queryStringParams map[string]string, inputYaml string, svc dynamodbiface.DynamoDBAPI) (*permissions.SecureWorkflowReponse, error) {
func SecureWorkflow(queryStringParams map[string]string, exemptedActions []string, inputYaml string, svc dynamodbiface.DynamoDBAPI) (*permissions.SecureWorkflowReponse, error) {
pinActions, addHardenRunner, addPermissions, addProjectComment := true, true, true, true
pinnedActions, addedHardenRunner, addedPermissions := false, false, false
ignoreMissingKBs := false
Expand Down Expand Up @@ -68,7 +68,7 @@ func SecureWorkflow(queryStringParams map[string]string, inputYaml string, svc d

if pinActions {
pinnedAction, pinnedDocker := false, false
secureWorkflowReponse.FinalOutput, pinnedAction, _ = pin.PinActions(secureWorkflowReponse.FinalOutput)
secureWorkflowReponse.FinalOutput, pinnedAction, _ = pin.PinActions(secureWorkflowReponse.FinalOutput, exemptedActions)
secureWorkflowReponse.FinalOutput, pinnedDocker, _ = pin.PinDocker(secureWorkflowReponse.FinalOutput)
pinnedActions = pinnedAction || pinnedDocker
}
Expand Down
2 changes: 1 addition & 1 deletion remediation/workflow/secureworkflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func TestSecureWorkflow(t *testing.T) {
}
queryParams["addProjectComment"] = "false"

output, err := SecureWorkflow(queryParams, string(input), &mockDynamoDBClient{})
output, err := SecureWorkflow(queryParams, nil, string(input), &mockDynamoDBClient{})

if err != nil {
t.Errorf("Error not expected")
Expand Down
44 changes: 44 additions & 0 deletions testfiles/pinactions/input/exemptaction.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: publish to nuget
on:
push:
branches:
- master # Default release branch
jobs:
publish:
name: build, pack & publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

# - name: Setup dotnet
# uses: actions/setup-dotnet@v1
# with:
# dotnet-version: 3.1.200

# Publish
- name: publish on version change
id: publish_nuget
uses: brandedoutcast/publish-nuget@v2
with:
PROJECT_FILE_PATH: Core/Core.csproj
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json
publish1:
name: build, pack & publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

# - name: Setup dotnet
# uses: actions/setup-dotnet@v1
# with:
# dotnet-version: 3.1.200

# Publish
- name: publish on version change
id: publish_nuget
uses: rohith/publish-nuget@v2
with:
PROJECT_FILE_PATH: Core/Core.csproj
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json
44 changes: 44 additions & 0 deletions testfiles/pinactions/output/exemptaction.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: publish to nuget
on:
push:
branches:
- master # Default release branch
jobs:
publish:
name: build, pack & publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

# - name: Setup dotnet
# uses: actions/setup-dotnet@v1
# with:
# dotnet-version: 3.1.200

# Publish
- name: publish on version change
id: publish_nuget
uses: brandedoutcast/publish-nuget@c12b8546b67672ee38ac87bea491ac94a587f7cc # v2.5.5
with:
PROJECT_FILE_PATH: Core/Core.csproj
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json
publish1:
name: build, pack & publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

# - name: Setup dotnet
# uses: actions/setup-dotnet@v1
# with:
# dotnet-version: 3.1.200

# Publish
- name: publish on version change
id: publish_nuget
uses: rohith/publish-nuget@v2
with:
PROJECT_FILE_PATH: Core/Core.csproj
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json

0 comments on commit 3f42f66

Please sign in to comment.