Fix reviewer canonical repo mounts

This commit is contained in:
Eyejoker
2026-04-04 22:01:08 +09:00
parent e9c400424d
commit 04802166a6
8 changed files with 404 additions and 37 deletions

View File

@@ -24,6 +24,7 @@ import {
type RoomRoleContext,
} from './room-role-context.js';
import {
assertReadonlyWorkspaceRepoConnectivity,
buildReviewerGitGuardEnv,
isReviewerMutatingShellCommand,
isReviewerRuntime,
@@ -948,7 +949,12 @@ async function main(): Promise<void> {
const reviewerRuntime =
process.env.EJCLAW_REVIEWER_RUNTIME === '1' ||
isReviewerRuntime(containerInput.roomRoleContext);
const readonlyRuntime =
reviewerRuntime ||
process.env.EJCLAW_ARBITER_RUNTIME === '1' ||
containerInput.roomRoleContext?.role === 'arbiter';
const guardedSdkEnv = buildReviewerGitGuardEnv(sdkEnv, reviewerRuntime);
assertReadonlyWorkspaceRepoConnectivity(guardedSdkEnv, readonlyRuntime);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const mcpServerPath = path.join(__dirname, 'ipc-mcp-stdio.js');

View File

@@ -190,6 +190,66 @@ exec "$real_git" "$@"
};
}
function readGitOutput(
args: string[],
baseEnv: NodeJS.ProcessEnv,
cwd: string,
): string {
return execFileSync('git', args, {
cwd,
env: baseEnv,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
}
export function assertReadonlyWorkspaceRepoConnectivity(
baseEnv: NodeJS.ProcessEnv,
enabled: boolean,
): void {
if (!enabled) {
return;
}
const protectedWorkDir = baseEnv.EJCLAW_WORK_DIR || '';
if (!protectedWorkDir) {
return;
}
let originUrl = '';
try {
originUrl = readGitOutput(
['config', '--get', 'remote.origin.url'],
baseEnv,
protectedWorkDir,
);
} catch {
return;
}
if (!path.isAbsolute(originUrl) || !fs.existsSync(originUrl)) {
throw new Error(
`EJClaw readonly runtime cannot access local git origin path: ${originUrl || '(missing)'}`,
);
}
try {
readGitOutput(['rev-parse', '--git-dir'], baseEnv, originUrl);
} catch {
throw new Error(
`EJClaw readonly runtime origin path is not mounted as a git repository: ${originUrl}`,
);
}
try {
readGitOutput(['ls-remote', 'origin', 'HEAD'], baseEnv, protectedWorkDir);
} catch {
throw new Error(
`EJClaw readonly runtime cannot resolve local git origin from ${protectedWorkDir}. Check canonical repo mount for ${originUrl}.`,
);
}
}
export function isReviewerMutatingShellCommand(command: string): boolean {
const normalized = command.trim();
return (

View File

@@ -6,6 +6,7 @@ import path from 'path';
import { describe, expect, it } from 'vitest';
import {
assertReadonlyWorkspaceRepoConnectivity,
buildReviewerGitGuardEnv,
isReviewerMutatingShellCommand,
isReviewerRuntime,
@@ -28,6 +29,17 @@ function createTempRepo(prefix: string): string {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
fs.writeFileSync(path.join(cwd, 'README.md'), 'seed\n');
execFileSync('git', ['add', 'README.md'], {
cwd,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
execFileSync('git', ['commit', '-m', 'seed'], {
cwd,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return cwd;
}
@@ -135,4 +147,47 @@ describe('claude reviewer runtime guard', () => {
);
}
});
it('accepts a mounted local origin path that resolves as a git repo', () => {
const originDir = createTempRepo('ejclaw-reviewer-origin-');
const cwd = createTempRepo('ejclaw-reviewer-workspace-');
execFileSync('git', ['remote', 'add', 'origin', originDir], {
cwd,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
const env = buildReviewerGitGuardEnv(
{
PATH: process.env.PATH,
EJCLAW_WORK_DIR: cwd,
},
true,
);
expect(() => assertReadonlyWorkspaceRepoConnectivity(env, true)).not.toThrow();
});
it('fails fast when the local origin path is not mounted as a git repo', () => {
const cwd = createTempRepo('ejclaw-reviewer-workspace-');
const missingOriginDir = path.join(
os.tmpdir(),
`ejclaw-reviewer-missing-origin-${Date.now()}`,
);
execFileSync('git', ['remote', 'add', 'origin', missingOriginDir], {
cwd,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
const env = buildReviewerGitGuardEnv(
{
PATH: process.env.PATH,
EJCLAW_WORK_DIR: cwd,
},
true,
);
expect(() => assertReadonlyWorkspaceRepoConnectivity(env, true)).toThrow(
`EJClaw readonly runtime cannot access local git origin path: ${missingOriginDir}`,
);
});
});