Skip to content

Commit 957e16f

Browse files
fix: resolve lambda timeout for pinToImmutable settings
1 parent cea4ccb commit 957e16f

File tree

2 files changed

+104
-4
lines changed

2 files changed

+104
-4
lines changed

remediation/workflow/pin/action_image_manifest.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"regexp"
77
"strings"
8+
"sync"
89

910
"net/http"
1011

@@ -22,6 +23,44 @@ type ociManifest struct {
2223
ArtifactType string `json:"artifactType"`
2324
}
2425

26+
// immutableResult holds the result for an image reference.
27+
type immutableResult struct {
28+
action string
29+
isImmutable bool
30+
}
31+
32+
// Run isImmutableAction concurrently for all the actions simultaneously
33+
// Individual runs takes up to 600 ms with concurrency it can be achieved under 1000 ms for all actions present
34+
func IsImmutableActionConcurrently(actions []string) map[string]bool {
35+
var wg sync.WaitGroup
36+
// Buffered channel to hold one result per image.
37+
resultChan := make(chan immutableResult, len(actions))
38+
39+
for _, action := range actions {
40+
wg.Add(1)
41+
go func(a string) {
42+
defer wg.Done()
43+
isImmutable := IsImmutableAction(a)
44+
resultChan <- immutableResult{
45+
action: a,
46+
isImmutable: isImmutable,
47+
}
48+
}(action)
49+
}
50+
51+
// Wait for all goroutines to finish.
52+
wg.Wait()
53+
close(resultChan)
54+
55+
// Collect the results.
56+
results := make(map[string]bool)
57+
for res := range resultChan {
58+
results[res.action] = res.isImmutable
59+
}
60+
61+
return results
62+
}
63+
2564
// isImmutableAction checks if the action is an immutable action or not
2665
// It queries the OCI manifest for the action and checks if the artifact type is "application/vnd.github.actions.package.v1+json"
2766
//

remediation/workflow/pin/pinactions.go

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ func PinActions(inputYaml string, exemptedActions []string, pinToImmutable bool)
2424

2525
out := inputYaml
2626

27+
// get immutable map for the semantic versions of the actions present in the workflow
28+
immutableMap := getSemanticActionsImmutableMap(workflow, pinToImmutable)
29+
2730
for _, job := range workflow.Jobs {
2831

2932
for _, step := range job.Steps {
3033
if len(step.Uses) > 0 {
3134
localUpdated := false
32-
out, localUpdated = PinAction(step.Uses, out, exemptedActions, pinToImmutable)
35+
out, localUpdated = PinAction(step.Uses, out, exemptedActions, immutableMap)
3336
updated = updated || localUpdated
3437
}
3538
}
@@ -38,14 +41,15 @@ func PinActions(inputYaml string, exemptedActions []string, pinToImmutable bool)
3841
return out, updated, nil
3942
}
4043

41-
func PinAction(action, inputYaml string, exemptedActions []string, pinToImmutable bool) (string, bool) {
44+
func PinAction(action, inputYaml string, exemptedActions []string, immutableMap map[string]bool) (string, bool) {
4245

4346
updated := false
4447
if !strings.Contains(action, "@") || strings.HasPrefix(action, "docker://") {
4548
return inputYaml, updated // Cannot pin local actions and docker actions
4649
}
4750

48-
if isAbsolute(action) || (pinToImmutable && IsImmutableAction(action)) {
51+
// if already semantic than the action must be present in immutableMap
52+
if isAbsolute(action) || immutableMap[action] {
4953
return inputYaml, updated
5054
}
5155
leftOfAt := strings.Split(action, "@")
@@ -84,7 +88,8 @@ func PinAction(action, inputYaml string, exemptedActions []string, pinToImmutabl
8488

8589
// if the action with version is immutable, then pin the action with version instead of sha
8690
pinnedActionWithVersion := fmt.Sprintf("%s@%s", leftOfAt[0], tagOrBranch)
87-
if pinToImmutable && semanticTagRegex.MatchString(tagOrBranch) && IsImmutableAction(pinnedActionWithVersion) {
91+
// if found update pinned action with immutable pinned version
92+
if semanticTagRegex.MatchString(tagOrBranch) && immutableMap[pinnedActionWithVersion] {
8893
pinnedAction = pinnedActionWithVersion
8994
}
9095

@@ -211,3 +216,59 @@ func ActionExists(actionName string, patterns []string) bool {
211216
}
212217
return false
213218
}
219+
220+
func getSemanticActionsImmutableMap(workflow metadata.Workflow, pinToImmutable bool) map[string]bool {
221+
var allActions []string
222+
var allSemanticActions []string
223+
immutableMap := make(map[string]bool)
224+
225+
// return if pinToImmutable is set to false
226+
if !pinToImmutable {
227+
return immutableMap
228+
}
229+
230+
// get all jobs present in the workflow
231+
for _, job := range workflow.Jobs {
232+
for _, step := range job.Steps {
233+
if strings.Contains(step.Uses, "@") && !strings.HasPrefix(step.Uses, "docker://") && !isAbsolute(step.Uses) {
234+
allActions = append(allActions, step.Uses)
235+
}
236+
}
237+
}
238+
239+
PAT := os.Getenv("PAT")
240+
241+
ctx := context.Background()
242+
ts := oauth2.StaticTokenSource(
243+
&oauth2.Token{AccessToken: PAT},
244+
)
245+
tc := oauth2.NewClient(ctx, ts)
246+
247+
client := github.NewClient(tc)
248+
249+
// get corresponding semantic actions for the actions present in the workflow
250+
for _, action := range allActions {
251+
leftOfAt := strings.Split(action, "@")
252+
tagOrBranch := leftOfAt[1]
253+
254+
splitOnSlash := strings.Split(leftOfAt[0], "/")
255+
owner := splitOnSlash[0]
256+
repo := splitOnSlash[1]
257+
258+
commitSHA, _, err := client.Repositories.GetCommitSHA1(ctx, owner, repo, tagOrBranch, "")
259+
if err != nil {
260+
return immutableMap
261+
}
262+
263+
tagOrBranch, err = getSemanticVersion(client, owner, repo, tagOrBranch, commitSHA)
264+
if err != nil {
265+
return immutableMap
266+
}
267+
268+
pinnedActionWithSemanticVersion := fmt.Sprintf("%s@%s", leftOfAt[0], tagOrBranch)
269+
270+
allSemanticActions = append(allSemanticActions, pinnedActionWithSemanticVersion)
271+
}
272+
273+
return IsImmutableActionConcurrently(allSemanticActions)
274+
}

0 commit comments

Comments
 (0)