Skip to content

Commit b4cfcdf

Browse files
committed
Incorporate feedback from review
1 parent 85551dd commit b4cfcdf

5 files changed

Lines changed: 34 additions & 8 deletions

File tree

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ app.use("*", async (c, next) => {
6565
await next()
6666
})
6767

68-
const mcpServer = createMcpServer()
6968
app.all("/mcp", async (c) => {
69+
const mcpServer = createMcpServer(c.env)
7070
const transport = new StreamableHTTPTransport()
7171
await mcpServer.connect(transport)
7272
return transport.handleRequest(c)

src/lib/external/policy.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,11 @@ function isPrivateIPv4(hostname: string): boolean {
341341
return false
342342
}
343343

344-
const [a, b] = octets.map((octet) => Number.parseInt(octet, 10))
345-
if (a > 255 || b > 255) {
344+
const octetNumbers = octets.map((octet) => Number.parseInt(octet, 10))
345+
if (octetNumbers.some((value) => value > 255)) {
346346
return false
347347
}
348+
const [a, b] = octetNumbers
348349

349350
return (
350351
a === 10 ||

src/lib/mcp.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { z } from "zod"
33

44
import {
55
assertExternalDocumentationAccess,
6+
type ExternalPolicyEnv,
67
extractExternalDocumentationBasePath,
78
fetchExternalDocCJSON,
89
validateExternalDocumentationUrl,
@@ -12,7 +13,7 @@ import { searchAppleDeveloperDocs } from "./search"
1213
import { generateAppleDocUrl, normalizeDocumentationPath } from "./url"
1314
import { fetchHIGPageData, renderHIGFromJSON } from "./hig"
1415

15-
export function createMcpServer() {
16+
export function createMcpServer(externalPolicyEnv: ExternalPolicyEnv = {}) {
1617
const server = new McpServer({
1718
name: "sosumi.ai",
1819
version: "1.0.0",
@@ -297,7 +298,7 @@ export function createMcpServer() {
297298
async ({ url }) => {
298299
try {
299300
const targetUrl = validateExternalDocumentationUrl(url)
300-
await assertExternalDocumentationAccess(targetUrl, {})
301+
await assertExternalDocumentationAccess(targetUrl, externalPolicyEnv)
301302
const jsonData = await fetchExternalDocCJSON(targetUrl)
302303
const externalBasePath = extractExternalDocumentationBasePath(targetUrl)
303304
const markdown = await renderFromJSON(jsonData, targetUrl.toString(), {

tests/external.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,32 @@ describe("External Swift-DocC support", () => {
4949
).rejects.toThrow(/not allowlisted/)
5050
})
5151

52+
it("should block local and private hosts unless explicitly allowlisted", async () => {
53+
const blockedHosts = ["127.0.0.1", "10.0.0.1", "192.168.1.1", "172.16.0.1", "[::1]"]
54+
55+
global.fetch = vi.fn()
56+
for (const host of blockedHosts) {
57+
await expect(
58+
assertExternalDocumentationAccess(new URL(`https://${host}/documentation/example`), {}),
59+
).rejects.toThrow(/local or private host/)
60+
}
61+
62+
// Host policy should reject before any robots.txt request is made.
63+
expect(global.fetch).not.toHaveBeenCalled()
64+
})
65+
66+
it("should allow an explicitly allowlisted private host", async () => {
67+
global.fetch = vi
68+
.fn()
69+
.mockResolvedValue(new Response("User-agent: *\nAllow: /", { status: 200 }))
70+
71+
await expect(
72+
assertExternalDocumentationAccess(new URL("https://127.0.0.1/documentation/example"), {
73+
EXTERNAL_DOC_HOST_ALLOWLIST: "127.0.0.1",
74+
}),
75+
).resolves.toBeUndefined()
76+
})
77+
5278
it("should deny when robots.txt disallows", async () => {
5379
global.fetch = vi.fn().mockResolvedValue(
5480
new Response("User-agent: *\nDisallow: /swift-argument-parser/documentation/", {

tests/render.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,7 @@ describe("Render Function", () => {
290290
[
291291
{
292292
type: "paragraph",
293-
inlineContent: [
294-
{ type: "text", text: "The value for the import option." },
295-
],
293+
inlineContent: [{ type: "text", text: "The value for the import option." }],
296294
},
297295
],
298296
],

0 commit comments

Comments
 (0)