From c6270bb61a0ca19abd4bae0c563f947b3d23db3a Mon Sep 17 00:00:00 2001 From: Shubham malik Date: Sat, 19 Nov 2022 20:53:09 +0530 Subject: [PATCH] [UPDATE] Pin actions to vx.y.z format (#1469) --- remediation/workflow/pin/pinactions.go | 35 +++++ remediation/workflow/pin/pinactions_test.go | 128 ++++++++++++++++++ testfiles/pinactions/output/basic.yml | 2 +- testfiles/pinactions/output/dockeraction.yml | 2 +- testfiles/pinactions/output/localaction.yml | 10 +- .../pinactions/output/multipleactions.yml | 6 +- testfiles/pinactions/output/multiplejobs.yml | 8 +- 7 files changed, 177 insertions(+), 14 deletions(-) diff --git a/remediation/workflow/pin/pinactions.go b/remediation/workflow/pin/pinactions.go index b7a93dfe..e7a18153 100644 --- a/remediation/workflow/pin/pinactions.go +++ b/remediation/workflow/pin/pinactions.go @@ -68,6 +68,11 @@ func pinAction(action, jobName, inputYaml string) (string, bool) { return inputYaml, updated } + tagOrBranch, err = getSemanticVersion(client, owner, repo, tagOrBranch, commitSHA) + if err != nil { + return inputYaml, updated + } + pinnedAction := fmt.Sprintf("%s@%s # %s", leftOfAt[0], commitSHA, tagOrBranch) updated = !strings.EqualFold(action, pinnedAction) inputYaml = strings.ReplaceAll(inputYaml, action, pinnedAction) @@ -100,3 +105,33 @@ func isAllHex(s string) bool { } return true } + +func getSemanticVersion(client *github.Client, owner, repo, tagOrBranch, commitSHA string) (string, error) { + tags, _, err := client.Git.ListMatchingRefs(context.Background(), owner, repo, &github.ReferenceListOptions{ + Ref: fmt.Sprintf("tags/%s.", tagOrBranch), + ListOptions: github.ListOptions{ + PerPage: 100, + }, + }) + if err != nil { + return "", err + } + + for i := len(tags) - 1; i >= 0; i-- { + tag := strings.TrimPrefix(*tags[i].Ref, "refs/tags/") + if *tags[i].Object.Type == "commit" { + if commitSHA == *tags[i].Object.SHA { + return tag, nil + } + } else { + commitsha, _, err := client.Repositories.GetCommitSHA1(context.Background(), owner, repo, tag, "") + if err != nil { + return "", err + } + if commitSHA == commitsha { + return tag, nil + } + } + } + return tagOrBranch, nil +} diff --git a/remediation/workflow/pin/pinactions_test.go b/remediation/workflow/pin/pinactions_test.go index d47dc41f..1eb15b17 100644 --- a/remediation/workflow/pin/pinactions_test.go +++ b/remediation/workflow/pin/pinactions_test.go @@ -19,30 +19,158 @@ func TestPinActions(t *testing.T) { httpmock.RegisterResponder("GET", "https://api.github.com/repos/peter-evans/close-issue/commits/v1", httpmock.NewStringResponder(200, `a700eac5bf2a1c7a8cb6da0c13f93ed96fd53dbe`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/peter-evans/close-issue/git/matching-refs/tags/v1.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v1.0.3", + "object": { + "sha": "a700eac5bf2a1c7a8cb6da0c13f93ed96fd53dbe", + "type": "commit" + } + } + ]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/commits/master", httpmock.NewStringResponder(200, `61b9e3751b92087fd0b06925ba6dd6314e06f089`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/git/matching-refs/tags/master.", + httpmock.NewStringResponder(200, `[]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/elgohr/Publish-Docker-Github-Action/commits/master", httpmock.NewStringResponder(200, `8217e91c0369a5342a4ef2d612de87492410a666`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/elgohr/Publish-Docker-Github-Action/git/matching-refs/tags/master.", + httpmock.NewStringResponder(200, `[]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/borales/actions-yarn/commits/v2.3.0", httpmock.NewStringResponder(200, `4965e1a0f0ae9c422a9a5748ebd1fb5e097d22b9`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/borales/actions-yarn/git/matching-refs/tags/v2.3.0.", + httpmock.NewStringResponder(200, `[]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/commits/v1", httpmock.NewStringResponder(200, `544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/git/matching-refs/tags/v1.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v1.0.0", + "node_id": "MDM6UmVmMTk3ODE0NjI5OnJlZnMvdGFncy92MS4wLjA=", + "url": "https://api.github.com/repos/actions/checkout/git/refs/tags/v1.0.0", + "object": { + "sha": "af513c7a016048ae468971c52ed77d9562c7c819", + "type": "commit", + "url": "https://api.github.com/repos/actions/checkout/git/commits/af513c7a016048ae468971c52ed77d9562c7c819" + } + }, + { + "ref": "refs/tags/v1.1.0", + "node_id": "MDM6UmVmMTk3ODE0NjI5OnJlZnMvdGFncy92MS4xLjA=", + "url": "https://api.github.com/repos/actions/checkout/git/refs/tags/v1.1.0", + "object": { + "sha": "ec3afacf7f605c9fc12c70bc1c9e1708ddb99eca", + "type": "tag", + "url": "https://api.github.com/repos/actions/checkout/git/tags/ec3afacf7f605c9fc12c70bc1c9e1708ddb99eca" + } + }, + { + "ref": "refs/tags/v1.2.0", + "node_id": "MDM6UmVmMTk3ODE0NjI5OnJlZnMvdGFncy92MS4yLjA=", + "url": "https://api.github.com/repos/actions/checkout/git/refs/tags/v1.2.0", + "object": { + "sha": "a2ca40438991a1ab62db1b7cad0fd4e36a2ac254", + "type": "tag", + "url": "https://api.github.com/repos/actions/checkout/git/tags/a2ca40438991a1ab62db1b7cad0fd4e36a2ac254" + } + } + ]`), + ) + + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/commits/v1.2.0", + httpmock.NewStringResponder(200, `544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/setup-node/commits/v1", httpmock.NewStringResponder(200, `f1f314fca9dfce2769ece7d933488f076716723e`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/setup-node/git/matching-refs/tags/v1.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v1.4.6", + "object": { + "sha": "f1f314fca9dfce2769ece7d933488f076716723e", + "type": "commit" + } + } + ]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/JS-DevTools/npm-publish/commits/v1", httpmock.NewStringResponder(200, `0f451a94170d1699fd50710966d48fb26194d939`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/JS-DevTools/npm-publish/git/matching-refs/tags/v1.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v1.4.3", + "object": { + "sha": "0f451a94170d1699fd50710966d48fb26194d939", + "type": "commit" + } + } + ]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/brandedoutcast/publish-nuget/commits/v2", httpmock.NewStringResponder(200, `c12b8546b67672ee38ac87bea491ac94a587f7cc`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/brandedoutcast/publish-nuget/git/matching-refs/tags/v2.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v2.5.3", + "node_id": "MDM6UmVmMjI4MTk2ODk5OnJlZnMvdGFncy92Mi41LjM=", + "url": "https://api.github.com/repos/brandedoutcast/publish-nuget/git/refs/tags/v2.5.3", + "object": { + "sha": "4637c3bdd3fb4c052235299664c57b14c398cbd0", + "type": "commit", + "url": "https://api.github.com/repos/brandedoutcast/publish-nuget/git/commits/4637c3bdd3fb4c052235299664c57b14c398cbd0" + } + }, + { + "ref": "refs/tags/v2.5.4", + "node_id": "MDM6UmVmMjI4MTk2ODk5OnJlZnMvdGFncy92Mi41LjQ=", + "url": "https://api.github.com/repos/brandedoutcast/publish-nuget/git/refs/tags/v2.5.4", + "object": { + "sha": "108c10b32aa03efa5f71af6a233dc2e8e32845cb", + "type": "commit", + "url": "https://api.github.com/repos/brandedoutcast/publish-nuget/git/commits/108c10b32aa03efa5f71af6a233dc2e8e32845cb" + } + }, + { + "ref": "refs/tags/v2.5.5", + "object": { + "sha": "c12b8546b67672ee38ac87bea491ac94a587f7cc", + "type": "commit" + } + } + ]`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/rohith/publish-nuget/commits/v2", httpmock.NewStringResponder(200, `c12b8546b67672ee38ac87bea491ac94a587f7cc`)) + httpmock.RegisterResponder("GET", "https://api.github.com/repos/rohith/publish-nuget/git/matching-refs/tags/v2.", + httpmock.NewStringResponder(200, + `[ + { + "ref": "refs/tags/v2.5.5", + "object": { + "sha": "c12b8546b67672ee38ac87bea491ac94a587f7cc", + "type": "commit" + } + } + ]`)) + tests := []struct { fileName string wantUpdated bool diff --git a/testfiles/pinactions/output/basic.yml b/testfiles/pinactions/output/basic.yml index 16af4640..39f5bbb5 100644 --- a/testfiles/pinactions/output/basic.yml +++ b/testfiles/pinactions/output/basic.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Close Issue - uses: peter-evans/close-issue@a700eac5bf2a1c7a8cb6da0c13f93ed96fd53dbe # v1 + uses: peter-evans/close-issue@a700eac5bf2a1c7a8cb6da0c13f93ed96fd53dbe # v1.0.3 with: issue-number: 1 comment: Auto-closing issue \ No newline at end of file diff --git a/testfiles/pinactions/output/dockeraction.yml b/testfiles/pinactions/output/dockeraction.yml index 462079bf..6e394864 100644 --- a/testfiles/pinactions/output/dockeraction.yml +++ b/testfiles/pinactions/output/dockeraction.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 - name: Integration test uses: docker://ghcr.io/step-security/integration-test/int:latest env: diff --git a/testfiles/pinactions/output/localaction.yml b/testfiles/pinactions/output/localaction.yml index 7595f306..c6d98688 100644 --- a/testfiles/pinactions/output/localaction.yml +++ b/testfiles/pinactions/output/localaction.yml @@ -11,10 +11,10 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1 + - uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1.4.6 with: node-version: 12.x - - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 - run: npm ci - run: npm run build - run: npm run format-check @@ -32,7 +32,7 @@ jobs: steps: # Clone this repo - name: Checkout - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 # Basic checkout - name: Checkout basic @@ -150,7 +150,7 @@ jobs: steps: # Clone this repo - name: Checkout - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 # Basic checkout using git - name: Checkout basic @@ -182,7 +182,7 @@ jobs: steps: # Clone this repo - name: Checkout - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 # Basic checkout using git - name: Checkout basic diff --git a/testfiles/pinactions/output/multipleactions.yml b/testfiles/pinactions/output/multipleactions.yml index dbec7a55..9b9abf9c 100644 --- a/testfiles/pinactions/output/multipleactions.yml +++ b/testfiles/pinactions/output/multipleactions.yml @@ -4,13 +4,13 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 - - uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1 + - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 + - uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1.4.6 with: node-version: 10 - run: npm install - run: npm test - - uses: JS-DevTools/npm-publish@0f451a94170d1699fd50710966d48fb26194d939 # v1 + - uses: JS-DevTools/npm-publish@0f451a94170d1699fd50710966d48fb26194d939 # v1.4.3 with: token: ${{ secrets.GITHUB_TOKEN }} registry: https://npm.pkg.github.com \ No newline at end of file diff --git a/testfiles/pinactions/output/multiplejobs.yml b/testfiles/pinactions/output/multiplejobs.yml index d27fe09b..6d2d4721 100644 --- a/testfiles/pinactions/output/multiplejobs.yml +++ b/testfiles/pinactions/output/multiplejobs.yml @@ -8,7 +8,7 @@ jobs: name: build, pack & publish runs-on: ubuntu-latest steps: - - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 # - name: Setup dotnet # uses: actions/setup-dotnet@v1 @@ -18,7 +18,7 @@ jobs: # Publish - name: publish on version change id: publish_nuget - uses: brandedoutcast/publish-nuget@c12b8546b67672ee38ac87bea491ac94a587f7cc # v2 + uses: brandedoutcast/publish-nuget@c12b8546b67672ee38ac87bea491ac94a587f7cc # v2.5.5 with: PROJECT_FILE_PATH: Core/Core.csproj NUGET_KEY: ${{ secrets.GITHUB_TOKEN }} @@ -27,7 +27,7 @@ jobs: name: build, pack & publish runs-on: ubuntu-latest steps: - - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1 + - uses: actions/checkout@544eadc6bf3d226fd7a7a9f0dc5b5bf7ca0675b9 # v1.2.0 # - name: Setup dotnet # uses: actions/setup-dotnet@v1 @@ -37,7 +37,7 @@ jobs: # Publish - name: publish on version change id: publish_nuget - uses: rohith/publish-nuget@c12b8546b67672ee38ac87bea491ac94a587f7cc # v2 + uses: rohith/publish-nuget@c12b8546b67672ee38ac87bea491ac94a587f7cc # v2.5.5 with: PROJECT_FILE_PATH: Core/Core.csproj NUGET_KEY: ${{ secrets.GITHUB_TOKEN }}