From f8c4724bc0b1b40603c41cf68505f219b715d9c3 Mon Sep 17 00:00:00 2001 From: Gregor Martynus <39992+gr2m@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:37:38 -0700 Subject: [PATCH] feat: initial version --- index.d.ts | 18 ++++++++++++++++++ index.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 index.d.ts create mode 100644 index.js diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..18f8180 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,18 @@ +import { request } from "@octokit/request"; + +type RequestInterface = typeof request; +type RequestOptions = { + request?: RequestInterface; + token?: string; +}; + +interface VerifyInterface { + ( + rawBody: string, + signature: string, + keyId: string, + requestOptions?: RequestOptions, + ): Promise; +} + +export declare const verify: VerifyInterface; diff --git a/index.js b/index.js new file mode 100644 index 0000000..7b8e50c --- /dev/null +++ b/index.js @@ -0,0 +1,56 @@ +// @ts-check + +import { createVerify } from "node:crypto"; + +import { request as defaultRequest } from "@octokit/request"; +import { RequestError } from "@octokit/request-error"; + +/** @type {import('.').VerifyInterface} */ +export async function verify( + rawBody, + signature, + keyId, + { token = "", request = defaultRequest } = { request: defaultRequest }, +) { + // verify arguments + assertValidString(rawBody, "Invalid payload"); + assertValidString(signature, "Invalid signature"); + assertValidString(keyId, "Invalid keyId"); + + // receive valid public keys from GitHub + const requestOptions = request.endpoint("GET /meta/public_keys/copilot_api", { + headers: token + ? { + Authorization: `token ${token}`, + } + : {}, + }); + const response = await request(requestOptions); + const { data: keys } = response; + + // verify provided key Id + const publicKey = keys.public_keys.find( + (key) => key.key_identifier === keyId, + ); + if (!publicKey) { + throw new RequestError( + "[@copilot-extensions/preview-sdk] No public key found matching key identifier", + 404, + { + request: requestOptions, + response, + }, + ); + } + + const verify = createVerify("SHA256").update(rawBody); + + // verify signature + return verify.verify(publicKey.key, signature, "base64"); +} + +function assertValidString(value, message) { + if (typeof value !== "string" || value.length === 0) { + throw new Error(`[@copilot-extensions/preview-sdk] ${message}`); + } +}