diff --git a/bun.lockb b/bun.lockb old mode 100644 new mode 100755 index eaaf7f1..650988c Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index ed44f40..6ad905d 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "proxy": "tsx src/proxy.ts", "knip": "knip --config .github/knip.ts", "knip-ci": "knip --no-exit-code --reporter json --config .github/knip.ts", - "test": "bun test" + "test": "bun test" }, "keywords": [ "typescript", @@ -42,7 +42,6 @@ "@octokit/types": "^12.6.0", "@octokit/webhooks": "^12.0.10", "@sinclair/typebox": "^0.32.5", - "@ubiquibot/configuration": "2.1.0", "dotenv": "^16.4.4", "smee-client": "^2.0.0", "yaml": "^2.4.1" diff --git a/src/github/github-event-handler.ts b/src/github/github-event-handler.ts index 235aff5..ef91e42 100644 --- a/src/github/github-event-handler.ts +++ b/src/github/github-event-handler.ts @@ -46,6 +46,29 @@ export class GitHubEventHandler { }); } + async importRsaPrivateKey(pem: string) { + const pemContents = pem.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").trim(); + const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0)); + + return await crypto.subtle.importKey( + "pkcs8", + binaryDer.buffer, + { + name: "RSASSA-PKCS1-v1_5", + hash: "SHA-256", + }, + true, + ["sign"] + ); + } + + async signPayload(payload: string) { + const data = new TextEncoder().encode(payload); + const privateKey = await this.importRsaPrivateKey(this._privateKey); + const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, data); + return btoa(String.fromCharCode(...new Uint8Array(signature))); + } + transformEvent(event: EmitterWebhookEvent) { if ("installation" in event.payload && event.payload.installation?.id !== undefined) { const octokit = this.getAuthenticatedOctokit(event.payload.installation.id); diff --git a/src/github/handlers/index.ts b/src/github/handlers/index.ts index 9ff74d2..ac56ab7 100644 --- a/src/github/handlers/index.ts +++ b/src/github/handlers/index.ts @@ -4,7 +4,7 @@ import { GitHubEventHandler } from "../github-event-handler"; import { getConfig } from "../utils/config"; import { repositoryDispatch } from "./repository-dispatch"; import { dispatchWorker, dispatchWorkflow, getDefaultBranch } from "../utils/workflow-dispatch"; -import { DelegatedComputeInputs } from "../types/plugin"; +import { PluginInput } from "../types/plugin"; import { isGithubPlugin, PluginConfiguration } from "../types/plugin-configuration"; function tryCatchWrapper(fn: (event: EmitterWebhookEvent) => unknown) { @@ -86,20 +86,20 @@ async function handleEvent(event: EmitterWebhookEvent, eventHandler: InstanceTyp const ref = isGithubPluginObject ? plugin.ref ?? (await getDefaultBranch(context, plugin.owner, plugin.repo)) : plugin; const token = await eventHandler.getToken(event.payload.installation.id); - const inputs = new DelegatedComputeInputs(stateId, context.key, event.payload, settings, token, ref); + const inputs = new PluginInput(context.eventHandler, stateId, context.key, event.payload, settings, token, ref); state.inputs[0] = inputs; await eventHandler.pluginChainState.put(stateId, state); if (!isGithubPluginObject) { - await dispatchWorker(plugin, inputs.getInputs()); + await dispatchWorker(plugin, await inputs.getWorkerInputs()); } else { await dispatchWorkflow(context, { owner: plugin.owner, repository: plugin.repo, workflowId: plugin.workflowId, ref: plugin.ref, - inputs: inputs.getInputs(), + inputs: inputs.getWorkflowInputs(), }); } } diff --git a/src/github/handlers/repository-dispatch.ts b/src/github/handlers/repository-dispatch.ts index 05b9953..b421b8e 100644 --- a/src/github/handlers/repository-dispatch.ts +++ b/src/github/handlers/repository-dispatch.ts @@ -1,7 +1,7 @@ import { GitHubContext } from "../github-context"; import { dispatchWorker, dispatchWorkflow, getDefaultBranch } from "../utils/workflow-dispatch"; import { Value } from "@sinclair/typebox/value"; -import { DelegatedComputeInputs, PluginChainState, expressionRegex, pluginOutputSchema } from "../types/plugin"; +import { PluginInput, PluginChainState, expressionRegex, pluginOutputSchema } from "../types/plugin"; import { isGithubPlugin } from "../types/plugin-configuration"; export async function repositoryDispatch(context: GitHubContext<"repository_dispatch">) { @@ -61,7 +61,7 @@ export async function repositoryDispatch(context: GitHubContext<"repository_disp } else { ref = nextPlugin.plugin; } - const inputs = new DelegatedComputeInputs(pluginOutput.state_id, state.eventName, state.eventPayload, settings, token, ref); + const inputs = new PluginInput(context.eventHandler, pluginOutput.state_id, state.eventName, state.eventPayload, settings, token, ref); state.currentPlugin++; state.inputs[state.currentPlugin] = inputs; @@ -73,10 +73,10 @@ export async function repositoryDispatch(context: GitHubContext<"repository_disp repository: nextPlugin.plugin.repo, ref: nextPlugin.plugin.ref, workflowId: nextPlugin.plugin.workflowId, - inputs: inputs.getInputs(), + inputs: inputs.getWorkflowInputs(), }); } else { - await dispatchWorker(nextPlugin.plugin, inputs.getInputs()); + await dispatchWorker(nextPlugin.plugin, await inputs.getWorkerInputs()); } } diff --git a/src/github/types/plugin-configuration.ts b/src/github/types/plugin-configuration.ts index abbcfd2..3b015b9 100644 --- a/src/github/types/plugin-configuration.ts +++ b/src/github/types/plugin-configuration.ts @@ -50,7 +50,7 @@ const pluginChainSchema = T.Array( id: T.Optional(T.String()), plugin: githubPluginType(), type: T.Union([T.Literal("github")], { default: "github" }), - with: T.Record(T.String(), T.Unknown()), + with: T.Record(T.String(), T.Unknown(), { default: {} }), }), { minItems: 1 } ); diff --git a/src/github/types/plugin.ts b/src/github/types/plugin.ts index 316e0ea..d429678 100644 --- a/src/github/types/plugin.ts +++ b/src/github/types/plugin.ts @@ -1,6 +1,7 @@ import { EmitterWebhookEvent, EmitterWebhookEventName } from "@octokit/webhooks"; import { StaticDecode, Type } from "@sinclair/typebox"; import { PluginChain } from "./plugin-configuration"; +import { GitHubEventHandler } from "../github-event-handler"; export const expressionRegex = /^\s*\${{\s*(\S+)\s*}}\s*$/; @@ -17,7 +18,8 @@ export const pluginOutputSchema = Type.Object({ export type PluginOutput = StaticDecode; -export class DelegatedComputeInputs { +export class PluginInput { + public eventHandler: GitHubEventHandler; public stateId: string; public eventName: T; public eventPayload: EmitterWebhookEvent["payload"]; @@ -25,7 +27,16 @@ export class DelegatedComputeInputs["payload"], settings: unknown, authToken: string, ref: string) { + constructor( + eventHandler: GitHubEventHandler, + stateId: string, + eventName: T, + eventPayload: EmitterWebhookEvent["payload"], + settings: unknown, + authToken: string, + ref: string + ) { + this.eventHandler = eventHandler; this.stateId = stateId; this.eventName = eventName; this.eventPayload = eventPayload; @@ -34,7 +45,7 @@ export class DelegatedComputeInputs = { @@ -52,6 +79,6 @@ export type PluginChainState["payload"]; currentPlugin: number; pluginChain: PluginChain; - inputs: DelegatedComputeInputs[]; + inputs: PluginInput[]; outputs: PluginOutput[]; }; diff --git a/src/github/utils/config.ts b/src/github/utils/config.ts index 6de223c..98e15ba 100644 --- a/src/github/utils/config.ts +++ b/src/github/utils/config.ts @@ -1,5 +1,4 @@ import { Value } from "@sinclair/typebox/value"; -import { generateConfiguration } from "@ubiquibot/configuration"; import YAML from "yaml"; import { GitHubContext } from "../github-context"; import { expressionRegex } from "../types/plugin"; @@ -10,7 +9,7 @@ const UBIQUIBOT_CONFIG_FULL_PATH = ".github/.ubiquibot-config.yml"; export async function getConfig(context: GitHubContext): Promise { const payload = context.payload; - const defaultConfiguration = generateConfiguration(); + const defaultConfiguration = Value.Decode(configSchema, Value.Default(configSchema, {})); if (!("repository" in payload) || !payload.repository) { console.warn("Repository is not defined"); return defaultConfiguration; diff --git a/src/github/utils/workflow-dispatch.ts b/src/github/utils/workflow-dispatch.ts index c9c8127..1e49c88 100644 --- a/src/github/utils/workflow-dispatch.ts +++ b/src/github/utils/workflow-dispatch.ts @@ -32,7 +32,7 @@ export async function dispatchWorkflow(context: GitHubContext, options: Workflow }); } -export async function dispatchWorker(targetUrl: string, payload: WorkflowDispatchOptions["inputs"]) { +export async function dispatchWorker(targetUrl: string, payload?: Record) { const result = await fetch(targetUrl, { body: JSON.stringify(payload), method: "POST",