Skip to content

Commit 396a288

Browse files
authored
Merge pull request #12 from TrustSignal-dev/copilot/fix-trustsignal-github-app
Fix three verification failure causes in push, check_suite, and TrustSignal client
2 parents 2d732ee + af15f0c commit 396a288

5 files changed

Lines changed: 119 additions & 4 deletions

File tree

apps/action/dist/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4175,6 +4175,7 @@ var TrustSignalVerificationClient = class {
41754175
method: "POST",
41764176
headers: {
41774177
"content-type": "application/json",
4178+
"accept": "application/json",
41784179
authorization: `Bearer ${this.apiKey}`
41794180
},
41804181
body: payloadText,
@@ -4296,11 +4297,12 @@ function normalizeReleasePayload(payload) {
42964297
}
42974298
};
42984299
}
4300+
var ZERO_SHA = "0000000000000000000000000000000000000000";
42994301
function normalizePushPayload(payload) {
43004302
const repository = payload.repository;
43014303
const ref = String(payload.ref || "");
43024304
const branchName = ref.replace("refs/heads/", "");
4303-
if (branchName && repository.default_branch && branchName !== repository.default_branch) {
4305+
if (branchName && repository.default_branch && branchName !== repository.default_branch || !payload.after || payload.after === ZERO_SHA) {
43044306
return null;
43054307
}
43064308
return {
@@ -4342,7 +4344,7 @@ function normalizeCheckSuitePayload(payload) {
43424344
headSha,
43434345
externalId: `check_suite:${checkSuite.id}`,
43444346
summaryContext: `check suite ${checkSuite.id}`,
4345-
detailsUrl: checkSuite.url,
4347+
detailsUrl: checkSuite.html_url || `${repository.html_url}/commit/${headSha}`,
43464348
provenance: {
43474349
action: payload.action,
43484350
checkSuiteStatus: checkSuite.status,

src/trustsignal/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export class TrustSignalVerificationClient {
5656
method: "POST",
5757
headers: {
5858
"content-type": "application/json",
59+
"accept": "application/json",
5960
authorization: `Bearer ${this.apiKey}`,
6061
},
6162
body: payloadText,

src/trustsignal/github.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,18 @@ export function normalizeReleasePayload(payload: Record<string, any>): GitHubVer
7979
};
8080
}
8181

82+
const ZERO_SHA = "0000000000000000000000000000000000000000";
83+
8284
export function normalizePushPayload(payload: Record<string, any>): GitHubVerificationEnvelope | null {
8385
const repository = payload.repository;
8486
const ref = String(payload.ref || "");
8587
const branchName = ref.replace("refs/heads/", "");
8688

87-
if (branchName && repository.default_branch && branchName !== repository.default_branch) {
89+
if (
90+
(branchName && repository.default_branch && branchName !== repository.default_branch) ||
91+
!payload.after ||
92+
payload.after === ZERO_SHA
93+
) {
8894
return null;
8995
}
9096

@@ -137,7 +143,7 @@ export function normalizeCheckSuitePayload(payload: Record<string, any>): GitHub
137143
headSha,
138144
externalId: `check_suite:${checkSuite.id}`,
139145
summaryContext: `check suite ${checkSuite.id}`,
140-
detailsUrl: checkSuite.url,
146+
detailsUrl: checkSuite.html_url || `${repository.html_url}/commit/${headSha}`,
141147
provenance: {
142148
action: payload.action,
143149
checkSuiteStatus: checkSuite.status,

tests/normalizeEvent.test.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, it } from "vitest";
22
import { normalizeWorkflowRunEvent } from "../src/webhooks/handlers/workflowRun";
3+
import { normalizePushPayload, normalizeCheckSuitePayload } from "../src/trustsignal/github";
34

45
describe("normalizeWorkflowRunEvent", () => {
56
it("normalizes a completed workflow run", () => {
@@ -29,3 +30,107 @@ describe("normalizeWorkflowRunEvent", () => {
2930
expect(job?.headSha).toBe("abc123");
3031
});
3132
});
33+
34+
describe("normalizePushPayload", () => {
35+
it("returns null for branch deletion events (zero SHA)", () => {
36+
const result = normalizePushPayload({
37+
ref: "refs/heads/main",
38+
before: "abc1234def5678901234567890abcdef12345678",
39+
after: "0000000000000000000000000000000000000000",
40+
repository: {
41+
name: "repo",
42+
default_branch: "main",
43+
html_url: "https://github.com/acme/repo",
44+
owner: { login: "acme" },
45+
},
46+
});
47+
48+
expect(result).toBeNull();
49+
});
50+
51+
it("returns null when after is missing", () => {
52+
const result = normalizePushPayload({
53+
ref: "refs/heads/main",
54+
before: "abc1234def5678901234567890abcdef12345678",
55+
repository: {
56+
name: "repo",
57+
default_branch: "main",
58+
html_url: "https://github.com/acme/repo",
59+
owner: { login: "acme" },
60+
},
61+
});
62+
63+
expect(result).toBeNull();
64+
});
65+
66+
it("returns an envelope for a default-branch push with a valid SHA", () => {
67+
const result = normalizePushPayload({
68+
ref: "refs/heads/main",
69+
before: "abc1234def5678901234567890abcdef12345678",
70+
after: "def5678abc1234901234567890abcdef12345678",
71+
repository: {
72+
name: "repo",
73+
default_branch: "main",
74+
html_url: "https://github.com/acme/repo",
75+
owner: { login: "acme" },
76+
},
77+
});
78+
79+
expect(result).not.toBeNull();
80+
expect(result?.headSha).toBe("def5678abc1234901234567890abcdef12345678");
81+
expect(result?.externalId).toBe("push:def5678abc1234901234567890abcdef12345678");
82+
});
83+
});
84+
85+
describe("normalizeCheckSuitePayload", () => {
86+
it("uses html_url for detailsUrl when available", () => {
87+
const result = normalizeCheckSuitePayload({
88+
action: "requested",
89+
check_suite: {
90+
id: 321,
91+
head_sha: "def5678abc1234901234567890abcdef12345678",
92+
status: "queued",
93+
conclusion: null,
94+
html_url: "https://github.com/acme/repo/actions/runs/321",
95+
url: "https://api.github.com/repos/acme/repo/check-suites/321",
96+
app: { slug: "some-app" },
97+
pull_requests: [],
98+
},
99+
repository: {
100+
id: 1,
101+
name: "repo",
102+
default_branch: "main",
103+
html_url: "https://github.com/acme/repo",
104+
owner: { login: "acme" },
105+
},
106+
});
107+
108+
expect(result).not.toBeNull();
109+
expect(result?.detailsUrl).toBe("https://github.com/acme/repo/actions/runs/321");
110+
});
111+
112+
it("falls back to commit URL for detailsUrl when html_url is absent", () => {
113+
const result = normalizeCheckSuitePayload({
114+
action: "requested",
115+
check_suite: {
116+
id: 654,
117+
head_sha: "abc1234def5678901234567890abcdef12345678",
118+
status: "queued",
119+
conclusion: null,
120+
url: "https://api.github.com/repos/acme/repo/check-suites/654",
121+
app: { slug: "some-app" },
122+
pull_requests: [],
123+
},
124+
repository: {
125+
id: 1,
126+
name: "repo",
127+
default_branch: "main",
128+
html_url: "https://github.com/acme/repo",
129+
owner: { login: "acme" },
130+
},
131+
});
132+
133+
expect(result).not.toBeNull();
134+
expect(result?.detailsUrl).toBe("https://github.com/acme/repo/commit/abc1234def5678901234567890abcdef12345678");
135+
});
136+
});

tests/trustsignalClient.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ describe("TrustSignalVerificationClient", () => {
5656
method: "POST",
5757
headers: expect.objectContaining({
5858
authorization: "Bearer secret",
59+
"accept": "application/json",
5960
}),
6061
})
6162
);

0 commit comments

Comments
 (0)