Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@copilot-extensions/preview-sdk

Package Overview
Dependencies
Maintainers
0
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@copilot-extensions/preview-sdk - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

dreamcode.md

25

index.d.ts

@@ -8,7 +8,26 @@ import { request } from "@octokit/request";

};
export type VerificationPublicKey = {
key_identifier: string;
key: string;
is_current: boolean;
};
interface VerifyInterface {
interface VerifyRequestInterface {
(
rawBody: string,
signature: string,
key: string
): Promise<boolean>;
}
interface FetchVerificationKeysInterface {
(
requestOptions?: RequestOptions,
): Promise<VerificationPublicKey[]>;
}
interface VerifyRequestByKeyIdInterface {
(
rawBody: string,
signature: string,
keyId: string,

@@ -19,2 +38,4 @@ requestOptions?: RequestOptions,

export declare const verify: VerifyInterface;
export declare const verifyRequest: VerifyRequestInterface;
export declare const fetchVerificationKeys: FetchVerificationKeysInterface;
export declare const verifyRequestByKeyId: VerifyRequestByKeyIdInterface;

72

index.js

@@ -8,16 +8,24 @@ // @ts-check

/** @type {import('.').VerifyInterface} */
export async function verify(
rawBody,
signature,
keyId,
{ token = "", request = defaultRequest } = { request: defaultRequest },
) {
/** @type {import('.').VerifyRequestByKeyIdInterface} */
export async function verifyRequest(rawBody, signature, key) {
// verify arguments
assertValidString(rawBody, "Invalid payload");
assertValidString(signature, "Invalid signature");
assertValidString(keyId, "Invalid keyId");
assertValidString(key, "Invalid key");
// receive valid public keys from GitHub
const requestOptions = request.endpoint("GET /meta/public_keys/copilot_api", {
const verify = createVerify("SHA256").update(rawBody);
// verify signature
try {
return verify.verify(key, signature, "base64");
} catch {
return false;
}
}
/** @type {import('.').FetchVerificationKeysInterface} */
export async function fetchVerificationKeys(
{ token = "", request = defaultRequest } = { request: defaultRequest }
) {
const { data } = await request("GET /meta/public_keys/copilot_api", {
headers: token

@@ -29,24 +37,38 @@ ? {

});
const response = await request(requestOptions);
const { data: keys } = response;
return data.public_keys;
}
/** @type {import('.').VerifyRequestByKeyIdInterface} */
export async function verifyRequestByKeyId(
rawBody,
signature,
keyId,
requestOptions
) {
// verify arguments
assertValidString(rawBody, "Invalid payload");
assertValidString(signature, "Invalid signature");
assertValidString(keyId, "Invalid keyId");
// receive valid public keys from GitHub
const keys = await fetchVerificationKeys(requestOptions);
// verify provided key Id
const publicKey = keys.public_keys.find(
(key) => key.key_identifier === keyId,
);
const publicKey = keys.find((key) => key.key_identifier === keyId);
if (!publicKey) {
throw new RequestError(
"[@copilot-extensions/preview-sdk] No public key found matching key identifier",
404,
const keyNotFoundError = Object.assign(
new Error(
"[@copilot-extensions/preview-sdk] No public key found matching key identifier"
),
{
request: requestOptions,
response,
},
keyId,
keys,
}
);
throw keyNotFoundError;
}
const verify = createVerify("SHA256").update(rawBody);
// verify signature
return verify.verify(publicKey.key, signature, "base64");
return verifyRequest(rawBody, signature, publicKey.key);
}

@@ -53,0 +75,0 @@

import { expectType } from "tsd";
import { request } from "@octokit/request";
import { verify } from "./index.js";
import {
fetchVerificationKeys,
verifyRequest,
verifyRequestByKeyId,
type VerificationPublicKey,
} from "./index.js";

@@ -9,25 +14,54 @@ const rawBody = "";

const keyId = "";
const key = ""
const token = "";
export async function verifyTest() {
const result = await verify(rawBody, signature, keyId);
export async function verifyRequestByKeyIdTest() {
const result = await verifyRequestByKeyId(rawBody, signature, keyId);
expectType<boolean>(result);
// @ts-expect-error - first 3 arguments are required
verify(rawBody, signature);
verifyRequestByKeyId(rawBody, signature);
// @ts-expect-error - rawBody must be a string
await verify(1, signature, keyId);
await verifyRequestByKeyId(1, signature, keyId);
// @ts-expect-error - signature must be a string
await verify(rawBody, 1, keyId);
await verifyRequestByKeyId(rawBody, 1, keyId);
// @ts-expect-error - keyId must be a string
await verify(rawBody, signature, 1);
await verifyRequestByKeyId(rawBody, signature, 1);
// accepts a token argument
await verify(rawBody, signature, keyId, { token });
await verifyRequestByKeyId(rawBody, signature, keyId, { token });
// accepts a request argument
await verify(rawBody, signature, keyId, { request });
await verifyRequestByKeyId(rawBody, signature, keyId, { request });
}
export async function verifyRequestTest() {
const result = await verifyRequest(rawBody, signature, key);
expectType<boolean>(result);
// @ts-expect-error - first 3 arguments are required
verifyRequest(rawBody, signature);
// @ts-expect-error - rawBody must be a string
await verifyRequest(1, signature, key);
// @ts-expect-error - signature must be a string
await verifyRequest(rawBody, 1, key);
// @ts-expect-error - key must be a string
await verifyRequest(rawBody, signature, 1);
}
export async function fetchVerificationKeysTest() {
const result = await fetchVerificationKeys();
expectType<VerificationPublicKey[]>(result);
// accepts a token argument
await fetchVerificationKeys({ token });
// accepts a request argument
await fetchVerificationKeys({ request });
}

@@ -8,3 +8,3 @@ {

"type": "module",
"version": "1.0.0",
"version": "2.0.0",
"keywords": [

@@ -11,0 +11,0 @@ "ai",

@@ -5,17 +5,85 @@ # `@copilot-extensions/preview-sdk`

⚠️ **This SDK is a preview and subjetct to change**. We will however adhere to [semantic versioning](https://semver.org/), so it's save to use for early experimentation. Just beware there will be breaking changes. Best to watch this repository's releases for updates.
⚠️ **This SDK is a preview and subject to change**. We will however adhere to [semantic versioning](https://semver.org/), so it's save to use for early experimentation. Just beware there will be breaking changes. Best to watch this repository's releases for updates.
## Usage
### `verify(rawBody, signature, keyId, options)`
### Verify a request
```js
import { verifyRequestByKeyId } from "@copilot-extensions/preview-sdk";
const payloadIsVerified = await verifyRequestByKeyId(
request.body,
signature,
key,
{
token: process.env.GITHUB_TOKEN,
}
);
// true or false
```
## API
### `async verifyRequestByKeyId(rawBody, signature, keyId, options)`
Verify the request payload using the provided signature and key ID. The method will request the public key from GitHub's API for the given keyId and then verify the payload.
The `options` argument is optional. It can contain a `token` to authenticate the request to GitHub's API, or a custom `request` instance to use for the request.
```js
import { verifyRequestByKeyId } from "@copilot-extensions/preview-sdk";
const payloadIsVerified = await verifyRequestByKeyId(
request.body,
signature,
key
);
// with token
await verifyRequestByKeyId(request.body, signature, key, { token: "ghp_1234" });
// with custom octokit request instance
await verifyRequestByKeyId(request.body, signature, key, { request });
```
### `async fetchVerificationKeys(options)`
Fetches public keys for verifying copilot extension requests [from GitHub's API](https://api.github.com/meta/public_keys/copilot_api)
and returns them as an array. The request can be made without authentication, with a token, or with a custom [octokit request](https://github.com/octokit/request.js) instance.
```js
import { fetchVerificationKeys } from "@copilot-extensions/preview-sdk";
// fetch without authentication
const [current] = await fetchVerificationKeys();
// with token
const [current] = await fetchVerificationKeys({ token: "ghp_1234" });
// with custom octokit request instance
const [current] = await fetchVerificationKeys({ request });)
```
### `async verifyRequestPayload(rawBody, signature, keyId)`
Verify the request payload using the provided signature and key. Note that the raw body as received by GitHub must be passed, before any parsing.
```js
import { verify } from "@copilot-extensions/preview-sdk";
const payloadIsVerified = await verify(request.body, signature, keyId, {
token,
});
const payloadIsVerified = await verifyRequestPayload(
request.body,
signature,
key
);
// true or false
```
## Dreamcode
While implementing the lower-level functionality, we also dream big: what would our dream SDK for Coplitot extensions look like? Please have a look and share your thoughts and ideas:
[dreamcode.md](./dreamcode.md)
## Contributing

@@ -22,0 +90,0 @@

@@ -7,3 +7,7 @@ import { test } from "node:test";

import { verify } from "../index.js";
import {
fetchVerificationKeys,
verifyRequest,
verifyRequestByKeyId,
} from "../index.js";

@@ -24,6 +28,6 @@ const RAW_BODY = `{"copilot_thread_id":"9a1cc23a-ab73-498b-87a5-96c94cb7e3f3","messages":[{"role":"user","content":"@gr2m hi","copilot_references":[{"type":"github.repository","data":{"type":"repository","id":102985470,"name":"sandbox","ownerLogin":"gr2m","ownerType":"User","readmePath":"README.md","description":"@gr2m's little sandbox to play","commitOID":"9b04fffccbb818b2e317394463731b66f1ec5e89","ref":"refs/heads/main","refInfo":{"name":"main","type":"branch"},"visibility":"public","languages":[{"name":"JavaScript","percent":100}]},"id":"gr2m/sandbox","is_implicit":false,"metadata":{"display_name":"gr2m/sandbox","display_icon":"","display_url":""}}],"copilot_confirmations":null},{"role":"user","content":"@gr2m test","copilot_references":[{"type":"github.repository","data":{"type":"repository","id":102985470,"name":"sandbox","ownerLogin":"gr2m","ownerType":"User","readmePath":"README.md","description":"@gr2m's little sandbox to play","commitOID":"9b04fffccbb818b2e317394463731b66f1ec5e89","ref":"refs/heads/main","refInfo":{"name":"main","type":"branch"},"visibility":"public","languages":[{"name":"JavaScript","percent":100}]},"id":"gr2m/sandbox","is_implicit":false,"metadata":{"display_name":"gr2m/sandbox","display_icon":"","display_url":""}}],"copilot_confirmations":null},{"role":"user","content":"@gr2m test","copilot_references":[{"type":"github.repository","data":{"type":"repository","id":102985470,"name":"sandbox","ownerLogin":"gr2m","ownerType":"User","readmePath":"README.md","description":"@gr2m's little sandbox to play","commitOID":"9b04fffccbb818b2e317394463731b66f1ec5e89","ref":"refs/heads/main","refInfo":{"name":"main","type":"branch"},"visibility":"public","languages":[{"name":"JavaScript","percent":100}]},"id":"gr2m/sandbox","is_implicit":false,"metadata":{"display_name":"gr2m/sandbox","display_icon":"","display_url":""}}],"copilot_confirmations":null},{"role":"user","content":"Current Date and Time (UTC): 2024-08-26 19:43:13\\nUser's Current URL: https://github.com/gr2m/sandbox\\nCurrent User's Login: gr2m\\n","name":"_session","copilot_references":[],"copilot_confirmations":null},{"role":"user","content":"","copilot_references":[{"type":"github.repository","data":{"type":"repository","id":102985470,"name":"sandbox","ownerLogin":"gr2m","ownerType":"User","readmePath":"README.md","description":"@gr2m's little sandbox to play","commitOID":"9b04fffccbb818b2e317394463731b66f1ec5e89","ref":"refs/heads/main","refInfo":{"name":"main","type":"branch"},"visibility":"public","languages":[{"name":"JavaScript","percent":100}]},"id":"gr2m/sandbox","is_implicit":false,"metadata":{"display_name":"gr2m/sandbox","display_icon":"","display_url":""}}],"copilot_confirmations":null},{"role":"user","content":"test","copilot_references":[],"copilot_confirmations":[]}],"stop":null,"top_p":0,"temperature":0,"max_tokens":0,"presence_penalty":0,"frequency_penalty":0,"copilot_skills":null,"agent":"gr2m"}`;

test("smoke", (t) => {
assert.equal(typeof verify, "function");
assert.equal(typeof verifyRequestByKeyId, "function");
});
test("minimal usage", async (t) => {
test("verifyRequestByKeyId()", async (t) => {
const mockAgent = new MockAgent();

@@ -59,3 +63,3 @@ function fetchMock(url, opts) {

},
},
}
);

@@ -66,3 +70,3 @@ const testRequest = defaultRequest.defaults({

const result = await verify(RAW_BODY, SIGNATURE, KEY_ID, {
const result = await verifyRequestByKeyId(RAW_BODY, SIGNATURE, KEY_ID, {
request: testRequest,

@@ -74,4 +78,4 @@ });

test("invalid arguments", (t) => {
assert.rejects(verify(RAW_BODY, SIGNATURE), {
test("verifyRequestByKeyId() - invalid arguments", (t) => {
assert.rejects(verifyRequestByKeyId(RAW_BODY, SIGNATURE), {
name: "Error",

@@ -81,3 +85,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid keyId",

assert.rejects(verify("", SIGNATURE, KEY_ID), {
assert.rejects(verifyRequestByKeyId("", SIGNATURE, KEY_ID), {
name: "Error",

@@ -87,3 +91,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid payload",

assert.rejects(verify(1, SIGNATURE, KEY_ID), {
assert.rejects(verifyRequestByKeyId(1, SIGNATURE, KEY_ID), {
name: "Error",

@@ -93,3 +97,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid payload",

assert.rejects(verify(undefined, SIGNATURE, KEY_ID), {
assert.rejects(verifyRequestByKeyId(undefined, SIGNATURE, KEY_ID), {
name: "Error",

@@ -99,3 +103,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid payload",

assert.rejects(verify(RAW_BODY, "", KEY_ID), {
assert.rejects(verifyRequestByKeyId(RAW_BODY, "", KEY_ID), {
name: "Error",

@@ -105,3 +109,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid signature",

assert.rejects(verify(RAW_BODY, 1, KEY_ID), {
assert.rejects(verifyRequestByKeyId(RAW_BODY, 1, KEY_ID), {
name: "Error",

@@ -111,3 +115,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid signature",

assert.rejects(verify(RAW_BODY, undefined, KEY_ID), {
assert.rejects(verifyRequestByKeyId(RAW_BODY, undefined, KEY_ID), {
name: "Error",

@@ -117,3 +121,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid signature",

assert.rejects(verify(RAW_BODY, SIGNATURE, ""), {
assert.rejects(verifyRequestByKeyId(RAW_BODY, SIGNATURE, ""), {
name: "Error",

@@ -123,3 +127,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid keyId",

assert.rejects(verify(RAW_BODY, SIGNATURE, 1), {
assert.rejects(verifyRequestByKeyId(RAW_BODY, SIGNATURE, 1), {
name: "Error",

@@ -129,3 +133,3 @@ message: "[@copilot-extensions/preview-sdk] Invalid keyId",

assert.rejects(verify(RAW_BODY, SIGNATURE, undefined), {
assert.rejects(verifyRequestByKeyId(RAW_BODY, SIGNATURE, undefined), {
name: "Error",

@@ -135,1 +139,62 @@ message: "[@copilot-extensions/preview-sdk] Invalid keyId",

});
test("verifyRequest() - valid", async (t) => {
const result = await verifyRequest(RAW_BODY, SIGNATURE, CURRENT_PUBLIC_KEY);
assert.deepEqual(result, true);
});
test("verifyRequest() - invalid", async (t) => {
const result = await verifyRequest(RAW_BODY, SIGNATURE, "invalid-key");
assert.deepEqual(result, false);
});
test("fetchVerificationKeys()", async (t) => {
const mockAgent = new MockAgent();
function fetchMock(url, opts) {
opts ||= {};
opts.dispatcher = mockAgent;
return fetch(url, opts);
}
const publicKeys = [
{
key: "<key 1>",
key_identifier: "<key-id 1>",
is_current: true,
},
{
key: "<key 2>",
key_identifier: "<key-id 2>",
is_current: true,
},
];
mockAgent.disableNetConnect();
const mockPool = mockAgent.get("https://api.github.com");
mockPool
.intercept({
method: "get",
path: `/meta/public_keys/copilot_api`,
})
.reply(
200,
{
public_keys: publicKeys,
},
{
headers: {
"content-type": "application/json",
"x-request-id": "<request-id>",
},
}
);
const testRequest = defaultRequest.defaults({
request: { fetch: fetchMock },
});
const result = await fetchVerificationKeys({
request: testRequest,
});
assert.deepEqual(result, publicKeys);
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc