Skip to content

Commit 3f42f66

Browse files
Merge pull request #2500 from step-security/feature/exclude_pin_actions
Skip pinning for actions present in exemption list
2 parents a324a94 + c6bd1e5 commit 3f42f66

File tree

8 files changed

+124
-11
lines changed

8 files changed

+124
-11
lines changed

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func (h Handler) Invoke(ctx context.Context, req []byte) ([]byte, error) {
128128
inputYaml = httpRequest.Body
129129
}
130130

131-
fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, inputYaml, dynamoDbSvc)
131+
fixResponse, err := workflow.SecureWorkflow(httpRequest.QueryStringParameters, nil, inputYaml, dynamoDbSvc)
132132

133133
if err != nil {
134134
response = events.APIGatewayProxyResponse{

remediation/workflow/hardenrunner/addaction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func AddAction(inputYaml, action string, pinActions bool) (string, bool, error)
4747
}
4848

4949
if updated && pinActions {
50-
out, _ = pin.PinAction(action, out)
50+
out, _ = pin.PinAction(action, out, nil)
5151
}
5252

5353
return out, updated, nil

remediation/workflow/pin/pinactions.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"path/filepath"
78
"regexp"
89
"strings"
910

@@ -13,7 +14,7 @@ import (
1314
"gopkg.in/yaml.v3"
1415
)
1516

16-
func PinActions(inputYaml string) (string, bool, error) {
17+
func PinActions(inputYaml string, exemptedActions []string) (string, bool, error) {
1718
workflow := metadata.Workflow{}
1819
updated := false
1920
err := yaml.Unmarshal([]byte(inputYaml), &workflow)
@@ -28,7 +29,7 @@ func PinActions(inputYaml string) (string, bool, error) {
2829
for _, step := range job.Steps {
2930
if len(step.Uses) > 0 {
3031
localUpdated := false
31-
out, localUpdated = PinAction(step.Uses, out)
32+
out, localUpdated = PinAction(step.Uses, out, exemptedActions)
3233
updated = updated || localUpdated
3334
}
3435
}
@@ -37,7 +38,7 @@ func PinActions(inputYaml string) (string, bool, error) {
3738
return out, updated, nil
3839
}
3940

40-
func PinAction(action, inputYaml string) (string, bool) {
41+
func PinAction(action, inputYaml string, exemptedActions []string) (string, bool) {
4142

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

54+
// skip pinning for exempted actions
55+
if actionExists(leftOfAt[0], exemptedActions) {
56+
return inputYaml, updated
57+
}
58+
5359
splitOnSlash := strings.Split(leftOfAt[0], "/")
5460
owner := splitOnSlash[0]
5561
repo := splitOnSlash[1]
@@ -188,3 +194,20 @@ func getSemanticVersion(client *github.Client, owner, repo, tagOrBranch, commitS
188194
}
189195
return tagOrBranch, nil
190196
}
197+
198+
// Function to check if an action matches any pattern in the list
199+
func actionExists(actionName string, patterns []string) bool {
200+
for _, pattern := range patterns {
201+
// Use filepath.Match to match the pattern
202+
matched, err := filepath.Match(pattern, actionName)
203+
if err != nil {
204+
// Handle invalid patterns
205+
fmt.Printf("Error matching pattern: %v\n", err)
206+
continue
207+
}
208+
if matched {
209+
return true
210+
}
211+
}
212+
return false
213+
}

remediation/workflow/pin/pinactions_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,9 @@ func TestPinActions(t *testing.T) {
263263
})
264264

265265
tests := []struct {
266-
fileName string
267-
wantUpdated bool
266+
fileName string
267+
wantUpdated bool
268+
exemptedActions []string
268269
}{
269270
{fileName: "alreadypinned.yml", wantUpdated: false},
270271
{fileName: "branch.yml", wantUpdated: true},
@@ -276,6 +277,7 @@ func TestPinActions(t *testing.T) {
276277
{fileName: "actionwithcomment.yml", wantUpdated: true},
277278
{fileName: "repeatedactionwithcomment.yml", wantUpdated: true},
278279
{fileName: "immutableaction-1.yml", wantUpdated: true},
280+
{fileName: "exemptaction.yml", wantUpdated: true, exemptedActions: []string{"actions/checkout", "rohith/*"}},
279281
}
280282
for _, tt := range tests {
281283
input, err := ioutil.ReadFile(path.Join(inputDirectory, tt.fileName))
@@ -284,7 +286,7 @@ func TestPinActions(t *testing.T) {
284286
log.Fatal(err)
285287
}
286288

287-
output, gotUpdated, err := PinActions(string(input))
289+
output, gotUpdated, err := PinActions(string(input), tt.exemptedActions)
288290
if tt.wantUpdated != gotUpdated {
289291
t.Errorf("test failed wantUpdated %v did not match gotUpdated %v", tt.wantUpdated, gotUpdated)
290292
}

remediation/workflow/secureworkflow.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const (
1313
HardenRunnerActionName = "Harden Runner"
1414
)
1515

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

6969
if pinActions {
7070
pinnedAction, pinnedDocker := false, false
71-
secureWorkflowReponse.FinalOutput, pinnedAction, _ = pin.PinActions(secureWorkflowReponse.FinalOutput)
71+
secureWorkflowReponse.FinalOutput, pinnedAction, _ = pin.PinActions(secureWorkflowReponse.FinalOutput, exemptedActions)
7272
secureWorkflowReponse.FinalOutput, pinnedDocker, _ = pin.PinDocker(secureWorkflowReponse.FinalOutput)
7373
pinnedActions = pinnedAction || pinnedDocker
7474
}

remediation/workflow/secureworkflow_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func TestSecureWorkflow(t *testing.T) {
148148
}
149149
queryParams["addProjectComment"] = "false"
150150

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

153153
if err != nil {
154154
t.Errorf("Error not expected")
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: publish to nuget
2+
on:
3+
push:
4+
branches:
5+
- master # Default release branch
6+
jobs:
7+
publish:
8+
name: build, pack & publish
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v1
12+
13+
# - name: Setup dotnet
14+
# uses: actions/setup-dotnet@v1
15+
# with:
16+
# dotnet-version: 3.1.200
17+
18+
# Publish
19+
- name: publish on version change
20+
id: publish_nuget
21+
uses: brandedoutcast/publish-nuget@v2
22+
with:
23+
PROJECT_FILE_PATH: Core/Core.csproj
24+
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
25+
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json
26+
publish1:
27+
name: build, pack & publish
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v1
31+
32+
# - name: Setup dotnet
33+
# uses: actions/setup-dotnet@v1
34+
# with:
35+
# dotnet-version: 3.1.200
36+
37+
# Publish
38+
- name: publish on version change
39+
id: publish_nuget
40+
uses: rohith/publish-nuget@v2
41+
with:
42+
PROJECT_FILE_PATH: Core/Core.csproj
43+
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
44+
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: publish to nuget
2+
on:
3+
push:
4+
branches:
5+
- master # Default release branch
6+
jobs:
7+
publish:
8+
name: build, pack & publish
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v1
12+
13+
# - name: Setup dotnet
14+
# uses: actions/setup-dotnet@v1
15+
# with:
16+
# dotnet-version: 3.1.200
17+
18+
# Publish
19+
- name: publish on version change
20+
id: publish_nuget
21+
uses: brandedoutcast/publish-nuget@c12b8546b67672ee38ac87bea491ac94a587f7cc # v2.5.5
22+
with:
23+
PROJECT_FILE_PATH: Core/Core.csproj
24+
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
25+
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json
26+
publish1:
27+
name: build, pack & publish
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v1
31+
32+
# - name: Setup dotnet
33+
# uses: actions/setup-dotnet@v1
34+
# with:
35+
# dotnet-version: 3.1.200
36+
37+
# Publish
38+
- name: publish on version change
39+
id: publish_nuget
40+
uses: rohith/publish-nuget@v2
41+
with:
42+
PROJECT_FILE_PATH: Core/Core.csproj
43+
NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}
44+
NUGET_SOURCE: https://nuget.pkg.github.com/OWNER/index.json

0 commit comments

Comments
 (0)