| 1 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | package events |
| 4 | |
| 5 | import ( |
| 6 | "encoding/json" |
| 7 | "strings" |
| 8 | "testing" |
| 9 | "time" |
| 10 | |
| 11 | "github.com/jackc/pgx/v5/pgtype" |
| 12 | |
| 13 | actionsdb "github.com/tenseleyFlow/shithub/internal/actions/sqlc" |
| 14 | ) |
| 15 | |
| 16 | func TestPayloadsExcludeSensitiveWorkflowState(t *testing.T) { |
| 17 | now := pgtype.Timestamptz{Time: time.Date(2026, 5, 12, 12, 0, 0, 0, time.UTC), Valid: true} |
| 18 | run := actionsdb.WorkflowRun{ |
| 19 | ID: 11, |
| 20 | RepoID: 22, |
| 21 | RunIndex: 3, |
| 22 | WorkflowFile: ".shithub/workflows/ci.yml", |
| 23 | WorkflowName: "CI", |
| 24 | HeadSha: strings.Repeat("a", 40), |
| 25 | HeadRef: "refs/heads/trunk", |
| 26 | Event: actionsdb.WorkflowRunEventPush, |
| 27 | EventPayload: []byte(`{"secret":"do-not-emit"}`), |
| 28 | ActorUserID: pgtype.Int8{Int64: 33, Valid: true}, |
| 29 | Status: actionsdb.WorkflowRunStatusRunning, |
| 30 | TriggerEventID: "push:demo", |
| 31 | CreatedAt: now, |
| 32 | UpdatedAt: now, |
| 33 | StartedAt: now, |
| 34 | } |
| 35 | job := actionsdb.WorkflowJob{ |
| 36 | ID: 44, |
| 37 | RunID: run.ID, |
| 38 | JobIndex: 0, |
| 39 | JobKey: "build", |
| 40 | JobName: "Build", |
| 41 | RunsOn: "ubuntu-latest", |
| 42 | NeedsJobs: []string{}, |
| 43 | TimeoutMinutes: 30, |
| 44 | Permissions: []byte(`{"contents":"read"}`), |
| 45 | JobEnv: []byte(`{"TOKEN":"do-not-emit"}`), |
| 46 | Status: actionsdb.WorkflowJobStatusCompleted, |
| 47 | Conclusion: actionsdb.NullCheckConclusion{CheckConclusion: actionsdb.CheckConclusionSuccess, Valid: true}, |
| 48 | CreatedAt: now, |
| 49 | UpdatedAt: now, |
| 50 | StartedAt: now, |
| 51 | CompletedAt: now, |
| 52 | } |
| 53 | |
| 54 | payload, err := json.Marshal(jobEvent(run, job, ActionCompleted).Extra) |
| 55 | if err != nil { |
| 56 | t.Fatalf("marshal payload: %v", err) |
| 57 | } |
| 58 | got := string(payload) |
| 59 | for _, forbidden := range []string{ |
| 60 | "event_payload", |
| 61 | "do-not-emit", |
| 62 | "permissions", |
| 63 | "job_env", |
| 64 | "TOKEN", |
| 65 | "secret", |
| 66 | } { |
| 67 | if strings.Contains(got, forbidden) { |
| 68 | t.Fatalf("payload leaked %q: %s", forbidden, got) |
| 69 | } |
| 70 | } |
| 71 | for _, required := range []string{ |
| 72 | `"action":"completed"`, |
| 73 | `"workflow_run"`, |
| 74 | `"workflow_job"`, |
| 75 | `"status":"completed"`, |
| 76 | `"conclusion":"success"`, |
| 77 | `"trigger_event_id":"push:demo"`, |
| 78 | } { |
| 79 | if !strings.Contains(got, required) { |
| 80 | t.Fatalf("payload missing %q: %s", required, got) |
| 81 | } |
| 82 | } |
| 83 | } |
| 84 |