Skip to content

Commit eb2e822

Browse files
Add option to disable checksums for transitive actions
Allow users to opt-out of checksums for transitive actions. This enables users that depend on actions with transitive dependencies to use ghasum despite the fact that their dependencies do not (yet) pin its transitive dependencies. This allows for an improvement in security even in cases where the best security guarantees cannot be realized. This is an opt-out option because the in an ideal scenario users do not need to disable this, even if today's reality may be that most users will need to disable it.
1 parent 018af0b commit eb2e822

File tree

10 files changed

+133
-54
lines changed

10 files changed

+133
-54
lines changed

SPECIFICATION.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,11 @@ To determine the set of actions a target depends on, first find all `uses:`
8888
entries in the target. For a repository this covers all workflows in the
8989
workflows directory, otherwise it covers only the target.
9090

91-
For each `uses:` value, excluding the list below, it is added to the set and the
92-
repository declared by the `uses:` value is fetched. The action manifest at the
93-
path specified in the `uses:` value is parsed for additional `uses:` values. For
94-
each of these transitive `uses:` values, this process is repeated.
91+
For each `uses:` value, excluding the list below, it is added to the set. If the
92+
`-no-transitive` option is NOT set the repository declared by the `uses:` value
93+
is fetched. The action manifest at the path specified in the `uses:` value is
94+
parsed for additional `uses:` values. For each of these transitive `uses:`
95+
values, this process is repeated.
9596

9697
The following `uses:` values are to be excluded from the set of actions a
9798
repository depends on.

cmd/ghasum/init.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ import (
2626

2727
func cmdInit(argv []string) error {
2828
var (
29-
flags = flag.NewFlagSet(cmdNameInit, flag.ContinueOnError)
30-
flagCache = flags.String(flagNameCache, "", "")
31-
flagNoCache = flags.Bool(flagNameNoCache, false, "")
29+
flags = flag.NewFlagSet(cmdNameInit, flag.ContinueOnError)
30+
flagCache = flags.String(flagNameCache, "", "")
31+
flagNoCache = flags.Bool(flagNameNoCache, false, "")
32+
flagNoTransitive = flags.Bool(flagNameNoTransitive, false, "")
3233
)
3334

3435
flags.Usage = func() { fmt.Fprintln(os.Stderr) }
@@ -57,9 +58,10 @@ func cmdInit(argv []string) error {
5758
}
5859

5960
cfg := ghasum.Config{
60-
Repo: repo.FS(),
61-
Path: target,
62-
Cache: c,
61+
Repo: repo.FS(),
62+
Path: target,
63+
Cache: c,
64+
Transitive: !(*flagNoTransitive),
6365
}
6466

6567
if err := ghasum.Initialize(&cfg); err != nil {
@@ -84,5 +86,7 @@ The available flags are:
8486
looks up repositories it needs.
8587
Defaults to a directory named .ghasum in the user's home directory.
8688
-no-cache
87-
Disable the use of the cache. Makes the -cache flag ineffective.`
89+
Disable the use of the cache. Makes the -cache flag ineffective.
90+
-no-transitive
91+
Do not compute checksums for transitive actions.`
8892
}

cmd/ghasum/main.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ const (
4545
)
4646

4747
const (
48-
flagNameCache = "cache"
49-
flagNameForce = "force"
50-
flagNameNoCache = "no-cache"
51-
flagNameNoEvict = "no-evict"
52-
flagNameOffline = "offline"
48+
flagNameCache = "cache"
49+
flagNameForce = "force"
50+
flagNameNoCache = "no-cache"
51+
flagNameNoEvict = "no-evict"
52+
flagNameNoTransitive = "no-transitive"
53+
flagNameOffline = "offline"
5354
)
5455

5556
var (

cmd/ghasum/update.go

+13-9
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import (
2626

2727
func cmdUpdate(argv []string) error {
2828
var (
29-
flags = flag.NewFlagSet(cmdNameUpdate, flag.ContinueOnError)
30-
flagCache = flags.String(flagNameCache, "", "")
31-
flagForce = flags.Bool(flagNameForce, false, "")
32-
flagNoCache = flags.Bool(flagNameNoCache, false, "")
33-
flagNoEvict = flags.Bool(flagNameNoEvict, false, "")
29+
flags = flag.NewFlagSet(cmdNameUpdate, flag.ContinueOnError)
30+
flagCache = flags.String(flagNameCache, "", "")
31+
flagForce = flags.Bool(flagNameForce, false, "")
32+
flagNoCache = flags.Bool(flagNameNoCache, false, "")
33+
flagNoEvict = flags.Bool(flagNameNoEvict, false, "")
34+
flagNoTransitive = flags.Bool(flagNameNoTransitive, false, "")
3435
)
3536

3637
flags.Usage = func() { fmt.Fprintln(os.Stderr) }
@@ -65,9 +66,10 @@ func cmdUpdate(argv []string) error {
6566
}
6667

6768
cfg := ghasum.Config{
68-
Repo: repo.FS(),
69-
Path: target,
70-
Cache: c,
69+
Repo: repo.FS(),
70+
Path: target,
71+
Cache: c,
72+
Transitive: !(*flagNoTransitive),
7173
}
7274

7375
if err := ghasum.Update(&cfg, *flagForce); err != nil {
@@ -98,5 +100,7 @@ The available flags are:
98100
-no-cache
99101
Disable the use of the cache. Makes the -cache flag ineffective.
100102
-no-evict
101-
Disable cache eviction.`
103+
Disable cache eviction.
104+
-no-transitive
105+
Do not compute checksums for transitive actions.`
102106
}

cmd/ghasum/verify.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ import (
2929

3030
func cmdVerify(argv []string) error {
3131
var (
32-
flags = flag.NewFlagSet(cmdNameVerify, flag.ContinueOnError)
33-
flagCache = flags.String(flagNameCache, "", "")
34-
flagNoCache = flags.Bool(flagNameNoCache, false, "")
35-
flagNoEvict = flags.Bool(flagNameNoEvict, false, "")
36-
flagOffline = flags.Bool(flagNameOffline, false, "")
32+
flags = flag.NewFlagSet(cmdNameVerify, flag.ContinueOnError)
33+
flagCache = flags.String(flagNameCache, "", "")
34+
flagNoCache = flags.Bool(flagNameNoCache, false, "")
35+
flagNoEvict = flags.Bool(flagNameNoEvict, false, "")
36+
flagOffline = flags.Bool(flagNameOffline, false, "")
37+
flagNoTransitive = flags.Bool(flagNameNoTransitive, false, "")
3738
)
3839

3940
flags.Usage = func() { fmt.Fprintln(os.Stderr) }
@@ -86,12 +87,13 @@ func cmdVerify(argv []string) error {
8687
}
8788

8889
cfg := ghasum.Config{
89-
Repo: repo.FS(),
90-
Path: target,
91-
Workflow: workflow,
92-
Job: job,
93-
Cache: c,
94-
Offline: *flagOffline,
90+
Repo: repo.FS(),
91+
Path: target,
92+
Workflow: workflow,
93+
Job: job,
94+
Cache: c,
95+
Offline: *flagOffline,
96+
Transitive: !(*flagNoTransitive),
9597
}
9698

9799
problems, err := ghasum.Verify(&cfg)
@@ -151,5 +153,7 @@ The available flags are:
151153
Disable cache eviction.
152154
-offline
153155
Run without fetching repositories from the internet, verify exclusively
154-
against the cache. If the cache is missing an entry it causes an error.`
156+
against the cache. If the cache is missing an entry it causes an error.
157+
-no-transitive
158+
Do not compute checksums for transitive actions.`
155159
}

internal/ghasum/atoms.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,13 @@ func compute(cfg *Config, actions []gha.GitHubAction, algo checksum.Algo) ([]sum
128128
}
129129
}
130130

131-
transitiveActions, err := gha.ManifestActions(os.DirFS(actionDir), action.Path)
132-
if err != nil {
133-
return nil, fmt.Errorf("action manifest parsing failed: %v", err)
131+
if cfg.Transitive {
132+
transitiveActions, err := gha.ManifestActions(os.DirFS(actionDir), action.Path)
133+
if err != nil {
134+
return nil, fmt.Errorf("action manifest parsing failed: %v", err)
135+
}
136+
actions = append(actions, transitiveActions...)
134137
}
135-
actions = append(actions, transitiveActions...)
136138

137139
id := fmt.Sprintf("%s%s%s", action.Owner, action.Project, action.Ref)
138140
if _, ok := entries[id]; !ok {

internal/ghasum/operations.go

+18-13
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,41 @@ import (
2828
type (
2929
// Config is the configuration for a ghasum operation.
3030
Config struct {
31-
// Repo is a pointer to the file system hierarchy of the target repository
32-
// for the operation.
31+
// Repo is a pointer to the file system hierarchy of the target
32+
// repository for the operation.
3333
Repo fs.FS
3434

3535
// Path is the absolute or relate path to the target repository for the
3636
// operation.
3737
//
38-
// This must be provided in addition to Repo because that does not allow for
39-
// non-read file system operation.
38+
// This must be provided in addition to Repo because that does not allow
39+
// for non-read file system operation.
4040
Path string
4141

42-
// Workflow is the file path (relative to Path) of the workflow that is the
43-
// subject of the operation. If this has the zero value all workflows in the
44-
// Repo will collectively be the subject of the operation instead.
42+
// Workflow is the file path (relative to Path) of the workflow that is
43+
// the subject of the operation. If this has the zero value all of the
44+
// workflows in the Repo will collectively be the subject of the
45+
// operation instead.
4546
Workflow string
4647

47-
// Job is the id (also known as key) of the job that is the subject of the
48-
// operation. If this has the zero value all jobs in the Workflow will
49-
// collectively be the subject of the operation instead. (If Workflow has
50-
// the zero value this value is ignored.)
48+
// Job is the id (also known as key) of the job that is the subject of
49+
// the operation. If this has the zero value all jobs in the Workflow
50+
// will collectively be the subject of the operation instead. (If
51+
// Workflow has the zero value this value is ignored.)
5152
Job string
5253

5354
// Cache is the cache that should be used for the operation.
5455
Cache cache.Cache
5556

56-
// Offline sets whether to rely exclusively on the cache or fetch missing
57-
// repositories from the internet.
57+
// Offline sets whether to rely exclusively on the cache or fetch
58+
// missing repositories from the internet.
5859
//
5960
// Only applies to verification.
6061
Offline bool
62+
63+
// Transitive sets whether to compute/verify checksums for transitive
64+
// dependencies.
65+
Transitive bool
6166
}
6267

6368
// Problem represents an issue detected when verifying ghasum checksums.

testdata/init/success.txtar

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ stdout 'Ok'
44
! stderr .
55
cmp target/.github/workflows/gha.sum want/gha.sum
66

7+
rm target/.github/workflows/gha.sum
8+
9+
# Without transitive actions
10+
exec ghasum init -cache .cache/ -no-transitive target/
11+
stdout 'Ok'
12+
! stderr .
13+
cmp target/.github/workflows/gha.sum want/gha-no-transitive.sum
14+
715
-- want/gha.sum --
816
version 1
917

@@ -12,6 +20,13 @@ actions/composite@v1 a3ht0IImDEBC7NqbohfejBtv7W5GdKiGJgc4OtYkjEs=
1220
actions/[email protected] NoW6+RttcHeApXsFxN2DfY/2Oc7t0g9mgq22uJ3rAbg=
1321
actions/[email protected] Gdoys4h+gIN02lzrWZW0uxjBQ8Rk5YSE6+q1SOrw/+o=
1422
golangci/golangci-lint-action@3a91952 Whvj26yZchrz2jiVS3IZrwZ2DXX71qu4gynanpV3G4I=
23+
-- want/gha-no-transitive.sum --
24+
version 1
25+
26+
actions/checkout@main JHipZi1UCvybC3fwi9RFLTK8vpI/gURTga/ColyHI4k=
27+
actions/composite@v1 a3ht0IImDEBC7NqbohfejBtv7W5GdKiGJgc4OtYkjEs=
28+
actions/[email protected] NoW6+RttcHeApXsFxN2DfY/2Oc7t0g9mgq22uJ3rAbg=
29+
golangci/golangci-lint-action@3a91952 Whvj26yZchrz2jiVS3IZrwZ2DXX71qu4gynanpV3G4I=
1530
-- target/.github/workflows/workflow.yml --
1631
name: Example workflow
1732
on: [push]

testdata/update/success.txtar

+15
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ stdout 'Ok'
3030
! stderr .
3131
cmp preserve/.github/workflows/gha.sum want/gha-preserve.sum
3232

33+
# Remove transitive
34+
cmp changed/.github/workflows/gha.sum want/gha.sum
35+
36+
exec ghasum update -cache .cache/ -no-transitive changed/
37+
stdout 'Ok'
38+
! stderr .
39+
cmp changed/.github/workflows/gha.sum want/gha-no-transitive.sum
40+
3341
-- want/gha.sum --
3442
version 1
3543

@@ -38,6 +46,13 @@ actions/composite@v1 a3ht0IImDEBC7NqbohfejBtv7W5GdKiGJgc4OtYkjEs=
3846
actions/[email protected] NoW6+RttcHeApXsFxN2DfY/2Oc7t0g9mgq22uJ3rAbg=
3947
actions/[email protected] Gdoys4h+gIN02lzrWZW0uxjBQ8Rk5YSE6+q1SOrw/+o=
4048
golangci/golangci-lint-action@3a91952 Whvj26yZchrz2jiVS3IZrwZ2DXX71qu4gynanpV3G4I=
49+
-- want/gha-no-transitive.sum --
50+
version 1
51+
52+
actions/[email protected] TTVf+dWEJueFyMoZnvuqlW5lX4aXYXxGWaFaV8lO910=
53+
actions/composite@v1 a3ht0IImDEBC7NqbohfejBtv7W5GdKiGJgc4OtYkjEs=
54+
actions/[email protected] NoW6+RttcHeApXsFxN2DfY/2Oc7t0g9mgq22uJ3rAbg=
55+
golangci/golangci-lint-action@3a91952 Whvj26yZchrz2jiVS3IZrwZ2DXX71qu4gynanpV3G4I=
4156
-- want/gha-preserve.sum --
4257
version 1
4358

testdata/verify/success.txtar

+28
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ exec ghasum verify -cache .cache/ redundant/.github/workflows/workflow.yml:examp
2828
stdout 'Ok'
2929
! stderr .
3030

31+
# Redundant checksum stored - Transitive
32+
exec ghasum verify -cache .cache/ -no-transitive no-transitive/
33+
stdout 'Ok'
34+
! stderr .
35+
3136
# Checksums match partially - Workflow
3237
exec ghasum verify -cache .cache/ partial/.github/workflows/valid.yml
3338
stdout 'Ok'
@@ -152,6 +157,29 @@ jobs:
152157
go-version-file: go.mod
153158
- name: This step uses transitive actions
154159
uses: actions/composite@v1
160+
-- no-transitive/.github/workflows/gha.sum --
161+
version 1
162+
163+
actions/composite@v1 a3ht0IImDEBC7NqbohfejBtv7W5GdKiGJgc4OtYkjEs=
164+
actions/[email protected] NoW6+RttcHeApXsFxN2DfY/2Oc7t0g9mgq22uJ3rAbg=
165+
actions/[email protected] this-action-is-only-used-transitively
166+
-- no-transitive/.github/workflows/workflow.yml --
167+
name: Example workflow
168+
on: [push]
169+
170+
jobs:
171+
example:
172+
name: example
173+
runs-on: ubuntu-22.04
174+
steps:
175+
- name: Install Go
176+
uses: actions/[email protected]
177+
with:
178+
go-version-file: go.mod
179+
- name: This step uses transitive actions
180+
uses: actions/composite@v1
181+
- name: This step does not use an action
182+
run: Echo 'hello world!'
155183
-- .cache/actions/checkout/main/action.yml --
156184
name: actions/checkout@main
157185
-- .cache/actions/composite/v1/action.yml --

0 commit comments

Comments
 (0)