History

Add hosted outbound HTTP policy

Route hosted plugin outbound requests through a shared Effect HttpClient layer that blocks local/private/metadata targets and re-checks redirects. Thread the executor-provided HTTP client through OpenAPI, GraphQL, MCP probing, and OAuth discovery, keep local harnesses on the normal fetch client, and split the restored security coverage into the relevant existing suites. Fix cloud migration scripts to invoke the local Drizzle binary under op run.

Rhys Sullivan committed 6 months ago

Commit 5fc67cd1 parent 745deac19 files · +559 −36Browse files

Showing the changes to packages/plugins/openapi/src (1 of 19 files). Show all

Unified · Split

packages/plugins/openapi/src/sdk/plugin.ts+6−4
@@ -1,6 +1,6 @@
1import { Effect, Option, Predicate, Schema } from "effect";1import { Effect, Option, Predicate, Schema } from "effect";
2import type { Layer } from "effect";2import type { Layer } from "effect";
3import { FetchHttpClient, HttpClient } from "effect/unstable/http";3import { HttpClient } from "effect/unstable/http";
4 4
5import { OpenApiGroup } from "../api/group";5import { OpenApiGroup } from "../api/group";
6import { OpenApiExtensionService, OpenApiHandlers } from "../api/handlers";6import { OpenApiExtensionService, OpenApiHandlers } from "../api/handlers";
@@ -754,8 +754,6 @@ const toOpenApiSourceConfig = ( · 747 unmodified lines
754const isHttpUrl = (s: string): boolean => s.startsWith("http://") || s.startsWith("https://");754const isHttpUrl = (s: string): boolean => s.startsWith("http://") || s.startsWith("https://");
755 755
756export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => {756export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => {
757 const httpClientLayer = options?.httpClientLayer ?? FetchHttpClient.layer;
758
759 type RebuildInput = {757 type RebuildInput = {
760 readonly specText: string;758 readonly specText: string;
761 readonly scope: string;759 readonly scope: string;
@@ -902,6 +900,7 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 140 unmodified lines
902 // than surface a 500 through the unwhitelisted error channel.900 // than surface a 500 through the unwhitelisted error channel.
903 const refreshSourceInternal = (ctx: PluginCtx<OpenapiStore>, sourceId: string, scope: string) =>901 const refreshSourceInternal = (ctx: PluginCtx<OpenapiStore>, sourceId: string, scope: string) =>
904 Effect.gen(function* () {902 Effect.gen(function* () {
903 const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
905 const existing = yield* ctx.storage.getSource(sourceId, scope);904 const existing = yield* ctx.storage.getSource(sourceId, scope);
906 if (!existing) return;905 if (!existing) return;
907 const effective = yield* resolveEffectiveSourceConfig(ctx, existing);906 const effective = yield* resolveEffectiveSourceConfig(ctx, existing);
@@ -938,6 +937,7 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 30 unmodified lines
938 storage: (deps): OpenapiStore => makeDefaultOpenapiStore(deps),937 storage: (deps): OpenapiStore => makeDefaultOpenapiStore(deps),
939 938
940 extension: (ctx) => {939 extension: (ctx) => {
940 const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
941 const addSpecInternal = (config: OpenApiSpecConfig) =>941 const addSpecInternal = (config: OpenApiSpecConfig) =>
942 Effect.gen(function* () {942 Effect.gen(function* () {
943 // Resolve URL → text and parse BEFORE opening a transaction.943 // Resolve URL → text and parse BEFORE opening a transaction.
@@ -1173,6 +1173,7 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 229 unmodified lines
1173 1173
1174 invokeTool: ({ ctx, toolRow, args }) =>1174 invokeTool: ({ ctx, toolRow, args }) =>
1175 Effect.gen(function* () {1175 Effect.gen(function* () {
1176 const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
1176 // toolRow.scope_id is the resolved owning scope of the tool1177 // toolRow.scope_id is the resolved owning scope of the tool
1177 // (innermost-wins from the executor's stack). The matching1178 // (innermost-wins from the executor's stack). The matching
1178 // openapi_operation + openapi_source rows live at the same1179 // openapi_operation + openapi_source rows live at the same
@@ -1313,8 +1314,9 @@ export const openApiPlugin = definePlugin((options?: OpenApiPluginOptions) => { · 134 unmodified lines
1313 // means stale UI state, which is worth surfacing to the caller.1314 // means stale UI state, which is worth surfacing to the caller.
1314 refreshSource: ({ ctx, sourceId, scope }) => refreshSourceInternal(ctx, sourceId, scope),1315 refreshSource: ({ ctx, sourceId, scope }) => refreshSourceInternal(ctx, sourceId, scope),
1315 1316
1316 detect: ({ url }) =>1317 detect: ({ ctx, url }) =>
1317 Effect.gen(function* () {1318 Effect.gen(function* () {
1319 const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
1318 const trimmed = url.trim();1320 const trimmed = url.trim();
1319 if (!trimmed) return null;1321 if (!trimmed) return null;
1320 const parsed = yield* Effect.try({1322 const parsed = yield* Effect.try({