feat: add GitHub App integration foundation - #13
Conversation
Reviewer's GuideEstablishes the GitHub App integration foundation with raw-body webhook signature verification, a security-constrained installation-token REST client for repository, workflow-run, and repository-dispatch operations, package exports, tests, and least-privilege setup documentation. Sequence diagram for verified GitHub webhook handlingsequenceDiagram
participant GitHub
participant WebhookEndpoint
participant FlowForge
GitHub->>WebhookEndpoint: POST webhook with raw body and x-hub-signature-256
WebhookEndpoint->>WebhookEndpoint: verifyGitHubWebhookSignature(secret, rawBody, signatureHeader)
alt signature valid
WebhookEndpoint->>FlowForge: Start workflow with parsed event
else signature invalid
WebhookEndpoint-->>GitHub: Reject request
end
Sequence diagram for GitHub App API operationssequenceDiagram
participant FlowForge
participant GitHubApiClient
participant GitHubAPI
FlowForge->>GitHubApiClient: getRepository(owner, repo)
GitHubApiClient->>GitHubAPI: GET /repos/{owner}/{repo} with Bearer token
GitHubAPI-->>GitHubApiClient: GitHubRepositorySummary
GitHubApiClient-->>FlowForge: Repository summary
FlowForge->>GitHubApiClient: listWorkflowRuns(owner, repo, branch)
GitHubApiClient->>GitHubAPI: GET /repos/{owner}/{repo}/actions/runs?branch={branch}
GitHubAPI-->>GitHubApiClient: GitHubWorkflowRunsResponse
GitHubApiClient-->>FlowForge: Workflow runs
FlowForge->>GitHubApiClient: createRepositoryDispatch(owner, repo, eventType, clientPayload)
GitHubApiClient->>GitHubAPI: POST /repos/{owner}/{repo}/dispatches
GitHubAPI-->>GitHubApiClient: 204 No Content
GitHubApiClient-->>FlowForge: Dispatch completed
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/github-app.ts" line_range="49" />
<code_context>
+ rawBody: string | Uint8Array,
+ signatureHeader: string | null | undefined,
+): boolean {
+ if (secret.length === 0 || !signatureHeader?.startsWith("sha256=")) {
+ return false;
+ }
</code_context>
<issue_to_address>
**issue (bug_risk):** Passing a Node.js `IncomingMessage` header directly, as shown in the documentation, supplies `string | string[] | undefined`; when GitHub's signature header is represented as an array, `signatureHeader.startsWith(...)` raises `TypeError` instead of rejecting the request. The documented usage also fails TypeScript checking because the function does not accept `string[]`.
**Triggers:** When the webhook framework exposes duplicate or multi-valued `x-hub-signature-256` headers.
**Suggested fix:** Accept only a string at runtime and return `false` for arrays, or have the integration adapter explicitly reject and normalize multi-valued headers before calling the verifier.
```suggestion
if (secret.length === 0 || typeof signatureHeader !== "string" || !signatureHeader.startsWith("sha256=")) {
```
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and a defect in webhook signature verification could accept forged GitHub events, while a client or endpoint defect could misuse an installation token or dispatch workflows with repository permissions; those requests and triggered actions cannot be undone by reverting the code. The implementation is isolated foundation code, so the impact depends on how it is wired into production, but incorrect security-boundary behavior would require remediation beyond a simple revert.
Blocking findings: src/github-app.ts:49
| rawBody: string | Uint8Array, | ||
| signatureHeader: string | null | undefined, | ||
| ): boolean { | ||
| if (secret.length === 0 || !signatureHeader?.startsWith("sha256=")) { |
There was a problem hiding this comment.
issue (bug_risk): Passing a Node.js IncomingMessage header directly, as shown in the documentation, supplies string | string[] | undefined; when GitHub's signature header is represented as an array, signatureHeader.startsWith(...) raises TypeError instead of rejecting the request. The documented usage also fails TypeScript checking because the function does not accept string[].
Triggers: When the webhook framework exposes duplicate or multi-valued x-hub-signature-256 headers.
Suggested fix: Accept only a string at runtime and return false for arrays, or have the integration adapter explicitly reject and normalize multi-valued headers before calling the verifier.
| if (secret.length === 0 || !signatureHeader?.startsWith("sha256=")) { | |
| if (secret.length === 0 || typeof signatureHeader !== "string" || !signatureHeader.startsWith("sha256=")) { |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9578613a27
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| private buildUrl(path: string): URL { | ||
| if (!path.startsWith("/") || path.startsWith("//") || path.includes("..")) { |
There was a problem hiding this comment.
Check traversal by complete path segment
For repositories whose valid GitHub name contains consecutive periods (for example release..archive), encodeURIComponent preserves the periods and this substring check rejects the generated path before any request is sent. Such periods are harmless unless the complete segment is ..; validate path segments rather than searching the entire URL so all three repository helpers remain usable for these repositories.
Useful? React with 👍 / 👎.
| if (!/^[A-Za-z0-9._-]{1,100}$/.test(eventType)) { | ||
| throw new Error("GitHub repository_dispatch event type is invalid"); |
There was a problem hiding this comment.
Allow all valid repository-dispatch event types
When a workflow listens for a valid custom event type containing characters outside this private allowlist, such as deploy:completed, the client throws locally even though GitHub's event_type contract only imposes a 100-character maximum. Since the value is JSON-encoded rather than interpolated into a URL, this restriction unnecessarily prevents dispatching events supported by the API.
Useful? React with 👍 / 👎.



Adds verified GitHub webhook signatures, a constrained REST client for GitHub App installation tokens, workflow-run and repository access helpers, repository-dispatch support, tests, exports, and least-privilege setup documentation.
This establishes a real GitHub API integration in development and prepares FlowForge for GitHub App registration.
Closes #12
Summary by Sourcery
Establish the foundation for secure GitHub App integration in FlowForge.
New Features:
Enhancements:
Documentation:
Tests:
Chores: