From 3ba8fe36b14871ebfc055056ed62c0e3ed1aa1d9 Mon Sep 17 00:00:00 2001 From: shubham-stepsecurity Date: Mon, 27 Jan 2025 15:39:54 +0000 Subject: [PATCH 1/4] skip pinning for actions present in exemption list --- main.go | 2 +- remediation/workflow/hardenrunner/addaction.go | 2 +- remediation/workflow/pin/pinactions.go | 16 +++++++++++++--- remediation/workflow/pin/pinactions_test.go | 2 +- remediation/workflow/secureworkflow.go | 4 ++-- remediation/workflow/secureworkflow_test.go | 2 +- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 8e4a81310..37e3a8336 100644 --- a/main.go +++ b/main.go @@ -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{ diff --git a/remediation/workflow/hardenrunner/addaction.go b/remediation/workflow/hardenrunner/addaction.go index 15703d029..40c6b0918 100644 --- a/remediation/workflow/hardenrunner/addaction.go +++ b/remediation/workflow/hardenrunner/addaction.go @@ -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 diff --git a/remediation/workflow/pin/pinactions.go b/remediation/workflow/pin/pinactions.go index 8f3837555..cd5631e94 100644 --- a/remediation/workflow/pin/pinactions.go +++ b/remediation/workflow/pin/pinactions.go @@ -13,9 +13,14 @@ 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 + exemptedActionsMap := make(map[string]bool) + for _, exemptedAction := range exemptedActions { + exemptedAction = strings.TrimRight(exemptedAction, "/") + exemptedActionsMap[exemptedAction] = true + } err := yaml.Unmarshal([]byte(inputYaml), &workflow) if err != nil { return inputYaml, updated, fmt.Errorf("unable to parse yaml %v", err) @@ -28,7 +33,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, exemptedActionsMap) updated = updated || localUpdated } } @@ -37,7 +42,7 @@ func PinActions(inputYaml string) (string, bool, error) { return out, updated, nil } -func PinAction(action, inputYaml string) (string, bool) { +func PinAction(action, inputYaml string, exemptedActionsMap map[string]bool) (string, bool) { updated := false if !strings.Contains(action, "@") || strings.HasPrefix(action, "docker://") { @@ -50,6 +55,11 @@ func PinAction(action, inputYaml string) (string, bool) { leftOfAt := strings.Split(action, "@") tagOrBranch := leftOfAt[1] + // skip pinning for exempted actions + if exemptedActionsMap[leftOfAt[0]] { + return inputYaml, updated + } + splitOnSlash := strings.Split(leftOfAt[0], "/") owner := splitOnSlash[0] repo := splitOnSlash[1] diff --git a/remediation/workflow/pin/pinactions_test.go b/remediation/workflow/pin/pinactions_test.go index 3e7c0ef8b..487070506 100644 --- a/remediation/workflow/pin/pinactions_test.go +++ b/remediation/workflow/pin/pinactions_test.go @@ -284,7 +284,7 @@ func TestPinActions(t *testing.T) { log.Fatal(err) } - output, gotUpdated, err := PinActions(string(input)) + output, gotUpdated, err := PinActions(string(input), nil) if tt.wantUpdated != gotUpdated { t.Errorf("test failed wantUpdated %v did not match gotUpdated %v", tt.wantUpdated, gotUpdated) } diff --git a/remediation/workflow/secureworkflow.go b/remediation/workflow/secureworkflow.go index 06cada8d2..94adbe381 100644 --- a/remediation/workflow/secureworkflow.go +++ b/remediation/workflow/secureworkflow.go @@ -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 @@ -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 } diff --git a/remediation/workflow/secureworkflow_test.go b/remediation/workflow/secureworkflow_test.go index 9b5baa8b7..41aeafc58 100644 --- a/remediation/workflow/secureworkflow_test.go +++ b/remediation/workflow/secureworkflow_test.go @@ -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") From c988535ad8c1e09a41526ce99e79975e424dd865 Mon Sep 17 00:00:00 2001 From: shubham-stepsecurity Date: Tue, 28 Jan 2025 10:55:53 +0000 Subject: [PATCH 2/4] update pattern matching & add test cases --- remediation/workflow/pin/pinactions.go | 29 +++++++++---- remediation/workflow/pin/pinactions_test.go | 8 ++-- testfiles/pinactions/input/exemptaction.yml | 44 ++++++++++++++++++++ testfiles/pinactions/output/exemptaction.yml | 44 ++++++++++++++++++++ 4 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 testfiles/pinactions/input/exemptaction.yml create mode 100644 testfiles/pinactions/output/exemptaction.yml diff --git a/remediation/workflow/pin/pinactions.go b/remediation/workflow/pin/pinactions.go index cd5631e94..6b6849ba6 100644 --- a/remediation/workflow/pin/pinactions.go +++ b/remediation/workflow/pin/pinactions.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "path/filepath" "regexp" "strings" @@ -16,11 +17,6 @@ import ( func PinActions(inputYaml string, exemptedActions []string) (string, bool, error) { workflow := metadata.Workflow{} updated := false - exemptedActionsMap := make(map[string]bool) - for _, exemptedAction := range exemptedActions { - exemptedAction = strings.TrimRight(exemptedAction, "/") - exemptedActionsMap[exemptedAction] = true - } err := yaml.Unmarshal([]byte(inputYaml), &workflow) if err != nil { return inputYaml, updated, fmt.Errorf("unable to parse yaml %v", err) @@ -33,7 +29,7 @@ func PinActions(inputYaml string, exemptedActions []string) (string, bool, error for _, step := range job.Steps { if len(step.Uses) > 0 { localUpdated := false - out, localUpdated = PinAction(step.Uses, out, exemptedActionsMap) + out, localUpdated = PinAction(step.Uses, out, exemptedActions) updated = updated || localUpdated } } @@ -42,7 +38,7 @@ func PinActions(inputYaml string, exemptedActions []string) (string, bool, error return out, updated, nil } -func PinAction(action, inputYaml string, exemptedActionsMap map[string]bool) (string, bool) { +func PinAction(action, inputYaml string, exemptedActions []string) (string, bool) { updated := false if !strings.Contains(action, "@") || strings.HasPrefix(action, "docker://") { @@ -56,7 +52,7 @@ func PinAction(action, inputYaml string, exemptedActionsMap map[string]bool) (st tagOrBranch := leftOfAt[1] // skip pinning for exempted actions - if exemptedActionsMap[leftOfAt[0]] { + if actionExists(leftOfAt[0], exemptedActions) { return inputYaml, updated } @@ -198,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 +} diff --git a/remediation/workflow/pin/pinactions_test.go b/remediation/workflow/pin/pinactions_test.go index 487070506..95fc2e044 100644 --- a/remediation/workflow/pin/pinactions_test.go +++ b/remediation/workflow/pin/pinactions_test.go @@ -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}, @@ -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)) @@ -284,7 +286,7 @@ func TestPinActions(t *testing.T) { log.Fatal(err) } - output, gotUpdated, err := PinActions(string(input), nil) + 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) } diff --git a/testfiles/pinactions/input/exemptaction.yml b/testfiles/pinactions/input/exemptaction.yml new file mode 100644 index 000000000..3a80dc799 --- /dev/null +++ b/testfiles/pinactions/input/exemptaction.yml @@ -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 \ No newline at end of file diff --git a/testfiles/pinactions/output/exemptaction.yml b/testfiles/pinactions/output/exemptaction.yml new file mode 100644 index 000000000..4c986d6fd --- /dev/null +++ b/testfiles/pinactions/output/exemptaction.yml @@ -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 \ No newline at end of file From a1871e213e0ba1ab0f118cc4a64915c4ff09215f Mon Sep 17 00:00:00 2001 From: shubham-stepsecurity Date: Wed, 29 Jan 2025 13:14:09 +0000 Subject: [PATCH 3/4] add pinToImmutable configuration --- main.go | 2 +- .../workflow/hardenrunner/addaction.go | 4 +- .../workflow/hardenrunner/addaction_test.go | 2 +- remediation/workflow/pin/pinactions.go | 10 ++--- remediation/workflow/pin/pinactions_test.go | 41 +++++++++++++------ remediation/workflow/secureworkflow.go | 6 +-- remediation/workflow/secureworkflow_test.go | 2 +- .../pinactions/input/donotpintoimmutable.yml | 12 ++++++ .../pinactions/output/donotpintoimmutable.yml | 12 ++++++ 9 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 testfiles/pinactions/input/donotpintoimmutable.yml create mode 100644 testfiles/pinactions/output/donotpintoimmutable.yml diff --git a/main.go b/main.go index 37e3a8336..af354d0fe 100644 --- a/main.go +++ b/main.go @@ -128,7 +128,7 @@ func (h Handler) Invoke(ctx context.Context, req []byte) ([]byte, error) { inputYaml = httpRequest.Body } - fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, nil, inputYaml, dynamoDbSvc) + fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, nil, false, inputYaml, dynamoDbSvc) if err != nil { response = events.APIGatewayProxyResponse{ diff --git a/remediation/workflow/hardenrunner/addaction.go b/remediation/workflow/hardenrunner/addaction.go index 40c6b0918..06780611c 100644 --- a/remediation/workflow/hardenrunner/addaction.go +++ b/remediation/workflow/hardenrunner/addaction.go @@ -15,7 +15,7 @@ const ( HardenRunnerActionName = "Harden Runner" ) -func AddAction(inputYaml, action string, pinActions bool) (string, bool, error) { +func AddAction(inputYaml, action string, pinActions, pinToImmutable bool) (string, bool, error) { workflow := metadata.Workflow{} updated := false err := yaml.Unmarshal([]byte(inputYaml), &workflow) @@ -47,7 +47,7 @@ func AddAction(inputYaml, action string, pinActions bool) (string, bool, error) } if updated && pinActions { - out, _ = pin.PinAction(action, out, nil) + out, _ = pin.PinAction(action, out, nil, pinToImmutable) } return out, updated, nil diff --git a/remediation/workflow/hardenrunner/addaction_test.go b/remediation/workflow/hardenrunner/addaction_test.go index 4e722f166..32070eee0 100644 --- a/remediation/workflow/hardenrunner/addaction_test.go +++ b/remediation/workflow/hardenrunner/addaction_test.go @@ -32,7 +32,7 @@ func TestAddAction(t *testing.T) { if err != nil { t.Fatalf("error reading test file") } - got, gotUpdated, err := AddAction(string(input), tt.args.action, false) + got, gotUpdated, err := AddAction(string(input), tt.args.action, false, false) if gotUpdated != tt.wantUpdated { t.Errorf("AddAction() updated = %v, wantUpdated %v", gotUpdated, tt.wantUpdated) diff --git a/remediation/workflow/pin/pinactions.go b/remediation/workflow/pin/pinactions.go index 6b6849ba6..531667fd5 100644 --- a/remediation/workflow/pin/pinactions.go +++ b/remediation/workflow/pin/pinactions.go @@ -14,7 +14,7 @@ import ( "gopkg.in/yaml.v3" ) -func PinActions(inputYaml string, exemptedActions []string) (string, bool, error) { +func PinActions(inputYaml string, exemptedActions []string, pinToImmutable bool) (string, bool, error) { workflow := metadata.Workflow{} updated := false err := yaml.Unmarshal([]byte(inputYaml), &workflow) @@ -29,7 +29,7 @@ func PinActions(inputYaml string, exemptedActions []string) (string, bool, error for _, step := range job.Steps { if len(step.Uses) > 0 { localUpdated := false - out, localUpdated = PinAction(step.Uses, out, exemptedActions) + out, localUpdated = PinAction(step.Uses, out, exemptedActions, pinToImmutable) updated = updated || localUpdated } } @@ -38,14 +38,14 @@ func PinActions(inputYaml string, exemptedActions []string) (string, bool, error return out, updated, nil } -func PinAction(action, inputYaml string, exemptedActions []string) (string, bool) { +func PinAction(action, inputYaml string, exemptedActions []string, pinToImmutable bool) (string, bool) { updated := false if !strings.Contains(action, "@") || strings.HasPrefix(action, "docker://") { return inputYaml, updated // Cannot pin local actions and docker actions } - if isAbsolute(action) || IsImmutableAction(action) { + if isAbsolute(action) || (pinToImmutable && IsImmutableAction(action)) { return inputYaml, updated } leftOfAt := strings.Split(action, "@") @@ -84,7 +84,7 @@ func PinAction(action, inputYaml string, exemptedActions []string) (string, bool // if the action with version is immutable, then pin the action with version instead of sha pinnedActionWithVersion := fmt.Sprintf("%s@%s", leftOfAt[0], tagOrBranch) - if semanticTagRegex.MatchString(tagOrBranch) && IsImmutableAction(pinnedActionWithVersion) { + if pinToImmutable && semanticTagRegex.MatchString(tagOrBranch) && IsImmutableAction(pinnedActionWithVersion) { pinnedAction = pinnedActionWithVersion } diff --git a/remediation/workflow/pin/pinactions_test.go b/remediation/workflow/pin/pinactions_test.go index 95fc2e044..7b36a2cb5 100644 --- a/remediation/workflow/pin/pinactions_test.go +++ b/remediation/workflow/pin/pinactions_test.go @@ -188,6 +188,21 @@ func TestPinActions(t *testing.T) { } ]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/github/codeql-action/commits/v3.28.2", + httpmock.NewStringResponder(200, `d68b2d4edb4189fd2a5366ac14e72027bd4b37dd`)) + + httpmock.RegisterResponder("GET", "https://api.github.com/repos/github/codeql-action/git/matching-refs/tags/v3.28.2.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v3.28.2", + "object": { + "sha": "d68b2d4edb4189fd2a5366ac14e72027bd4b37dd", + "type": "commit" + } + } + ]`)) + // mock ping response httpmock.RegisterResponder("GET", "https://ghcr.io/v2/", httpmock.NewStringResponder(200, ``)) @@ -266,18 +281,20 @@ func TestPinActions(t *testing.T) { fileName string wantUpdated bool exemptedActions []string + pinToImmutable bool }{ - {fileName: "alreadypinned.yml", wantUpdated: false}, - {fileName: "branch.yml", wantUpdated: true}, - {fileName: "localaction.yml", wantUpdated: true}, - {fileName: "multiplejobs.yml", wantUpdated: true}, - {fileName: "basic.yml", wantUpdated: true}, - {fileName: "dockeraction.yml", wantUpdated: true}, - {fileName: "multipleactions.yml", wantUpdated: true}, - {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/*"}}, + {fileName: "alreadypinned.yml", wantUpdated: false, pinToImmutable: true}, + {fileName: "branch.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "localaction.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "multiplejobs.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "basic.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "dockeraction.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "multipleactions.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "actionwithcomment.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "repeatedactionwithcomment.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "immutableaction-1.yml", wantUpdated: true, pinToImmutable: true}, + {fileName: "exemptaction.yml", wantUpdated: true, exemptedActions: []string{"actions/checkout", "rohith/*"}, pinToImmutable: true}, + {fileName: "donotpintoimmutable.yml", wantUpdated: true, pinToImmutable: false}, } for _, tt := range tests { input, err := ioutil.ReadFile(path.Join(inputDirectory, tt.fileName)) @@ -286,7 +303,7 @@ func TestPinActions(t *testing.T) { log.Fatal(err) } - output, gotUpdated, err := PinActions(string(input), tt.exemptedActions) + output, gotUpdated, err := PinActions(string(input), tt.exemptedActions, tt.pinToImmutable) if tt.wantUpdated != gotUpdated { t.Errorf("test failed wantUpdated %v did not match gotUpdated %v", tt.wantUpdated, gotUpdated) } diff --git a/remediation/workflow/secureworkflow.go b/remediation/workflow/secureworkflow.go index 94adbe381..f51ab0b10 100644 --- a/remediation/workflow/secureworkflow.go +++ b/remediation/workflow/secureworkflow.go @@ -13,7 +13,7 @@ const ( HardenRunnerActionName = "Harden Runner" ) -func SecureWorkflow(queryStringParams map[string]string, exemptedActions []string, inputYaml string, svc dynamodbiface.DynamoDBAPI) (*permissions.SecureWorkflowReponse, error) { +func SecureWorkflow(queryStringParams map[string]string, exemptedActions []string, pinToImmutable bool, inputYaml string, svc dynamodbiface.DynamoDBAPI) (*permissions.SecureWorkflowReponse, error) { pinActions, addHardenRunner, addPermissions, addProjectComment := true, true, true, true pinnedActions, addedHardenRunner, addedPermissions := false, false, false ignoreMissingKBs := false @@ -68,13 +68,13 @@ func SecureWorkflow(queryStringParams map[string]string, exemptedActions []strin if pinActions { pinnedAction, pinnedDocker := false, false - secureWorkflowReponse.FinalOutput, pinnedAction, _ = pin.PinActions(secureWorkflowReponse.FinalOutput, exemptedActions) + secureWorkflowReponse.FinalOutput, pinnedAction, _ = pin.PinActions(secureWorkflowReponse.FinalOutput, exemptedActions, pinToImmutable) secureWorkflowReponse.FinalOutput, pinnedDocker, _ = pin.PinDocker(secureWorkflowReponse.FinalOutput) pinnedActions = pinnedAction || pinnedDocker } if addHardenRunner { - secureWorkflowReponse.FinalOutput, addedHardenRunner, _ = hardenrunner.AddAction(secureWorkflowReponse.FinalOutput, HardenRunnerActionPathWithTag, pinActions) + secureWorkflowReponse.FinalOutput, addedHardenRunner, _ = hardenrunner.AddAction(secureWorkflowReponse.FinalOutput, HardenRunnerActionPathWithTag, pinActions, pinToImmutable) } // Setting appropriate flags diff --git a/remediation/workflow/secureworkflow_test.go b/remediation/workflow/secureworkflow_test.go index 41aeafc58..38dce6079 100644 --- a/remediation/workflow/secureworkflow_test.go +++ b/remediation/workflow/secureworkflow_test.go @@ -148,7 +148,7 @@ func TestSecureWorkflow(t *testing.T) { } queryParams["addProjectComment"] = "false" - output, err := SecureWorkflow(queryParams, nil, string(input), &mockDynamoDBClient{}) + output, err := SecureWorkflow(queryParams, nil, false, string(input), &mockDynamoDBClient{}) if err != nil { t.Errorf("Error not expected") diff --git a/testfiles/pinactions/input/donotpintoimmutable.yml b/testfiles/pinactions/input/donotpintoimmutable.yml new file mode 100644 index 000000000..922c6f8ef --- /dev/null +++ b/testfiles/pinactions/input/donotpintoimmutable.yml @@ -0,0 +1,12 @@ +name: Integration Test Github +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: github/codeql-action/analyze@v3.28.2 + - uses: borales/actions-yarn@v2.3.0 + with: + auth-token: ${{ secrets.GITHUB_TOKEN }} + registry-url: npm.pkg.github.com diff --git a/testfiles/pinactions/output/donotpintoimmutable.yml b/testfiles/pinactions/output/donotpintoimmutable.yml new file mode 100644 index 000000000..4bfaf3f66 --- /dev/null +++ b/testfiles/pinactions/output/donotpintoimmutable.yml @@ -0,0 +1,12 @@ +name: Integration Test Github +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 + - uses: github/codeql-action/analyze@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 + - uses: borales/actions-yarn@4965e1a0f0ae9c422a9a5748ebd1fb5e097d22b9 # v2.3.0 + with: + auth-token: ${{ secrets.GITHUB_TOKEN }} + registry-url: npm.pkg.github.com From 38df01f36b110f0c303c4d8a7870f7aa99c1c9a5 Mon Sep 17 00:00:00 2001 From: shubham-stepsecurity Date: Wed, 29 Jan 2025 15:54:13 +0000 Subject: [PATCH 4/4] add exemptedActions & pinToImmutable as optional params --- main.go | 2 +- remediation/workflow/secureworkflow.go | 13 ++++++++++++- remediation/workflow/secureworkflow_test.go | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index af354d0fe..8e4a81310 100644 --- a/main.go +++ b/main.go @@ -128,7 +128,7 @@ func (h Handler) Invoke(ctx context.Context, req []byte) ([]byte, error) { inputYaml = httpRequest.Body } - fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, nil, false, inputYaml, dynamoDbSvc) + fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, inputYaml, dynamoDbSvc) if err != nil { response = events.APIGatewayProxyResponse{ diff --git a/remediation/workflow/secureworkflow.go b/remediation/workflow/secureworkflow.go index f51ab0b10..f6246b4f5 100644 --- a/remediation/workflow/secureworkflow.go +++ b/remediation/workflow/secureworkflow.go @@ -13,10 +13,21 @@ const ( HardenRunnerActionName = "Harden Runner" ) -func SecureWorkflow(queryStringParams map[string]string, exemptedActions []string, pinToImmutable bool, inputYaml string, svc dynamodbiface.DynamoDBAPI) (*permissions.SecureWorkflowReponse, error) { +func SecureWorkflow(queryStringParams map[string]string, inputYaml string, svc dynamodbiface.DynamoDBAPI, params ...interface{}) (*permissions.SecureWorkflowReponse, error) { pinActions, addHardenRunner, addPermissions, addProjectComment := true, true, true, true pinnedActions, addedHardenRunner, addedPermissions := false, false, false ignoreMissingKBs := false + exemptedActions, pinToImmutable := []string{}, false + if len(params) > 0 { + if v, ok := params[0].([]string); ok { + exemptedActions = v + } + } + if len(params) > 1 { + if v, ok := params[1].(bool); ok { + pinToImmutable = v + } + } if queryStringParams["pinActions"] == "false" { pinActions = false diff --git a/remediation/workflow/secureworkflow_test.go b/remediation/workflow/secureworkflow_test.go index 38dce6079..9b5baa8b7 100644 --- a/remediation/workflow/secureworkflow_test.go +++ b/remediation/workflow/secureworkflow_test.go @@ -148,7 +148,7 @@ func TestSecureWorkflow(t *testing.T) { } queryParams["addProjectComment"] = "false" - output, err := SecureWorkflow(queryParams, nil, false, string(input), &mockDynamoDBClient{}) + output, err := SecureWorkflow(queryParams, string(input), &mockDynamoDBClient{}) if err != nil { t.Errorf("Error not expected")