fix: authorize CLI probes and preserve executable identity
This commit is contained in:
@@ -6,6 +6,7 @@ type BinaryLookup = {
|
||||
env: Record<string, string | undefined>;
|
||||
platform: NodeJS.Platform;
|
||||
homeDir: string;
|
||||
cwd?: string;
|
||||
isExecutable: (path: string) => boolean;
|
||||
};
|
||||
|
||||
@@ -51,7 +52,7 @@ function getPosixSearchSpec(env: BinaryLookup["env"], homeDir: string): SearchSp
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveClaudeBinary({ env, platform, homeDir, isExecutable }: BinaryLookup): string | null {
|
||||
export function resolveClaudeBinary({ env, platform, homeDir, cwd = process.cwd(), isExecutable }: BinaryLookup): string | null {
|
||||
const { paths, names, knownDirs } = platform === "win32"
|
||||
? getWindowsSearchSpec(env, homeDir)
|
||||
: getPosixSearchSpec(env, homeDir);
|
||||
@@ -60,7 +61,9 @@ export function resolveClaudeBinary({ env, platform, homeDir, isExecutable }: Bi
|
||||
|
||||
for (const name of names) {
|
||||
for (const dir of [...pathDirs, ...knownDirs]) {
|
||||
const candidate = paths.join(dir, name);
|
||||
// Discovery is relative to the server cwd; probes/model work use
|
||||
// different directories and must receive the same absolute identity.
|
||||
const candidate = paths.resolve(cwd, dir, name);
|
||||
if (isExecutable(candidate)) return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
import { inspectClaudeInstallation } from "../agent/claude-code/installation.ts";
|
||||
import { initAuthEnv } from "./keys.ts";
|
||||
import { subscriptionRequestOrigin } from "./web-subscription-http.ts";
|
||||
|
||||
/** The Settings status seam: initialize provenance before any child env is copied. */
|
||||
export async function claudeCodeInstallationStatus() {
|
||||
initAuthEnv();
|
||||
return inspectClaudeInstallation();
|
||||
}
|
||||
|
||||
/** Same-origin browser intent, not user authentication. No CORS grants.
|
||||
* A public/shared server still requires its own access-control boundary. */
|
||||
export async function claudeCodeInstallationResponse(
|
||||
request: Request,
|
||||
inspect = claudeCodeInstallationStatus,
|
||||
): Promise<Response> {
|
||||
const origin = subscriptionRequestOrigin(request);
|
||||
const supplied = request.headers.get("origin");
|
||||
const headers = { "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" };
|
||||
if (request.method !== "GET" || !origin
|
||||
|| request.headers.get("x-learnharness-claude-code") !== "1"
|
||||
|| request.headers.get("sec-fetch-site") === "cross-site"
|
||||
|| (supplied !== null && supplied !== origin)) {
|
||||
return Response.json({ error: "Claude Code checks require a same-origin LearnHarness request." }, { status: 403, headers });
|
||||
}
|
||||
return Response.json(await inspect(), { headers });
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,4 +45,4 @@ export * from "./observability.ts";
|
||||
export * from "./model-catalog.ts";
|
||||
export { subscriptionControlsResponse, subscriptionBrowserResponse } from "./web-subscription-http.ts";
|
||||
export { sessionCost } from "./pricing.ts";
|
||||
export { claudeCodeInstallationStatus } from "./claude-code.ts";
|
||||
export { claudeCodeInstallationResponse } from "./claude-code.ts";
|
||||
|
||||
Reference in New Issue
Block a user