Skip to content

Commit 98caa81

Browse files
Merge pull request #2554 from vamshi-stepsecurity/pin-composite-actions
pin composite actions
2 parents 1a3db37 + e74ceea commit 98caa81

File tree

5 files changed

+355
-0
lines changed

5 files changed

+355
-0
lines changed

remediation/workflow/metadata/actionmetadata.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Workflow struct {
2020
//On string `yaml:"on"`
2121
Env Env `yaml:"env"`
2222
Jobs Jobs `yaml:"jobs"`
23+
Runs Runs `yaml:"runs"`
2324
}
2425
type Step struct {
2526
Run string `yaml:"run"`
@@ -36,6 +37,11 @@ type Job struct {
3637
Steps []Step `yaml:"steps"`
3738
}
3839

40+
type Runs struct {
41+
Using string `yaml:"using"`
42+
Steps []Step `yaml:"steps"`
43+
}
44+
3945
type Container struct {
4046
Image string `yaml:"image"`
4147
Options string `yaml:"options"`

remediation/workflow/pin/pinactions.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ func PinActions(inputYaml string, exemptedActions []string, pinToImmutable bool,
3838
}
3939
}
4040

41+
// For composite actions
42+
if workflow.Runs.Using == "composite" {
43+
for _, run := range workflow.Runs.Steps {
44+
if len(run.Uses) > 0 {
45+
localUpdated := false
46+
out, localUpdated, err = PinAction(run.Uses, out, exemptedActions, pinToImmutable, actionCommitMap)
47+
if err != nil {
48+
return out, updated, err
49+
}
50+
updated = updated || localUpdated
51+
}
52+
}
53+
}
54+
4155
return out, updated, nil
4256
}
4357

remediation/workflow/pin/pinactions_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,36 @@ func TestPinActions(t *testing.T) {
173173
}
174174
]`))
175175

176+
httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/setup-java/commits/v4",
177+
httpmock.NewStringResponder(200, `c12b8546b67672ee38ac87bea491ac94a587f7cc`))
178+
179+
httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/setup-java/git/matching-refs/tags/v4.",
180+
httpmock.NewStringResponder(200,
181+
`[
182+
{
183+
"ref": "refs/tags/v4.5.5",
184+
"object": {
185+
"sha": "c12b8546b67672ee38ac87bea491ac94a587f7cc",
186+
"type": "commit"
187+
}
188+
}
189+
]`))
190+
191+
httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/commits/v4",
192+
httpmock.NewStringResponder(200, `c12b8546b67672ee38ac87bea491ac94a587f7ch`))
193+
194+
httpmock.RegisterResponder("GET", "https://api.github.com/repos/actions/checkout/git/matching-refs/tags/v4.",
195+
httpmock.NewStringResponder(200,
196+
`[
197+
{
198+
"ref": "refs/tags/v4.5.6",
199+
"object": {
200+
"sha": "c12b8546b67672ee38ac87bea491ac94a587f7sh",
201+
"type": "commit"
202+
}
203+
}
204+
]`))
205+
176206
httpmock.RegisterResponder("GET", "https://api.github.com/repos/rohith/publish-nuget/commits/v2",
177207
httpmock.NewStringResponder(200, `c12b8546b67672ee38ac87bea491ac94a587f7cc`))
178208

@@ -327,6 +357,7 @@ func TestPinActions(t *testing.T) {
327357
{fileName: "donotpintoimmutable.yml", wantUpdated: true, pinToImmutable: false},
328358
{fileName: "invertedcommas.yml", wantUpdated: true, pinToImmutable: false},
329359
{fileName: "pinusingmap.yml", wantUpdated: true, pinToImmutable: true},
360+
{fileName: "action.yml", wantUpdated: true, pinToImmutable: false},
330361
}
331362
for _, tt := range tests {
332363

@@ -349,6 +380,12 @@ func TestPinActions(t *testing.T) {
349380
}
350381
}
351382

383+
if tt.fileName == "action.yml" {
384+
actionCommitMap = map[string]string{
385+
"actions/checkout@v4": "c12b8546b67672ee38ac87bea491ac94a587f7sh",
386+
}
387+
}
388+
352389
output, gotUpdated, err = PinActions(string(input), tt.exemptedActions, tt.pinToImmutable, actionCommitMap)
353390
if tt.wantUpdated != gotUpdated {
354391
t.Errorf("test failed wantUpdated %v did not match gotUpdated %v", tt.wantUpdated, gotUpdated)
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
name: 'Create Component Version Composite Action'
2+
description: 'Create Component Version, Add Version Link, Add Version Files, Add Version Properties'
3+
branding:
4+
icon: 'arrow-up-right'
5+
color: 'purple'
6+
inputs:
7+
component:
8+
description: 'Component Name/ID'
9+
required: true
10+
versionname:
11+
description: 'Component Version Name'
12+
required: true
13+
description:
14+
description: 'Component version description'
15+
required: false
16+
default: 'Created by following GitHub action: Create Component Version Composite Action'
17+
linkName:
18+
description: 'Name of link to add to component version'
19+
required: false
20+
default: 'GitHub Composition Action Link Name'
21+
link:
22+
description: 'Value of the link to add to component version'
23+
required: false
24+
base:
25+
description: 'Local base directory containing files to upload if file upload is required'
26+
required: false
27+
offset:
28+
description: 'Target path offset (the directory in the version files to which these files should be added)'
29+
required: false
30+
include:
31+
description: 'An include file pattern for selecting files to add (may be repeated)'
32+
required: false
33+
exclude:
34+
description: 'An exclude file pattern for excluding files (may be repeated). Overrides includes.'
35+
required: false
36+
saveExecuteBits:
37+
description: 'Saves execute bits for files.'
38+
required: false
39+
type: boolean
40+
versionProperties:
41+
description: 'Properties to set on the component version. Each property must name name:value:secure. If you have multiple properties, then they should be separated by a new line character'
42+
required: false
43+
urlType:
44+
description: 'URL protocol to use to connect to DevOps Deploy hostname'
45+
required: false
46+
default: 'https:'
47+
hostname:
48+
description: 'DevOps Deploy hostname'
49+
required: true
50+
port:
51+
description: 'port'
52+
required: true
53+
default: '8443'
54+
username:
55+
description: 'DevOps Deploy username'
56+
required: false
57+
password:
58+
description: 'DevOps Deploy password'
59+
required: false
60+
secret: true
61+
authToken:
62+
description: 'DevOps Deploy authentication token'
63+
required: false
64+
secret: true
65+
runs:
66+
using: "composite"
67+
68+
steps:
69+
# Download udclient package and command shell scripts from composite action github repository
70+
- uses: actions/checkout@v4
71+
with:
72+
repository: jwcarmichael12/test-composite-action
73+
sparse-checkout: |
74+
artifacts/devops-deploy-client.zip
75+
scripts/addVersionFiles.sh
76+
scripts/addVersionProperties.sh
77+
sparse-checkout-cone-mode: false
78+
path: deploy
79+
80+
# Setup java environment needed to run the udclient
81+
- uses: actions/setup-java@v4
82+
with:
83+
distribution: 'adopt-openj9' # See 'Supported distributions' for available options
84+
java-version: '11'
85+
86+
# Expand the udclient package to access the executable
87+
- name: Install udclient
88+
id: udclient-install
89+
run: unzip deploy/artifacts/devops-deploy-client.zip
90+
shell: bash
91+
92+
# Setup global environment variables used by udclient
93+
- name: Set global environment variables.
94+
id: set-env-variables
95+
run: |
96+
if [ ! -z ${{ inputs.authtoken }} ]; then echo "DS_AUTH_TOKEN=${{ inputs.authtoken }}" >> $GITHUB_ENV; fi &&
97+
if [ ! -z ${{ inputs.username }} ]; then echo "DS_USERNAME=${{ inputs.username }}" >> $GITHUB_ENV; fi &&
98+
if [ ! -z ${{ inputs.password }} ]; then echo "DS_PASSWORD=${{ inputs.password }}" >> $GITHUB_ENV; fi &&
99+
echo "DS_WEB_URL=${{ inputs.urlType }}//${{ inputs.hostname }}:${{ inputs.port }}" >> $GITHUB_ENV
100+
shell: bash
101+
102+
# Create component version
103+
- name: Create component version
104+
id: create-component-version
105+
run: udclient/udclient createVersion -component "${{ inputs.component }}" -name "${{ inputs.versionname }}" -description "${{ inputs.description }}" --importing true
106+
shell: bash
107+
108+
# Add Version Link if one is specified
109+
- name: Add component version link
110+
id: add-version-link
111+
if: inputs.linkName && inputs.link
112+
run: udclient/udclient addVersionLink -component "${{ inputs.component }}" -version "${{ inputs.versionname }}" -linkName "${{ inputs.linkName }}" -link "${{ inputs.link }}"
113+
shell: bash
114+
115+
# Add Version Files if specified
116+
- name: Add component version files
117+
id: add-version-files
118+
if: ${{ inputs.base }}
119+
run: |
120+
deploy/scripts/addVersionFiles.sh
121+
shell: bash
122+
env:
123+
FILES_CMD: "udclient/udclient"
124+
FILES_COMPONENTNAME: "${{ inputs.component }}"
125+
FILES_VERSIONNAME: "${{ inputs.versionname }}"
126+
FILES_BASE: "${{ inputs.base }}"
127+
FILES_OFFSET: "${{ inputs.offset }}"
128+
FILES_INCLUDE: "${{ inputs.include }}"
129+
FILES_EXCLUDE: "${{ inputs.exclude }}"
130+
FILES_SAVEEXECUTEBITS: ${{ inputs.saveExecuteBits }}
131+
132+
# Add Version Properties if specified
133+
- name: Add component version properties
134+
id: add-version-properties
135+
if: ${{ inputs.versionProperties }}
136+
run: |
137+
deploy/scripts/addVersionProperties.sh
138+
shell: bash
139+
env:
140+
VERSION_PROPERTIES_CMD: "udclient/udclient"
141+
VERSION_PROPERTIES_COMPONENTNAME: "${{ inputs.component }}"
142+
VERSION_PROPERTIES_VERSIONNAME: "${{ inputs.versionname }}"
143+
VERSION_PROPERTIES: "${{ inputs.versionProperties }}"
144+
145+
# Tell devops deploy server that component version creation is complete
146+
- name: Finish Importing
147+
id: finish-importing
148+
run: udclient/udclient finishedImporting -component "${{ inputs.component }}" -version "${{ inputs.versionname }}"
149+
shell: bash

0 commit comments

Comments
 (0)