Skip to content

Commit a1a0cb2

Browse files
authored
🐛 include workflow uses when checking for unpinned dependencies (#4681)
* 🐛 include workflow uses when checking for unpinned dependencies Signed-off-by: Adam Korczynski <adam@adalogics.com> * rename createUnpinnedDep -> newGHActionDependency Signed-off-by: Adam Korczynski <adam@adalogics.com> * remove comment Signed-off-by: Adam Korczynski <adam@adalogics.com> --------- Signed-off-by: Adam Korczynski <adam@adalogics.com>
1 parent 8bf81c5 commit a1a0cb2

File tree

3 files changed

+79
-19
lines changed

3 files changed

+79
-19
lines changed

checks/raw/pinned_dependencies.go

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,17 @@ var validateGitHubActionWorkflow fileparser.DoWhileTrueOnFileContent = func(
744744
if len(fileparser.GetJobName(job)) > 0 {
745745
jobName = fileparser.GetJobName(job)
746746
}
747+
748+
if job.WorkflowCall != nil && job.WorkflowCall.Uses != nil {
749+
//nolint:lll
750+
// Check whether this is an action defined in the same repo,
751+
// https://docs.github.com/en/actions/learn-github-actions/finding-and-customizing-actions#referencing-an-action-in-the-same-repository-where-a-workflow-file-uses-the-action.
752+
if !strings.HasPrefix(job.WorkflowCall.Uses.Value, "./") {
753+
dep := newGHActionDependency(job.WorkflowCall.Uses.Value, pathfn, job.WorkflowCall.Uses.Pos.Line)
754+
pdata.Dependencies = append(pdata.Dependencies, dep)
755+
}
756+
}
757+
747758
for _, step := range job.Steps {
748759
if !fileparser.IsStepExecKind(step, actionlint.ExecKindAction) {
749760
continue
@@ -767,32 +778,36 @@ var validateGitHubActionWorkflow fileparser.DoWhileTrueOnFileContent = func(
767778
if strings.HasPrefix(execAction.Uses.Value, "./") {
768779
continue
769780
}
770-
771-
dep := checker.Dependency{
772-
Location: &checker.File{
773-
Path: pathfn,
774-
Type: finding.FileTypeSource,
775-
Offset: uint(execAction.Uses.Pos.Line),
776-
EndOffset: uint(execAction.Uses.Pos.Line), // `Uses` always span a single line.
777-
Snippet: execAction.Uses.Value,
778-
},
779-
Pinned: asBoolPointer(isActionDependencyPinned(execAction.Uses.Value)),
780-
Type: checker.DependencyUseTypeGHAction,
781-
}
782-
parts := strings.SplitN(execAction.Uses.Value, "@", 2)
783-
if len(parts) > 0 {
784-
dep.Name = asPointer(parts[0])
785-
if len(parts) > 1 {
786-
dep.PinnedAt = asPointer(parts[1])
787-
}
788-
}
781+
dep := newGHActionDependency(execAction.Uses.Value, pathfn, execAction.Uses.Pos.Line)
789782
pdata.Dependencies = append(pdata.Dependencies, dep)
790783
}
791784
}
792785

793786
return true, nil
794787
}
795788

789+
func newGHActionDependency(uses, pathfn string, line int) checker.Dependency {
790+
dep := checker.Dependency{
791+
Location: &checker.File{
792+
Path: pathfn,
793+
Type: finding.FileTypeSource,
794+
Offset: uint(line),
795+
EndOffset: uint(line), // `Uses` always span a single line.
796+
Snippet: uses,
797+
},
798+
Pinned: asBoolPointer(isActionDependencyPinned(uses)),
799+
Type: checker.DependencyUseTypeGHAction,
800+
}
801+
parts := strings.SplitN(uses, "@", 2)
802+
if len(parts) > 0 {
803+
dep.Name = asPointer(parts[0])
804+
if len(parts) > 1 {
805+
dep.PinnedAt = asPointer(parts[1])
806+
}
807+
}
808+
return dep
809+
}
810+
796811
func isActionDependencyPinned(actionUses string) bool {
797812
localActionRegex := regexp.MustCompile(`^\..+[^/]`)
798813
if localActionRegex.MatchString(actionUses) {

checks/raw/pinned_dependencies_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,21 @@ func TestGitHubWorkflowUsesLineNumber(t *testing.T) {
17531753
},
17541754
},
17551755
},
1756+
{
1757+
name: "job uses with no steps",
1758+
filename: "./testdata/.github/workflows/github-workflow-job-uses.yaml",
1759+
expected: []struct {
1760+
dependency string
1761+
startLine uint
1762+
endLine uint
1763+
}{
1764+
{
1765+
dependency: "slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.2.0",
1766+
startLine: 30,
1767+
endLine: 30,
1768+
},
1769+
},
1770+
},
17561771
}
17571772
for _, tt := range tests {
17581773
t.Run(tt.name, func(t *testing.T) {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2025 OpenSSF Scorecard Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Docker
16+
on:
17+
push:
18+
branches:
19+
- main
20+
# Publish `v1.2.3` tags as releases.
21+
tags:
22+
- v*
23+
# Run tests for any PRs.
24+
pull_request:
25+
env:
26+
IMAGE_NAME: gitcache
27+
28+
jobs:
29+
push:
30+
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.2.0

0 commit comments

Comments
 (0)